@@ -29,45 +29,68 @@ pub fn analyze_cargo_lints_table(
29
29
let manifest = pkg. manifest ( ) ;
30
30
let manifest_path = rel_cwd_manifest_path ( path, gctx) ;
31
31
let ws_path = rel_cwd_manifest_path ( ws_path, gctx) ;
32
-
33
- for lint_name in pkg_lints
32
+ let mut unknown_lints = Vec :: new ( ) ;
33
+ for ( lint_name, specified_in ) in pkg_lints
34
34
. keys ( )
35
- . chain ( ws_lints. map ( |l| l. keys ( ) ) . unwrap_or_default ( ) )
35
+ . map ( |name| ( name, SpecifiedIn :: Package ) )
36
+ . chain (
37
+ ws_lints
38
+ . map ( |l| l. keys ( ) )
39
+ . unwrap_or_default ( )
40
+ . map ( |name| ( name, SpecifiedIn :: Workspace ) ) ,
41
+ )
36
42
{
37
- if let Some ( ( name, default_level, edition_lint_opts, feature_gate) ) =
43
+ let Some ( ( name, default_level, edition_lint_opts, feature_gate) ) =
38
44
find_lint_or_group ( lint_name)
39
- {
40
- let ( _, reason, _) = level_priority (
41
- name,
42
- * default_level,
43
- * edition_lint_opts,
44
- pkg_lints,
45
- ws_lints,
46
- manifest. edition ( ) ,
47
- ) ;
48
-
49
- // Only run analysis on user-specified lints
50
- if !reason. is_user_specified ( ) {
51
- continue ;
52
- }
45
+ else {
46
+ unknown_lints. push ( ( lint_name, specified_in) ) ;
47
+ continue ;
48
+ } ;
53
49
54
- // Only run this on lints that are gated by a feature
55
- if let Some ( feature_gate) = feature_gate {
56
- verify_feature_enabled (
57
- name,
58
- feature_gate,
59
- reason,
60
- manifest,
61
- & manifest_path,
62
- ws_contents,
63
- ws_document,
64
- & ws_path,
65
- & mut error_count,
66
- gctx,
67
- ) ?;
68
- }
50
+ let ( _, reason, _) = level_priority (
51
+ name,
52
+ * default_level,
53
+ * edition_lint_opts,
54
+ pkg_lints,
55
+ ws_lints,
56
+ manifest. edition ( ) ,
57
+ ) ;
58
+
59
+ // Only run analysis on user-specified lints
60
+ if !reason. is_user_specified ( ) {
61
+ continue ;
62
+ }
63
+
64
+ // Only run this on lints that are gated by a feature
65
+ if let Some ( feature_gate) = feature_gate {
66
+ verify_feature_enabled (
67
+ name,
68
+ feature_gate,
69
+ reason,
70
+ manifest,
71
+ & manifest_path,
72
+ ws_contents,
73
+ ws_document,
74
+ & ws_path,
75
+ & mut error_count,
76
+ gctx,
77
+ ) ?;
69
78
}
70
79
}
80
+
81
+ output_unknown_lints (
82
+ unknown_lints,
83
+ manifest,
84
+ & manifest_path,
85
+ pkg_lints,
86
+ ws_lints,
87
+ ws_contents,
88
+ ws_document,
89
+ & ws_path,
90
+ & mut error_count,
91
+ gctx,
92
+ ) ?;
93
+
71
94
if error_count > 0 {
72
95
Err ( anyhow:: anyhow!(
73
96
"encountered {error_count} errors(s) while verifying lints" ,
@@ -117,31 +140,21 @@ fn verify_feature_enabled(
117
140
gctx : & GlobalContext ,
118
141
) -> CargoResult < ( ) > {
119
142
if !manifest. unstable_features ( ) . is_enabled ( feature_gate) {
120
- let dash_name = lint_name. replace ( "_" , "-" ) ;
121
143
let dash_feature_name = feature_gate. name ( ) . replace ( "_" , "-" ) ;
122
- let title = format ! ( "use of unstable lint `{}`" , dash_name ) ;
144
+ let title = format ! ( "use of unstable lint `{}`" , lint_name ) ;
123
145
let label = format ! (
124
146
"this is behind `{}`, which is not enabled" ,
125
147
dash_feature_name
126
148
) ;
127
- let second_title = format ! ( "`cargo::{}` was inherited" , dash_name ) ;
149
+ let second_title = format ! ( "`cargo::{}` was inherited" , lint_name ) ;
128
150
let help = format ! (
129
151
"consider adding `cargo-features = [\" {}\" ]` to the top of the manifest" ,
130
152
dash_feature_name
131
153
) ;
132
154
let message = match reason {
133
155
LintLevelReason :: Package => {
134
- let span = get_span (
135
- manifest. document ( ) ,
136
- & [ "lints" , "cargo" , dash_name. as_str ( ) ] ,
137
- false ,
138
- )
139
- . or ( get_span (
140
- manifest. document ( ) ,
141
- & [ "lints" , "cargo" , lint_name] ,
142
- false ,
143
- ) )
144
- . unwrap ( ) ;
156
+ let span =
157
+ get_span ( manifest. document ( ) , & [ "lints" , "cargo" , lint_name] , false ) . unwrap ( ) ;
145
158
146
159
Level :: Error
147
160
. title ( & title)
@@ -155,15 +168,10 @@ fn verify_feature_enabled(
155
168
}
156
169
LintLevelReason :: Workspace => {
157
170
let lint_span = get_span (
158
- ws_document,
159
- & [ "workspace" , "lints" , "cargo" , dash_name. as_str ( ) ] ,
160
- false ,
161
- )
162
- . or ( get_span (
163
171
ws_document,
164
172
& [ "workspace" , "lints" , "cargo" , lint_name] ,
165
173
false ,
166
- ) )
174
+ )
167
175
. unwrap ( ) ;
168
176
let inherit_span_key =
169
177
get_span ( manifest. document ( ) , & [ "lints" , "workspace" ] , false ) . unwrap ( ) ;
@@ -395,6 +403,11 @@ impl LintLevelReason {
395
403
}
396
404
}
397
405
406
+ enum SpecifiedIn {
407
+ Package ,
408
+ Workspace ,
409
+ }
410
+
398
411
fn level_priority (
399
412
name : & str ,
400
413
default_level : LintLevel ,
@@ -588,6 +601,120 @@ pub fn check_implicit_features(
588
601
Ok ( ( ) )
589
602
}
590
603
604
+ const UNKNOWN_LINTS : Lint = Lint {
605
+ name : "unknown_lints" ,
606
+ desc : "unknown lint" ,
607
+ groups : & [ ] ,
608
+ default_level : LintLevel :: Warn ,
609
+ edition_lint_opts : None ,
610
+ feature_gate : None ,
611
+ } ;
612
+
613
+ fn output_unknown_lints (
614
+ unknown_lints : Vec < ( & String , SpecifiedIn ) > ,
615
+ manifest : & Manifest ,
616
+ manifest_path : & str ,
617
+ pkg_lints : & TomlToolLints ,
618
+ ws_lints : Option < & TomlToolLints > ,
619
+ ws_contents : & str ,
620
+ ws_document : & ImDocument < String > ,
621
+ ws_path : & str ,
622
+ error_count : & mut usize ,
623
+ gctx : & GlobalContext ,
624
+ ) -> CargoResult < ( ) > {
625
+ let ( lint_level, reason) = UNKNOWN_LINTS . level (
626
+ pkg_lints,
627
+ ws_lints,
628
+ manifest. edition ( ) ,
629
+ manifest. unstable_features ( ) ,
630
+ ) ;
631
+ if lint_level == LintLevel :: Allow {
632
+ return Ok ( ( ) ) ;
633
+ }
634
+
635
+ let level = lint_level. to_diagnostic_level ( ) ;
636
+ let mut emitted_source = None ;
637
+ for ( lint_name, specified_in) in unknown_lints {
638
+ if lint_level == LintLevel :: Forbid || lint_level == LintLevel :: Deny {
639
+ * error_count += 1 ;
640
+ }
641
+ let title = format ! ( "{}: `{lint_name}`" , UNKNOWN_LINTS . desc) ;
642
+ let second_title = format ! ( "`cargo::{}` was inherited" , lint_name) ;
643
+ let underscore_lint_name = lint_name. replace ( "-" , "_" ) ;
644
+ let matching = if let Some ( lint) = LINTS . iter ( ) . find ( |l| l. name == underscore_lint_name) {
645
+ Some ( ( lint. name , "lint" ) )
646
+ } else if let Some ( group) = LINT_GROUPS . iter ( ) . find ( |g| g. name == underscore_lint_name) {
647
+ Some ( ( group. name , "group" ) )
648
+ } else {
649
+ None
650
+ } ;
651
+ let help =
652
+ matching. map ( |( name, kind) | format ! ( "there is a {kind} with a similar name: `{name}`" ) ) ;
653
+
654
+ let mut message = match specified_in {
655
+ SpecifiedIn :: Package => {
656
+ let span =
657
+ get_span ( manifest. document ( ) , & [ "lints" , "cargo" , lint_name] , false ) . unwrap ( ) ;
658
+
659
+ level. title ( & title) . snippet (
660
+ Snippet :: source ( manifest. contents ( ) )
661
+ . origin ( & manifest_path)
662
+ . annotation ( Level :: Error . span ( span) )
663
+ . fold ( true ) ,
664
+ )
665
+ }
666
+ SpecifiedIn :: Workspace => {
667
+ let lint_span = get_span (
668
+ ws_document,
669
+ & [ "workspace" , "lints" , "cargo" , lint_name] ,
670
+ false ,
671
+ )
672
+ . unwrap ( ) ;
673
+ let inherit_span_key =
674
+ get_span ( manifest. document ( ) , & [ "lints" , "workspace" ] , false ) . unwrap ( ) ;
675
+ let inherit_span_value =
676
+ get_span ( manifest. document ( ) , & [ "lints" , "workspace" ] , true ) . unwrap ( ) ;
677
+
678
+ level
679
+ . title ( & title)
680
+ . snippet (
681
+ Snippet :: source ( ws_contents)
682
+ . origin ( & ws_path)
683
+ . annotation ( Level :: Error . span ( lint_span) )
684
+ . fold ( true ) ,
685
+ )
686
+ . footer (
687
+ Level :: Note . title ( & second_title) . snippet (
688
+ Snippet :: source ( manifest. contents ( ) )
689
+ . origin ( & manifest_path)
690
+ . annotation (
691
+ Level :: Note
692
+ . span ( inherit_span_key. start ..inherit_span_value. end ) ,
693
+ )
694
+ . fold ( true ) ,
695
+ ) ,
696
+ )
697
+ }
698
+ } ;
699
+
700
+ if emitted_source. is_none ( ) {
701
+ emitted_source = Some ( format ! (
702
+ "`cargo::{}` is set to `{lint_level}` {reason}" ,
703
+ UNKNOWN_LINTS . name
704
+ ) ) ;
705
+ message = message. footer ( Level :: Note . title ( emitted_source. as_ref ( ) . unwrap ( ) ) ) ;
706
+ }
707
+
708
+ if let Some ( help) = help. as_ref ( ) {
709
+ message = message. footer ( Level :: Help . title ( help) ) ;
710
+ }
711
+
712
+ gctx. shell ( ) . print_message ( message) ?;
713
+ }
714
+
715
+ Ok ( ( ) )
716
+ }
717
+
591
718
const UNUSED_OPTIONAL_DEPENDENCY : Lint = Lint {
592
719
name : "unused_optional_dependency" ,
593
720
desc : "unused optional dependency" ,
0 commit comments