Skip to content

Commit 72f8043

Browse files
committedAug 12, 2019
Auto merge of #63469 - Centril:refactor-parser, r=petrochenkov
libsyntax: Refactor `parser.rs` into reasonably sized logical units Here we split `parser.rs` (~7.9 KLOC) into more reasonably sized files (all < 1.8 KLOC): - `./src/libsyntax/parse/` - `parser.rs` - `parser/` - `pat.rs` - `expr.rs` - `stmt.rs` - `ty.rs` - `path.rs` - `generics.rs` - `item.rs` - `module.rs` Closes #60015. r? @petrochenkov
·
1.88.01.38.0
2 parents 899efd5 + bcfcbfc commit 72f8043

File tree

9 files changed

+6606
-6473
lines changed

9 files changed

+6606
-6473
lines changed
 

‎src/libsyntax/parse/parser.rs

Lines changed: 308 additions & 6473 deletions
Large diffs are not rendered by default.

‎src/libsyntax/parse/parser/expr.rs

Lines changed: 1748 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
use super::{Parser, PResult};
2+
3+
use crate::ast::{self, WhereClause, GenericParam, GenericParamKind, GenericBounds, Attribute};
4+
use crate::parse::token;
5+
use crate::source_map::DUMMY_SP;
6+
use crate::symbol::kw;
7+
8+
impl<'a> Parser<'a> {
9+
/// Parses bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
10+
///
11+
/// ```
12+
/// BOUND = LT_BOUND (e.g., `'a`)
13+
/// ```
14+
fn parse_lt_param_bounds(&mut self) -> GenericBounds {
15+
let mut lifetimes = Vec::new();
16+
while self.check_lifetime() {
17+
lifetimes.push(ast::GenericBound::Outlives(self.expect_lifetime()));
18+
19+
if !self.eat_plus() {
20+
break
21+
}
22+
}
23+
lifetimes
24+
}
25+
26+
/// Matches `typaram = IDENT (`?` unbound)? optbounds ( EQ ty )?`.
27+
fn parse_ty_param(&mut self,
28+
preceding_attrs: Vec<Attribute>)
29+
-> PResult<'a, GenericParam> {
30+
let ident = self.parse_ident()?;
31+
32+
// Parse optional colon and param bounds.
33+
let bounds = if self.eat(&token::Colon) {
34+
self.parse_generic_bounds(Some(self.prev_span))?
35+
} else {
36+
Vec::new()
37+
};
38+
39+
let default = if self.eat(&token::Eq) {
40+
Some(self.parse_ty()?)
41+
} else {
42+
None
43+
};
44+
45+
Ok(GenericParam {
46+
ident,
47+
id: ast::DUMMY_NODE_ID,
48+
attrs: preceding_attrs.into(),
49+
bounds,
50+
kind: GenericParamKind::Type {
51+
default,
52+
}
53+
})
54+
}
55+
56+
fn parse_const_param(&mut self, preceding_attrs: Vec<Attribute>) -> PResult<'a, GenericParam> {
57+
self.expect_keyword(kw::Const)?;
58+
let ident = self.parse_ident()?;
59+
self.expect(&token::Colon)?;
60+
let ty = self.parse_ty()?;
61+
62+
Ok(GenericParam {
63+
ident,
64+
id: ast::DUMMY_NODE_ID,
65+
attrs: preceding_attrs.into(),
66+
bounds: Vec::new(),
67+
kind: GenericParamKind::Const {
68+
ty,
69+
}
70+
})
71+
}
72+
73+
/// Parses a (possibly empty) list of lifetime and type parameters, possibly including
74+
/// a trailing comma and erroneous trailing attributes.
75+
crate fn parse_generic_params(&mut self) -> PResult<'a, Vec<ast::GenericParam>> {
76+
let mut params = Vec::new();
77+
loop {
78+
let attrs = self.parse_outer_attributes()?;
79+
if self.check_lifetime() {
80+
let lifetime = self.expect_lifetime();
81+
// Parse lifetime parameter.
82+
let bounds = if self.eat(&token::Colon) {
83+
self.parse_lt_param_bounds()
84+
} else {
85+
Vec::new()
86+
};
87+
params.push(ast::GenericParam {
88+
ident: lifetime.ident,
89+
id: lifetime.id,
90+
attrs: attrs.into(),
91+
bounds,
92+
kind: ast::GenericParamKind::Lifetime,
93+
});
94+
} else if self.check_keyword(kw::Const) {
95+
// Parse const parameter.
96+
params.push(self.parse_const_param(attrs)?);
97+
} else if self.check_ident() {
98+
// Parse type parameter.
99+
params.push(self.parse_ty_param(attrs)?);
100+
} else {
101+
// Check for trailing attributes and stop parsing.
102+
if !attrs.is_empty() {
103+
if !params.is_empty() {
104+
self.struct_span_err(
105+
attrs[0].span,
106+
&format!("trailing attribute after generic parameter"),
107+
)
108+
.span_label(attrs[0].span, "attributes must go before parameters")
109+
.emit();
110+
} else {
111+
self.struct_span_err(
112+
attrs[0].span,
113+
&format!("attribute without generic parameters"),
114+
)
115+
.span_label(
116+
attrs[0].span,
117+
"attributes are only permitted when preceding parameters",
118+
)
119+
.emit();
120+
}
121+
}
122+
break
123+
}
124+
125+
if !self.eat(&token::Comma) {
126+
break
127+
}
128+
}
129+
Ok(params)
130+
}
131+
132+
/// Parses a set of optional generic type parameter declarations. Where
133+
/// clauses are not parsed here, and must be added later via
134+
/// `parse_where_clause()`.
135+
///
136+
/// matches generics = ( ) | ( < > ) | ( < typaramseq ( , )? > ) | ( < lifetimes ( , )? > )
137+
/// | ( < lifetimes , typaramseq ( , )? > )
138+
/// where typaramseq = ( typaram ) | ( typaram , typaramseq )
139+
pub(super) fn parse_generics(&mut self) -> PResult<'a, ast::Generics> {
140+
let span_lo = self.token.span;
141+
let (params, span) = if self.eat_lt() {
142+
let params = self.parse_generic_params()?;
143+
self.expect_gt()?;
144+
(params, span_lo.to(self.prev_span))
145+
} else {
146+
(vec![], self.prev_span.between(self.token.span))
147+
};
148+
Ok(ast::Generics {
149+
params,
150+
where_clause: WhereClause {
151+
predicates: Vec::new(),
152+
span: DUMMY_SP,
153+
},
154+
span,
155+
})
156+
}
157+
158+
/// Parses an optional where-clause and places it in `generics`.
159+
///
160+
/// ```ignore (only-for-syntax-highlight)
161+
/// where T : Trait<U, V> + 'b, 'a : 'b
162+
/// ```
163+
pub(super) fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> {
164+
let mut where_clause = WhereClause {
165+
predicates: Vec::new(),
166+
span: self.prev_span.to(self.prev_span),
167+
};
168+
169+
if !self.eat_keyword(kw::Where) {
170+
return Ok(where_clause);
171+
}
172+
let lo = self.prev_span;
173+
174+
// We are considering adding generics to the `where` keyword as an alternative higher-rank
175+
// parameter syntax (as in `where<'a>` or `where<T>`. To avoid that being a breaking
176+
// change we parse those generics now, but report an error.
177+
if self.choose_generics_over_qpath() {
178+
let generics = self.parse_generics()?;
179+
self.struct_span_err(
180+
generics.span,
181+
"generic parameters on `where` clauses are reserved for future use",
182+
)
183+
.span_label(generics.span, "currently unsupported")
184+
.emit();
185+
}
186+
187+
loop {
188+
let lo = self.token.span;
189+
if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
190+
let lifetime = self.expect_lifetime();
191+
// Bounds starting with a colon are mandatory, but possibly empty.
192+
self.expect(&token::Colon)?;
193+
let bounds = self.parse_lt_param_bounds();
194+
where_clause.predicates.push(ast::WherePredicate::RegionPredicate(
195+
ast::WhereRegionPredicate {
196+
span: lo.to(self.prev_span),
197+
lifetime,
198+
bounds,
199+
}
200+
));
201+
} else if self.check_type() {
202+
// Parse optional `for<'a, 'b>`.
203+
// This `for` is parsed greedily and applies to the whole predicate,
204+
// the bounded type can have its own `for` applying only to it.
205+
// Examples:
206+
// * `for<'a> Trait1<'a>: Trait2<'a /* ok */>`
207+
// * `(for<'a> Trait1<'a>): Trait2<'a /* not ok */>`
208+
// * `for<'a> for<'b> Trait1<'a, 'b>: Trait2<'a /* ok */, 'b /* not ok */>`
209+
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
210+
211+
// Parse type with mandatory colon and (possibly empty) bounds,
212+
// or with mandatory equality sign and the second type.
213+
let ty = self.parse_ty()?;
214+
if self.eat(&token::Colon) {
215+
let bounds = self.parse_generic_bounds(Some(self.prev_span))?;
216+
where_clause.predicates.push(ast::WherePredicate::BoundPredicate(
217+
ast::WhereBoundPredicate {
218+
span: lo.to(self.prev_span),
219+
bound_generic_params: lifetime_defs,
220+
bounded_ty: ty,
221+
bounds,
222+
}
223+
));
224+
// FIXME: Decide what should be used here, `=` or `==`.
225+
// FIXME: We are just dropping the binders in lifetime_defs on the floor here.
226+
} else if self.eat(&token::Eq) || self.eat(&token::EqEq) {
227+
let rhs_ty = self.parse_ty()?;
228+
where_clause.predicates.push(ast::WherePredicate::EqPredicate(
229+
ast::WhereEqPredicate {
230+
span: lo.to(self.prev_span),
231+
lhs_ty: ty,
232+
rhs_ty,
233+
id: ast::DUMMY_NODE_ID,
234+
}
235+
));
236+
} else {
237+
return self.unexpected();
238+
}
239+
} else {
240+
break
241+
}
242+
243+
if !self.eat(&token::Comma) {
244+
break
245+
}
246+
}
247+
248+
where_clause.span = lo.to(self.prev_span);
249+
Ok(where_clause)
250+
}
251+
252+
pub(super) fn choose_generics_over_qpath(&self) -> bool {
253+
// There's an ambiguity between generic parameters and qualified paths in impls.
254+
// If we see `<` it may start both, so we have to inspect some following tokens.
255+
// The following combinations can only start generics,
256+
// but not qualified paths (with one exception):
257+
// `<` `>` - empty generic parameters
258+
// `<` `#` - generic parameters with attributes
259+
// `<` (LIFETIME|IDENT) `>` - single generic parameter
260+
// `<` (LIFETIME|IDENT) `,` - first generic parameter in a list
261+
// `<` (LIFETIME|IDENT) `:` - generic parameter with bounds
262+
// `<` (LIFETIME|IDENT) `=` - generic parameter with a default
263+
// `<` const - generic const parameter
264+
// The only truly ambiguous case is
265+
// `<` IDENT `>` `::` IDENT ...
266+
// we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`)
267+
// because this is what almost always expected in practice, qualified paths in impls
268+
// (`impl <Type>::AssocTy { ... }`) aren't even allowed by type checker at the moment.
269+
self.token == token::Lt &&
270+
(self.look_ahead(1, |t| t == &token::Pound || t == &token::Gt) ||
271+
self.look_ahead(1, |t| t.is_lifetime() || t.is_ident()) &&
272+
self.look_ahead(2, |t| t == &token::Gt || t == &token::Comma ||
273+
t == &token::Colon || t == &token::Eq) ||
274+
self.is_keyword_ahead(1, &[kw::Const]))
275+
}
276+
}

