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