11use crate :: core:: dependency:: DepKind ;
22use crate :: core:: FeatureValue :: Dep ;
3- use crate :: core:: { Edition , Feature , FeatureValue , Features , Package } ;
3+ use crate :: core:: { Edition , Feature , FeatureValue , Features , Manifest , Package } ;
44use crate :: util:: interning:: InternedString ;
55use crate :: { CargoResult , GlobalContext } ;
66use annotate_snippets:: { Level , Snippet } ;
@@ -12,6 +12,164 @@ use std::ops::Range;
1212use std:: path:: Path ;
1313use toml_edit:: ImDocument ;
1414
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+
15173fn get_span ( document : & ImDocument < String > , path : & [ & str ] , get_value : bool ) -> Option < Range < usize > > {
16174 let mut table = document. as_item ( ) . as_table_like ( ) ?;
17175 let mut iter = path. into_iter ( ) . peekable ( ) ;
@@ -194,6 +352,17 @@ impl Display for LintLevelReason {
194352 }
195353}
196354
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+
197366fn level_priority (
198367 name : & str ,
199368 default_level : LintLevel ,
0 commit comments