22
33#![ allow( clippy:: module_name_repetitions) ]  
44
5+ use  rustc_session:: Session ; 
6+ use  rustc_span:: { BytePos ,  Pos ,  SourceFile ,  Span ,  SyntaxContext } ; 
57use  serde:: de:: { Deserializer ,  IgnoredAny ,  IntoDeserializer ,  MapAccess ,  Visitor } ; 
68use  serde:: Deserialize ; 
7- use  std:: error:: Error ; 
9+ use  std:: fmt:: { Debug ,  Display ,  Formatter } ; 
10+ use  std:: ops:: Range ; 
811use  std:: path:: { Path ,  PathBuf } ; 
912use  std:: str:: FromStr ; 
10- use  std:: { cmp,  env,  fmt,  fs,  io,  iter } ; 
13+ use  std:: { cmp,  env,  fmt,  fs,  io} ; 
1114
1215#[ rustfmt:: skip]  
1316const  DEFAULT_DOC_VALID_IDENTS :  & [ & str ]  = & [ 
@@ -67,33 +70,70 @@ impl DisallowedPath {
6770#[ derive( Default ) ]  
6871pub  struct  TryConf  { 
6972    pub  conf :  Conf , 
70-     pub  errors :  Vec < Box < dyn   Error > > , 
71-     pub  warnings :  Vec < Box < dyn   Error > > , 
73+     pub  errors :  Vec < ConfError > , 
74+     pub  warnings :  Vec < ConfError > , 
7275} 
7376
7477impl  TryConf  { 
75-     fn  from_error ( error :  impl  Error  + ' static )  -> Self  { 
78+     fn  from_toml_error ( file :  & SourceFile ,  error :  & toml:: de:: Error )  -> Self  { 
79+         ConfError :: from_toml ( file,  error) . into ( ) 
80+     } 
81+ } 
82+ 
83+ impl  From < ConfError >  for  TryConf  { 
84+     fn  from ( value :  ConfError )  -> Self  { 
7685        Self  { 
7786            conf :  Conf :: default ( ) , 
78-             errors :  vec ! [ Box :: new ( error ) ] , 
87+             errors :  vec ! [ value ] , 
7988            warnings :  vec ! [ ] , 
8089        } 
8190    } 
8291} 
8392
93+ impl  From < io:: Error >  for  TryConf  { 
94+     fn  from ( value :  io:: Error )  -> Self  { 
95+         ConfError :: from ( value) . into ( ) 
96+     } 
97+ } 
98+ 
8499#[ derive( Debug ) ]  
85- struct  ConfError ( String ) ; 
100+ pub  struct  ConfError  { 
101+     pub  message :  String , 
102+     pub  span :  Option < Span > , 
103+ } 
104+ 
105+ impl  ConfError  { 
106+     fn  from_toml ( file :  & SourceFile ,  error :  & toml:: de:: Error )  -> Self  { 
107+         if  let  Some ( span)  = error. span ( )  { 
108+             Self :: spanned ( file,  error. message ( ) ,  span) 
109+         }  else  { 
110+             Self  { 
111+                 message :  error. message ( ) . to_string ( ) , 
112+                 span :  None , 
113+             } 
114+         } 
115+     } 
86116
87- impl  fmt:: Display  for  ConfError  { 
88-     fn  fmt ( & self ,  f :  & mut  fmt:: Formatter < ' _ > )  -> fmt:: Result  { 
89-         <String  as  fmt:: Display >:: fmt ( & self . 0 ,  f) 
117+     fn  spanned ( file :  & SourceFile ,  message :  impl  Into < String > ,  span :  Range < usize > )  -> Self  { 
118+         Self  { 
119+             message :  message. into ( ) , 
120+             span :  Some ( Span :: new ( 
121+                 file. start_pos  + BytePos :: from_usize ( span. start ) , 
122+                 file. start_pos  + BytePos :: from_usize ( span. end ) , 
123+                 SyntaxContext :: root ( ) , 
124+                 None , 
125+             ) ) , 
126+         } 
90127    } 
91128} 
92129
93- impl  Error  for  ConfError  { } 
94- 
95- fn  conf_error ( s :  impl  Into < String > )  -> Box < dyn  Error >  { 
96-     Box :: new ( ConfError ( s. into ( ) ) ) 
130+ impl  From < io:: Error >  for  ConfError  { 
131+     fn  from ( value :  io:: Error )  -> Self  { 
132+         Self  { 
133+             message :  value. to_string ( ) , 
134+             span :  None , 
135+         } 
136+     } 
97137} 
98138
99139macro_rules!  define_Conf { 
@@ -117,20 +157,14 @@ macro_rules! define_Conf {
117157            } 
118158        } 
119159
120-         impl <' de> Deserialize <' de> for  TryConf  { 
121-             fn  deserialize<D >( deserializer:  D )  -> Result <Self ,  D :: Error > where  D :  Deserializer <' de> { 
122-                 deserializer. deserialize_map( ConfVisitor ) 
123-             } 
124-         } 
125- 
126160        #[ derive( Deserialize ) ] 
127161        #[ serde( field_identifier,  rename_all = "kebab-case" ) ] 
128162        #[ allow( non_camel_case_types) ] 
129163        enum  Field  {  $( $name, ) *  third_party,  } 
130164
131-         struct  ConfVisitor ; 
165+         struct  ConfVisitor < ' a> ( & ' a  SourceFile ) ; 
132166
133-         impl <' de> Visitor <' de> for  ConfVisitor  { 
167+         impl <' de> Visitor <' de> for  ConfVisitor < ' _>  { 
134168            type  Value  = TryConf ; 
135169
136170            fn  expecting( & self ,  formatter:  & mut  fmt:: Formatter <' _>)  -> fmt:: Result  { 
@@ -141,32 +175,38 @@ macro_rules! define_Conf {
141175                let  mut  errors = Vec :: new( ) ; 
142176                let  mut  warnings = Vec :: new( ) ; 
143177                $( let  mut  $name = None ; ) * 
144-                 // could get `Field` here directly, but get `str` first for diagnostics 
145-                 while  let  Some ( name)  = map. next_key:: <& str >( ) ? { 
146-                     match  Field :: deserialize( name. into_deserializer( ) ) ? { 
147-                         $( Field :: $name => { 
148-                             $( warnings. push( conf_error( format!( "deprecated field `{}`. {}" ,  name,  $dep) ) ) ; ) ?
149-                             match  map. next_value( )  { 
150-                                 Err ( e)  => errors. push( conf_error( e. to_string( ) ) ) , 
178+                 // could get `Field` here directly, but get `String` first for diagnostics 
179+                 while  let  Some ( name)  = map. next_key:: <toml:: Spanned <String >>( ) ? { 
180+                     match  Field :: deserialize( name. get_ref( ) . as_str( ) . into_deserializer( ) )  { 
181+                         Err ( e)  => { 
182+                             let  e:  FieldError  = e; 
183+                             errors. push( ConfError :: spanned( self . 0 ,  e. 0 ,  name. span( ) ) ) ; 
184+                         } 
185+                         $( Ok ( Field :: $name)  => { 
186+                             $( warnings. push( ConfError :: spanned( self . 0 ,  format!( "deprecated field `{}`. {}" ,  name. get_ref( ) ,  $dep) ,  name. span( ) ) ) ; ) ?
187+                             let  raw_value = map. next_value:: <toml:: Spanned <toml:: Value >>( ) ?; 
188+                             let  value_span = raw_value. span( ) ; 
189+                             match  <$ty>:: deserialize( raw_value. into_inner( ) )  { 
190+                                 Err ( e)  => errors. push( ConfError :: spanned( self . 0 ,  e. to_string( ) . replace( '\n' ,  " " ) . trim( ) ,  value_span) ) , 
151191                                Ok ( value)  => match  $name { 
152-                                     Some ( _)  => errors. push( conf_error ( format!( "duplicate field `{}`" ,  name) ) ) , 
192+                                     Some ( _)  => errors. push( ConfError :: spanned ( self . 0 ,   format!( "duplicate field `{}`" ,  name. get_ref ( ) ) ,  name . span ( ) ) ) , 
153193                                    None  => { 
154194                                        $name = Some ( value) ; 
155195                                        // $new_conf is the same as one of the defined `$name`s, so 
156196                                        // this variable is defined in line 2 of this function. 
157197                                        $( match  $new_conf { 
158-                                             Some ( _)  => errors. push( conf_error ( concat!( 
198+                                             Some ( _)  => errors. push( ConfError :: spanned ( self . 0 ,   concat!( 
159199                                                "duplicate field `" ,  stringify!( $new_conf) , 
160200                                                "` (provided as `" ,  stringify!( $name) ,  "`)" 
161-                                             ) ) ) , 
201+                                             ) ,  name . span ( ) ) ) , 
162202                                            None  => $new_conf = $name. clone( ) , 
163203                                        } ) ?
164204                                    } , 
165205                                } 
166206                            } 
167207                        } ) * 
168-                         // white-listed;  ignore 
169-                         Field :: third_party => drop( map. next_value:: <IgnoredAny >( ) ) 
208+                         // ignore contents of the third_party key  
209+                         Ok ( Field :: third_party)  => drop( map. next_value:: <IgnoredAny >( ) ) 
170210                    } 
171211                } 
172212                let  conf = Conf  {  $( $name:  $name. unwrap_or_else( defaults:: $name) , ) *  } ; 
@@ -532,19 +572,19 @@ pub fn lookup_conf_file() -> io::Result<(Option<PathBuf>, Vec<String>)> {
532572/// Read the `toml` configuration file. 
533573/// 
534574/// In case of error, the function tries to continue as much as possible. 
535- pub  fn  read ( path :  & Path )  -> TryConf  { 
536-     let  content  = match  fs :: read_to_string ( path)  { 
537-         Err ( e)  => return  TryConf :: from_error ( e ) , 
538-         Ok ( content )  => content , 
575+ pub  fn  read ( sess :   & Session ,   path :  & Path )  -> TryConf  { 
576+     let  file  = match  sess . source_map ( ) . load_file ( path)  { 
577+         Err ( e)  => return  e . into ( ) , 
578+         Ok ( file )  => file , 
539579    } ; 
540-     match  toml:: from_str :: < TryConf > ( & content )  { 
580+     match  toml:: de :: Deserializer :: new ( file . src . as_ref ( ) . unwrap ( ) ) . deserialize_map ( ConfVisitor ( & file ) )  { 
541581        Ok ( mut  conf)  => { 
542582            extend_vec_if_indicator_present ( & mut  conf. conf . doc_valid_idents ,  DEFAULT_DOC_VALID_IDENTS ) ; 
543583            extend_vec_if_indicator_present ( & mut  conf. conf . disallowed_names ,  DEFAULT_DISALLOWED_NAMES ) ; 
544584
545585            conf
546586        } , 
547-         Err ( e)  => TryConf :: from_error ( e) , 
587+         Err ( e)  => TryConf :: from_toml_error ( & file ,   & e) , 
548588    } 
549589} 
550590
@@ -556,65 +596,42 @@ fn extend_vec_if_indicator_present(vec: &mut Vec<String>, default: &[&str]) {
556596
557597const  SEPARATOR_WIDTH :  usize  = 4 ; 
558598
559- // Check whether the error is "unknown field" and, if so, list the available fields sorted and at 
560- // least one per line, more if `CLIPPY_TERMINAL_WIDTH` is set and allows it. 
561- pub  fn  format_error ( error :  Box < dyn  Error > )  -> String  { 
562-     let  s = error. to_string ( ) ; 
563- 
564-     if_chain !  { 
565-         if  error. downcast:: <toml:: de:: Error >( ) . is_ok( ) ; 
566-         if  let  Some ( ( prefix,  mut  fields,  suffix) )  = parse_unknown_field_message( & s) ; 
567-         then { 
568-             use  fmt:: Write ; 
569- 
570-             fields. sort_unstable( ) ; 
571- 
572-             let  ( rows,  column_widths)  = calculate_dimensions( & fields) ; 
573- 
574-             let  mut  msg = String :: from( prefix) ; 
575-             for  row in 0 ..rows { 
576-                 writeln!( msg) . unwrap( ) ; 
577-                 for  ( column,  column_width)  in column_widths. iter( ) . copied( ) . enumerate( )  { 
578-                     let  index = column *  rows + row; 
579-                     let  field = fields. get( index) . copied( ) . unwrap_or_default( ) ; 
580-                     write!( 
581-                         msg, 
582-                         "{:SEPARATOR_WIDTH$}{field:column_width$}" , 
583-                         " " 
584-                     ) 
585-                     . unwrap( ) ; 
586-                 } 
587-             } 
588-             write!( msg,  "\n {suffix}" ) . unwrap( ) ; 
589-             msg
590-         }  else { 
591-             s
592-         } 
599+ #[ derive( Debug ) ]  
600+ struct  FieldError ( String ) ; 
601+ 
602+ impl  std:: error:: Error  for  FieldError  { } 
603+ 
604+ impl  Display  for  FieldError  { 
605+     fn  fmt ( & self ,  f :  & mut  Formatter < ' _ > )  -> fmt:: Result  { 
606+         f. pad ( & self . 0 ) 
593607    } 
594608} 
595609
596- // `parse_unknown_field_message` will become unnecessary if 
597- // https://github.com/alexcrichton/toml-rs/pull/364 is merged. 
598- fn  parse_unknown_field_message ( s :  & str )  -> Option < ( & str ,  Vec < & str > ,  & str ) >  { 
599-     // An "unknown field" message has the following form: 
600-     //   unknown field `UNKNOWN`, expected one of `FIELD0`, `FIELD1`, ..., `FIELDN` at line X column Y 
601-     //                                           ^^      ^^^^                     ^^ 
602-     if_chain !  { 
603-         if  s. starts_with( "unknown field" ) ; 
604-         let  slices = s. split( "`, `" ) . collect:: <Vec <_>>( ) ; 
605-         let  n = slices. len( ) ; 
606-         if  n >= 2 ; 
607-         if  let  Some ( ( prefix,  first_field) )  = slices[ 0 ] . rsplit_once( " `" ) ; 
608-         if  let  Some ( ( last_field,  suffix) )  = slices[ n - 1 ] . split_once( "` " ) ; 
609-         then { 
610-             let  fields = iter:: once( first_field) 
611-                 . chain( slices[ 1 ..n - 1 ] . iter( ) . copied( ) ) 
612-                 . chain( iter:: once( last_field) ) 
613-                 . collect:: <Vec <_>>( ) ; 
614-             Some ( ( prefix,  fields,  suffix) ) 
615-         }  else { 
616-             None 
610+ impl  serde:: de:: Error  for  FieldError  { 
611+     fn  custom < T :  Display > ( msg :  T )  -> Self  { 
612+         Self ( msg. to_string ( ) ) 
613+     } 
614+ 
615+     fn  unknown_field ( field :  & str ,  expected :  & ' static  [ & ' static  str ] )  -> Self  { 
616+         // List the available fields sorted and at least one per line, more if `CLIPPY_TERMINAL_WIDTH` is 
617+         // set and allows it. 
618+         use  fmt:: Write ; 
619+ 
620+         let  mut  expected = expected. to_vec ( ) ; 
621+         expected. sort_unstable ( ) ; 
622+ 
623+         let  ( rows,  column_widths)  = calculate_dimensions ( & expected) ; 
624+ 
625+         let  mut  msg = format ! ( "unknown field `{field}`, expected one of" ) ; 
626+         for  row in  0 ..rows { 
627+             writeln ! ( msg) . unwrap ( ) ; 
628+             for  ( column,  column_width)  in  column_widths. iter ( ) . copied ( ) . enumerate ( )  { 
629+                 let  index = column *  rows + row; 
630+                 let  field = expected. get ( index) . copied ( ) . unwrap_or_default ( ) ; 
631+                 write ! ( msg,  "{:SEPARATOR_WIDTH$}{field:column_width$}" ,  " " ) . unwrap ( ) ; 
632+             } 
617633        } 
634+         Self ( msg) 
618635    } 
619636} 
620637
0 commit comments