mir-analyzer 0.45.0

Analysis engine for the mir PHP static analyzer
Documentation
use super::*;

impl<'a> BodyAnalyzer<'a> {
    /// body analysis: walk all function/method bodies in one file, return issues, and
    /// write inferred return types back to the codebase.
    pub(crate) fn analyze_bodies(
        &self,
        program: &php_ast::owned::Program,
        file: Arc<str>,
        source: &str,
        source_map: &php_rs_parser::source_map::SourceMap,
    ) -> (Vec<Issue>, Vec<ResolvedSymbol>) {
        let mut all_issues = Vec::new();
        let mut all_symbols = Vec::new();

        if self.mode == AnalysisMode::Full {
            check_duplicate_declarations(
                &program.stmts,
                &file,
                source,
                source_map,
                &mut all_issues,
            );
        }

        self.analyze_top_level_stmts(
            &program.stmts,
            &file,
            source,
            source_map,
            &mut all_issues,
            &mut all_symbols,
        );

        // Analyze top-level executable statements in global scope. The
        // inference-only sweep only primes function/method return types; top-
        // level diagnostics and references are produced by the main sweep.
        self.analyze_global_exec(
            program,
            &file,
            source,
            source_map,
            &mut all_issues,
            &mut all_symbols,
        );

        (all_issues, all_symbols)
    }

    /// Analyze top-level executable statements in global scope (Full mode
    /// only). Extracted from [`Self::analyze_bodies`] so the per-scope
    /// tracked query can run it as its own scope.
    pub(crate) fn analyze_global_exec(
        &self,
        program: &php_ast::owned::Program,
        file: &Arc<str>,
        source: &str,
        source_map: &php_rs_parser::source_map::SourceMap,
        all_issues: &mut Vec<Issue>,
        all_symbols: &mut Vec<ResolvedSymbol>,
    ) {
        use php_ast::owned::StmtKind;
        if self.mode != AnalysisMode::Full {
            return;
        }
        use crate::flow_state::FlowState;
        use crate::stmt::StatementsAnalyzer;
        use mir_issues::IssueBuffer;

        let mut ctx = FlowState::new();
        let mut buf = IssueBuffer::new();
        let mut sa = StatementsAnalyzer::new(
            self.db,
            file.clone(),
            source,
            source_map,
            &mut buf,
            all_symbols,
            self.php_version,
            self.mode,
        );
        for stmt in program.stmts.iter() {
            match &stmt.kind {
                StmtKind::Function(_)
                | StmtKind::Class(_)
                | StmtKind::Enum(_)
                | StmtKind::Interface(_)
                | StmtKind::Trait(_)
                | StmtKind::Namespace(_)
                | StmtKind::Use(_) => {}
                // Process Declare so that `declare(strict_types=1)` updates
                // ctx.strict_types before later executable stmts are analyzed.
                _ => {
                    sa.analyze_stmt(stmt, &mut ctx);
                }
            }
        }
        drop(sa);
        crate::diagnostics::emit_unused_variables(&ctx, file, all_issues);
        all_issues.extend(buf.into_all_issues());
    }

    /// Like `analyze_bodies` but also populates `type_envs` with per-scope type environments.
    pub(crate) fn analyze_bodies_typed(
        &self,
        program: &php_ast::owned::Program,
        file: Arc<str>,
        source: &str,
        source_map: &php_rs_parser::source_map::SourceMap,
        type_envs: &mut FxHashMap<crate::type_env::ScopeId, crate::type_env::TypeEnv>,
        all_symbols: &mut Vec<ResolvedSymbol>,
    ) -> Vec<Issue> {
        use php_ast::owned::StmtKind;
        let mut all_issues = Vec::new();
        self.analyze_top_level_stmts_typed(
            &program.stmts,
            &file,
            source,
            source_map,
            &mut all_issues,
            type_envs,
            all_symbols,
        );

        // Analyze top-level executable statements in global scope.
        {
            use crate::flow_state::FlowState;
            use crate::stmt::StatementsAnalyzer;
            use mir_issues::IssueBuffer;

            let mut ctx = FlowState::new();
            let mut buf = IssueBuffer::new();
            let mut sa = StatementsAnalyzer::new(
                self.db,
                file.clone(),
                source,
                source_map,
                &mut buf,
                all_symbols,
                self.php_version,
                self.mode,
            );
            for stmt in program.stmts.iter() {
                match &stmt.kind {
                    StmtKind::Function(_)
                    | StmtKind::Class(_)
                    | StmtKind::Enum(_)
                    | StmtKind::Interface(_)
                    | StmtKind::Trait(_)
                    | StmtKind::Namespace(_)
                    | StmtKind::Use(_) => {}
                    _ => {
                        sa.analyze_stmt(stmt, &mut ctx);
                    }
                }
            }
            drop(sa);
            crate::diagnostics::emit_unused_variables(&ctx, &file, &mut all_issues);
            all_issues.extend(buf.into_all_issues());
        }

        all_issues
    }

