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,70 @@ 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 {
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
+ }
86
116
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
+ }
90
127
}
91
128
}
92
129
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
+ }
97
137
}
98
138
99
139
macro_rules! define_Conf {
@@ -117,20 +157,14 @@ macro_rules! define_Conf {
117
157
}
118
158
}
119
159
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
160
#[ derive( Deserialize ) ]
127
161
#[ serde( field_identifier, rename_all = "kebab-case" ) ]
128
162
#[ allow( non_camel_case_types) ]
129
163
enum Field { $( $name, ) * third_party, }
130
164
131
- struct ConfVisitor ;
165
+ struct ConfVisitor < ' a> ( & ' a SourceFile ) ;
132
166
133
- impl <' de> Visitor <' de> for ConfVisitor {
167
+ impl <' de> Visitor <' de> for ConfVisitor < ' _> {
134
168
type Value = TryConf ;
135
169
136
170
fn expecting( & self , formatter: & mut fmt:: Formatter <' _>) -> fmt:: Result {
@@ -141,32 +175,38 @@ macro_rules! define_Conf {
141
175
let mut errors = Vec :: new( ) ;
142
176
let mut warnings = Vec :: new( ) ;
143
177
$( 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) ) ,
151
191
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 ( ) ) ) ,
153
193
None => {
154
194
$name = Some ( value) ;
155
195
// $new_conf is the same as one of the defined `$name`s, so
156
196
// this variable is defined in line 2 of this function.
157
197
$( match $new_conf {
158
- Some ( _) => errors. push( conf_error ( concat!(
198
+ Some ( _) => errors. push( ConfError :: spanned ( self . 0 , concat!(
159
199
"duplicate field `" , stringify!( $new_conf) ,
160
200
"` (provided as `" , stringify!( $name) , "`)"
161
- ) ) ) ,
201
+ ) , name . span ( ) ) ) ,
162
202
None => $new_conf = $name. clone( ) ,
163
203
} ) ?
164
204
} ,
165
205
}
166
206
}
167
207
} ) *
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 >( ) )
170
210
}
171
211
}
172
212
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>)> {
532
572
/// Read the `toml` configuration file.
533
573
///
534
574
/// 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 ,
539
579
} ;
540
- match toml:: from_str :: < TryConf > ( & content ) {
580
+ match toml:: de :: Deserializer :: new ( file . src . as_ref ( ) . unwrap ( ) ) . deserialize_map ( ConfVisitor ( & file ) ) {
541
581
Ok ( mut conf) => {
542
582
extend_vec_if_indicator_present ( & mut conf. conf . doc_valid_idents , DEFAULT_DOC_VALID_IDENTS ) ;
543
583
extend_vec_if_indicator_present ( & mut conf. conf . disallowed_names , DEFAULT_DISALLOWED_NAMES ) ;
544
584
545
585
conf
546
586
} ,
547
- Err ( e) => TryConf :: from_error ( e) ,
587
+ Err ( e) => TryConf :: from_toml_error ( & file , & e) ,
548
588
}
549
589
}
550
590
@@ -556,65 +596,42 @@ fn extend_vec_if_indicator_present(vec: &mut Vec<String>, default: &[&str]) {
556
596
557
597
const SEPARATOR_WIDTH : usize = 4 ;
558
598
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 )
593
607
}
594
608
}
595
609
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
+ }
617
633
}
634
+ Self ( msg)
618
635
}
619
636
}
620
637
0 commit comments