@@ -2,7 +2,9 @@ use crate::msrvs::Msrv;
2
2
use crate :: types:: { DisallowedPath , MacroMatcher , MatchLintBehaviour , PubUnderscoreFieldsBehaviour , Rename } ;
3
3
use crate :: ClippyConfiguration ;
4
4
use rustc_data_structures:: fx:: FxHashSet ;
5
+ use rustc_errors:: Applicability ;
5
6
use rustc_session:: Session ;
7
+ use rustc_span:: edit_distance:: edit_distance;
6
8
use rustc_span:: { BytePos , Pos , SourceFile , Span , SyntaxContext } ;
7
9
use serde:: de:: { IgnoredAny , IntoDeserializer , MapAccess , Visitor } ;
8
10
use serde:: { Deserialize , Deserializer , Serialize } ;
@@ -26,7 +28,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
26
28
"NaN" , "NaNs" ,
27
29
"OAuth" , "GraphQL" ,
28
30
"OCaml" ,
29
- "OpenGL" , "OpenMP" , "OpenSSH" , "OpenSSL" , "OpenStreetMap" , "OpenDNS " ,
31
+ "OpenDNS" , " OpenGL", "OpenMP" , "OpenSSH" , "OpenSSL" , "OpenStreetMap" , "OpenTelemetry " ,
30
32
"WebGL" , "WebGL2" , "WebGPU" ,
31
33
"TensorFlow" ,
32
34
"TrueType" ,
@@ -59,18 +61,25 @@ impl TryConf {
59
61
#[ derive( Debug ) ]
60
62
struct ConfError {
61
63
message : String ,
64
+ suggestion : Option < Suggestion > ,
62
65
span : Span ,
63
66
}
64
67
65
68
impl ConfError {
66
69
fn from_toml ( file : & SourceFile , error : & toml:: de:: Error ) -> Self {
67
70
let span = error. span ( ) . unwrap_or ( 0 ..file. source_len . 0 as usize ) ;
68
- Self :: spanned ( file, error. message ( ) , span)
71
+ Self :: spanned ( file, error. message ( ) , None , span)
69
72
}
70
73
71
- fn spanned ( file : & SourceFile , message : impl Into < String > , span : Range < usize > ) -> Self {
74
+ fn spanned (
75
+ file : & SourceFile ,
76
+ message : impl Into < String > ,
77
+ suggestion : Option < Suggestion > ,
78
+ span : Range < usize > ,
79
+ ) -> Self {
72
80
Self {
73
81
message : message. into ( ) ,
82
+ suggestion,
74
83
span : Span :: new (
75
84
file. start_pos + BytePos :: from_usize ( span. start ) ,
76
85
file. start_pos + BytePos :: from_usize ( span. end ) ,
@@ -147,16 +156,18 @@ macro_rules! define_Conf {
147
156
match Field :: deserialize( name. get_ref( ) . as_str( ) . into_deserializer( ) ) {
148
157
Err ( e) => {
149
158
let e: FieldError = e;
150
- errors. push( ConfError :: spanned( self . 0 , e. 0 , name. span( ) ) ) ;
159
+ errors. push( ConfError :: spanned( self . 0 , e. error , e . suggestion , name. span( ) ) ) ;
151
160
}
152
161
$( Ok ( Field :: $name) => {
153
- $( warnings. push( ConfError :: spanned( self . 0 , format!( "deprecated field `{}`. {}" , name. get_ref( ) , $dep) , name. span( ) ) ) ; ) ?
162
+ $( warnings. push( ConfError :: spanned( self . 0 , format!( "deprecated field `{}`. {}" , name. get_ref( ) , $dep) , None , name. span( ) ) ) ; ) ?
154
163
let raw_value = map. next_value:: <toml:: Spanned <toml:: Value >>( ) ?;
155
164
let value_span = raw_value. span( ) ;
156
165
match <$ty>:: deserialize( raw_value. into_inner( ) ) {
157
- Err ( e) => errors. push( ConfError :: spanned( self . 0 , e. to_string( ) . replace( '\n' , " " ) . trim( ) , value_span) ) ,
166
+ Err ( e) => errors. push( ConfError :: spanned( self . 0 , e. to_string( ) . replace( '\n' , " " ) . trim( ) , None , value_span) ) ,
158
167
Ok ( value) => match $name {
159
- Some ( _) => errors. push( ConfError :: spanned( self . 0 , format!( "duplicate field `{}`" , name. get_ref( ) ) , name. span( ) ) ) ,
168
+ Some ( _) => {
169
+ errors. push( ConfError :: spanned( self . 0 , format!( "duplicate field `{}`" , name. get_ref( ) ) , None , name. span( ) ) ) ;
170
+ }
160
171
None => {
161
172
$name = Some ( value) ;
162
173
// $new_conf is the same as one of the defined `$name`s, so
@@ -165,7 +176,7 @@ macro_rules! define_Conf {
165
176
Some ( _) => errors. push( ConfError :: spanned( self . 0 , concat!(
166
177
"duplicate field `" , stringify!( $new_conf) ,
167
178
"` (provided as `" , stringify!( $name) , "`)"
168
- ) , name. span( ) ) ) ,
179
+ ) , None , name. span( ) ) ) ,
169
180
None => $new_conf = $name. clone( ) ,
170
181
} ) ?
171
182
} ,
@@ -523,7 +534,11 @@ define_Conf! {
523
534
///
524
535
/// Additional dotfiles (files or directories starting with a dot) to allow
525
536
( allowed_dotfiles: FxHashSet <String > = FxHashSet :: default ( ) ) ,
526
- /// Lint: EXPLICIT_ITER_LOOP
537
+ /// Lint: MULTIPLE_CRATE_VERSIONS.
538
+ ///
539
+ /// A list of crate names to allow duplicates of
540
+ ( allowed_duplicate_crates: FxHashSet <String > = FxHashSet :: default ( ) ) ,
541
+ /// Lint: EXPLICIT_ITER_LOOP.
527
542
///
528
543
/// Whether to recommend using implicit into iter for reborrowed values.
529
544
///
@@ -543,15 +558,15 @@ define_Conf! {
543
558
/// for _ in &mut *rmvec {}
544
559
/// ```
545
560
( enforce_iter_loop_reborrow: bool = false ) ,
546
- /// Lint: MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC, MISSING_PANICS_DOC, MISSING_ERRORS_DOC
561
+ /// Lint: MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC, MISSING_PANICS_DOC, MISSING_ERRORS_DOC.
547
562
///
548
563
/// Whether to also run the listed lints on private items.
549
564
( check_private_items: bool = false ) ,
550
- /// Lint: PUB_UNDERSCORE_FIELDS
565
+ /// Lint: PUB_UNDERSCORE_FIELDS.
551
566
///
552
567
/// Lint "public" fields in a struct that are prefixed with an underscore based on their
553
568
/// exported visibility, or whether they are marked as "pub".
554
- ( pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour :: PublicallyExported ) ,
569
+ ( pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour :: PubliclyExported ) ,
555
570
}
556
571
557
572
/// Search for the configuration file.
@@ -669,10 +684,16 @@ impl Conf {
669
684
670
685
// all conf errors are non-fatal, we just use the default conf in case of error
671
686
for error in errors {
672
- sess. dcx ( ) . span_err (
687
+ let mut diag = sess. dcx ( ) . struct_span_err (
673
688
error. span ,
674
689
format ! ( "error reading Clippy's configuration file: {}" , error. message) ,
675
690
) ;
691
+
692
+ if let Some ( sugg) = error. suggestion {
693
+ diag. span_suggestion ( error. span , sugg. message , sugg. suggestion , Applicability :: MaybeIncorrect ) ;
694
+ }
695
+
696
+ diag. emit ( ) ;
676
697
}
677
698
678
699
for warning in warnings {
@@ -689,19 +710,31 @@ impl Conf {
689
710
const SEPARATOR_WIDTH : usize = 4 ;
690
711
691
712
#[ derive( Debug ) ]
692
- struct FieldError ( String ) ;
713
+ struct FieldError {
714
+ error : String ,
715
+ suggestion : Option < Suggestion > ,
716
+ }
717
+
718
+ #[ derive( Debug ) ]
719
+ struct Suggestion {
720
+ message : & ' static str ,
721
+ suggestion : & ' static str ,
722
+ }
693
723
694
724
impl std:: error:: Error for FieldError { }
695
725
696
726
impl Display for FieldError {
697
727
fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
698
- f. pad ( & self . 0 )
728
+ f. pad ( & self . error )
699
729
}
700
730
}
701
731
702
732
impl serde:: de:: Error for FieldError {
703
733
fn custom < T : Display > ( msg : T ) -> Self {
704
- Self ( msg. to_string ( ) )
734
+ Self {
735
+ error : msg. to_string ( ) ,
736
+ suggestion : None ,
737
+ }
705
738
}
706
739
707
740
fn unknown_field ( field : & str , expected : & ' static [ & ' static str ] ) -> Self {
@@ -723,7 +756,20 @@ impl serde::de::Error for FieldError {
723
756
write ! ( msg, "{:SEPARATOR_WIDTH$}{field:column_width$}" , " " ) . unwrap ( ) ;
724
757
}
725
758
}
726
- Self ( msg)
759
+
760
+ let suggestion = expected
761
+ . iter ( )
762
+ . filter_map ( |expected| {
763
+ let dist = edit_distance ( field, expected, 4 ) ?;
764
+ Some ( ( dist, expected) )
765
+ } )
766
+ . min_by_key ( |& ( dist, _) | dist)
767
+ . map ( |( _, suggestion) | Suggestion {
768
+ message : "perhaps you meant" ,
769
+ suggestion,
770
+ } ) ;
771
+
772
+ Self { error : msg, suggestion }
727
773
}
728
774
}
729
775
0 commit comments