Skip to content

Commit f24b0a5

Browse files
committed
Deny using path-segment kw when parsing cfg pred
1 parent 9725c4b commit f24b0a5

File tree

40 files changed

+773
-10
lines changed

40 files changed

+773
-10
lines changed

compiler/rustc_attr_parsing/src/interface.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ impl<'sess> AttributeParser<'sess, Early> {
118118
target_node_id: NodeId,
119119
features: Option<&'sess Features>,
120120
emit_errors: ShouldEmit,
121+
parsing_cfg: bool,
121122
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> Option<T>,
122123
template: &AttributeTemplate,
123124
) -> Option<T> {
@@ -133,7 +134,8 @@ impl<'sess> AttributeParser<'sess, Early> {
133134
};
134135
let parts =
135136
normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
136-
let meta_parser = MetaItemParser::from_attr(normal_attr, &parts, &sess.psess, emit_errors)?;
137+
let meta_parser =
138+
MetaItemParser::from_attr(normal_attr, &parts, &sess.psess, emit_errors, parsing_cfg)?;
137139
let path = meta_parser.path();
138140
let args = meta_parser.args();
139141
let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
@@ -251,6 +253,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
251253
&parts,
252254
&self.sess.psess,
253255
self.stage.should_emit(),
256+
false,
254257
) else {
255258
continue;
256259
};

compiler/rustc_attr_parsing/src/parser.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ impl<'a> ArgParser<'a> {
104104
parts: &[Symbol],
105105
psess: &'sess ParseSess,
106106
should_emit: ShouldEmit,
107+
parsing_cfg: bool,
107108
) -> Option<Self> {
108109
Some(match value {
109110
AttrArgs::Empty => Self::NoArgs,
@@ -124,7 +125,7 @@ impl<'a> ArgParser<'a> {
124125
return None;
125126
}
126127

127-
Self::List(MetaItemListParser::new(args, psess, should_emit)?)
128+
Self::List(MetaItemListParser::new(args, psess, should_emit, parsing_cfg)?)
128129
}
129130
AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
130131
eq_span: *eq_span,
@@ -248,10 +249,17 @@ impl<'a> MetaItemParser<'a> {
248249
parts: &[Symbol],
249250
psess: &'sess ParseSess,
250251
should_emit: ShouldEmit,
252+
parsing_cfg: bool,
251253
) -> Option<Self> {
252254
Some(Self {
253255
path: PathParser(Cow::Borrowed(&attr.item.path)),
254-
args: ArgParser::from_attr_args(&attr.item.args, parts, psess, should_emit)?,
256+
args: ArgParser::from_attr_args(
257+
&attr.item.args,
258+
parts,
259+
psess,
260+
should_emit,
261+
parsing_cfg,
262+
)?,
255263
})
256264
}
257265
}
@@ -425,7 +433,13 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
425433
};
426434
}
427435

428-
let path = self.parser.parse_path(PathStyle::Mod)?;
436+
let path = if self.parser.parse_cfg_pred {
437+
let path = self.parser.parse_path(PathStyle::CfgPred)?;
438+
self.parser.parse_cfg_pred = false;
439+
path
440+
} else {
441+
self.parser.parse_path(PathStyle::Mod)?
442+
};
429443

