]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use std::borrow::Cow; |
2 | use std::collections::BTreeMap; | |
3 | use std::path::{Path, PathBuf}; | |
4 | ||
5 | use rustc_ast::ast; | |
f20569fa XL |
6 | use rustc_ast::visit::Visitor; |
7 | use rustc_span::symbol::{self, sym, Symbol}; | |
cdc7bbd5 | 8 | use rustc_span::Span; |
f20569fa XL |
9 | use thiserror::Error; |
10 | ||
11 | use crate::attr::MetaVisitor; | |
12 | use crate::config::FileName; | |
13 | use crate::items::is_mod_decl; | |
a2a8927a | 14 | use crate::parse::parser::{ |
cdc7bbd5 | 15 | Directory, DirectoryOwnership, ModError, ModulePathSuccess, Parser, ParserError, |
f20569fa | 16 | }; |
a2a8927a | 17 | use crate::parse::session::ParseSess; |
3c0e092e | 18 | use crate::utils::{contains_skip, mk_sp}; |
f20569fa XL |
19 | |
20 | mod visitor; | |
21 | ||
22 | type FileModMap<'ast> = BTreeMap<FileName, Module<'ast>>; | |
23 | ||
24 | /// Represents module with its inner attributes. | |
25 | #[derive(Debug, Clone)] | |
26 | pub(crate) struct Module<'a> { | |
cdc7bbd5 XL |
27 | ast_mod_kind: Option<Cow<'a, ast::ModKind>>, |
28 | pub(crate) items: Cow<'a, Vec<rustc_ast::ptr::P<ast::Item>>>, | |
f20569fa | 29 | inner_attr: Vec<ast::Attribute>, |
cdc7bbd5 | 30 | pub(crate) span: Span, |
f20569fa XL |
31 | } |
32 | ||
33 | impl<'a> Module<'a> { | |
cdc7bbd5 XL |
34 | pub(crate) fn new( |
35 | mod_span: Span, | |
36 | ast_mod_kind: Option<Cow<'a, ast::ModKind>>, | |
37 | mod_items: Cow<'a, Vec<rustc_ast::ptr::P<ast::Item>>>, | |
38 | mod_attrs: Cow<'a, Vec<ast::Attribute>>, | |
39 | ) -> Self { | |
40 | let inner_attr = mod_attrs | |
f20569fa XL |
41 | .iter() |
42 | .filter(|attr| attr.style == ast::AttrStyle::Inner) | |
43 | .cloned() | |
44 | .collect(); | |
45 | Module { | |
cdc7bbd5 | 46 | items: mod_items, |
f20569fa | 47 | inner_attr, |
cdc7bbd5 XL |
48 | span: mod_span, |
49 | ast_mod_kind, | |
f20569fa XL |
50 | } |
51 | } | |
f20569fa | 52 | |
04454e1e | 53 | pub(crate) fn attrs(&self) -> &[ast::Attribute] { |
f20569fa XL |
54 | &self.inner_attr |
55 | } | |
f20569fa XL |
56 | } |
57 | ||
58 | /// Maps each module to the corresponding file. | |
59 | pub(crate) struct ModResolver<'ast, 'sess> { | |
60 | parse_sess: &'sess ParseSess, | |
61 | directory: Directory, | |
62 | file_map: FileModMap<'ast>, | |
63 | recursive: bool, | |
64 | } | |
65 | ||
66 | /// Represents errors while trying to resolve modules. | |
f20569fa | 67 | #[derive(Debug, Error)] |
cdc7bbd5 | 68 | #[error("failed to resolve mod `{module}`: {kind}")] |
f20569fa XL |
69 | pub struct ModuleResolutionError { |
70 | pub(crate) module: String, | |
71 | pub(crate) kind: ModuleResolutionErrorKind, | |
72 | } | |
73 | ||
5e7ed085 | 74 | /// Defines variants similar to those of [rustc_expand::module::ModError] |
f20569fa XL |
75 | #[derive(Debug, Error)] |
76 | pub(crate) enum ModuleResolutionErrorKind { | |
77 | /// Find a file that cannot be parsed. | |
78 | #[error("cannot parse {file}")] | |
79 | ParseError { file: PathBuf }, | |
80 | /// File cannot be found. | |
81 | #[error("{file} does not exist")] | |
82 | NotFound { file: PathBuf }, | |
5e7ed085 FG |
83 | /// File a.rs and a/mod.rs both exist |
84 | #[error("file for module found at both {default_path:?} and {secondary_path:?}")] | |
85 | MultipleCandidates { | |
86 | default_path: PathBuf, | |
87 | secondary_path: PathBuf, | |
88 | }, | |
f20569fa XL |
89 | } |
90 | ||
91 | #[derive(Clone)] | |
92 | enum SubModKind<'a, 'ast> { | |
93 | /// `mod foo;` | |
94 | External(PathBuf, DirectoryOwnership, Module<'ast>), | |
95 | /// `mod foo;` with multiple sources. | |
96 | MultiExternal(Vec<(PathBuf, DirectoryOwnership, Module<'ast>)>), | |
97 | /// `mod foo {}` | |
98 | Internal(&'a ast::Item), | |
99 | } | |
100 | ||
101 | impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { | |
102 | /// Creates a new `ModResolver`. | |
103 | pub(crate) fn new( | |
104 | parse_sess: &'sess ParseSess, | |
105 | directory_ownership: DirectoryOwnership, | |
106 | recursive: bool, | |
107 | ) -> Self { | |
108 | ModResolver { | |
109 | directory: Directory { | |
110 | path: PathBuf::new(), | |
111 | ownership: directory_ownership, | |
112 | }, | |
113 | file_map: BTreeMap::new(), | |
114 | parse_sess, | |
115 | recursive, | |
116 | } | |
117 | } | |
118 | ||
119 | /// Creates a map that maps a file name to the module in AST. | |
120 | pub(crate) fn visit_crate( | |
121 | mut self, | |
122 | krate: &'ast ast::Crate, | |
123 | ) -> Result<FileModMap<'ast>, ModuleResolutionError> { | |
5e7ed085 | 124 | let root_filename = self.parse_sess.span_to_filename(krate.spans.inner_span); |
f20569fa XL |
125 | self.directory.path = match root_filename { |
126 | FileName::Real(ref p) => p.parent().unwrap_or(Path::new("")).to_path_buf(), | |
127 | _ => PathBuf::new(), | |
128 | }; | |
129 | ||
130 | // Skip visiting sub modules when the input is from stdin. | |
131 | if self.recursive { | |
cdc7bbd5 | 132 | self.visit_mod_from_ast(&krate.items)?; |
f20569fa XL |
133 | } |
134 | ||
5e7ed085 | 135 | let snippet_provider = self.parse_sess.snippet_provider(krate.spans.inner_span); |
3c0e092e | 136 | |
f20569fa XL |
137 | self.file_map.insert( |
138 | root_filename, | |
cdc7bbd5 | 139 | Module::new( |
3c0e092e | 140 | mk_sp(snippet_provider.start_pos(), snippet_provider.end_pos()), |
cdc7bbd5 XL |
141 | None, |
142 | Cow::Borrowed(&krate.items), | |
143 | Cow::Borrowed(&krate.attrs), | |
144 | ), | |
f20569fa XL |
145 | ); |
146 | Ok(self.file_map) | |
147 | } | |
148 | ||
149 | /// Visit `cfg_if` macro and look for module declarations. | |
150 | fn visit_cfg_if(&mut self, item: Cow<'ast, ast::Item>) -> Result<(), ModuleResolutionError> { | |
151 | let mut visitor = visitor::CfgIfVisitor::new(self.parse_sess); | |
152 | visitor.visit_item(&item); | |
153 | for module_item in visitor.mods() { | |
cdc7bbd5 | 154 | if let ast::ItemKind::Mod(_, ref sub_mod_kind) = module_item.item.kind { |
f20569fa XL |
155 | self.visit_sub_mod( |
156 | &module_item.item, | |
cdc7bbd5 XL |
157 | Module::new( |
158 | module_item.item.span, | |
159 | Some(Cow::Owned(sub_mod_kind.clone())), | |
160 | Cow::Owned(vec![]), | |
161 | Cow::Owned(vec![]), | |
162 | ), | |
f20569fa XL |
163 | )?; |
164 | } | |
165 | } | |
166 | Ok(()) | |
167 | } | |
168 | ||
169 | /// Visit modules defined inside macro calls. | |
cdc7bbd5 XL |
170 | fn visit_mod_outside_ast( |
171 | &mut self, | |
172 | items: Vec<rustc_ast::ptr::P<ast::Item>>, | |
173 | ) -> Result<(), ModuleResolutionError> { | |
174 | for item in items { | |
f20569fa XL |
175 | if is_cfg_if(&item) { |
176 | self.visit_cfg_if(Cow::Owned(item.into_inner()))?; | |
177 | continue; | |
178 | } | |
179 | ||
cdc7bbd5 XL |
180 | if let ast::ItemKind::Mod(_, ref sub_mod_kind) = item.kind { |
181 | let span = item.span; | |
182 | self.visit_sub_mod( | |
183 | &item, | |
184 | Module::new( | |
185 | span, | |
186 | Some(Cow::Owned(sub_mod_kind.clone())), | |
187 | Cow::Owned(vec![]), | |
188 | Cow::Owned(vec![]), | |
189 | ), | |
190 | )?; | |
f20569fa XL |
191 | } |
192 | } | |
193 | Ok(()) | |
194 | } | |
195 | ||
196 | /// Visit modules from AST. | |
cdc7bbd5 XL |
197 | fn visit_mod_from_ast( |
198 | &mut self, | |
3c0e092e | 199 | items: &'ast [rustc_ast::ptr::P<ast::Item>], |
cdc7bbd5 XL |
200 | ) -> Result<(), ModuleResolutionError> { |
201 | for item in items { | |
f20569fa XL |
202 | if is_cfg_if(item) { |
203 | self.visit_cfg_if(Cow::Borrowed(item))?; | |
204 | } | |
205 | ||
cdc7bbd5 XL |
206 | if let ast::ItemKind::Mod(_, ref sub_mod_kind) = item.kind { |
207 | let span = item.span; | |
208 | self.visit_sub_mod( | |
209 | item, | |
210 | Module::new( | |
211 | span, | |
212 | Some(Cow::Borrowed(sub_mod_kind)), | |
213 | Cow::Owned(vec![]), | |
214 | Cow::Borrowed(&item.attrs), | |
215 | ), | |
216 | )?; | |
f20569fa XL |
217 | } |
218 | } | |
219 | Ok(()) | |
220 | } | |
221 | ||
222 | fn visit_sub_mod( | |
223 | &mut self, | |
224 | item: &'c ast::Item, | |
225 | sub_mod: Module<'ast>, | |
226 | ) -> Result<(), ModuleResolutionError> { | |
227 | let old_directory = self.directory.clone(); | |
228 | let sub_mod_kind = self.peek_sub_mod(item, &sub_mod)?; | |
229 | if let Some(sub_mod_kind) = sub_mod_kind { | |
230 | self.insert_sub_mod(sub_mod_kind.clone())?; | |
231 | self.visit_sub_mod_inner(sub_mod, sub_mod_kind)?; | |
232 | } | |
233 | self.directory = old_directory; | |
234 | Ok(()) | |
235 | } | |
236 | ||
237 | /// Inspect the given sub-module which we are about to visit and returns its kind. | |
238 | fn peek_sub_mod( | |
239 | &self, | |
240 | item: &'c ast::Item, | |
241 | sub_mod: &Module<'ast>, | |
242 | ) -> Result<Option<SubModKind<'c, 'ast>>, ModuleResolutionError> { | |
243 | if contains_skip(&item.attrs) { | |
244 | return Ok(None); | |
245 | } | |
246 | ||
247 | if is_mod_decl(item) { | |
248 | // mod foo; | |
249 | // Look for an extern file. | |
250 | self.find_external_module(item.ident, &item.attrs, sub_mod) | |
251 | } else { | |
252 | // An internal module (`mod foo { /* ... */ }`); | |
253 | Ok(Some(SubModKind::Internal(item))) | |
254 | } | |
255 | } | |
256 | ||
257 | fn insert_sub_mod( | |
258 | &mut self, | |
259 | sub_mod_kind: SubModKind<'c, 'ast>, | |
260 | ) -> Result<(), ModuleResolutionError> { | |
261 | match sub_mod_kind { | |
262 | SubModKind::External(mod_path, _, sub_mod) => { | |
263 | self.file_map | |
264 | .entry(FileName::Real(mod_path)) | |
265 | .or_insert(sub_mod); | |
266 | } | |
267 | SubModKind::MultiExternal(mods) => { | |
268 | for (mod_path, _, sub_mod) in mods { | |
269 | self.file_map | |
270 | .entry(FileName::Real(mod_path)) | |
271 | .or_insert(sub_mod); | |
272 | } | |
273 | } | |
274 | _ => (), | |
275 | } | |
276 | Ok(()) | |
277 | } | |
278 | ||
279 | fn visit_sub_mod_inner( | |
280 | &mut self, | |
281 | sub_mod: Module<'ast>, | |
282 | sub_mod_kind: SubModKind<'c, 'ast>, | |
283 | ) -> Result<(), ModuleResolutionError> { | |
284 | match sub_mod_kind { | |
285 | SubModKind::External(mod_path, directory_ownership, sub_mod) => { | |
286 | let directory = Directory { | |
287 | path: mod_path.parent().unwrap().to_path_buf(), | |
288 | ownership: directory_ownership, | |
289 | }; | |
290 | self.visit_sub_mod_after_directory_update(sub_mod, Some(directory)) | |
291 | } | |
3c0e092e | 292 | SubModKind::Internal(item) => { |
f20569fa XL |
293 | self.push_inline_mod_directory(item.ident, &item.attrs); |
294 | self.visit_sub_mod_after_directory_update(sub_mod, None) | |
295 | } | |
296 | SubModKind::MultiExternal(mods) => { | |
297 | for (mod_path, directory_ownership, sub_mod) in mods { | |
298 | let directory = Directory { | |
299 | path: mod_path.parent().unwrap().to_path_buf(), | |
300 | ownership: directory_ownership, | |
301 | }; | |
302 | self.visit_sub_mod_after_directory_update(sub_mod, Some(directory))?; | |
303 | } | |
304 | Ok(()) | |
305 | } | |
306 | } | |
307 | } | |
308 | ||
309 | fn visit_sub_mod_after_directory_update( | |
310 | &mut self, | |
311 | sub_mod: Module<'ast>, | |
312 | directory: Option<Directory>, | |
313 | ) -> Result<(), ModuleResolutionError> { | |
314 | if let Some(directory) = directory { | |
315 | self.directory = directory; | |
316 | } | |
cdc7bbd5 | 317 | match (sub_mod.ast_mod_kind, sub_mod.items) { |
17df50a5 | 318 | (Some(Cow::Borrowed(ast::ModKind::Loaded(items, _, _))), _) => { |
3c0e092e XL |
319 | self.visit_mod_from_ast(items) |
320 | } | |
321 | (Some(Cow::Owned(ast::ModKind::Loaded(items, _, _))), _) | (_, Cow::Owned(items)) => { | |
322 | self.visit_mod_outside_ast(items) | |
cdc7bbd5 | 323 | } |
cdc7bbd5 | 324 | (_, _) => Ok(()), |
f20569fa XL |
325 | } |
326 | } | |
327 | ||
328 | /// Find a file path in the filesystem which corresponds to the given module. | |
329 | fn find_external_module( | |
330 | &self, | |
331 | mod_name: symbol::Ident, | |
332 | attrs: &[ast::Attribute], | |
333 | sub_mod: &Module<'ast>, | |
334 | ) -> Result<Option<SubModKind<'c, 'ast>>, ModuleResolutionError> { | |
335 | let relative = match self.directory.ownership { | |
336 | DirectoryOwnership::Owned { relative } => relative, | |
cdc7bbd5 | 337 | DirectoryOwnership::UnownedViaBlock => None, |
f20569fa XL |
338 | }; |
339 | if let Some(path) = Parser::submod_path_from_attr(attrs, &self.directory.path) { | |
340 | if self.parse_sess.is_file_parsed(&path) { | |
341 | return Ok(None); | |
342 | } | |
cdc7bbd5 XL |
343 | return match Parser::parse_file_as_module(self.parse_sess, &path, sub_mod.span) { |
344 | Ok((ref attrs, _, _)) if contains_skip(attrs) => Ok(None), | |
345 | Ok((attrs, items, span)) => Ok(Some(SubModKind::External( | |
f20569fa XL |
346 | path, |
347 | DirectoryOwnership::Owned { relative: None }, | |
cdc7bbd5 XL |
348 | Module::new( |
349 | span, | |
350 | Some(Cow::Owned(ast::ModKind::Unloaded)), | |
351 | Cow::Owned(items), | |
352 | Cow::Owned(attrs), | |
353 | ), | |
f20569fa XL |
354 | ))), |
355 | Err(ParserError::ParseError) => Err(ModuleResolutionError { | |
356 | module: mod_name.to_string(), | |
357 | kind: ModuleResolutionErrorKind::ParseError { file: path }, | |
358 | }), | |
359 | Err(..) => Err(ModuleResolutionError { | |
360 | module: mod_name.to_string(), | |
361 | kind: ModuleResolutionErrorKind::NotFound { file: path }, | |
362 | }), | |
363 | }; | |
364 | } | |
365 | ||
366 | // Look for nested path, like `#[cfg_attr(feature = "foo", path = "bar.rs")]`. | |
367 | let mut mods_outside_ast = self.find_mods_outside_of_ast(attrs, sub_mod); | |
368 | ||
369 | match self | |
370 | .parse_sess | |
371 | .default_submod_path(mod_name, relative, &self.directory.path) | |
f20569fa XL |
372 | { |
373 | Ok(ModulePathSuccess { | |
cdc7bbd5 XL |
374 | file_path, |
375 | dir_ownership, | |
376 | .. | |
f20569fa XL |
377 | }) => { |
378 | let outside_mods_empty = mods_outside_ast.is_empty(); | |
379 | let should_insert = !mods_outside_ast | |
380 | .iter() | |
cdc7bbd5 XL |
381 | .any(|(outside_path, _, _)| outside_path == &file_path); |
382 | if self.parse_sess.is_file_parsed(&file_path) { | |
f20569fa XL |
383 | if outside_mods_empty { |
384 | return Ok(None); | |
385 | } else { | |
386 | if should_insert { | |
cdc7bbd5 | 387 | mods_outside_ast.push((file_path, dir_ownership, sub_mod.clone())); |
f20569fa XL |
388 | } |
389 | return Ok(Some(SubModKind::MultiExternal(mods_outside_ast))); | |
390 | } | |
391 | } | |
cdc7bbd5 XL |
392 | match Parser::parse_file_as_module(self.parse_sess, &file_path, sub_mod.span) { |
393 | Ok((ref attrs, _, _)) if contains_skip(attrs) => Ok(None), | |
394 | Ok((attrs, items, span)) if outside_mods_empty => { | |
395 | Ok(Some(SubModKind::External( | |
396 | file_path, | |
397 | dir_ownership, | |
398 | Module::new( | |
399 | span, | |
400 | Some(Cow::Owned(ast::ModKind::Unloaded)), | |
401 | Cow::Owned(items), | |
402 | Cow::Owned(attrs), | |
403 | ), | |
404 | ))) | |
405 | } | |
406 | Ok((attrs, items, span)) => { | |
f20569fa | 407 | mods_outside_ast.push(( |
cdc7bbd5 XL |
408 | file_path.clone(), |
409 | dir_ownership, | |
410 | Module::new( | |
411 | span, | |
412 | Some(Cow::Owned(ast::ModKind::Unloaded)), | |
413 | Cow::Owned(items), | |
414 | Cow::Owned(attrs), | |
415 | ), | |
f20569fa XL |
416 | )); |
417 | if should_insert { | |
cdc7bbd5 | 418 | mods_outside_ast.push((file_path, dir_ownership, sub_mod.clone())); |
f20569fa XL |
419 | } |
420 | Ok(Some(SubModKind::MultiExternal(mods_outside_ast))) | |
421 | } | |
422 | Err(ParserError::ParseError) => Err(ModuleResolutionError { | |
423 | module: mod_name.to_string(), | |
cdc7bbd5 | 424 | kind: ModuleResolutionErrorKind::ParseError { file: file_path }, |
f20569fa XL |
425 | }), |
426 | Err(..) if outside_mods_empty => Err(ModuleResolutionError { | |
427 | module: mod_name.to_string(), | |
cdc7bbd5 | 428 | kind: ModuleResolutionErrorKind::NotFound { file: file_path }, |
f20569fa XL |
429 | }), |
430 | Err(..) => { | |
431 | if should_insert { | |
cdc7bbd5 | 432 | mods_outside_ast.push((file_path, dir_ownership, sub_mod.clone())); |
f20569fa XL |
433 | } |
434 | Ok(Some(SubModKind::MultiExternal(mods_outside_ast))) | |
435 | } | |
436 | } | |
437 | } | |
cdc7bbd5 | 438 | Err(mod_err) if !mods_outside_ast.is_empty() => { |
5e7ed085 | 439 | if let ModError::ParserError(e) = mod_err { |
cdc7bbd5 XL |
440 | e.cancel(); |
441 | } | |
f20569fa XL |
442 | Ok(Some(SubModKind::MultiExternal(mods_outside_ast))) |
443 | } | |
5e7ed085 FG |
444 | Err(e) => match e { |
445 | ModError::FileNotFound(_, default_path, _secondary_path) => { | |
446 | Err(ModuleResolutionError { | |
447 | module: mod_name.to_string(), | |
448 | kind: ModuleResolutionErrorKind::NotFound { file: default_path }, | |
449 | }) | |
450 | } | |
451 | ModError::MultipleCandidates(_, default_path, secondary_path) => { | |
452 | Err(ModuleResolutionError { | |
453 | module: mod_name.to_string(), | |
454 | kind: ModuleResolutionErrorKind::MultipleCandidates { | |
455 | default_path, | |
456 | secondary_path, | |
457 | }, | |
458 | }) | |
459 | } | |
460 | ModError::ParserError(_) | |
461 | | ModError::CircularInclusion(_) | |
462 | | ModError::ModInBlock(_) => Err(ModuleResolutionError { | |
463 | module: mod_name.to_string(), | |
464 | kind: ModuleResolutionErrorKind::ParseError { | |
465 | file: self.directory.path.clone(), | |
466 | }, | |
467 | }), | |
468 | }, | |
f20569fa XL |
469 | } |
470 | } | |
471 | ||
472 | fn push_inline_mod_directory(&mut self, id: symbol::Ident, attrs: &[ast::Attribute]) { | |
473 | if let Some(path) = find_path_value(attrs) { | |
a2a8927a | 474 | self.directory.path.push(path.as_str()); |
f20569fa XL |
475 | self.directory.ownership = DirectoryOwnership::Owned { relative: None }; |
476 | } else { | |
5e7ed085 | 477 | let id = id.as_str(); |
f20569fa XL |
478 | // We have to push on the current module name in the case of relative |
479 | // paths in order to ensure that any additional module paths from inline | |
480 | // `mod x { ... }` come after the relative extension. | |
481 | // | |
482 | // For example, a `mod z { ... }` inside `x/y.rs` should set the current | |
483 | // directory path to `/x/y/z`, not `/x/z` with a relative offset of `y`. | |
484 | if let DirectoryOwnership::Owned { relative } = &mut self.directory.ownership { | |
485 | if let Some(ident) = relative.take() { | |
486 | // remove the relative offset | |
a2a8927a | 487 | self.directory.path.push(ident.as_str()); |
5e7ed085 FG |
488 | |
489 | // In the case where there is an x.rs and an ./x directory we want | |
490 | // to prevent adding x twice. For example, ./x/x | |
491 | if self.directory.path.exists() && !self.directory.path.join(id).exists() { | |
492 | return; | |
493 | } | |
f20569fa XL |
494 | } |
495 | } | |
5e7ed085 | 496 | self.directory.path.push(id); |
f20569fa XL |
497 | } |
498 | } | |
499 | ||
500 | fn find_mods_outside_of_ast( | |
501 | &self, | |
502 | attrs: &[ast::Attribute], | |
503 | sub_mod: &Module<'ast>, | |
504 | ) -> Vec<(PathBuf, DirectoryOwnership, Module<'ast>)> { | |
505 | // Filter nested path, like `#[cfg_attr(feature = "foo", path = "bar.rs")]`. | |
506 | let mut path_visitor = visitor::PathVisitor::default(); | |
507 | for attr in attrs.iter() { | |
508 | if let Some(meta) = attr.meta() { | |
509 | path_visitor.visit_meta_item(&meta) | |
510 | } | |
511 | } | |
512 | let mut result = vec![]; | |
513 | for path in path_visitor.paths() { | |
514 | let mut actual_path = self.directory.path.clone(); | |
515 | actual_path.push(&path); | |
516 | if !actual_path.exists() { | |
517 | continue; | |
518 | } | |
519 | if self.parse_sess.is_file_parsed(&actual_path) { | |
520 | // If the specified file is already parsed, then we just use that. | |
521 | result.push(( | |
522 | actual_path, | |
523 | DirectoryOwnership::Owned { relative: None }, | |
524 | sub_mod.clone(), | |
525 | )); | |
526 | continue; | |
527 | } | |
cdc7bbd5 XL |
528 | let (attrs, items, span) = |
529 | match Parser::parse_file_as_module(self.parse_sess, &actual_path, sub_mod.span) { | |
530 | Ok((ref attrs, _, _)) if contains_skip(attrs) => continue, | |
531 | Ok(m) => m, | |
532 | Err(..) => continue, | |
533 | }; | |
f20569fa XL |
534 | |
535 | result.push(( | |
536 | actual_path, | |
537 | DirectoryOwnership::Owned { relative: None }, | |
cdc7bbd5 XL |
538 | Module::new( |
539 | span, | |
540 | Some(Cow::Owned(ast::ModKind::Unloaded)), | |
541 | Cow::Owned(items), | |
542 | Cow::Owned(attrs), | |
543 | ), | |
f20569fa XL |
544 | )) |
545 | } | |
546 | result | |
547 | } | |
548 | } | |
549 | ||
550 | fn path_value(attr: &ast::Attribute) -> Option<Symbol> { | |
551 | if attr.has_name(sym::path) { | |
552 | attr.value_str() | |
553 | } else { | |
554 | None | |
555 | } | |
556 | } | |
557 | ||
558 | // N.B., even when there are multiple `#[path = ...]` attributes, we just need to | |
559 | // examine the first one, since rustc ignores the second and the subsequent ones | |
560 | // as unused attributes. | |
561 | fn find_path_value(attrs: &[ast::Attribute]) -> Option<Symbol> { | |
562 | attrs.iter().flat_map(path_value).next() | |
563 | } | |
564 | ||
565 | fn is_cfg_if(item: &ast::Item) -> bool { | |
566 | match item.kind { | |
567 | ast::ItemKind::MacCall(ref mac) => { | |
568 | if let Some(first_segment) = mac.path.segments.first() { | |
569 | if first_segment.ident.name == Symbol::intern("cfg_if") { | |
570 | return true; | |
571 | } | |
572 | } | |
573 | false | |
574 | } | |
575 | _ => false, | |
576 | } | |
577 | } |