1 use super::{Parser, PResult}
;
2 use super::item
::ItemInfo
;
5 use crate::ast
::{self, Ident, Attribute, ItemKind, Mod, Crate}
;
6 use crate::parse
::{new_sub_parser_from_file, DirectoryOwnership}
;
7 use crate::parse
::token
::{self, TokenKind}
;
8 use crate::parse
::diagnostics
::{Error}
;
9 use crate::source_map
::{SourceMap, Span, DUMMY_SP, FileName}
;
10 use crate::symbol
::sym
;
12 use std
::path
::{self, Path, PathBuf}
;
14 /// Information about the path to a module.
15 pub struct ModulePath
{
18 pub result
: Result
<ModulePathSuccess
, Error
>,
21 pub struct ModulePathSuccess
{
23 pub directory_ownership
: DirectoryOwnership
,
28 /// Parses a source module as a crate. This is the main entry point for the parser.
29 pub fn parse_crate_mod(&mut self) -> PResult
<'a
, Crate
> {
30 let lo
= self.token
.span
;
31 let krate
= Ok(ast
::Crate
{
32 attrs
: self.parse_inner_attributes()?
,
33 module
: self.parse_mod_items(&token
::Eof
, lo
)?
,
34 span
: lo
.to(self.token
.span
),
39 /// Parse a `mod <foo> { ... }` or `mod <foo>;` item
40 pub(super) fn parse_item_mod(&mut self, outer_attrs
: &[Attribute
]) -> PResult
<'a
, ItemInfo
> {
41 let (in_cfg
, outer_attrs
) = {
42 let mut strip_unconfigured
= crate::config
::StripUnconfigured
{
44 features
: None
, // don't perform gated feature checking
46 let mut outer_attrs
= outer_attrs
.to_owned();
47 strip_unconfigured
.process_cfg_attrs(&mut outer_attrs
);
48 (!self.cfg_mods
|| strip_unconfigured
.in_cfg(&outer_attrs
), outer_attrs
)
51 let id_span
= self.token
.span
;
52 let id
= self.parse_ident()?
;
53 if self.eat(&token
::Semi
) {
54 if in_cfg
&& self.recurse_into_file_modules
{
55 // This mod is in an external file. Let's go get it!
56 let ModulePathSuccess { path, directory_ownership, warn }
=
57 self.submod_path(id
, &outer_attrs
, id_span
)?
;
58 let (module
, mut attrs
) =
59 self.eval_src_mod(path
, directory_ownership
, id
.to_string(), id_span
)?
;
60 // Record that we fetched the mod from an external file
62 let attr
= attr
::mk_attr_outer(
63 attr
::mk_word_item(Ident
::with_empty_ctxt(sym
::warn_directory_ownership
)));
64 attr
::mark_known(&attr
);
67 Ok((id
, ItemKind
::Mod(module
), Some(attrs
)))
69 let placeholder
= ast
::Mod
{
74 Ok((id
, ItemKind
::Mod(placeholder
), None
))
77 let old_directory
= self.directory
.clone();
78 self.push_directory(id
, &outer_attrs
);
80 self.expect(&token
::OpenDelim(token
::Brace
))?
;
81 let mod_inner_lo
= self.token
.span
;
82 let attrs
= self.parse_inner_attributes()?
;
83 let module
= self.parse_mod_items(&token
::CloseDelim(token
::Brace
), mod_inner_lo
)?
;
85 self.directory
= old_directory
;
86 Ok((id
, ItemKind
::Mod(module
), Some(attrs
)))
90 /// Given a termination token, parses all of the items in a module.
91 fn parse_mod_items(&mut self, term
: &TokenKind
, inner_lo
: Span
) -> PResult
<'a
, Mod
> {
92 let mut items
= vec
![];
93 while let Some(item
) = self.parse_item()?
{
95 self.maybe_consume_incorrect_semicolon(&items
);
99 let token_str
= self.this_token_descr();
100 if !self.maybe_consume_incorrect_semicolon(&items
) {
101 let mut err
= self.fatal(&format
!("expected item, found {}", token_str
));
102 err
.span_label(self.token
.span
, "expected item");
107 let hi
= if self.token
.span
.is_dummy() {
114 inner
: inner_lo
.to(hi
),
123 outer_attrs
: &[Attribute
],
125 ) -> PResult
<'a
, ModulePathSuccess
> {
126 if let Some(path
) = Parser
::submod_path_from_attr(outer_attrs
, &self.directory
.path
) {
127 return Ok(ModulePathSuccess
{
128 directory_ownership
: match path
.file_name().and_then(|s
| s
.to_str()) {
129 // All `#[path]` files are treated as though they are a `mod.rs` file.
130 // This means that `mod foo;` declarations inside `#[path]`-included
131 // files are siblings,
133 // Note that this will produce weirdness when a file named `foo.rs` is
134 // `#[path]` included and contains a `mod foo;` declaration.
135 // If you encounter this, it's your own darn fault :P
136 Some(_
) => DirectoryOwnership
::Owned { relative: None }
,
137 _
=> DirectoryOwnership
::UnownedViaMod(true),
144 let relative
= match self.directory
.ownership
{
145 DirectoryOwnership
::Owned { relative }
=> relative
,
146 DirectoryOwnership
::UnownedViaBlock
|
147 DirectoryOwnership
::UnownedViaMod(_
) => None
,
149 let paths
= Parser
::default_submod_path(
150 id
, relative
, &self.directory
.path
, self.sess
.source_map());
152 match self.directory
.ownership
{
153 DirectoryOwnership
::Owned { .. }
=> {
154 paths
.result
.map_err(|err
| self.span_fatal_err(id_sp
, err
))
156 DirectoryOwnership
::UnownedViaBlock
=> {
158 "Cannot declare a non-inline module inside a block \
159 unless it has a path attribute";
160 let mut err
= self.diagnostic().struct_span_err(id_sp
, msg
);
161 if paths
.path_exists
{
162 let msg
= format
!("Maybe `use` the module `{}` instead of redeclaring it",
164 err
.span_note(id_sp
, &msg
);
168 DirectoryOwnership
::UnownedViaMod(warn
) => {
170 if let Ok(result
) = paths
.result
{
171 return Ok(ModulePathSuccess { warn: true, ..result }
);
174 let mut err
= self.diagnostic().struct_span_err(id_sp
,
175 "cannot declare a new module at this location");
176 if !id_sp
.is_dummy() {
177 let src_path
= self.sess
.source_map().span_to_filename(id_sp
);
178 if let FileName
::Real(src_path
) = src_path
{
179 if let Some(stem
) = src_path
.file_stem() {
180 let mut dest_path
= src_path
.clone();
181 dest_path
.set_file_name(stem
);
182 dest_path
.push("mod.rs");
184 &format
!("maybe move this module `{}` to its own \
185 directory via `{}`", src_path
.display(),
186 dest_path
.display()));
190 if paths
.path_exists
{
192 &format
!("... or maybe `use` the module `{}` instead \
193 of possibly redeclaring it",
201 pub fn submod_path_from_attr(attrs
: &[Attribute
], dir_path
: &Path
) -> Option
<PathBuf
> {
202 if let Some(s
) = attr
::first_attr_value_str_by_name(attrs
, sym
::path
) {
205 // On windows, the base path might have the form
206 // `\\?\foo\bar` in which case it does not tolerate
207 // mixed `/` and `\` separators, so canonicalize
210 let s
= s
.replace("/", "\\");
211 Some(dir_path
.join(s
))
217 /// Returns a path to a module.
218 pub fn default_submod_path(
220 relative
: Option
<ast
::Ident
>,
222 source_map
: &SourceMap
) -> ModulePath
224 // If we're in a foo.rs file instead of a mod.rs file,
225 // we need to look for submodules in
226 // `./foo/<id>.rs` and `./foo/<id>/mod.rs` rather than
227 // `./<id>.rs` and `./<id>/mod.rs`.
228 let relative_prefix_string
;
229 let relative_prefix
= if let Some(ident
) = relative
{
230 relative_prefix_string
= format
!("{}{}", ident
.as_str(), path
::MAIN_SEPARATOR
);
231 &relative_prefix_string
236 let mod_name
= id
.to_string();
237 let default_path_str
= format
!("{}{}.rs", relative_prefix
, mod_name
);
238 let secondary_path_str
= format
!("{}{}{}mod.rs",
239 relative_prefix
, mod_name
, path
::MAIN_SEPARATOR
);
240 let default_path
= dir_path
.join(&default_path_str
);
241 let secondary_path
= dir_path
.join(&secondary_path_str
);
242 let default_exists
= source_map
.file_exists(&default_path
);
243 let secondary_exists
= source_map
.file_exists(&secondary_path
);
245 let result
= match (default_exists
, secondary_exists
) {
246 (true, false) => Ok(ModulePathSuccess
{
248 directory_ownership
: DirectoryOwnership
::Owned
{
253 (false, true) => Ok(ModulePathSuccess
{
254 path
: secondary_path
,
255 directory_ownership
: DirectoryOwnership
::Owned
{
260 (false, false) => Err(Error
::FileNotFoundForModule
{
261 mod_name
: mod_name
.clone(),
262 default_path
: default_path_str
,
263 secondary_path
: secondary_path_str
,
264 dir_path
: dir_path
.display().to_string(),
266 (true, true) => Err(Error
::DuplicatePaths
{
267 mod_name
: mod_name
.clone(),
268 default_path
: default_path_str
,
269 secondary_path
: secondary_path_str
,
275 path_exists
: default_exists
|| secondary_exists
,
280 /// Reads a module from a source file.
284 directory_ownership
: DirectoryOwnership
,
287 ) -> PResult
<'a
, (Mod
, Vec
<Attribute
>)> {
288 let mut included_mod_stack
= self.sess
.included_mod_stack
.borrow_mut();
289 if let Some(i
) = included_mod_stack
.iter().position(|p
| *p
== path
) {
290 let mut err
= String
::from("circular modules: ");
291 let len
= included_mod_stack
.len();
292 for p
in &included_mod_stack
[i
.. len
] {
293 err
.push_str(&p
.to_string_lossy());
294 err
.push_str(" -> ");
296 err
.push_str(&path
.to_string_lossy());
297 return Err(self.span_fatal(id_sp
, &err
[..]));
299 included_mod_stack
.push(path
.clone());
300 drop(included_mod_stack
);
303 new_sub_parser_from_file(self.sess
, &path
, directory_ownership
, Some(name
), id_sp
);
304 p0
.cfg_mods
= self.cfg_mods
;
305 let mod_inner_lo
= p0
.token
.span
;
306 let mod_attrs
= p0
.parse_inner_attributes()?
;
307 let mut m0
= p0
.parse_mod_items(&token
::Eof
, mod_inner_lo
)?
;
309 self.sess
.included_mod_stack
.borrow_mut().pop();
313 fn push_directory(&mut self, id
: Ident
, attrs
: &[Attribute
]) {
314 if let Some(path
) = attr
::first_attr_value_str_by_name(attrs
, sym
::path
) {
315 self.directory
.path
.to_mut().push(&path
.as_str());
316 self.directory
.ownership
= DirectoryOwnership
::Owned { relative: None }
;
318 // We have to push on the current module name in the case of relative
319 // paths in order to ensure that any additional module paths from inline
320 // `mod x { ... }` come after the relative extension.
322 // For example, a `mod z { ... }` inside `x/y.rs` should set the current
323 // directory path to `/x/y/z`, not `/x/z` with a relative offset of `y`.
324 if let DirectoryOwnership
::Owned { relative }
= &mut self.directory
.ownership
{
325 if let Some(ident
) = relative
.take() { // remove the relative offset
326 self.directory
.path
.to_mut().push(ident
.as_str());
329 self.directory
.path
.to_mut().push(&id
.as_str());