430444
// Check style of arguments that this meta item has
431445
let args = if self.parser.check(exp!(OpenParen)) {
@@ -513,8 +527,11 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
513527
psess: &'sess ParseSess,
514528
span: Span,
515529
should_emit: ShouldEmit,
530+
parsing_cfg: bool,
516531
) -> PResult<'sess, MetaItemListParser<'static>> {
517532
let mut parser = Parser::new(psess, tokens, None);
533+
parser.parse_cfg_pred = parsing_cfg;
534+
518535
let mut this = MetaItemListParserContext { parser: &mut parser, should_emit };
519536

520537
// Presumably, the majority of the time there will only be one attr.
@@ -546,12 +563,14 @@ impl<'a> MetaItemListParser<'a> {
546563
delim: &'a DelimArgs,
547564
psess: &'sess ParseSess,
548565
should_emit: ShouldEmit,
566+
parsing_cfg: bool,
549567
) -> Option<Self> {
550568
match MetaItemListParserContext::parse(
551569
delim.tokens.clone(),
552570
psess,
553571
delim.dspan.entire(),
554572
should_emit,
573+
parsing_cfg,
555574
) {
556575
Ok(s) => Some(s),
557576
Err(e) => {

compiler/rustc_builtin_macros/src/cfg.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ fn parse_cfg<'a>(
4242
tts: TokenStream,
4343
) -> PResult<'a, ast::MetaItemInner> {
4444
let mut p = cx.new_parser_from_tts(tts);
45+
p.parse_cfg_pred = true;
4546

4647
if p.token == token::Eof {
4748
return Err(cx.dcx().create_err(errors::RequiresCfgPattern { span }));

compiler/rustc_expand/src/config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,7 @@ impl<'a> StripUnconfigured<'a> {
428428
node,
429429
self.features,
430430
emit_errors,
431+
true,
431432
parse_cfg_attr,
432433
&CFG_TEMPLATE,
433434
) else {

compiler/rustc_interface/src/interface.rs

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg {
6363
#[allow(rustc::untranslatable_diagnostic)]
6464
#[allow(rustc::diagnostic_outside_of_impl)]
6565
dcx.fatal(format!(
66-
concat!("invalid `--cfg` argument: `{}` (", $reason, ")"),
67-
s
66+
concat!("invalid `--cfg` argument: `{}` ({})"),
67+
s, $reason,
6868
));
6969
};
7070
}
@@ -83,6 +83,19 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg {
8383
}
8484
MetaItemKind::NameValue(..) | MetaItemKind::Word => {
8585
let ident = meta_item.ident().expect("multi-segment cfg key");
86+
87+
if ident.is_reserved() {
88+
if !ident.name.can_be_raw() {
89+
if s.trim().starts_with(&format!("r#{}", ident.as_str())) {
90+
error!(format!("argument key must be an identifier, but `{}` cannot be a raw identifier", ident.name));
91+
} else {
92+
error!(format!("argument key must be an identifier but found keyword `{}`", ident.name));
93+
}
94+
} else if !s.trim().starts_with(&ident.to_string()) {
95+
error!(format!("argument key must be an identifier but found keyword `{}`, escape it using `{}`", ident.as_str(), ident));
96+
}
97+
}
98+
8699
return (ident.name, meta_item.value_str());
87100
}
88101
}
@@ -91,7 +104,7 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg {
91104
Err(err) => err.cancel(),
92105
},
93106
Err(errs) => errs.into_iter().for_each(|err| err.cancel()),
94-
}
107+
};
95108

96109
// If the user tried to use a key="value" flag, but is missing the quotes, provide
97110
// a hint about how to resolve this.
@@ -202,18 +215,54 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
202215
let mut values_specified = false;
203216
let mut values_any_specified = false;
204217

218+
let arg_strs = s
219+
.trim()
220+
.trim_start_matches("cfg(")
221+
.trim_end_matches(')')
222+
.split(',')
223+
.collect::<Vec<_>>();
224+
205225
for arg in args {
206226
if arg.is_word()
207227
&& let Some(ident) = arg.ident()
208228
{
209229
if values_specified {
210230
error!("`cfg()` names cannot be after values");
211231
}
232+
233+
if ident.is_reserved() {
234+
if !ident.name.can_be_raw() {
235+
if arg_strs[names.len()].starts_with(&format!("r#{}", ident.as_str())) {
236+
error!(format!(
237+
"argument key must be an identifier, but `{}` cannot be a raw identifier",
238+
ident.name
239+
));
240+
} else {
241+
error!(format!(
242+
"argument key must be an identifier but found keyword `{}`",
243+
ident.name
244+
));
245+
}
246+
} else if !arg_strs[names.len()].starts_with(&ident.to_string()) {
247+
error!(format!(
248+
"argument key must be an identifier but found keyword `{}`, escape it using `{}`",
249+
ident.as_str(),
250+
ident
251+
));
252+
}
253+
}
254+
212255
names.push(ident);
213256
} else if let Some(boolean) = arg.boolean_literal() {
214257
if values_specified {
215258
error!("`cfg()` names cannot be after values");
216259
}
260+
261+
let lit_str = arg_strs[names.len()];
262+
if !lit_str.starts_with("r#") {
263+
error!(in arg, format!("`cfg()` names must be identifiers but found keyword `{lit_str}`, escape it using `r#{lit_str}`"));
264+
}
265+
217266
names.push(rustc_span::Ident::new(
218267
if boolean { rustc_span::kw::True } else { rustc_span::kw::False },
219268
arg.span(),

compiler/rustc_parse/src/parser/attr.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ impl<'a> Parser<'a> {
381381
pub fn parse_cfg_attr(
382382
&mut self,
383383
) -> PResult<'a, (ast::MetaItemInner, Vec<(ast::AttrItem, Span)>)> {
384+
self.parse_cfg_pred = true;
384385
let cfg_predicate = self.parse_meta_item_inner()?;
385386
self.expect(exp!(Comma))?;
386387

@@ -449,7 +450,14 @@ impl<'a> Parser<'a> {
449450
ast::Safety::Default
450451
};
451452

452-
let path = self.parse_path(PathStyle::Mod)?;
453+
let path = if self.parse_cfg_pred {
454+
let path = self.parse_path(PathStyle::CfgPred)?;
455+
self.parse_cfg_pred = false;
456+
path
457+
} else {
458+
self.parse_path(PathStyle::Mod)?
459+
};
460+
453461
let kind = self.parse_meta_item_kind()?;
454462
if is_unsafe {
455463
self.expect(exp!(CloseParen))?;

compiler/rustc_parse/src/parser/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ pub struct Parser<'a> {
181181
/// The previous token.
182182
pub prev_token: Token,
183183
pub capture_cfg: bool,
184+
pub parse_cfg_pred: bool,
184185
restrictions: Restrictions,
185186
expected_token_types: TokenTypeSet,
186187
token_cursor: TokenCursor,
@@ -372,6 +373,7 @@ impl<'a> Parser<'a> {
372373
},
373374
current_closure: None,
374375
recovery: Recovery::Allowed,
376+
parse_cfg_pred: false,
375377
};
376378

377379
// Make parser point to the first token.

compiler/rustc_parse/src/parser/path.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ pub enum PathStyle {
5757
/// anyway, due to macros), but it is used to avoid weird suggestions about expected
5858
/// tokens when something goes wrong.
5959
Mod,
60+
/// Disallow path segment keywords when parsing cfg preds
61+
CfgPred,
6062
}
6163

6264
impl PathStyle {
@@ -299,7 +301,12 @@ impl<'a> Parser<'a> {
299301
style: PathStyle,
300302
ty_generics: Option<&Generics>,
301303
) -> PResult<'a, PathSegment> {
302-
let ident = self.parse_path_segment_ident()?;
304+
let ident = if style == PathStyle::CfgPred {
305+
self.parse_ident()?
306+
} else {
307+
self.parse_path_segment_ident()?
308+
};
309+
303310
let is_args_start = |token: &Token| {
304311
matches!(token.kind, token::Lt | token::Shl | token::OpenParen | token::LArrow)
305312
};

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3048,7 +3048,7 @@ impl Symbol {
30483048
self == kw::True || self == kw::False
30493049
}
30503050

3051-
/// Returns `true` if this symbol can be a raw identifier.
3051+
/// Returns `true` if this symbol can be a raw identifier in path segment.
30523052
pub fn can_be_raw(self) -> bool {
30533053
self != sym::empty && self != kw::Underscore && !self.is_path_segment_keyword()
30543054
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
error: invalid `--cfg` argument: `async` (argument key must be an identifier but found keyword `async`, escape it using `r#async`)
2+

0 commit comments

Comments
 (0)