Skip to main content

lisette_semantics/passes/lints/
from_facts.rs

1use rustc_hash::FxHashSet as HashSet;
2
3use crate::facts::Facts;
4use diagnostics::LisetteDiagnostic;
5use diagnostics::LocalSink;
6use syntax::program::UnusedInfo;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub enum Lint {
10    UnusedVariable,
11    UnusedParameter,
12    UnusedMut,
13    UnusedImport,
14    UnusedType,
15    UnusedFunction,
16    UnusedConstant,
17    UnusedStructField,
18    UnusedEnumVariant,
19    UnusedLiteral,
20    UnusedResult,
21    UnusedOption,
22    UnusedValue,
23    DeadCodeAfterReturn,
24    DeadCodeAfterBreak,
25    DeadCodeAfterContinue,
26    DeadCodeAfterDivergingIf,
27    DeadCodeAfterDivergingMatch,
28    DeadCodeAfterInfiniteLoop,
29    DeadCodeAfterDivergingCall,
30    DoubleBoolNegation,
31    DoubleIntNegation,
32    SelfComparison,
33    SelfAssignment,
34    MatchLiteralCollection,
35    EmptyMatchArm,
36    InternalTypeLeak,
37    UnnecessaryReference,
38    UnusedTypeParameter,
39    TypeParamOnlyInBound,
40    RestOnlySlicePattern,
41    NonPascalCaseType,
42    NonPascalCaseTypeParameter,
43    NonPascalCaseEnumVariant,
44    NonSnakeCaseFunction,
45    NonSnakeCaseVariable,
46    NonSnakeCaseParameter,
47    NonSnakeCaseStructField,
48    NonScreamingSnakeCaseConstant,
49    RedundantIfLet,
50    RedundantLetElse,
51    SingleArmMatch,
52    RedundantIfLetElse,
53    UnreachableIfLetElse,
54    TryBlockNoSuccessPath,
55    ExcessParensOnCondition,
56    ReplaceableWithZeroFill,
57}
58
59#[derive(Debug, Clone, Default)]
60pub struct LintConfig {
61    disabled: HashSet<Lint>,
62}
63
64impl LintConfig {
65    pub fn is_enabled(&self, lint: Lint) -> bool {
66        !self.disabled.contains(&lint)
67    }
68}
69
70pub(crate) fn run(facts: &Facts, unused: &mut UnusedInfo, sink: &LocalSink) {
71    let mut diagnostics: Vec<LisetteDiagnostic> = Vec::new();
72
73    collect_bindings(facts, unused, &mut diagnostics);
74    collect_dead_code(facts, &mut diagnostics);
75    collect_pattern_issues(facts, &mut diagnostics);
76    collect_unused_expressions(facts, &mut diagnostics);
77    collect_discarded_tail_expressions(facts, &mut diagnostics);
78    collect_overused_references(facts, &mut diagnostics);
79    collect_unused_type_params(facts, &mut diagnostics);
80    collect_type_params_only_in_bound(facts, &mut diagnostics);
81    collect_always_failing_try_blocks(facts, &mut diagnostics);
82    collect_expression_only_fstrings(facts, &mut diagnostics);
83
84    diagnostics.sort_by(LisetteDiagnostic::sort_key);
85    sink.extend(diagnostics);
86}
87
88fn collect_bindings(facts: &Facts, unused: &mut UnusedInfo, out: &mut Vec<LisetteDiagnostic>) {
89    for b in facts.bindings.values() {
90        let is_anon = b.name.starts_with('_');
91        let written_but_not_read = b.kind.is_mutable() && b.mutated && !b.used && !is_anon;
92        let is_write_only_param = written_but_not_read && b.kind.is_param();
93
94        if !b.used && !is_write_only_param {
95            if !is_anon && b.kind.is_param() && !b.is_typedef && b.name != "self" {
96                out.push(diagnostics::lint::unused_parameter(&b.span, &b.name));
97            } else if !written_but_not_read
98                && !is_anon
99                && !b.kind.is_param()
100                && (!b.kind.is_pattern_position() || b.is_as_alias)
101            {
102                out.push(diagnostics::lint::unused_variable(
103                    &b.span,
104                    &b.name,
105                    b.is_struct_field,
106                ));
107            }
108            unused.mark_binding_unused(b.span);
109        }
110
111        if b.kind.is_mutable() && !b.mutated {
112            out.push(diagnostics::lint::unused_mut(&b.span));
113        }
114
115        if written_but_not_read {
116            out.push(diagnostics::lint::written_but_not_read(&b.span, &b.name));
117        }
118    }
119}
120
121fn collect_dead_code(facts: &Facts, out: &mut Vec<LisetteDiagnostic>) {
122    for dc in &facts.dead_code {
123        out.push(diagnostics::lint::dead_code(&dc.span, dc.cause));
124    }
125}
126
127fn collect_pattern_issues(facts: &Facts, out: &mut Vec<LisetteDiagnostic>) {
128    for issue in &facts.pattern_issues {
129        out.push(diagnostics::lint::pattern_issue(&issue.span, issue.kind));
130    }
131}
132
133fn collect_unused_expressions(facts: &Facts, out: &mut Vec<LisetteDiagnostic>) {
134    for fact in &facts.unused_expressions {
135        out.push(diagnostics::lint::unused_expression(&fact.span, fact.kind));
136    }
137}
138
139fn collect_discarded_tail_expressions(facts: &Facts, out: &mut Vec<LisetteDiagnostic>) {
140    for fact in &facts.discarded_tail_expressions {
141        out.push(diagnostics::infer::mismatched_tail_value(
142            &fact.span,
143            &fact.return_type,
144            &fact.expected_span,
145            &fact.expected_type,
146        ));
147    }
148}
149
150fn collect_overused_references(facts: &Facts, out: &mut Vec<LisetteDiagnostic>) {
151    for fact in &facts.overused_references {
152        out.push(diagnostics::lint::unnecessary_reference(
153            &fact.span,
154            fact.name.as_deref(),
155        ));
156    }
157}
158
159fn collect_unused_type_params(facts: &Facts, out: &mut Vec<LisetteDiagnostic>) {
160    for fact in &facts.unused_type_params {
161        out.push(diagnostics::lint::unused_type_parameter(&fact.span));
162    }
163}
164
165fn collect_type_params_only_in_bound(facts: &Facts, out: &mut Vec<LisetteDiagnostic>) {
166    for fact in &facts.type_params_only_in_bound {
167        out.push(diagnostics::lint::type_param_only_in_bound(
168            &fact.span, &fact.name,
169        ));
170    }
171}
172
173fn collect_always_failing_try_blocks(facts: &Facts, out: &mut Vec<LisetteDiagnostic>) {
174    for span in &facts.always_failing_try_blocks {
175        out.push(diagnostics::lint::ineffective_try_block(span));
176    }
177}
178
179fn collect_expression_only_fstrings(facts: &Facts, out: &mut Vec<LisetteDiagnostic>) {
180    for span in &facts.expression_only_fstrings {
181        out.push(diagnostics::lint::expression_only_fstring(span));
182    }
183}