‎src/libsyntax/parse/parser/item.rs

Lines changed: 1915 additions & 0 deletions
Large diffs are not rendered by default.

‎src/libsyntax/parse/parser/module.rs

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
use super::{Parser, PResult};
2+
use super::item::ItemInfo;
3+
4+
use crate::attr;
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;
11+
12+
use std::path::{self, Path, PathBuf};
13+
14+
/// Information about the path to a module.
15+
pub struct ModulePath {
16+
name: String,
17+
path_exists: bool,
18+
pub result: Result<ModulePathSuccess, Error>,
19+
}
20+
21+
pub struct ModulePathSuccess {
22+
pub path: PathBuf,
23+
pub directory_ownership: DirectoryOwnership,
24+
warn: bool,
25+
}
26+
27+
impl<'a> Parser<'a> {
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),
35+
});
36+
krate
37+
}
38+
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 {
43+
sess: self.sess,
44+
features: None, // don't perform gated feature checking
45+
};
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)
49+
};
50+
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
61+
if warn {
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);
65+
attrs.push(attr);
66+
}
67+
Ok((id, ItemKind::Mod(module), Some(attrs)))
68+
} else {
69+
let placeholder = ast::Mod {
70+
inner: DUMMY_SP,
71+
items: Vec::new(),
72+
inline: false
73+
};
74+
Ok((id, ItemKind::Mod(placeholder), None))
75+
}
76+
} else {
77+
let old_directory = self.directory.clone();
78+
self.push_directory(id, &outer_attrs);
79+
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)?;
84+
85+
self.directory = old_directory;
86+
Ok((id, ItemKind::Mod(module), Some(attrs)))
87+
}
88+
}
89+
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()? {
94+
items.push(item);
95+
self.maybe_consume_incorrect_semicolon(&items);
96+
}
97+
98+
if !self.eat(term) {
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");
103+
return Err(err);
104+
}
105+
}
106+
107+
let hi = if self.token.span.is_dummy() {
108+
inner_lo
109+
} else {
110+
self.prev_span
111+
};
112+
113+
Ok(Mod {
114+
inner: inner_lo.to(hi),
115+
items,
116+
inline: true
117+
})
118+
}
119+
120+
fn submod_path(
121+
&mut self,
122+
id: ast::Ident,
123+
outer_attrs: &[Attribute],
124+
id_sp: Span
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,
132+
//
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),
138+
},
139+
path,
140+
warn: false,
141+
});
142+
}
143+
144+
let relative = match self.directory.ownership {
145+
DirectoryOwnership::Owned { relative } => relative,
146+
DirectoryOwnership::UnownedViaBlock |
147+
DirectoryOwnership::UnownedViaMod(_) => None,
148+
};
149+
let paths = Parser::default_submod_path(
150+
id, relative, &self.directory.path, self.sess.source_map());
151+
152+
match self.directory.ownership {
153+
DirectoryOwnership::Owned { .. } => {
154+
paths.result.map_err(|err| self.span_fatal_err(id_sp, err))
155+
},
156+
DirectoryOwnership::UnownedViaBlock => {
157+
let msg =
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",
163+
paths.name);
164+
err.span_note(id_sp, &msg);
165+
}
166+
Err(err)
167+
}
168+
DirectoryOwnership::UnownedViaMod(warn) => {
169+
if warn {
170+
if let Ok(result) = paths.result {
171+
return Ok(ModulePathSuccess { warn: true, ..result });
172+
}
173+
}
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");
183+
err.span_note(id_sp,
184+
&format!("maybe move this module `{}` to its own \
185+
directory via `{}`", src_path.display(),
186+
dest_path.display()));
187+
}
188+
}
189+
}
190+
if paths.path_exists {
191+
err.span_note(id_sp,
192+
&format!("... or maybe `use` the module `{}` instead \
193+
of possibly redeclaring it",
194+
paths.name));
195+
}
196+
Err(err)
197+
}
198+
}
199+
}
200+
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) {
203+
let s = s.as_str();
204+
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
208+
// `/` to `\`.
209+
#[cfg(windows)]
210+
let s = s.replace("/", "\\");
211+
Some(dir_path.join(s))
212+
} else {
213+
None
214+
}
215+
}
216+
217+
/// Returns a path to a module.
218+
pub fn default_submod_path(
219+
id: ast::Ident,
220+
relative: Option<ast::Ident>,
221+
dir_path: &Path,
222+
source_map: &SourceMap) -> ModulePath
223+
{
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
232+
} else {
233+
""
234+
};
235+
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);
244+
245+
let result = match (default_exists, secondary_exists) {
246+
(true, false) => Ok(ModulePathSuccess {
247+
path: default_path,
248+
directory_ownership: DirectoryOwnership::Owned {
249+
relative: Some(id),
250+
},
251+
warn: false,
252+
}),
253+
(false, true) => Ok(ModulePathSuccess {
254+
path: secondary_path,
255+
directory_ownership: DirectoryOwnership::Owned {
256+
relative: None,
257+
},
258+
warn: false,
259+
}),
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(),
265+
}),
266+
(true, true) => Err(Error::DuplicatePaths {
267+
mod_name: mod_name.clone(),
268+
default_path: default_path_str,
269+
secondary_path: secondary_path_str,
270+
}),
271+
};
272+
273+
ModulePath {
274+
name: mod_name,
275+
path_exists: default_exists || secondary_exists,
276+
result,
277+
}
278+
}
279+
280+
/// Reads a module from a source file.
281+
fn eval_src_mod(
282+
&mut self,
283+
path: PathBuf,
284+
directory_ownership: DirectoryOwnership,
285+
name: String,
286+
id_sp: Span,
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(" -> ");
295+
}
296+
err.push_str(&path.to_string_lossy());
297+
return Err(self.span_fatal(id_sp, &err[..]));
298+
}
299+
included_mod_stack.push(path.clone());
300+
drop(included_mod_stack);
301+
302+
let mut p0 =
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)?;
308+
m0.inline = false;
309+
self.sess.included_mod_stack.borrow_mut().pop();
310+
Ok((m0, mod_attrs))
311+
}
312+
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 };
317+
} else {
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.
321+
//
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());
327+
}
328+
}
329+
self.directory.path.to_mut().push(&id.as_str());
330+
}
331+
}
332+
}

‎src/libsyntax/parse/parser/pat.rs

Lines changed: 634 additions & 0 deletions
Large diffs are not rendered by default.

‎src/libsyntax/parse/parser/path.rs

Lines changed: 474 additions & 0 deletions
Large diffs are not rendered by default.

‎src/libsyntax/parse/parser/stmt.rs

Lines changed: 458 additions & 0 deletions
Large diffs are not rendered by default.

‎src/libsyntax/parse/parser/ty.rs

Lines changed: 461 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.