    fn analyze_top_level_stmts(
        &self,
        stmts: &[php_ast::owned::Stmt],
        file: &Arc<str>,
        source: &str,
        source_map: &php_rs_parser::source_map::SourceMap,
        all_issues: &mut Vec<Issue>,
        all_symbols: &mut Vec<ResolvedSymbol>,
    ) {
        use php_ast::owned::StmtKind;
        for stmt in stmts.iter() {
            match &stmt.kind {
                StmtKind::Function(decl) => {
                    self.analyze_fn_decl(decl, file, source, source_map, all_issues, all_symbols);
                }
                StmtKind::Class(decl) => {
                    self.analyze_class_decl(
                        decl,
                        file,
                        source,
                        source_map,
                        all_issues,
                        all_symbols,
                    );
                }
                StmtKind::Enum(decl) => {
                    self.analyze_enum_decl(decl, file, source, source_map, all_issues, all_symbols);
                }
                StmtKind::Interface(decl) => {
                    self.analyze_interface_decl(decl, file, source, source_map, all_issues);
                }
                StmtKind::Trait(decl) => {
                    self.analyze_trait_decl(
                        decl,
                        file,
                        source,
                        source_map,
                        all_issues,
                        all_symbols,
                    );
                }
                StmtKind::Namespace(ns) => {
                    if let php_ast::owned::NamespaceBody::Braced(inner) = &ns.body {
                        self.analyze_top_level_stmts(
                            &inner.stmts,
                            file,
                            source,
                            source_map,
                            all_issues,
                            all_symbols,
                        );
                    }
                }
                StmtKind::Use(use_decl) => {
                    check_use_decl_casing(use_decl, self.db, file, source, source_map, all_issues);
                }
                _ => {}
            }
        }
    }

    #[allow(clippy::too_many_arguments)]
    fn analyze_top_level_stmts_typed(
        &self,
        stmts: &[php_ast::owned::Stmt],
        file: &Arc<str>,
        source: &str,
        source_map: &php_rs_parser::source_map::SourceMap,
        all_issues: &mut Vec<Issue>,
        type_envs: &mut rustc_hash::FxHashMap<crate::type_env::ScopeId, crate::type_env::TypeEnv>,
        all_symbols: &mut Vec<ResolvedSymbol>,
    ) {
        use php_ast::owned::StmtKind;
        for stmt in stmts.iter() {
            match &stmt.kind {
                StmtKind::Function(decl) => {
                    self.analyze_fn_decl_typed(
                        decl,
                        file,
                        source,
                        source_map,
                        all_issues,
                        type_envs,
                        all_symbols,
                    );
                }
                StmtKind::Class(decl) => {
                    self.analyze_class_decl_typed(
                        decl,
                        file,
                        source,
                        source_map,
                        all_issues,
                        type_envs,
                        all_symbols,
                    );
                }
                StmtKind::Enum(decl) => {
                    self.analyze_enum_decl_typed(
                        decl,
                        file,
                        source,
                        source_map,
                        all_issues,
                        type_envs,
                        all_symbols,
                    );
                }
                StmtKind::Interface(decl) => {
                    self.analyze_interface_decl(decl, file, source, source_map, all_issues);
                }
                StmtKind::Trait(decl) => {
                    self.analyze_trait_decl_typed(
                        decl,
                        file,
                        source,
                        source_map,
                        all_issues,
                        type_envs,
                        all_symbols,
                    );
                }
                StmtKind::Namespace(ns) => {
                    if let php_ast::owned::NamespaceBody::Braced(inner) = &ns.body {
                        self.analyze_top_level_stmts_typed(
                            &inner.stmts,
                            file,
                            source,
                            source_map,
                            all_issues,
                            type_envs,
                            all_symbols,
                        );
                    }
                }
                StmtKind::Use(use_decl) => {
                    check_use_decl_casing(use_decl, self.db, file, source, source_map, all_issues);
                }
                _ => {}
            }
        }
    }
}