2
2
3
3
#![ allow( clippy:: module_name_repetitions) ]
4
4
5
+ use rustc_session:: Session ;
6
+ use rustc_span:: { BytePos , Pos , SourceFile , Span , SyntaxContext } ;
5
7
use serde:: de:: { Deserializer , IgnoredAny , IntoDeserializer , MapAccess , Visitor } ;
6
8
use serde:: Deserialize ;
7
- use std:: error:: Error ;
9
+ use std:: fmt:: { Debug , Display , Formatter } ;
10
+ use std:: ops:: Range ;
8
11
use std:: path:: { Path , PathBuf } ;
9
12
use std:: str:: FromStr ;
10
- use std:: { cmp, env, fmt, fs, io, iter } ;
13
+ use std:: { cmp, env, fmt, fs, io} ;
11
14
12
15
#[ rustfmt:: skip]
13
16
const DEFAULT_DOC_VALID_IDENTS : & [ & str ] = & [
@@ -67,33 +70,61 @@ impl DisallowedPath {
67
70
#[ derive( Default ) ]
68
71
pub struct TryConf {
69
72
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 > ,
72
75
}
73
76
74
77
impl 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 {
76
85
Self {
77
86
conf : Conf :: default ( ) ,
78
- errors : vec ! [ Box :: new ( error ) ] ,
87
+ errors : vec ! [ value ] ,
79
88
warnings : vec ! [ ] ,
80
89
}
81
90
}
82
91
}
83
92
93
+ impl From < io:: Error > for TryConf {
94
+ fn from ( value : io:: Error ) -> Self {
95
+ ConfError :: from ( value) . into ( )
96
+ }
97
+ }
98
+
84
99
#[ derive( Debug ) ]
85
- struct ConfError ( String ) ;
100
+ pub struct ConfError ( pub String , pub Option < Span > ) ;
86
101
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)
102
+ impl ConfError {
103
+ fn from_toml ( file : & SourceFile , error : & toml:: de:: Error ) -> Self {
104
+ if let Some ( span) = error. span ( ) {
105
+ Self :: spanned ( file, error. message ( ) , span)
106
+ } else {
107
+ Self ( error. message ( ) . to_string ( ) , None )
108
+ }
90
109
}
91
- }
92
110
93
- impl Error for ConfError { }
111
+ fn spanned ( file : & SourceFile , message : impl Into < String > , span : Range < usize > ) -> Self {
112
+ Self (
113
+ message. into ( ) ,
114
+ Some ( Span :: new (
115
+ file. start_pos + BytePos :: from_usize ( span. start ) ,
116
+ file. start_pos + BytePos :: from_usize ( span. end ) ,
117
+ SyntaxContext :: root ( ) ,
118
+ None ,
119
+ ) ) ,
120
+ )
121
+ }
122
+ }
94
123
95
- fn conf_error ( s : impl Into < String > ) -> Box < dyn Error > {
96
- Box :: new ( ConfError ( s. into ( ) ) )
124
+ impl From < io:: Error > for ConfError {
125
+ fn from ( value : io:: Error ) -> Self {
126
+ Self ( value. to_string ( ) , None )
127
+ }
97
128
}
98
129
99
130
macro_rules! define_Conf {
@@ -117,20 +148,14 @@ macro_rules! define_Conf {
117
148
}
118
149
}
119
150
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
-
126
151
#[ derive( Deserialize ) ]
127
152
#[ serde( field_identifier, rename_all = "kebab-case" ) ]
128
153
#[ allow( non_camel_case_types) ]
129
154
enum Field { $( $name, ) * third_party, }
130
155
131
- struct ConfVisitor ;
156
+ struct ConfVisitor < ' a> ( & ' a SourceFile ) ;
132
157
133
- impl <' de> Visitor <' de> for ConfVisitor {
158
+ impl <' de> Visitor <' de> for ConfVisitor < ' _> {
134
159
type Value = TryConf ;
135
160
136
161
fn expecting( & self , formatter: & mut fmt:: Formatter <' _>) -> fmt:: Result {
@@ -141,32 +166,38 @@ macro_rules! define_Conf {
141
166
let mut errors = Vec :: new( ) ;
142
167
let mut warnings = Vec :: new( ) ;
143
168
$( 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( ) ) ) ,
169
+ // could get `Field` here directly, but get `String` first for diagnostics
170
+ while let Some ( name) = map. next_key:: <toml:: Spanned <String >>( ) ? {
171
+ match Field :: deserialize( name. get_ref( ) . as_str( ) . into_deserializer( ) ) {
172
+ Err ( e) => {
173
+ let e: FieldError = e;
174
+ errors. push( ConfError :: spanned( self . 0 , e. 0 , name. span( ) ) ) ;
175
+ }
176
+ $( Ok ( Field :: $name) => {
177
+ $( warnings. push( ConfError :: spanned( self . 0 , format!( "deprecated field `{}`. {}" , name. get_ref( ) , $dep) , name. span( ) ) ) ; ) ?
178
+ let raw_value = map. next_value:: <toml:: Spanned <toml:: Value >>( ) ?;
179
+ let value_span = raw_value. span( ) ;
180
+ match <$ty>:: deserialize( raw_value. into_inner( ) ) {
181
+ Err ( e) => errors. push( ConfError :: spanned( self . 0 , e. to_string( ) . replace( '\n' , " " ) . trim( ) , value_span) ) ,
151
182
Ok ( value) => match $name {
152
- Some ( _) => errors. push( conf_error ( format!( "duplicate field `{}`" , name) ) ) ,
183
+ Some ( _) => errors. push( ConfError :: spanned ( self . 0 , format!( "duplicate field `{}`" , name. get_ref ( ) ) , name . span ( ) ) ) ,
153
184
None => {
154
185
$name = Some ( value) ;
155
186
// $new_conf is the same as one of the defined `$name`s, so
156
187
// this variable is defined in line 2 of this function.
157
188
$( match $new_conf {
158
- Some ( _) => errors. push( conf_error ( concat!(
189
+ Some ( _) => errors. push( ConfError :: spanned ( self . 0 , concat!(
159
190
"duplicate field `" , stringify!( $new_conf) ,
160
191
"` (provided as `" , stringify!( $name) , "`)"
161
- ) ) ) ,
192
+ ) , name . span ( ) ) ) ,
162
193
None => $new_conf = $name. clone( ) ,
163
194
} ) ?
164
195
} ,
165
196
}
166
197
}
167
198
} ) *
168
- // white-listed; ignore
169
- Field :: third_party => drop( map. next_value:: <IgnoredAny >( ) )
199
+ // ignore contents of the third_party key
200
+ Ok ( Field :: third_party) => drop( map. next_value:: <IgnoredAny >( ) )
170
201
}
171
202
}
172
203
let conf = Conf { $( $name: $name. unwrap_or_else( defaults:: $name) , ) * } ;
@@ -521,19 +552,19 @@ pub fn lookup_conf_file() -> io::Result<(Option<PathBuf>, Vec<String>)> {
521
552
/// Read the `toml` configuration file.
522
553
///
523
554
/// In case of error, the function tries to continue as much as possible.
524
- pub fn read ( path : & Path ) -> TryConf {
525
- let content = match fs :: read_to_string ( path) {
526
- Err ( e) => return TryConf :: from_error ( e ) ,
527
- Ok ( content ) => content ,
555
+ pub fn read ( sess : & Session , path : & Path ) -> TryConf {
556
+ let file = match sess . source_map ( ) . load_file ( path) {
557
+ Err ( e) => return e . into ( ) ,
558
+ Ok ( file ) => file ,
528
559
} ;
529
- match toml:: from_str :: < TryConf > ( & content ) {
560
+ match toml:: de :: Deserializer :: new ( file . src . as_ref ( ) . unwrap ( ) ) . deserialize_map ( ConfVisitor ( & file ) ) {
530
561
Ok ( mut conf) => {
531
562
extend_vec_if_indicator_present ( & mut conf. conf . doc_valid_idents , DEFAULT_DOC_VALID_IDENTS ) ;
532
563
extend_vec_if_indicator_present ( & mut conf. conf . disallowed_names , DEFAULT_DISALLOWED_NAMES ) ;
533
564
534
565
conf
535
566
} ,
536
- Err ( e) => TryConf :: from_error ( e) ,
567
+ Err ( e) => TryConf :: from_toml_error ( & file , & e) ,
537
568
}
538
569
}
539
570
@@ -545,65 +576,42 @@ fn extend_vec_if_indicator_present(vec: &mut Vec<String>, default: &[&str]) {
545
576
546
577
const SEPARATOR_WIDTH : usize = 4 ;
547
578
548
- // Check whether the error is "unknown field" and, if so, list the available fields sorted and at
549
- // least one per line, more if `CLIPPY_TERMINAL_WIDTH` is set and allows it.
550
- pub fn format_error ( error : Box < dyn Error > ) -> String {
551
- let s = error. to_string ( ) ;
552
-
553
- if_chain ! {
554
- if error. downcast:: <toml:: de:: Error >( ) . is_ok( ) ;
555
- if let Some ( ( prefix, mut fields, suffix) ) = parse_unknown_field_message( & s) ;
556
- then {
557
- use fmt:: Write ;
558
-
559
- fields. sort_unstable( ) ;
560
-
561
- let ( rows, column_widths) = calculate_dimensions( & fields) ;
562
-
563
- let mut msg = String :: from( prefix) ;
564
- for row in 0 ..rows {
565
- writeln!( msg) . unwrap( ) ;
566
- for ( column, column_width) in column_widths. iter( ) . copied( ) . enumerate( ) {
567
- let index = column * rows + row;
568
- let field = fields. get( index) . copied( ) . unwrap_or_default( ) ;
569
- write!(
570
- msg,
571
- "{:SEPARATOR_WIDTH$}{field:column_width$}" ,
572
- " "
573
- )
574
- . unwrap( ) ;
575
- }
576
- }
577
- write!( msg, "\n {suffix}" ) . unwrap( ) ;
578
- msg
579
- } else {
580
- s
581
- }
579
+ #[ derive( Debug ) ]
580
+ struct FieldError ( String ) ;
581
+
582
+ impl std:: error:: Error for FieldError { }
583
+
584
+ impl Display for FieldError {
585
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
586
+ f. pad ( & self . 0 )
582
587
}
583
588
}
584
589
585
- // `parse_unknown_field_message` will become unnecessary if
586
- // https://github.com/alexcrichton/toml-rs/pull/364 is merged.
587
- fn parse_unknown_field_message ( s : & str ) -> Option < ( & str , Vec < & str > , & str ) > {
588
- // An "unknown field" message has the following form:
589
- // unknown field `UNKNOWN`, expected one of `FIELD0`, `FIELD1`, ..., `FIELDN` at line X column Y
590
- // ^^ ^^^^ ^^
591
- if_chain ! {
592
- if s. starts_with( "unknown field" ) ;
593
- let slices = s. split( "`, `" ) . collect:: <Vec <_>>( ) ;
594
- let n = slices. len( ) ;
595
- if n >= 2 ;
596
- if let Some ( ( prefix, first_field) ) = slices[ 0 ] . rsplit_once( " `" ) ;
597
- if let Some ( ( last_field, suffix) ) = slices[ n - 1 ] . split_once( "` " ) ;
598
- then {
599
- let fields = iter:: once( first_field)
600
- . chain( slices[ 1 ..n - 1 ] . iter( ) . copied( ) )
601
- . chain( iter:: once( last_field) )
602
- . collect:: <Vec <_>>( ) ;
603
- Some ( ( prefix, fields, suffix) )
604
- } else {
605
- None
590
+ impl serde:: de:: Error for FieldError {
591
+ fn custom < T : Display > ( msg : T ) -> Self {
592
+ Self ( msg. to_string ( ) )
593
+ }
594
+
595
+ fn unknown_field ( field : & str , expected : & ' static [ & ' static str ] ) -> Self {
596
+ // List the available fields sorted and at least one per line, more if `CLIPPY_TERMINAL_WIDTH` is
597
+ // set and allows it.
598
+ use fmt:: Write ;
599
+
600
+ let mut expected = expected. to_vec ( ) ;
601
+ expected. sort_unstable ( ) ;
602
+
603
+ let ( rows, column_widths) = calculate_dimensions ( & expected) ;
604
+
605
+ let mut msg = format ! ( "unknown field `{field}`, expected one of" ) ;
606
+ for row in 0 ..rows {
607
+ writeln ! ( msg) . unwrap ( ) ;
608
+ for ( column, column_width) in column_widths. iter ( ) . copied ( ) . enumerate ( ) {
609
+ let index = column * rows + row;
610
+ let field = expected. get ( index) . copied ( ) . unwrap_or_default ( ) ;
611
+ write ! ( msg, "{:SEPARATOR_WIDTH$}{field:column_width$}" , " " ) . unwrap ( ) ;
612
+ }
606
613
}
614
+ Self ( msg)
607
615
}
608
616
}
609
617
0 commit comments