@@ -12,6 +12,157 @@ use std::ops::Range;
12
12
use std:: path:: Path ;
13
13
use toml_edit:: ImDocument ;
14
14
15
+ const LINTS : & [ Lint ] = & [ IM_A_TEAPOT , IMPLICIT_FEATURES , UNUSED_OPTIONAL_DEPENDENCY ] ;
16
+
17
+ pub fn analyze_cargo_lints_table (
18
+ pkg : & Package ,
19
+ path : & Path ,
20
+ pkg_lints : & TomlToolLints ,
21
+ ws_lints : Option < & TomlToolLints > ,
22
+ ws_contents : & str ,
23
+ ws_document : & ImDocument < String > ,
24
+ ws_path : & Path ,
25
+ gctx : & GlobalContext ,
26
+ ) -> CargoResult < ( ) > {
27
+ let mut error_count = 0 ;
28
+ let manifest = pkg. manifest ( ) ;
29
+ let manifest_path = rel_cwd_manifest_path ( path, gctx) ;
30
+ let ws_path = rel_cwd_manifest_path ( ws_path, gctx) ;
31
+
32
+ for lint_name in pkg_lints
33
+ . keys ( )
34
+ . chain ( ws_lints. map ( |l| l. keys ( ) ) . unwrap_or_default ( ) )
35
+ {
36
+ if let Some ( lint) = LINTS . iter ( ) . find ( |l| l. name == lint_name) {
37
+ let ( _, reason) = lint. level ( pkg_lints, ws_lints, manifest. edition ( ) ) ;
38
+
39
+ // Only run analysis on user-specified lints
40
+ if !reason. is_user_specified ( ) {
41
+ continue ;
42
+ }
43
+
44
+ // Only run this on lints that are gated by a feature
45
+ if let Some ( feature_gate) = lint. feature_gate {
46
+ verify_feature_enabled (
47
+ lint. name ,
48
+ feature_gate,
49
+ reason,
50
+ manifest,
51
+ & manifest_path,
52
+ ws_contents,
53
+ ws_document,
54
+ & ws_path,
55
+ & mut error_count,
56
+ gctx,
57
+ ) ?;
58
+ }
59
+ }
60
+ }
61
+ if error_count > 0 {
62
+ Err ( anyhow:: anyhow!(
63
+ "encountered {error_count} errors(s) while verifying lints" ,
64
+ ) )
65
+ } else {
66
+ Ok ( ( ) )
67
+ }
68
+ }
69
+
70
+ fn verify_feature_enabled (
71
+ lint_name : & str ,
72
+ feature_gate : & Feature ,
73
+ reason : LintLevelReason ,
74
+ manifest : & Manifest ,
75
+ manifest_path : & str ,
76
+ ws_contents : & str ,
77
+ ws_document : & ImDocument < String > ,
78
+ ws_path : & str ,
79
+ error_count : & mut usize ,
80
+ gctx : & GlobalContext ,
81
+ ) -> CargoResult < ( ) > {
82
+ if !manifest. unstable_features ( ) . is_enabled ( feature_gate) {
83
+ let dash_name = lint_name. replace ( "_" , "-" ) ;
84
+ let dash_feature_name = feature_gate. name ( ) . replace ( "_" , "-" ) ;
85
+ let title = format ! ( "use of unstable lint `{}`" , dash_name) ;
86
+ let label = format ! (
87
+ "this is behind `{}`, which is not enabled" ,
88
+ dash_feature_name
89
+ ) ;
90
+ let second_title = format ! ( "`cargo::{}` was inherited" , dash_name) ;
91
+ let help = format ! (
92
+ "consider adding `cargo-features = [\" {}\" ]` to the top of the manifest" ,
93
+ dash_feature_name
94
+ ) ;
95
+ let message = match reason {
96
+ LintLevelReason :: Package => {
97
+ let span = get_span (
98
+ manifest. document ( ) ,
99
+ & [ "lints" , "cargo" , dash_name. as_str ( ) ] ,
100
+ false ,
101
+ )
102
+ . or ( get_span (
103
+ manifest. document ( ) ,
104
+ & [ "lints" , "cargo" , lint_name] ,
105
+ false ,
106
+ ) )
107
+ . unwrap ( ) ;
108
+
109
+ Level :: Error
110
+ . title ( & title)
111
+ . snippet (
112
+ Snippet :: source ( manifest. contents ( ) )
113
+ . origin ( & manifest_path)
114
+ . annotation ( Level :: Error . span ( span) . label ( & label) )
115
+ . fold ( true ) ,
116
+ )
117
+ . footer ( Level :: Help . title ( & help) )
118
+ }
119
+ LintLevelReason :: Workspace => {
120
+ let lint_span = get_span (
121
+ ws_document,
122
+ & [ "workspace" , "lints" , "cargo" , dash_name. as_str ( ) ] ,
123
+ false ,
124
+ )
125
+ . or ( get_span (
126
+ ws_document,
127
+ & [ "workspace" , "lints" , "cargo" , lint_name] ,
128
+ false ,
129
+ ) )
130
+ . unwrap ( ) ;
131
+ let inherit_span_key =
132
+ get_span ( manifest. document ( ) , & [ "lints" , "workspace" ] , false ) . unwrap ( ) ;
133
+ let inherit_span_value =
134
+ get_span ( manifest. document ( ) , & [ "lints" , "workspace" ] , true ) . unwrap ( ) ;
135
+
136
+ Level :: Error
137
+ . title ( & title)
138
+ . snippet (
139
+ Snippet :: source ( ws_contents)
140
+ . origin ( & ws_path)
141
+ . annotation ( Level :: Error . span ( lint_span) . label ( & label) )
142
+ . fold ( true ) ,
143
+ )
144
+ . footer (
145
+ Level :: Note . title ( & second_title) . snippet (
146
+ Snippet :: source ( manifest. contents ( ) )
147
+ . origin ( & manifest_path)
148
+ . annotation (
149
+ Level :: Note
150
+ . span ( inherit_span_key. start ..inherit_span_value. end ) ,
151
+ )
152
+ . fold ( true ) ,
153
+ ) ,
154
+ )
155
+ . footer ( Level :: Help . title ( & help) )
156
+ }
157
+ _ => unreachable ! ( "LintLevelReason should be one that is user specified" ) ,
158
+ } ;
159
+
160
+ * error_count += 1 ;
161
+ gctx. shell ( ) . print_message ( message) ?;
162
+ }
163
+ Ok ( ( ) )
164
+ }
165
+
15
166
fn get_span ( document : & ImDocument < String > , path : & [ & str ] , get_value : bool ) -> Option < Range < usize > > {
16
167
let mut table = document. as_item ( ) . as_table_like ( ) ?;
17
168
let mut iter = path. into_iter ( ) . peekable ( ) ;
@@ -122,12 +273,6 @@ impl Lint {
122
273
. map ( |( _, ( l, r, _) ) | ( l, r) )
123
274
. unwrap ( )
124
275
}
125
-
126
- /// Returns true if the lint is feature gated and the feature is not enabled
127
- fn is_feature_gated ( & self , manifest : & Manifest ) -> bool {
128
- self . feature_gate
129
- . map_or ( false , |f| !manifest. unstable_features ( ) . is_enabled ( f) )
130
- }
131
276
}
132
277
133
278
#[ derive( Copy , Clone , Debug , PartialEq ) ]
@@ -190,6 +335,17 @@ impl Display for LintLevelReason {
190
335
}
191
336
}
192
337
338
+ impl LintLevelReason {
339
+ fn is_user_specified ( & self ) -> bool {
340
+ match self {
341
+ LintLevelReason :: Default => false ,
342
+ LintLevelReason :: Edition ( _) => false ,
343
+ LintLevelReason :: Package => true ,
344
+ LintLevelReason :: Workspace => true ,
345
+ }
346
+ }
347
+ }
348
+
193
349
fn level_priority (
194
350
name : & str ,
195
351
default_level : LintLevel ,
@@ -249,10 +405,6 @@ pub fn check_im_a_teapot(
249
405
let manifest = pkg. manifest ( ) ;
250
406
let ( lint_level, reason) = IM_A_TEAPOT . level ( pkg_lints, ws_lints, manifest. edition ( ) ) ;
251
407
252
- if IM_A_TEAPOT . is_feature_gated ( manifest) {
253
- return Ok ( ( ) ) ;
254
- }
255
-
256
408
if lint_level == LintLevel :: Allow {
257
409
return Ok ( ( ) ) ;
258
410
}
0 commit comments