pmat 3.11.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
// CudaSimdAnalyzer directory traversal, path filtering, and file dispatch

impl CudaSimdAnalyzer {
    /// Directories to skip during analysis (common ignore patterns)
    const IGNORED_DIRS: &'static [&'static str] = &[
        ".venv", "venv", "node_modules", "target", ".git", "__pycache__",
        ".tox", ".nox", "dist", "build", ".eggs", "*.egg-info",
        ".mypy_cache", ".pytest_cache", ".cargo", "vendor",
    ];

    /// Check if a path should be skipped (in an ignored directory)
    fn should_skip_path(path: &Path) -> bool {
        for component in path.components() {
            if let std::path::Component::Normal(name) = component {
                let name_str = name.to_string_lossy();
                for ignored in Self::IGNORED_DIRS {
                    if name_str == *ignored || name_str.ends_with(".egg-info") {
                        return true;
                    }
                }
            }
        }
        false
    }

    fn analyze_directory(
        &self,
        path: &Path,
        defects: &mut Vec<DetectedDefect>,
        cuda_files: &mut usize,
        simd_files: &mut usize,
        wgpu_files: &mut usize,
        files_analyzed: &mut usize,
        barrier_safety: &mut BarrierSafetyResult,
        coalescing: &mut CoalescingResult,
    ) -> anyhow::Result<()> {
        for entry in walkdir::WalkDir::new(path)
            .follow_links(true)
            .into_iter()
            .filter_entry(|e| !Self::should_skip_path(e.path()))
            .filter_map(Result::ok)
        {
            let file_path = entry.path();
            if file_path.is_file() {
                if let Some(ext) = file_path.extension() {
                    let ext_str = ext.to_string_lossy().to_lowercase();
                    if matches!(
                        ext_str.as_str(),
                        "cu" | "cuh" | "ptx" | "rs" | "wgsl" | "c" | "cpp" | "h" | "hpp"
                    ) {
                        if let Ok(analysis) = self.analyze_file(file_path) {
                            *files_analyzed += 1;
                            *cuda_files += analysis.cuda_files;
                            *simd_files += analysis.simd_files;
                            *wgpu_files += analysis.wgpu_files;
                            defects.extend(analysis.defects);
                            barrier_safety.total_barriers += analysis.barrier_safety.total_barriers;
                            barrier_safety.safe_barriers += analysis.barrier_safety.safe_barriers;
                            barrier_safety.unsafe_barriers.extend(analysis.barrier_safety.unsafe_barriers);
                            coalescing.total_operations += analysis.coalescing.total_operations;
                            coalescing.coalesced_operations += analysis.coalescing.coalesced_operations;
                            coalescing.problematic_accesses.extend(analysis.coalescing.problematic_accesses);
                        }
                    }
                }
            }
        }

        if barrier_safety.total_barriers > 0 {
            barrier_safety.safety_score =
                barrier_safety.safe_barriers as f64 / barrier_safety.total_barriers as f64;
        }
        if coalescing.total_operations > 0 {
            coalescing.efficiency =
                coalescing.coalesced_operations as f64 / coalescing.total_operations as f64;
        }

        Ok(())
    }

    fn analyze_file(&self, path: &Path) -> anyhow::Result<FileAnalysis> {
        let content = std::fs::read_to_string(path)?;
        let ext = path
            .extension()
            .map(|e| e.to_string_lossy().to_lowercase())
            .unwrap_or_default();

        let mut analysis = FileAnalysis::default();

        match ext.as_str() {
            "cu" | "cuh" | "ptx" => {
                analysis.cuda_files = 1;
                self.analyze_cuda_content(&content, path, &mut analysis);
            }
            "wgsl" => {
                analysis.wgpu_files = 1;
                self.analyze_wgpu_content(&content, path, &mut analysis);
            }
            "rs" => {
                if content.contains("std::arch::") || content.contains("core::arch::") {
                    analysis.simd_files = 1;
                    self.analyze_simd_content(&content, path, &mut analysis);
                }
                if content.contains("wgpu::")
                    || content.contains("@compute")
                    || content.contains("@workgroup_size")
                {
                    analysis.wgpu_files = 1;
                    self.analyze_wgpu_content(&content, path, &mut analysis);
                }
            }
            "c" | "cpp" | "h" | "hpp" => {
                // Use concat! to avoid self-matching during CB-021 compliance scanning
                if content.contains("immintrin.h")
                    || content.contains(concat!("_mm", "256_"))
                    || content.contains(concat!("_mm", "512_"))
                    || content.contains("arm_neon.h")
                {
                    analysis.simd_files = 1;
                    self.analyze_simd_content(&content, path, &mut analysis);
                }
            }
            _ => {}
        }

        Ok(analysis)
    }
}