mir-analyzer 0.78.0

Analysis engine for the mir PHP static analyzer
Documentation
use std::path::{Path, PathBuf};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PerfFixtureKind {
    Laravel,
    Symfony,
}

#[derive(Debug, Clone)]
pub struct PerfFixture {
    kind: PerfFixtureKind,
    root: PathBuf,
}

impl PerfFixture {
    pub fn discover() -> Option<Self> {
        let manifest_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("benches/fixtures");
        let candidates = [
            std::env::var_os("MIR_PERF_FIXTURE").map(PathBuf::from),
            std::env::var_os("MIR_LARAVEL_FIXTURE").map(PathBuf::from),
            std::env::var_os("MIR_SYMFONY_FIXTURE").map(PathBuf::from),
            Some(manifest_root.join("laravel")),
            Some(manifest_root.join("symfony")),
        ];

        for root in candidates.into_iter().flatten() {
            if let Some(kind) = detect_fixture_kind(&root) {
                return Some(Self { kind, root });
            }
        }

        None
    }

    pub fn kind(&self) -> PerfFixtureKind {
        self.kind
    }

    pub fn id(&self) -> &'static str {
        match self.kind {
            PerfFixtureKind::Laravel => "laravel",
            PerfFixtureKind::Symfony => "symfony",
        }
    }

    pub fn label(&self) -> &'static str {
        match self.kind {
            PerfFixtureKind::Laravel => "Laravel",
            PerfFixtureKind::Symfony => "Symfony",
        }
    }

    pub fn root(&self) -> &Path {
        &self.root
    }

    pub fn src_root(&self) -> PathBuf {
        self.root.join("src")
    }

    pub fn vendor_root(&self) -> PathBuf {
        self.root.join("vendor")
    }

    pub fn has_full_corpus(&self) -> bool {
        self.src_root().is_dir() && self.vendor_root().is_dir()
    }

    pub fn open_file(&self) -> PathBuf {
        self.root.join(self.open_file_rel())
    }

    pub fn open_file_label(&self) -> &'static str {
        match self.kind {
            PerfFixtureKind::Laravel => "Login.php",
            PerfFixtureKind::Symfony => "Request.php",
        }
    }

    pub fn open_symbol_probe(&self) -> &'static str {
        match self.kind {
            PerfFixtureKind::Laravel => "SerializesModels",
            PerfFixtureKind::Symfony => "Request",
        }
    }

    pub fn lazy_load_targets(&self) -> &'static [&'static str] {
        match self.kind {
            PerfFixtureKind::Laravel => &[
                "Illuminate\\Foundation\\Application",
                "Illuminate\\Database\\Eloquent\\Model",
                "Illuminate\\Support\\Collection",
                "Illuminate\\Http\\Request",
            ],
            PerfFixtureKind::Symfony => &[
                "Symfony\\Component\\Routing\\Router",
                "Symfony\\Component\\HttpFoundation\\RequestStack",
                "Symfony\\Component\\String\\Slugger\\AsciiSlugger",
                "Symfony\\Component\\String\\TruncateMode",
            ],
        }
    }

    pub fn high_fanout_file(&self) -> PathBuf {
        self.root.join(self.high_fanout_rel())
    }

    pub fn leaf_file(&self) -> PathBuf {
        self.root.join(self.leaf_rel())
    }

    pub fn read_query_target_class(&self) -> &'static str {
        match self.kind {
            PerfFixtureKind::Laravel => "Illuminate\\Database\\Eloquent\\Model",
            PerfFixtureKind::Symfony => "Symfony\\Component\\HttpFoundation\\Request",
        }
    }

    pub fn concurrent_target_class(&self) -> &'static str {
        match self.kind {
            PerfFixtureKind::Laravel => "Illuminate\\Auth\\Events\\Login",
            PerfFixtureKind::Symfony => "Symfony\\Component\\HttpFoundation\\Request",
        }
    }

    pub fn open_file_closure_candidates(&self) -> &'static [&'static str] {
        match self.kind {
            PerfFixtureKind::Laravel => &[
                "src/Illuminate/Database/Eloquent/Builder.php",
                "src/Illuminate/Routing/Router.php",
                "src/Illuminate/Http/Request.php",
            ],
            PerfFixtureKind::Symfony => &[
                "src/Symfony/Component/HttpFoundation/Request.php",
                "src/Symfony/Component/Routing/Router.php",
                "src/Symfony/Component/String/Slugger/AsciiSlugger.php",
            ],
        }
    }

    fn open_file_rel(&self) -> &'static str {
        match self.kind {
            PerfFixtureKind::Laravel => "src/Illuminate/Auth/Events/Login.php",
            PerfFixtureKind::Symfony => "src/Symfony/Component/HttpFoundation/Request.php",
        }
    }

    fn high_fanout_rel(&self) -> &'static str {
        match self.kind {
            PerfFixtureKind::Laravel => "src/Illuminate/Database/Eloquent/Model.php",
            PerfFixtureKind::Symfony => "src/Symfony/Component/HttpFoundation/Request.php",
        }
    }

    fn leaf_rel(&self) -> &'static str {
        match self.kind {
            PerfFixtureKind::Laravel => "src/Illuminate/Auth/Events/Login.php",
            PerfFixtureKind::Symfony => "src/Symfony/Component/String/TruncateMode.php",
        }
    }
}

