11use crate :: base:: ModuleData ;
22use rustc_ast:: ptr:: P ;
33use rustc_ast:: { token, Attribute , Item } ;
4- use rustc_errors:: { struct_span_err, PResult } ;
4+ use rustc_errors:: { struct_span_err, DiagnosticBuilder } ;
55use rustc_parse:: new_parser_from_file;
66use rustc_session:: parse:: ParseSess ;
77use rustc_session:: Session ;
@@ -19,14 +19,6 @@ pub enum DirOwnership {
1919 UnownedViaBlock ,
2020}
2121
22- /// Information about the path to a module.
23- // Public for rustfmt usage.
24- pub struct ModulePath < ' a > {
25- name : String ,
26- path_exists : bool ,
27- pub result : PResult < ' a , ModulePathSuccess > ,
28- }
29-
3022// Public for rustfmt usage.
3123pub struct ModulePathSuccess {
3224 pub file_path : PathBuf ,
@@ -41,6 +33,14 @@ crate struct ParsedExternalMod {
4133 pub dir_ownership : DirOwnership ,
4234}
4335
36+ pub enum ModError < ' a > {
37+ CircularInclusion ( Vec < PathBuf > ) ,
38+ ModInBlock ( Option < Ident > ) ,
39+ FileNotFound ( Ident , PathBuf ) ,
40+ MultipleCandidates ( Ident , String , String ) ,
41+ ParserError ( DiagnosticBuilder < ' a > ) ,
42+ }
43+
4444crate fn parse_external_mod (
4545 sess : & Session ,
4646 ident : Ident ,
@@ -50,47 +50,33 @@ crate fn parse_external_mod(
5050 attrs : & mut Vec < Attribute > ,
5151) -> ParsedExternalMod {
5252 // We bail on the first error, but that error does not cause a fatal error... (1)
53- let result: PResult < ' _ , _ > = try {
53+ let result: Result < _ , ModError < ' _ > > = try {
5454 // Extract the file path and the new ownership.
55- let mp = mod_file_path ( sess, ident, span , & attrs, & module. dir_path , dir_ownership) ?;
55+ let mp = mod_file_path ( sess, ident, & attrs, & module. dir_path , dir_ownership) ?;
5656 dir_ownership = mp. dir_ownership ;
5757
5858 // Ensure file paths are acyclic.
59- error_on_circular_module ( & sess. parse_sess , span, & mp. file_path , & module. file_path_stack ) ?;
59+ if let Some ( pos) = module. file_path_stack . iter ( ) . position ( |p| p == & mp. file_path ) {
60+ Err ( ModError :: CircularInclusion ( module. file_path_stack [ pos..] . to_vec ( ) ) ) ?;
61+ }
6062
6163 // Actually parse the external file as a module.
6264 let mut parser = new_parser_from_file ( & sess. parse_sess , & mp. file_path , Some ( span) ) ;
63- let ( mut inner_attrs, items, inner_span) = parser. parse_mod ( & token:: Eof ) ?;
65+ let ( mut inner_attrs, items, inner_span) =
66+ parser. parse_mod ( & token:: Eof ) . map_err ( |err| ModError :: ParserError ( err) ) ?;
6467 attrs. append ( & mut inner_attrs) ;
6568 ( items, inner_span, mp. file_path )
6669 } ;
6770 // (1) ...instead, we return a dummy module.
68- let ( items, inner_span, file_path) = result. map_err ( |mut err| err. emit ( ) ) . unwrap_or_default ( ) ;
71+ let ( items, inner_span, file_path) =
72+ result. map_err ( |err| err. report ( sess, span) ) . unwrap_or_default ( ) ;
6973
7074 // Extract the directory path for submodules of the module.
7175 let dir_path = file_path. parent ( ) . unwrap_or ( & file_path) . to_owned ( ) ;
7276
7377 ParsedExternalMod { items, inner_span, file_path, dir_path, dir_ownership }
7478}
7579
76- fn error_on_circular_module < ' a > (
77- sess : & ' a ParseSess ,
78- span : Span ,
79- file_path : & Path ,
80- file_path_stack : & [ PathBuf ] ,
81- ) -> PResult < ' a , ( ) > {
82- if let Some ( i) = file_path_stack. iter ( ) . position ( |p| * p == file_path) {
83- let mut err = String :: from ( "circular modules: " ) ;
84- for p in & file_path_stack[ i..] {
85- err. push_str ( & p. to_string_lossy ( ) ) ;
86- err. push_str ( " -> " ) ;
87- }
88- err. push_str ( & file_path. to_string_lossy ( ) ) ;
89- return Err ( sess. span_diagnostic . struct_span_err ( span, & err[ ..] ) ) ;
90- }
91- Ok ( ( ) )
92- }
93-
9480crate fn mod_dir_path (
9581 sess : & Session ,
9682 ident : Ident ,
@@ -125,11 +111,10 @@ crate fn mod_dir_path(
125111fn mod_file_path < ' a > (
126112 sess : & ' a Session ,
127113 ident : Ident ,
128- span : Span ,
129114 attrs : & [ Attribute ] ,
130115 dir_path : & Path ,
131116 dir_ownership : DirOwnership ,
132- ) -> PResult < ' a , ModulePathSuccess > {
117+ ) -> Result < ModulePathSuccess , ModError < ' a > > {
133118 if let Some ( file_path) = mod_file_path_from_attr ( sess, attrs, dir_path) {
134119 // All `#[path]` files are treated as though they are a `mod.rs` file.
135120 // This means that `mod foo;` declarations inside `#[path]`-included
@@ -146,30 +131,14 @@ fn mod_file_path<'a>(
146131 DirOwnership :: Owned { relative } => relative,
147132 DirOwnership :: UnownedViaBlock => None ,
148133 } ;
149- let ModulePath { path_exists, name, result } =
150- default_submod_path ( & sess. parse_sess , ident, span, relative, dir_path) ;
134+ let result = default_submod_path ( & sess. parse_sess , ident, relative, dir_path) ;
151135 match dir_ownership {
152- DirOwnership :: Owned { .. } => Ok ( result?) ,
153- DirOwnership :: UnownedViaBlock => {
154- let _ = result. map_err ( |mut err| err. cancel ( ) ) ;
155- error_decl_mod_in_block ( & sess. parse_sess , span, path_exists, & name)
156- }
157- }
158- }
159-
160- fn error_decl_mod_in_block < ' a , T > (
161- sess : & ' a ParseSess ,
162- span : Span ,
163- path_exists : bool ,
164- name : & str ,
165- ) -> PResult < ' a , T > {
166- let msg = "Cannot declare a non-inline module inside a block unless it has a path attribute" ;
167- let mut err = sess. span_diagnostic . struct_span_err ( span, msg) ;
168- if path_exists {
169- let msg = format ! ( "Maybe `use` the module `{}` instead of redeclaring it" , name) ;
170- err. span_note ( span, & msg) ;
136+ DirOwnership :: Owned { .. } => result,
137+ DirOwnership :: UnownedViaBlock => Err ( ModError :: ModInBlock ( match result {
138+ Ok ( _) | Err ( ModError :: MultipleCandidates ( ..) ) => Some ( ident) ,
139+ _ => None ,
140+ } ) ) ,
171141 }
172- Err ( err)
173142}
174143
175144/// Derive a submodule path from the first found `#[path = "path_string"]`.
@@ -197,10 +166,9 @@ fn mod_file_path_from_attr(
197166pub fn default_submod_path < ' a > (
198167 sess : & ' a ParseSess ,
199168 ident : Ident ,
200- span : Span ,
201169 relative : Option < Ident > ,
202170 dir_path : & Path ,
203- ) -> ModulePath < ' a > {
171+ ) -> Result < ModulePathSuccess , ModError < ' a > > {
204172 // If we're in a foo.rs file instead of a mod.rs file,
205173 // we need to look for submodules in
206174 // `./foo/<ident>.rs` and `./foo/<ident>/mod.rs` rather than
@@ -222,7 +190,7 @@ pub fn default_submod_path<'a>(
222190 let default_exists = sess. source_map ( ) . file_exists ( & default_path) ;
223191 let secondary_exists = sess. source_map ( ) . file_exists ( & secondary_path) ;
224192
225- let result = match ( default_exists, secondary_exists) {
193+ match ( default_exists, secondary_exists) {
226194 ( true , false ) => Ok ( ModulePathSuccess {
227195 file_path : default_path,
228196 dir_ownership : DirOwnership :: Owned { relative : Some ( ident) } ,
@@ -231,35 +199,65 @@ pub fn default_submod_path<'a>(
231199 file_path : secondary_path,
232200 dir_ownership : DirOwnership :: Owned { relative : None } ,
233201 } ) ,
234- ( false , false ) => {
235- let mut err = struct_span_err ! (
236- sess. span_diagnostic,
237- span,
238- E0583 ,
239- "file not found for module `{}`" ,
240- mod_name,
241- ) ;
242- err. help ( & format ! (
243- "to create the module `{}`, create file \" {}\" " ,
244- mod_name,
245- default_path. display( ) ,
246- ) ) ;
247- Err ( err)
248- }
202+ ( false , false ) => Err ( ModError :: FileNotFound ( ident, default_path) ) ,
249203 ( true , true ) => {
250- let mut err = struct_span_err ! (
251- sess. span_diagnostic,
252- span,
253- E0761 ,
254- "file for module `{}` found at both {} and {}" ,
255- mod_name,
256- default_path_str,
257- secondary_path_str,
258- ) ;
259- err. help ( "delete or rename one of them to remove the ambiguity" ) ;
260- Err ( err)
204+ Err ( ModError :: MultipleCandidates ( ident, default_path_str, secondary_path_str) )
261205 }
262- } ;
206+ }
207+ }
263208
264- ModulePath { name : mod_name, path_exists : default_exists || secondary_exists, result }
209+ impl ModError < ' _ > {
210+ fn report ( self , sess : & Session , span : Span ) {
211+ let diag = & sess. parse_sess . span_diagnostic ;
212+ match self {
213+ ModError :: CircularInclusion ( file_paths) => {
214+ let mut msg = String :: from ( "circular modules: " ) ;
215+ for file_path in & file_paths {
216+ msg. push_str ( & file_path. display ( ) . to_string ( ) ) ;
217+ msg. push_str ( " -> " ) ;
218+ }
219+ msg. push_str ( & file_paths[ 0 ] . display ( ) . to_string ( ) ) ;
220+ diag. struct_span_err ( span, & msg)
221+ }
222+ ModError :: ModInBlock ( ident) => {
223+ let msg = "cannot declare a non-inline module inside a block unless it has a path attribute" ;
224+ let mut err = diag. struct_span_err ( span, msg) ;
225+ if let Some ( ident) = ident {
226+ let note =
227+ format ! ( "maybe `use` the module `{}` instead of redeclaring it" , ident) ;
228+ err. span_note ( span, & note) ;
229+ }
230+ err
231+ }
232+ ModError :: FileNotFound ( ident, default_path) => {
233+ let mut err = struct_span_err ! (
234+ diag,
235+ span,
236+ E0583 ,
237+ "file not found for module `{}`" ,
238+ ident,
239+ ) ;
240+ err. help ( & format ! (
241+ "to create the module `{}`, create file \" {}\" " ,
242+ ident,
243+ default_path. display( ) ,
244+ ) ) ;
245+ err
246+ }
247+ ModError :: MultipleCandidates ( ident, default_path_short, secondary_path_short) => {
248+ let mut err = struct_span_err ! (
249+ diag,
250+ span,
251+ E0761 ,
252+ "file for module `{}` found at both {} and {}" ,
253+ ident,
254+ default_path_short,
255+ secondary_path_short,
256+ ) ;
257+ err. help ( "delete or rename one of them to remove the ambiguity" ) ;
258+ err
259+ }
260+ ModError :: ParserError ( err) => err,
261+ } . emit ( )
262+ }
265263}
0 commit comments