1
1
use crate :: core:: dependency:: DepKind ;
2
2
use crate :: core:: FeatureValue :: Dep ;
3
- use crate :: core:: { Edition , Feature , FeatureValue , Features , Package } ;
3
+ use crate :: core:: { Edition , Feature , FeatureValue , Features , Manifest , Package } ;
4
4
use crate :: util:: interning:: InternedString ;
5
5
use crate :: { CargoResult , GlobalContext } ;
6
6
use annotate_snippets:: { Level , Snippet } ;
@@ -12,6 +12,164 @@ 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, _) = level_priority (
38
+ lint. name ,
39
+ lint. default_level ,
40
+ lint. edition_lint_opts ,
41
+ pkg_lints,
42
+ ws_lints,
43
+ manifest. edition ( ) ,
44
+ ) ;
45
+
46
+ // Only run analysis on user-specified lints
47
+ if !reason. is_user_specified ( ) {
48
+ continue ;
49
+ }
50
+
51
+ // Only run this on lints that are gated by a feature
52
+ if let Some ( feature_gate) = lint. feature_gate {
53
+ verify_feature_enabled (
54
+ lint. name ,
55
+ feature_gate,
56
+ reason,
57
+ manifest,
58
+ & manifest_path,
59
+ ws_contents,
60
+ ws_document,
61
+ & ws_path,
62
+ & mut error_count,
63
+ gctx,
64
+ ) ?;
65
+ }
66
+ }
67
+ }
68
+ if error_count > 0 {
69
+ Err ( anyhow:: anyhow!(
70
+ "encountered {error_count} errors(s) while verifying lints" ,
71
+ ) )
72
+ } else {
73
+ Ok ( ( ) )
74
+ }
75
+ }
76
+
77
+ fn verify_feature_enabled (
78
+ lint_name : & str ,
79
+ feature_gate : & Feature ,
80
+ reason : LintLevelReason ,
81
+ manifest : & Manifest ,
82
+ manifest_path : & str ,
83
+ ws_contents : & str ,
84
+ ws_document : & ImDocument < String > ,
85
+ ws_path : & str ,
86
+ error_count : & mut usize ,
87
+ gctx : & GlobalContext ,
88
+ ) -> CargoResult < ( ) > {
89
+ if !manifest. unstable_features ( ) . is_enabled ( feature_gate) {
90
+ let dash_name = lint_name. replace ( "_" , "-" ) ;
91
+ let dash_feature_name = feature_gate. name ( ) . replace ( "_" , "-" ) ;
92
+ let title = format ! ( "use of unstable lint `{}`" , dash_name) ;
93
+ let label = format ! (
94
+ "this is behind `{}`, which is not enabled" ,
95
+ dash_feature_name
96
+ ) ;
97
+ let second_title = format ! ( "`cargo::{}` was inherited" , dash_name) ;
98
+ let help = format ! (
99
+ "consider adding `cargo-features = [\" {}\" ]` to the top of the manifest" ,
100
+ dash_feature_name
101
+ ) ;
102
+ let message = match reason {
103
+ LintLevelReason :: Package => {
104
+ let span = get_span (
105
+ manifest. document ( ) ,
106
+ & [ "lints" , "cargo" , dash_name. as_str ( ) ] ,
107
+ false ,
108
+ )
109
+ . or ( get_span (
110
+ manifest. document ( ) ,
111
+ & [ "lints" , "cargo" , lint_name] ,
112
+ false ,
113
+ ) )
114
+ . unwrap ( ) ;
115
+
116
+ Level :: Error
117
+ . title ( & title)
118
+ . snippet (
119
+ Snippet :: source ( manifest. contents ( ) )
120
+ . origin ( & manifest_path)
121
+ . annotation ( Level :: Error . span ( span) . label ( & label) )
122
+ . fold ( true ) ,
123
+ )
124
+ . footer ( Level :: Help . title ( & help) )
125
+ }
126
+ LintLevelReason :: Workspace => {
127
+ let lint_span = get_span (
128
+ ws_document,
129
+ & [ "workspace" , "lints" , "cargo" , dash_name. as_str ( ) ] ,
130
+ false ,
131
+ )
132
+ . or ( get_span (
133
+ ws_document,
134
+ & [ "workspace" , "lints" , "cargo" , lint_name] ,
135
+ false ,
136
+ ) )
137
+ . unwrap ( ) ;
138
+ let inherit_span_key =
139
+ get_span ( manifest. document ( ) , & [ "lints" , "workspace" ] , false ) . unwrap ( ) ;
140
+ let inherit_span_value =
141
+ get_span ( manifest. document ( ) , & [ "lints" , "workspace" ] , true ) . unwrap ( ) ;
142
+
143
+ Level :: Error
144
+ . title ( & title)
145
+ . snippet (
146
+ Snippet :: source ( ws_contents)
147
+ . origin ( & ws_path)
148
+ . annotation ( Level :: Error . span ( lint_span) . label ( & label) )
149
+ . fold ( true ) ,
150
+ )
151
+ . footer (
152
+ Level :: Note . title ( & second_title) . snippet (
153
+ Snippet :: source ( manifest. contents ( ) )
154
+ . origin ( & manifest_path)
155
+ . annotation (
156
+ Level :: Note
157
+ . span ( inherit_span_key. start ..inherit_span_value. end ) ,
158
+ )
159
+ . fold ( true ) ,
160
+ ) ,
161
+ )
162
+ . footer ( Level :: Help . title ( & help) )
163
+ }
164
+ _ => unreachable ! ( "LintLevelReason should be one that is user specified" ) ,
165
+ } ;
166
+
167
+ * error_count += 1 ;
168
+ gctx. shell ( ) . print_message ( message) ?;
169
+ }
170
+ Ok ( ( ) )
171
+ }
172
+
15
173
fn get_span ( document : & ImDocument < String > , path : & [ & str ] , get_value : bool ) -> Option < Range < usize > > {
16
174
let mut table = document. as_item ( ) . as_table_like ( ) ?;
17
175
let mut iter = path. into_iter ( ) . peekable ( ) ;
@@ -194,6 +352,17 @@ impl Display for LintLevelReason {
194
352
}
195
353
}
196
354
355
+ impl LintLevelReason {
356
+ fn is_user_specified ( & self ) -> bool {
357
+ match self {
358
+ LintLevelReason :: Default => false ,
359
+ LintLevelReason :: Edition ( _) => false ,
360
+ LintLevelReason :: Package => true ,
361
+ LintLevelReason :: Workspace => true ,
362
+ }
363
+ }
364
+ }
365
+
197
366
fn level_priority (
198
367
name : & str ,
199
368
default_level : LintLevel ,
0 commit comments