fn detect_fixture_kind(root: &Path) -> Option<PerfFixtureKind> {
    if is_laravel_fixture(root) {
        return Some(PerfFixtureKind::Laravel);
    }
    if is_symfony_fixture(root) {
        return Some(PerfFixtureKind::Symfony);
    }
    None
}

fn is_laravel_fixture(root: &Path) -> bool {
    root.join("composer.json").is_file()
        && root.join("src/Illuminate/Auth/Events/Login.php").is_file()
        && root
            .join("src/Illuminate/Database/Eloquent/Model.php")
            .is_file()
}

fn is_symfony_fixture(root: &Path) -> bool {
    root.join("composer.json").is_file()
        && root
            .join("src/Symfony/Component/HttpFoundation/Request.php")
            .is_file()
        && root
            .join("src/Symfony/Component/String/TruncateMode.php")
            .is_file()
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;

    fn write_file(root: &Path, rel: &str) {
        let path = root.join(rel);
        if let Some(parent) = path.parent() {
            fs::create_dir_all(parent).expect("create fixture parent");
        }
        fs::write(path, "<?php\n").expect("write fixture file");
    }

    #[test]
    fn detects_minimal_laravel_fixture() {
        let dir = tempfile::tempdir().expect("tempdir");
        write_file(dir.path(), "composer.json");
        write_file(dir.path(), "src/Illuminate/Auth/Events/Login.php");
        write_file(dir.path(), "src/Illuminate/Database/Eloquent/Model.php");

        assert_eq!(
            detect_fixture_kind(dir.path()),
            Some(PerfFixtureKind::Laravel)
        );
    }

    #[test]
    fn detects_minimal_symfony_fixture() {
        let dir = tempfile::tempdir().expect("tempdir");
        write_file(dir.path(), "composer.json");
        write_file(
            dir.path(),
            "src/Symfony/Component/HttpFoundation/Request.php",
        );
        write_file(dir.path(), "src/Symfony/Component/String/TruncateMode.php");

        assert_eq!(
            detect_fixture_kind(dir.path()),
            Some(PerfFixtureKind::Symfony)
        );
    }

    #[test]
    fn preset_paths_match_kind() {
        let laravel = PerfFixture {
            kind: PerfFixtureKind::Laravel,
            root: PathBuf::from("/tmp/laravel"),
        };
        assert_eq!(
            laravel.open_file(),
            PathBuf::from("/tmp/laravel/src/Illuminate/Auth/Events/Login.php")
        );
        assert_eq!(
            laravel.high_fanout_file(),
            PathBuf::from("/tmp/laravel/src/Illuminate/Database/Eloquent/Model.php")
        );

        let symfony = PerfFixture {
            kind: PerfFixtureKind::Symfony,
            root: PathBuf::from("/tmp/symfony"),
        };
        assert_eq!(
            symfony.open_file(),
            PathBuf::from("/tmp/symfony/src/Symfony/Component/HttpFoundation/Request.php")
        );
        assert_eq!(
            symfony.leaf_file(),
            PathBuf::from("/tmp/symfony/src/Symfony/Component/String/TruncateMode.php")
        );
    }
}