@@ -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" ,
@@ -380,6 +403,11 @@ impl LintLevelReason {
380
403
}
381
404
}
382
405
406
+ enum SpecifiedIn {
407
+ Package ,
408
+ Workspace ,
409
+ }
410
+
383
411
fn level_priority (
384
412
name : & str ,
385
413
default_level : LintLevel ,
@@ -573,6 +601,120 @@ pub fn check_implicit_features(
573
601
Ok ( ( ) )
574
602
}
575
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
+
576
718
const UNUSED_OPTIONAL_DEPENDENCY : Lint = Lint {
577
719
name : "unused_optional_dependency" ,
578
720
desc : "unused optional dependency" ,
0 commit comments