33#![ allow( clippy:: module_name_repetitions) ]  
44
55use  rustc_session:: Session ; 
6- use  rustc_span:: { BytePos ,  Pos ,  SourceFile ,  Span ,  SyntaxContext } ; 
6+ use  rustc_span:: { BytePos ,  FileName ,   Pos ,  SourceFile ,  Span ,  SyntaxContext } ; 
77use  serde:: de:: { Deserializer ,  IgnoredAny ,  IntoDeserializer ,  MapAccess ,  Visitor } ; 
88use  serde:: Deserialize ; 
9+ use  std:: error:: Error ; 
910use  std:: fmt:: { Debug ,  Display ,  Formatter } ; 
1011use  std:: ops:: Range ; 
11- use  std:: path:: { Path ,   PathBuf } ; 
12+ use  std:: path:: PathBuf ; 
1213use  std:: str:: FromStr ; 
1314use  std:: { cmp,  env,  fmt,  fs,  io} ; 
1415
@@ -100,38 +101,50 @@ impl From<io::Error> for TryConf {
100101#[ derive( Debug ) ]  
101102pub  struct  ConfError  { 
102103    pub  message :  String , 
104+     pub  file :  Option < PathBuf > , 
103105    pub  span :  Option < Span > , 
104106} 
105107
106108impl  ConfError  { 
107109    fn  from_toml ( file :  & SourceFile ,  error :  & toml:: de:: Error )  -> Self  { 
108110        if  let  Some ( span)  = error. span ( )  { 
109-             Self :: spanned ( file,  error. message ( ) ,  span) 
110-         }  else  { 
111-             Self  { 
112-                 message :  error. message ( ) . to_string ( ) , 
113-                 span :  None , 
114-             } 
111+             return  Self :: spanned ( file,  error. message ( ) ,  span) ; 
112+         }  else  if  let  FileName :: Real ( filename)  = & file. name 
113+             && let  Some ( filename)  = filename. local_path ( ) 
114+         { 
115+                 return  Self  { 
116+                     message :  error. message ( ) . to_string ( ) , 
117+                     file :  Some ( filename. to_owned ( ) ) , 
118+                     span :  None , 
119+                 } ; 
115120        } 
121+ 
122+         unreachable ! ( ) ; 
116123    } 
117124
118125    fn  spanned ( file :  & SourceFile ,  message :  impl  Into < String > ,  span :  Range < usize > )  -> Self  { 
119-         Self  { 
120-             message :  message. into ( ) , 
121-             span :  Some ( Span :: new ( 
122-                 file. start_pos  + BytePos :: from_usize ( span. start ) , 
123-                 file. start_pos  + BytePos :: from_usize ( span. end ) , 
124-                 SyntaxContext :: root ( ) , 
125-                 None , 
126-             ) ) , 
126+         if  let  FileName :: Real ( filename)  = & file. name  && let  Some ( filename)  = filename. local_path ( )  { 
127+             return  Self  { 
128+                 message :  message. into ( ) , 
129+                 file :  Some ( filename. to_owned ( ) ) , 
130+                 span :  Some ( Span :: new ( 
131+                     file. start_pos  + BytePos :: from_usize ( span. start ) , 
132+                     file. start_pos  + BytePos :: from_usize ( span. end ) , 
133+                     SyntaxContext :: root ( ) , 
134+                     None , 
135+                 ) ) , 
136+             } ; 
127137        } 
138+ 
139+         unreachable ! ( ) ; 
128140    } 
129141} 
130142
131143impl  From < io:: Error >  for  ConfError  { 
132144    fn  from ( value :  io:: Error )  -> Self  { 
133145        Self  { 
134146            message :  value. to_string ( ) , 
147+             file :  None , 
135148            span :  None , 
136149        } 
137150    } 
@@ -144,6 +157,7 @@ macro_rules! define_Conf {
144157        ( $name: ident:  $ty: ty = $default: expr) , 
145158    ) * )  => { 
146159        /// Clippy lint configuration 
160+          #[ derive( Deserialize ) ] 
147161        pub  struct  Conf  { 
148162            $( $( #[ doc = $doc] ) + pub  $name:  $ty, ) * 
149163        } 
@@ -158,15 +172,15 @@ macro_rules! define_Conf {
158172            } 
159173        } 
160174
175+         #[ allow( non_camel_case_types) ] 
161176        #[ derive( Deserialize ) ] 
162177        #[ serde( field_identifier,  rename_all = "kebab-case" ) ] 
163-         #[ allow( non_camel_case_types) ] 
164178        enum  Field  {  $( $name, ) *  third_party,  } 
165179
166-         struct  ConfVisitor <' a>( & ' a SourceFile ) ; 
180+         struct  ConfVisitor <' a>( & ' a SourceFile ,   & ' a  mut   TryConf ) ; 
167181
168182        impl <' de> Visitor <' de> for  ConfVisitor <' _> { 
169-             type  Value  = TryConf ; 
183+             type  Value  = ( ) ; 
170184
171185            fn  expecting( & self ,  formatter:  & mut  fmt:: Formatter <' _>)  -> fmt:: Result  { 
172186                formatter. write_str( "Conf" ) 
@@ -210,8 +224,14 @@ macro_rules! define_Conf {
210224                        Ok ( Field :: third_party)  => drop( map. next_value:: <IgnoredAny >( ) ) 
211225                    } 
212226                } 
213-                 let  conf = Conf  {  $( $name:  $name. unwrap_or_else( defaults:: $name) , ) *  } ; 
214-                 Ok ( TryConf  {  conf,  errors,  warnings } ) 
227+                 $( 
228+                     if  let  Some ( $name)  = $name { 
229+                         self . 1 . conf. $name = $name; 
230+                     } 
231+                 ) * 
232+                 self . 1 . errors. extend( errors) ; 
233+                 self . 1 . warnings. extend( warnings) ; 
234+                 Ok ( ( ) ) 
215235            } 
216236        } 
217237
@@ -536,12 +556,17 @@ define_Conf! {
536556     ( min_ident_chars_threshold:  u64  = 1 ) , 
537557} 
538558
539- /// Search for the configuration file. 
559+ /// Search for any configuration files. The index corresponds to the priority; the higher the index, 
560+ /// the lower the priority. 
561+ /// 
562+ /// Note: It's up to the caller to reverse the priority of configuration files, otherwise the last 
563+ /// configuration file will have the highest priority. 
540564/// 
541565/// # Errors 
542566/// 
543- /// Returns any unexpected filesystem error encountered when searching for the config file 
544- pub  fn  lookup_conf_file ( )  -> io:: Result < ( Option < PathBuf > ,  Vec < String > ) >  { 
567+ /// Returns any unexpected filesystem error encountered when searching for the config file or when 
568+ /// running `cargo metadata`. 
569+ pub  fn  lookup_conf_files ( )  -> Result < ( Vec < PathBuf > ,  Vec < String > ) ,  Box < dyn  Error  + Send  + Sync > >  { 
545570    /// Possible filename to search for. 
546571     const  CONFIG_FILE_NAMES :  [ & str ;  2 ]  = [ ".clippy.toml" ,  "clippy.toml" ] ; 
547572
@@ -552,66 +577,74 @@ pub fn lookup_conf_file() -> io::Result<(Option<PathBuf>, Vec<String>)> {
552577        . map_or_else ( || PathBuf :: from ( "." ) ,  PathBuf :: from) 
553578        . canonicalize ( ) ?; 
554579
555-     let  mut  found_config :   Option < PathBuf >  = None ; 
580+     let  mut  found_configs :   Vec < PathBuf >  = vec ! [ ] ; 
556581    let  mut  warnings = vec ! [ ] ; 
557582
583+     // TODO: This will continue searching even outside of the workspace, and even add an erroneous 
584+     // configuration file to the list! Is it worth fixing this? `workspace_root` on `cargo metadata` 
585+     // doesn't work for clippy_lints' clippy.toml in cwd. We likely can't just use cwd as what if 
586+     // it's called in src? 
558587    loop  { 
559588        for  config_file_name in  & CONFIG_FILE_NAMES  { 
560589            if  let  Ok ( config_file)  = current. join ( config_file_name) . canonicalize ( )  { 
561590                match  fs:: metadata ( & config_file)  { 
562591                    Err ( e)  if  e. kind ( )  == io:: ErrorKind :: NotFound  => { } , 
563-                     Err ( e)  => return  Err ( e) , 
592+                     Err ( e)  => return  Err ( e. into ( ) ) , 
564593                    Ok ( md)  if  md. is_dir ( )  => { } , 
565594                    Ok ( _)  => { 
566-                         // warn if we happen to find two config files #8323 
567-                         if  let  Some ( ref  found_config)  = found_config { 
595+                         // Warn if we happen to find two config files #8323 
596+                         if  let  [ ..,  last_config]  = & * found_configs
597+                             && let  Some ( last_config_dir)  = last_config. parent ( ) 
598+                             && let  Some ( config_file_dir)  = config_file. parent ( ) 
599+                             && last_config_dir == config_file_dir
600+                         { 
568601                            warnings. push ( format ! ( 
569602                                "using config file `{}`, `{}` will be ignored" , 
570-                                 found_config . display( ) , 
603+                                 last_config . display( ) , 
571604                                config_file. display( ) 
572605                            ) ) ; 
573606                        }  else  { 
574-                             found_config =  Some ( config_file) ; 
607+                             found_configs . push ( config_file) ; 
575608                        } 
576609                    } , 
577610                } 
578611            } 
579612        } 
580613
581-         if  found_config. is_some ( )  { 
582-             return  Ok ( ( found_config,  warnings) ) ; 
583-         } 
584- 
585-         // If the current directory has no parent, we're done searching. 
586614        if  !current. pop ( )  { 
587-             return   Ok ( ( None ,  warnings ) ) ; 
615+             break ; 
588616        } 
589617    } 
618+ 
619+     Ok ( ( found_configs,  warnings) ) 
590620} 
591621
592622/// Read the `toml` configuration file. 
593623/// 
594624/// In case of error, the function tries to continue as much as possible. 
595- pub  fn  read ( sess :  & Session ,  path :  & Path )  -> TryConf  { 
596-     let  file = match  sess. source_map ( ) . load_file ( path)  { 
597-         Err ( e)  => return  e. into ( ) , 
598-         Ok ( file)  => file, 
599-     } ; 
600-     match  toml:: de:: Deserializer :: new ( file. src . as_ref ( ) . unwrap ( ) ) . deserialize_map ( ConfVisitor ( & file) )  { 
601-         Ok ( mut  conf)  => { 
602-             extend_vec_if_indicator_present ( & mut  conf. conf . doc_valid_idents ,  DEFAULT_DOC_VALID_IDENTS ) ; 
603-             extend_vec_if_indicator_present ( & mut  conf. conf . disallowed_names ,  DEFAULT_DISALLOWED_NAMES ) ; 
604-             // TODO: THIS SHOULD BE TESTED, this comment will be gone soon 
605-             if  conf. conf . allowed_idents_below_min_chars . contains ( & ".." . to_owned ( ) )  { 
606-                 conf. conf 
607-                     . allowed_idents_below_min_chars 
608-                     . extend ( DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS . iter ( ) . map ( ToString :: to_string) ) ; 
609-             } 
610- 
611-             conf
612-         } , 
613-         Err ( e)  => TryConf :: from_toml_error ( & file,  & e) , 
625+ pub  fn  read ( sess :  & Session ,  paths :  & [ PathBuf ] )  -> TryConf  { 
626+     let  mut  conf = TryConf :: default ( ) ; 
627+     for  file in  paths. iter ( ) . rev ( )  { 
628+         let  file = match  sess. source_map ( ) . load_file ( file)  { 
629+             Err ( e)  => return  e. into ( ) , 
630+             Ok ( file)  => file, 
631+         } ; 
632+         match  toml:: de:: Deserializer :: new ( file. src . as_ref ( ) . unwrap ( ) ) . deserialize_map ( ConfVisitor ( & file,  & mut  conf) )  { 
633+             Ok ( _)  => { 
634+                 extend_vec_if_indicator_present ( & mut  conf. conf . doc_valid_idents ,  DEFAULT_DOC_VALID_IDENTS ) ; 
635+                 extend_vec_if_indicator_present ( & mut  conf. conf . disallowed_names ,  DEFAULT_DISALLOWED_NAMES ) ; 
636+                 // TODO: THIS SHOULD BE TESTED, this comment will be gone soon 
637+                 if  conf. conf . allowed_idents_below_min_chars . contains ( & ".." . to_owned ( ) )  { 
638+                     conf. conf 
639+                         . allowed_idents_below_min_chars 
640+                         . extend ( DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS . iter ( ) . map ( ToString :: to_string) ) ; 
641+                 } 
642+             } , 
643+             Err ( e)  => return  TryConf :: from_toml_error ( & file,  & e) , 
644+         } 
614645    } 
646+ 
647+     conf
615648} 
616649
617650fn  extend_vec_if_indicator_present ( vec :  & mut  Vec < String > ,  default :  & [ & str ] )  { 
0 commit comments