@@ -29,45 +29,68 @@ pub fn analyze_cargo_lints_table(
2929 let manifest = pkg. manifest ( ) ;
3030 let manifest_path = rel_cwd_manifest_path ( path, gctx) ;
3131 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
3434 . 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+ )
3642 {
37- if let Some ( ( name, default_level, edition_lint_opts, feature_gate) ) =
43+ let Some ( ( name, default_level, edition_lint_opts, feature_gate) ) =
3844 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+ } ;
5349
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+ ) ?;
6978 }
7079 }
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+
7194 if error_count > 0 {
7295 Err ( anyhow:: anyhow!(
7396 "encountered {error_count} errors(s) while verifying lints" ,
@@ -380,6 +403,11 @@ impl LintLevelReason {
380403 }
381404}
382405
406+ enum SpecifiedIn {
407+ Package ,
408+ Workspace ,
409+ }
410+
383411fn level_priority (
384412 name : & str ,
385413 default_level : LintLevel ,
@@ -573,6 +601,120 @@ pub fn check_implicit_features(
573601 Ok ( ( ) )
574602}
575603
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+
576718const UNUSED_OPTIONAL_DEPENDENCY : Lint = Lint {
577719 name : "unused_optional_dependency" ,
578720 desc : "unused optional dependency" ,
0 commit comments