| use std::path::Path; | |
| use cargo_util_schemas::manifest; | |
| use crate::CargoResult; | |
| use crate::GlobalContext; | |
| use crate::diagnostics::GlobalDiagnosticStats; | |
| use crate::diagnostics::Lint; | |
| use crate::diagnostics::LintLevel; | |
| use crate::diagnostics::LintLevelProduct; | |
| use crate::diagnostics::ManifestFor; | |
| use crate::diagnostics::PassOutput; | |
| use crate::diagnostics::ScopedDiagnosticStats; | |
| use crate::workspace::MaybePackage; | |
| use crate::workspace::Package; | |
| use crate::workspace::Workspace; | |
| #[derive(Clone)] | |
| pub enum ParsePassRule<'r> { | |
| DiagnosticManifest { | |
| rule: FnDiagnosticManifest, | |
| }, | |
| LintManifest { | |
| rule: FnLintManifest, | |
| lint: &'r Lint, | |
| }, | |
| DiagnosticWorkspace { | |
| rule: FnDiagnosticWorkspace, | |
| }, | |
| LintWorkspace { | |
| rule: FnLintWorkspace, | |
| lint: &'r Lint, | |
| }, | |
| DiagnosticPackage { | |
| rule: FnDiagnosticPackage, | |
| }, | |
| LintPackage { | |
| rule: FnLintPackage, | |
| lint: &'r Lint, | |
| }, | |
| } | |
| type FnDiagnosticManifest = fn( | |
| &Workspace<'_>, | |
| ManifestFor<'_>, | |
| &Path, | |
| &mut ScopedDiagnosticStats<'_>, | |
| &GlobalContext, | |
| ) -> CargoResult<()>; | |
| type FnDiagnosticWorkspace = fn( | |
| &Workspace<'_>, | |
| &MaybePackage, | |
| &Path, | |
| &mut ScopedDiagnosticStats<'_>, | |
| &GlobalContext, | |
| ) -> CargoResult<()>; | |
| type FnDiagnosticPackage = fn( | |
| &Workspace<'_>, | |
| &Package, | |
| &Path, | |
| &mut ScopedDiagnosticStats<'_>, | |
| &GlobalContext, | |
| ) -> CargoResult<()>; | |
| type FnLintManifest = fn( | |
| &Workspace<'_>, | |
| manifest: ManifestFor<'_>, | |
| manifest_path: &Path, | |
| LintLevelProduct, | |
| stats: &mut ScopedDiagnosticStats<'_>, | |
| gctx: &GlobalContext, | |
| ) -> CargoResult<()>; | |
| type FnLintWorkspace = fn( | |
| &Workspace<'_>, | |
| &MaybePackage, | |
| &Path, | |
| LintLevelProduct, | |
| &mut ScopedDiagnosticStats<'_>, | |
| &GlobalContext, | |
| ) -> CargoResult<()>; | |
| type FnLintPackage = fn( | |
| &Workspace<'_>, | |
| &Package, | |
| &Path, | |
| LintLevelProduct, | |
| &mut ScopedDiagnosticStats<'_>, | |
| &GlobalContext, | |
| ) -> CargoResult<()>; | |
| pub fn emit_parse_diagnostics( | |
| workspace: &Workspace<'_>, | |
| rules: &[ParsePassRule<'_>], | |
| ) -> CargoResult<PassOutput> { | |
| let mut stats = GlobalDiagnosticStats::new(); | |
| if is_local_workspace(workspace) { | |
| emit_parse_ws_diagnostics(workspace, rules, &mut stats)?; | |
| } | |
| for maybe_pkg in workspace.loaded_maybe() { | |
| if let MaybePackage::Package(pkg) = maybe_pkg { | |
| if is_local_package(pkg) { | |
| let path = pkg.manifest_path(); | |
| emit_parse_pkg_diagnostics(workspace, pkg, &path, rules, &mut stats)?; | |
| } | |
| } | |
| } | |
| stats.ok() | |
| } | |
| fn is_local_workspace(workspace: &Workspace<'_>) -> bool { | |
| workspace | |
| .root_maybe() | |
| .as_package() | |
| .map(is_local_package) | |
| .unwrap_or_else(|| workspace.members().any(is_local_package)) | |
| } | |
| fn is_local_package(pkg: &Package) -> bool { | |
| pkg.package_id().source_id().is_path() | |
| } | |
| fn emit_parse_pkg_diagnostics( | |
| workspace: &Workspace<'_>, | |
| pkg: &Package, | |
| path: &Path, | |
| rules: &[ParsePassRule<'_>], | |
| global_stats: &mut GlobalDiagnosticStats, | |
| ) -> CargoResult<()> { | |
| let mut pkg_stats = global_stats.scope(); | |
| let toml_lints = pkg | |
| .manifest() | |
| .normalized_toml() | |
| .lints | |
| .clone() | |
| .map(|lints| lints.lints) | |
| .unwrap_or(manifest::TomlLints::default()); | |
| let cargo_lints = toml_lints | |
| .get("cargo") | |
| .cloned() | |
| .unwrap_or(manifest::TomlToolLints::default()); | |
| for rule in rules { | |
| match rule { | |
| ParsePassRule::DiagnosticManifest { rule } => { | |
| let manifest = pkg.into(); | |
| rule(workspace, manifest, &path, &mut pkg_stats, workspace.gctx())?; | |
| } | |
| ParsePassRule::LintManifest { rule, lint } => { | |
| let manifest: ManifestFor<'_> = pkg.into(); | |
| let level = manifest.lint_level(&cargo_lints, lint, workspace.gctx()); | |
| if level.level != LintLevel::Allow { | |
| rule( | |
| workspace, | |
| manifest, | |
| &path, | |
| level, | |
| &mut pkg_stats, | |
| workspace.gctx(), | |
| )?; | |
| } | |
| } | |
| ParsePassRule::DiagnosticWorkspace { .. } | ParsePassRule::LintWorkspace { .. } => {} | |
| ParsePassRule::DiagnosticPackage { rule } => { | |
| rule(workspace, pkg, &path, &mut pkg_stats, workspace.gctx())?; | |
| } | |
| ParsePassRule::LintPackage { rule, lint } => { | |
| let level = lint.level( | |
| &cargo_lints, | |
| pkg.rust_version(), | |
| pkg.manifest().unstable_features(), | |
| workspace.gctx(), | |
| ); | |
| if level.level != LintLevel::Allow { | |
| rule( | |
| workspace, | |
| pkg, | |
| &path, | |
| level, | |
| &mut pkg_stats, | |
| workspace.gctx(), | |
| )?; | |
| } | |
| } | |
| } | |
| } | |
| pkg_stats.report_summary("parse", Some(&*pkg.name()), workspace.gctx())?; | |
| Ok(()) | |
| } | |
| fn emit_parse_ws_diagnostics( | |
| workspace: &Workspace<'_>, | |
| rules: &[ParsePassRule<'_>], | |
| global_stats: &mut GlobalDiagnosticStats, | |
| ) -> CargoResult<()> { | |
| let mut pkg_stats = global_stats.scope(); | |
| let cargo_lints = match workspace.root_maybe() { | |
| MaybePackage::Package(pkg) => { | |
| let toml = pkg.manifest().normalized_toml(); | |
| if let Some(ws) = &toml.workspace { | |
| ws.lints.as_ref() | |
| } else { | |
| toml.lints.as_ref().map(|l| &l.lints) | |
| } | |
| } | |
| MaybePackage::Virtual(vm) => vm | |
| .normalized_toml() | |
| .workspace | |
| .as_ref() | |
| .unwrap() | |
| .lints | |
| .as_ref(), | |
| } | |
| .and_then(|t| t.get("cargo")) | |
| .cloned() | |
| .unwrap_or(manifest::TomlToolLints::default()); | |
| for rule in rules { | |
| match rule { | |
| ParsePassRule::DiagnosticManifest { rule } => { | |
| let manifest = (workspace, workspace.root_maybe()).into(); | |
| rule( | |
| workspace, | |
| manifest, | |
| workspace.root_manifest(), | |
| &mut pkg_stats, | |
| workspace.gctx(), | |
| )?; | |
| } | |
| ParsePassRule::LintManifest { rule, lint } => { | |
| let manifest: ManifestFor<'_> = (workspace, workspace.root_maybe()).into(); | |
| let level = manifest.lint_level(&cargo_lints, lint, workspace.gctx()); | |
| if level.level != LintLevel::Allow { | |
| rule( | |
| workspace, | |
| manifest, | |
| workspace.root_manifest(), | |
| level, | |
| &mut pkg_stats, | |
| workspace.gctx(), | |
| )?; | |
| } | |
| } | |
| ParsePassRule::DiagnosticWorkspace { rule } => { | |
| rule( | |
| workspace, | |
| workspace.root_maybe(), | |
| workspace.root_manifest(), | |
| &mut pkg_stats, | |
| workspace.gctx(), | |
| )?; | |
| } | |
| ParsePassRule::LintWorkspace { rule, lint } => { | |
| let level = lint.level( | |
| &cargo_lints, | |
| workspace.lowest_rust_version(), | |
| workspace.root_maybe().unstable_features(), | |
| workspace.gctx(), | |
| ); | |
| if level.level != LintLevel::Allow { | |
| rule( | |
| workspace, | |
| workspace.root_maybe(), | |
| workspace.root_manifest(), | |
| level, | |
| &mut pkg_stats, | |
| workspace.gctx(), | |
| )?; | |
| } | |
| } | |
| ParsePassRule::DiagnosticPackage { .. } | ParsePassRule::LintPackage { .. } => {} | |
| } | |
| } | |
| pkg_stats.report_summary("parse", None, workspace.gctx())?; | |
| Ok(()) | |
| } |