Skip to main content

mago_analyzer/
analysis_result.rs

1#[cfg(not(target_arch = "wasm32"))]
2use std::time::Duration;
3
4use mago_codex::metadata::CodebaseMetadata;
5use mago_codex::reference::SymbolReferences;
6use mago_collector::DeferredPragmas;
7use mago_reporting::IssueCollection;
8
9use crate::code::IssueCode;
10use crate::statement::class_like::unused_members::UnusedMemberSpans;
11use crate::statement::class_like::unused_members::find_unused_member_spans;
12
13#[derive(Clone, Debug)]
14pub struct AnalysisResult {
15    pub issues: IssueCollection,
16    pub symbol_references: SymbolReferences,
17    deferred_pragmas: Vec<DeferredPragmas>,
18    #[cfg(not(target_arch = "wasm32"))]
19    pub time_in_analysis: Duration,
20}
21
22impl AnalysisResult {
23    #[must_use]
24    pub fn new(symbol_references: SymbolReferences) -> Self {
25        Self {
26            issues: IssueCollection::default(),
27            symbol_references,
28            deferred_pragmas: Vec::new(),
29            #[cfg(not(target_arch = "wasm32"))]
30            time_in_analysis: Duration::default(),
31        }
32    }
33
34    pub fn extend(&mut self, other: Self) {
35        self.issues.extend(other.issues);
36        self.symbol_references.extend(other.symbol_references);
37        self.deferred_pragmas.extend(other.deferred_pragmas);
38    }
39
40    pub(crate) fn add_deferred_pragmas(&mut self, pragmas: Option<DeferredPragmas>) {
41        self.deferred_pragmas.extend(pragmas);
42    }
43
44    #[doc(hidden)]
45    pub fn take_deferred_pragmas(&mut self) -> Vec<DeferredPragmas> {
46        std::mem::take(&mut self.deferred_pragmas)
47    }
48}
49
50/// Reconciles unused-member diagnostics against references discovered after file analysis.
51#[derive(Debug)]
52pub struct LateSymbolReferenceIssueReconciler {
53    spans: UnusedMemberSpans,
54}
55
56impl LateSymbolReferenceIssueReconciler {
57    #[must_use]
58    pub fn new(codebase: &CodebaseMetadata, symbol_references: &SymbolReferences) -> Self {
59        Self { spans: find_unused_member_spans(codebase, symbol_references) }
60    }
61
62    #[must_use]
63    pub fn reconcile(&self, issues: IssueCollection) -> IssueCollection {
64        IssueCollection::from(issues.into_iter().filter(|issue| {
65            let Some(code) = issue.code.as_deref() else {
66                return true;
67            };
68
69            let Some(primary) = issue.annotations.iter().find(|annotation| annotation.kind.is_primary()) else {
70                return true;
71            };
72
73            match code {
74                code if code == IssueCode::UnusedMethod.as_str() => self.spans.unused_methods.contains(&primary.span),
75                code if code == IssueCode::UnusedProperty.as_str() => {
76                    self.spans.unused_properties.contains(&primary.span)
77                }
78                code if code == IssueCode::WriteOnlyProperty.as_str() => {
79                    !self.spans.read_properties.contains(&primary.span)
80                }
81                _ => true,
82            }
83        }))
84    }
85}