1
+ use std:: borrow:: Cow ;
1
2
use std:: collections:: BTreeMap ;
2
3
use std:: path:: { Path , PathBuf } ;
3
4
4
5
use syntax:: ast;
5
- use syntax:: parse:: { parser, DirectoryOwnership } ;
6
+ use syntax:: parse:: { parser, DirectoryOwnership , ParseSess } ;
6
7
use syntax:: source_map;
7
8
use syntax:: symbol:: sym;
9
+ use syntax:: visit:: Visitor ;
8
10
use syntax_pos:: symbol:: Symbol ;
9
11
10
12
use crate :: config:: FileName ;
11
13
use crate :: items:: is_mod_decl;
12
14
use crate :: utils:: contains_skip;
13
15
14
- type FileModMap < ' a > = BTreeMap < FileName , ( & ' a ast:: Mod , String ) > ;
16
+ mod visitor;
17
+
18
+ type FileModMap < ' ast > = BTreeMap < FileName , ( Cow < ' ast , ast:: Mod > , String ) > ;
15
19
16
20
/// Maps each module to the corresponding file.
17
- pub ( crate ) struct ModResolver < ' a , ' b > {
18
- source_map : & ' b source_map :: SourceMap ,
21
+ pub ( crate ) struct ModResolver < ' ast , ' sess > {
22
+ parse_sess : & ' sess ParseSess ,
19
23
directory : Directory ,
20
- file_map : FileModMap < ' a > ,
24
+ file_map : FileModMap < ' ast > ,
21
25
recursive : bool ,
22
26
}
23
27
@@ -27,10 +31,28 @@ struct Directory {
27
31
ownership : DirectoryOwnership ,
28
32
}
29
33
30
- impl < ' a , ' b > ModResolver < ' a , ' b > {
34
+ impl < ' a > Directory {
35
+ fn to_syntax_directory ( & ' a self ) -> syntax:: parse:: Directory < ' a > {
36
+ syntax:: parse:: Directory {
37
+ path : Cow :: Borrowed ( & self . path ) ,
38
+ ownership : self . ownership . clone ( ) ,
39
+ }
40
+ }
41
+ }
42
+
43
+ enum SubModKind {
44
+ /// `mod foo;`
45
+ External ( PathBuf , DirectoryOwnership ) ,
46
+ /// `#[path = "..."] mod foo {}`
47
+ InternalWithPath ( PathBuf ) ,
48
+ /// `mod foo {}`
49
+ Internal ,
50
+ }
51
+
52
+ impl < ' ast , ' sess , ' c > ModResolver < ' ast , ' sess > {
31
53
/// Creates a new `ModResolver`.
32
54
pub ( crate ) fn new (
33
- source_map : & ' b source_map :: SourceMap ,
55
+ parse_sess : & ' sess ParseSess ,
34
56
directory_ownership : DirectoryOwnership ,
35
57
recursive : bool ,
36
58
) -> Self {
@@ -40,14 +62,17 @@ impl<'a, 'b> ModResolver<'a, 'b> {
40
62
ownership : directory_ownership,
41
63
} ,
42
64
file_map : BTreeMap :: new ( ) ,
43
- source_map ,
65
+ parse_sess ,
44
66
recursive,
45
67
}
46
68
}
47
69
48
70
/// Creates a map that maps a file name to the module in AST.
49
- pub ( crate ) fn visit_crate ( mut self , krate : & ' a ast:: Crate ) -> Result < FileModMap < ' a > , String > {
50
- let root_filename = self . source_map . span_to_filename ( krate. span ) ;
71
+ pub ( crate ) fn visit_crate (
72
+ mut self ,
73
+ krate : & ' ast ast:: Crate ,
74
+ ) -> Result < FileModMap < ' ast > , String > {
75
+ let root_filename = self . parse_sess . source_map ( ) . span_to_filename ( krate. span ) ;
51
76
self . directory . path = match root_filename {
52
77
source_map:: FileName :: Real ( ref path) => path
53
78
. parent ( )
@@ -58,54 +83,125 @@ impl<'a, 'b> ModResolver<'a, 'b> {
58
83
59
84
// Skip visiting sub modules when the input is from stdin.
60
85
if self . recursive {
61
- self . visit_mod ( & krate. module ) ?;
86
+ self . visit_mod_from_ast ( & krate. module ) ?;
62
87
}
63
88
64
- self . file_map
65
- . insert ( root_filename. into ( ) , ( & krate. module , String :: new ( ) ) ) ;
89
+ self . file_map . insert (
90
+ root_filename. into ( ) ,
91
+ ( Cow :: Borrowed ( & krate. module ) , String :: new ( ) ) ,
92
+ ) ;
66
93
Ok ( self . file_map )
67
94
}
68
95
69
- fn visit_mod ( & mut self , module : & ' a ast:: Mod ) -> Result < ( ) , String > {
96
+ /// Visit macro calls and look for module declarations. Currently only supports `cfg_if` macro.
97
+ fn visit_mac ( & mut self , item : Cow < ' ast , ast:: Item > ) -> Result < ( ) , String > {
98
+ let mut visitor =
99
+ visitor:: CfgIfVisitor :: new ( self . parse_sess , self . directory . to_syntax_directory ( ) ) ;
100
+ visitor. visit_item ( & item) ;
101
+ for module_item in visitor. mods ( ) {
102
+ if let ast:: ItemKind :: Mod ( ref sub_mod) = module_item. item . node {
103
+ self . visit_mod_from_mac_inner ( & item, Cow :: Owned ( sub_mod. clone ( ) ) ) ?;
104
+ }
105
+ }
106
+ Ok ( ( ) )
107
+ }
108
+
109
+ /// Visit modules defined inside macro calls.
110
+ fn visit_mod_from_macro ( & mut self , module : Cow < ' ast , ast:: Mod > ) -> Result < ( ) , String > {
70
111
for item in & module. items {
112
+ if let ast:: ItemKind :: Mac ( ..) = item. node {
113
+ self . visit_mac ( Cow :: Owned ( item. clone ( ) . into_inner ( ) ) ) ?;
114
+ }
115
+
71
116
if let ast:: ItemKind :: Mod ( ref sub_mod) = item. node {
72
- if contains_skip ( & item. attrs ) {
73
- continue ;
74
- }
117
+ self . visit_mod_from_mac_inner ( item, Cow :: Owned ( sub_mod. clone ( ) ) ) ?;
118
+ }
119
+ }
120
+ Ok ( ( ) )
121
+ }
75
122
76
- let old_direcotry = self . directory . clone ( ) ;
77
- if is_mod_decl ( item) {
78
- // mod foo;
79
- // Look for an extern file.
80
- let ( mod_path, directory_ownership) =
81
- self . find_external_module ( item. ident , & item. attrs ) ?;
82
- self . file_map . insert (
83
- FileName :: Real ( mod_path. clone ( ) ) ,
84
- ( sub_mod, item. ident . as_str ( ) . get ( ) . to_owned ( ) ) ,
85
- ) ;
86
- self . directory = Directory {
87
- path : mod_path. parent ( ) . unwrap ( ) . to_path_buf ( ) ,
88
- ownership : directory_ownership,
89
- }
90
- } else {
91
- // An internal module (`mod foo { /* ... */ }`);
92
- if let Some ( path) = find_path_value ( & item. attrs ) {
93
- // All `#[path]` files are treated as though they are a `mod.rs` file.
94
- self . directory = Directory {
95
- path : Path :: new ( & path. as_str ( ) ) . to_path_buf ( ) ,
96
- ownership : DirectoryOwnership :: Owned { relative : None } ,
97
- } ;
98
- } else {
99
- self . push_inline_mod_directory ( item. ident , & item. attrs ) ;
100
- }
101
- }
102
- self . visit_mod ( sub_mod) ?;
103
- self . directory = old_direcotry;
123
+ fn visit_mod_from_mac_inner (
124
+ & mut self ,
125
+ item : & ' c ast:: Item ,
126
+ sub_mod : Cow < ' ast , ast:: Mod > ,
127
+ ) -> Result < ( ) , String > {
128
+ let old_directory = self . directory . clone ( ) ;
129
+ self . visit_sub_mod ( item, & sub_mod) ?;
130
+ self . visit_mod_from_macro ( sub_mod) ?;
131
+ self . directory = old_directory;
132
+ Ok ( ( ) )
133
+ }
134
+
135
+ /// Visit modules from AST.
136
+ fn visit_mod_from_ast ( & mut self , module : & ' ast ast:: Mod ) -> Result < ( ) , String > {
137
+ for item in & module. items {
138
+ if let ast:: ItemKind :: Mac ( ..) = item. node {
139
+ self . visit_mac ( Cow :: Borrowed ( item) ) ?;
140
+ }
141
+
142
+ if let ast:: ItemKind :: Mod ( ref sub_mod) = item. node {
143
+ let old_directory = self . directory . clone ( ) ;
144
+ self . visit_sub_mod ( item, & Cow :: Borrowed ( sub_mod) ) ?;
145
+ self . visit_mod_from_ast ( sub_mod) ?;
146
+ self . directory = old_directory;
147
+ }
148
+ }
149
+ Ok ( ( ) )
150
+ }
151
+
152
+ fn visit_sub_mod (
153
+ & mut self ,
154
+ item : & ' c ast:: Item ,
155
+ sub_mod : & Cow < ' ast , ast:: Mod > ,
156
+ ) -> Result < ( ) , String > {
157
+ match self . peek_sub_mod ( item) ? {
158
+ Some ( SubModKind :: External ( mod_path, directory_ownership) ) => {
159
+ self . file_map . insert (
160
+ FileName :: Real ( mod_path. clone ( ) ) ,
161
+ ( sub_mod. clone ( ) , item. ident . name . as_str ( ) . get ( ) . to_owned ( ) ) ,
162
+ ) ;
163
+ self . directory = Directory {
164
+ path : mod_path. parent ( ) . unwrap ( ) . to_path_buf ( ) ,
165
+ ownership : directory_ownership,
166
+ } ;
167
+ }
168
+ Some ( SubModKind :: InternalWithPath ( mod_path) ) => {
169
+ // All `#[path]` files are treated as though they are a `mod.rs` file.
170
+ self . directory = Directory {
171
+ path : mod_path,
172
+ ownership : DirectoryOwnership :: Owned { relative : None } ,
173
+ } ;
104
174
}
175
+ Some ( SubModKind :: Internal ) => self . push_inline_mod_directory ( item. ident , & item. attrs ) ,
176
+ None => ( ) , // rustfmt::skip
105
177
}
106
178
Ok ( ( ) )
107
179
}
108
180
181
+ /// Inspect the given sub-module which we are about to visit and returns its kind.
182
+ fn peek_sub_mod ( & self , item : & ' c ast:: Item ) -> Result < Option < SubModKind > , String > {
183
+ if contains_skip ( & item. attrs ) {
184
+ return Ok ( None ) ;
185
+ }
186
+
187
+ if is_mod_decl ( item) {
188
+ // mod foo;
189
+ // Look for an extern file.
190
+ let ( mod_path, directory_ownership) =
191
+ self . find_external_module ( item. ident , & item. attrs ) ?;
192
+ Ok ( Some ( SubModKind :: External ( mod_path, directory_ownership) ) )
193
+ } else {
194
+ // An internal module (`mod foo { /* ... */ }`);
195
+ if let Some ( path) = find_path_value ( & item. attrs ) {
196
+ let path = Path :: new ( & path. as_str ( ) ) . to_path_buf ( ) ;
197
+ Ok ( Some ( SubModKind :: InternalWithPath ( path) ) )
198
+ } else {
199
+ Ok ( Some ( SubModKind :: Internal ) )
200
+ }
201
+ }
202
+ }
203
+
204
+ /// Find a file path in the filesystem which corresponds to the given module.
109
205
fn find_external_module (
110
206
& self ,
111
207
mod_name : ast:: Ident ,
@@ -123,7 +219,7 @@ impl<'a, 'b> ModResolver<'a, 'b> {
123
219
mod_name,
124
220
relative,
125
221
& self . directory . path ,
126
- self . source_map ,
222
+ self . parse_sess . source_map ( ) ,
127
223
)
128
224
. result
129
225
{
0 commit comments