From b0a05c59818a149d67e1fba0b8a37671204c089d Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 2 Sep 2018 00:13:22 +0300 Subject: [PATCH 1/2] Validate syntax of `cfg` attributes --- src/libsyntax/attr/builtin.rs | 16 ++++- src/libsyntax/config.rs | 44 ++++++++++---- src/test/ui/cfg-attr-syntax-validation.rs | 32 ++++++++++ src/test/ui/cfg-attr-syntax-validation.stderr | 60 +++++++++++++++++++ 4 files changed, 139 insertions(+), 13 deletions(-) create mode 100644 src/test/ui/cfg-attr-syntax-validation.rs create mode 100644 src/test/ui/cfg-attr-syntax-validation.stderr diff --git a/src/libsyntax/attr/builtin.rs b/src/libsyntax/attr/builtin.rs index 3eecdf14a4e5..5fc9c5578e1f 100644 --- a/src/libsyntax/attr/builtin.rs +++ b/src/libsyntax/attr/builtin.rs @@ -433,7 +433,21 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) { gated_cfg.check_and_emit(sess, feats); } - sess.config.contains(&(cfg.name(), cfg.value_str())) + let error = |span, msg| { sess.span_diagnostic.span_err(span, msg); true }; + if cfg.ident.segments.len() != 1 { + return error(cfg.ident.span, "`cfg` predicate key must be an identifier"); + } + match &cfg.node { + MetaItemKind::List(..) => { + error(cfg.span, "unexpected parentheses after `cfg` predicate key") + } + MetaItemKind::NameValue(lit) if !lit.node.is_str() => { + error(lit.span, "literal in `cfg` predicate value must be a string") + } + MetaItemKind::NameValue(..) | MetaItemKind::Word => { + sess.config.contains(&(cfg.name(), cfg.value_str())) + } + } }) } diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 5233267e3a95..63b70b122484 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -116,25 +116,45 @@ impl<'a> StripUnconfigured<'a> { // Determine if a node with the given attributes should be included in this configuration. pub fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool { attrs.iter().all(|attr| { - let mis = if !is_cfg(attr) { + if !is_cfg(attr) { return true; - } else if let Some(mis) = attr.meta_item_list() { - mis + } + + let error = |span, msg, suggestion: &str| { + let mut err = self.sess.span_diagnostic.struct_span_err(span, msg); + if !suggestion.is_empty() { + err.span_suggestion(span, "expected syntax is", suggestion.into()); + } + err.emit(); + true + }; + + let meta_item = if let Some(meta_item) = attr.meta() { + meta_item } else { - return true; + // Not a well-formed meta-item. Why? We don't know. + return error(attr.span, "`cfg` is not a well-formed meta-item", + "#[cfg(/* predicate */)]"); + }; + let nested_meta_items = if let Some(nested_meta_items) = meta_item.meta_item_list() { + nested_meta_items + } else { + return error(meta_item.span, "`cfg` is not followed by parentheses", + "cfg(/* predicate */)"); }; - if mis.len() != 1 { - self.sess.span_diagnostic.span_err(attr.span, "expected 1 cfg-pattern"); - return true; + if nested_meta_items.is_empty() { + return error(meta_item.span, "`cfg` predicate is not specified", ""); + } else if nested_meta_items.len() > 1 { + return error(nested_meta_items.last().unwrap().span, + "multiple `cfg` predicates are specified", ""); } - if !mis[0].is_meta_item() { - self.sess.span_diagnostic.span_err(mis[0].span, "unexpected literal"); - return true; + match nested_meta_items[0].meta_item() { + Some(meta_item) => attr::cfg_matches(meta_item, self.sess, self.features), + None => error(nested_meta_items[0].span, + "`cfg` predicate key cannot be a literal", ""), } - - attr::cfg_matches(mis[0].meta_item().unwrap(), self.sess, self.features) }) } diff --git a/src/test/ui/cfg-attr-syntax-validation.rs b/src/test/ui/cfg-attr-syntax-validation.rs new file mode 100644 index 000000000000..06a22eff25c2 --- /dev/null +++ b/src/test/ui/cfg-attr-syntax-validation.rs @@ -0,0 +1,32 @@ +#[cfg] //~ ERROR `cfg` is not followed by parentheses +struct S1; + +#[cfg = 10] //~ ERROR `cfg` is not followed by parentheses +struct S2; + +#[cfg()] //~ ERROR `cfg` predicate is not specified +struct S3; + +#[cfg(a, b)] //~ ERROR multiple `cfg` predicates are specified +struct S4; + +#[cfg("str")] //~ ERROR `cfg` predicate key cannot be a literal +struct S5; + +#[cfg(a::b)] //~ ERROR `cfg` predicate key must be an identifier +struct S6; + +#[cfg(a())] //~ ERROR invalid predicate `a` +struct S7; + +#[cfg(a = 10)] //~ ERROR literal in `cfg` predicate value must be a string +struct S8; + +macro_rules! generate_s9 { + ($expr: expr) => { + #[cfg(feature = $expr)] //~ ERROR `cfg` is not a well-formed meta-item + struct S9; + } +} + +generate_s9!(concat!("nonexistent")); diff --git a/src/test/ui/cfg-attr-syntax-validation.stderr b/src/test/ui/cfg-attr-syntax-validation.stderr new file mode 100644 index 000000000000..7773fdb8cf98 --- /dev/null +++ b/src/test/ui/cfg-attr-syntax-validation.stderr @@ -0,0 +1,60 @@ +error: `cfg` is not followed by parentheses + --> $DIR/cfg-attr-syntax-validation.rs:1:1 + | +LL | #[cfg] //~ ERROR `cfg` is not followed by parentheses + | ^^^^^^ help: expected syntax is: `cfg(/* predicate */)` + +error: `cfg` is not followed by parentheses + --> $DIR/cfg-attr-syntax-validation.rs:4:1 + | +LL | #[cfg = 10] //~ ERROR `cfg` is not followed by parentheses + | ^^^^^^^^^^^ help: expected syntax is: `cfg(/* predicate */)` + +error: `cfg` predicate is not specified + --> $DIR/cfg-attr-syntax-validation.rs:7:1 + | +LL | #[cfg()] //~ ERROR `cfg` predicate is not specified + | ^^^^^^^^ + +error: multiple `cfg` predicates are specified + --> $DIR/cfg-attr-syntax-validation.rs:10:10 + | +LL | #[cfg(a, b)] //~ ERROR multiple `cfg` predicates are specified + | ^ + +error: `cfg` predicate key cannot be a literal + --> $DIR/cfg-attr-syntax-validation.rs:13:7 + | +LL | #[cfg("str")] //~ ERROR `cfg` predicate key cannot be a literal + | ^^^^^ + +error: `cfg` predicate key must be an identifier + --> $DIR/cfg-attr-syntax-validation.rs:16:7 + | +LL | #[cfg(a::b)] //~ ERROR `cfg` predicate key must be an identifier + | ^^^^ + +error[E0537]: invalid predicate `a` + --> $DIR/cfg-attr-syntax-validation.rs:19:7 + | +LL | #[cfg(a())] //~ ERROR invalid predicate `a` + | ^^^ + +error: literal in `cfg` predicate value must be a string + --> $DIR/cfg-attr-syntax-validation.rs:22:11 + | +LL | #[cfg(a = 10)] //~ ERROR literal in `cfg` predicate value must be a string + | ^^ + +error: `cfg` is not a well-formed meta-item + --> $DIR/cfg-attr-syntax-validation.rs:27:9 + | +LL | #[cfg(feature = $expr)] //~ ERROR `cfg` is not a well-formed meta-item + | ^^^^^^^^^^^^^^^^^^^^^^^ help: expected syntax is: `#[cfg(/* predicate */)]` +... +LL | generate_s9!(concat!("nonexistent")); + | ------------------------------------- in this macro invocation + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0537`. From a6adeae104c7dcc20025d92b984fd56427d93c1f Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 6 Sep 2018 01:15:46 +0300 Subject: [PATCH 2/2] Validate syntax of `--cfg` command line arguments --- src/librustc/session/config.rs | 39 ++++++++++++++++++++----------- src/test/ui/cfg-arg-invalid-1.rs | 3 +++ src/test/ui/cfg-arg-invalid-2.rs | 3 +++ src/test/ui/cfg-arg-invalid-3.rs | 3 +++ src/test/ui/cfg-arg-invalid-4.rs | 3 +++ src/test/ui/cfg-arg-invalid-5.rs | 3 +++ src/test/ui/cfg-arg-invalid.rs | 13 ----------- src/test/ui/cfg-empty-codemap.rs | 2 +- src/test/ui/issues/issue-31495.rs | 13 ----------- 9 files changed, 41 insertions(+), 41 deletions(-) create mode 100644 src/test/ui/cfg-arg-invalid-1.rs create mode 100644 src/test/ui/cfg-arg-invalid-2.rs create mode 100644 src/test/ui/cfg-arg-invalid-3.rs create mode 100644 src/test/ui/cfg-arg-invalid-4.rs create mode 100644 src/test/ui/cfg-arg-invalid-5.rs delete mode 100644 src/test/ui/cfg-arg-invalid.rs delete mode 100644 src/test/ui/issues/issue-31495.rs diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index ee3fabc58d53..73c9729feee3 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -21,7 +21,7 @@ use rustc_target::spec::{Target, TargetTriple}; use lint; use middle::cstore; -use syntax::ast::{self, IntTy, UintTy}; +use syntax::ast::{self, IntTy, UintTy, MetaItemKind}; use syntax::source_map::{FileName, FilePathMapping}; use syntax::edition::{Edition, EDITION_NAME_LIST, DEFAULT_EDITION}; use syntax::parse::token; @@ -1735,22 +1735,33 @@ pub fn parse_cfgspecs(cfgspecs: Vec) -> ast::CrateConfig { let mut parser = parse::new_parser_from_source_str(&sess, FileName::CfgSpec, s.to_string()); - let meta_item = panictry!(parser.parse_meta_item()); + macro_rules! error {($reason: expr) => { + early_error(ErrorOutputType::default(), + &format!(concat!("invalid `--cfg` argument: `{}` (", $reason, ")"), s)); + }} - if parser.token != token::Eof { - early_error( - ErrorOutputType::default(), - &format!("invalid --cfg argument: {}", s), - ) - } else if meta_item.is_meta_item_list() { - let msg = format!( - "invalid predicate in --cfg command line argument: `{}`", - meta_item.ident - ); - early_error(ErrorOutputType::default(), &msg) + match &mut parser.parse_meta_item() { + Ok(meta_item) if parser.token == token::Eof => { + if meta_item.ident.segments.len() != 1 { + error!("argument key must be an identifier"); + } + match &meta_item.node { + MetaItemKind::List(..) => { + error!(r#"expected `key` or `key="value"`"#); + } + MetaItemKind::NameValue(lit) if !lit.node.is_str() => { + error!("argument value must be a string"); + } + MetaItemKind::NameValue(..) | MetaItemKind::Word => { + return (meta_item.name(), meta_item.value_str()); + } + } + } + Ok(..) => {} + Err(err) => err.cancel(), } - (meta_item.name(), meta_item.value_str()) + error!(r#"expected `key` or `key="value"`"#); }) .collect::() } diff --git a/src/test/ui/cfg-arg-invalid-1.rs b/src/test/ui/cfg-arg-invalid-1.rs new file mode 100644 index 000000000000..36dd78dd2b19 --- /dev/null +++ b/src/test/ui/cfg-arg-invalid-1.rs @@ -0,0 +1,3 @@ +// compile-flags: --cfg a(b=c) +// error-pattern: invalid `--cfg` argument: `a(b=c)` (expected `key` or `key="value"`) +fn main() {} diff --git a/src/test/ui/cfg-arg-invalid-2.rs b/src/test/ui/cfg-arg-invalid-2.rs new file mode 100644 index 000000000000..48d656a4a28d --- /dev/null +++ b/src/test/ui/cfg-arg-invalid-2.rs @@ -0,0 +1,3 @@ +// compile-flags: --cfg a{b} +// error-pattern: invalid `--cfg` argument: `a{b}` (expected `key` or `key="value"`) +fn main() {} diff --git a/src/test/ui/cfg-arg-invalid-3.rs b/src/test/ui/cfg-arg-invalid-3.rs new file mode 100644 index 000000000000..96ac7828c5c3 --- /dev/null +++ b/src/test/ui/cfg-arg-invalid-3.rs @@ -0,0 +1,3 @@ +// compile-flags: --cfg a::b +// error-pattern: invalid `--cfg` argument: `a::b` (argument key must be an identifier) +fn main() {} diff --git a/src/test/ui/cfg-arg-invalid-4.rs b/src/test/ui/cfg-arg-invalid-4.rs new file mode 100644 index 000000000000..e7dfa17b4b6c --- /dev/null +++ b/src/test/ui/cfg-arg-invalid-4.rs @@ -0,0 +1,3 @@ +// compile-flags: --cfg a(b) +// error-pattern: invalid `--cfg` argument: `a(b)` (expected `key` or `key="value"`) +fn main() {} diff --git a/src/test/ui/cfg-arg-invalid-5.rs b/src/test/ui/cfg-arg-invalid-5.rs new file mode 100644 index 000000000000..a939f4510388 --- /dev/null +++ b/src/test/ui/cfg-arg-invalid-5.rs @@ -0,0 +1,3 @@ +// compile-flags: --cfg a=10 +// error-pattern: invalid `--cfg` argument: `a=10` (argument value must be a string) +fn main() {} diff --git a/src/test/ui/cfg-arg-invalid.rs b/src/test/ui/cfg-arg-invalid.rs deleted file mode 100644 index 404630399c60..000000000000 --- a/src/test/ui/cfg-arg-invalid.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags: --cfg a{b} -// error-pattern: invalid --cfg argument: a{b} -fn main() {} diff --git a/src/test/ui/cfg-empty-codemap.rs b/src/test/ui/cfg-empty-codemap.rs index f06d22d985f4..5cf8135ca6bc 100644 --- a/src/test/ui/cfg-empty-codemap.rs +++ b/src/test/ui/cfg-empty-codemap.rs @@ -12,7 +12,7 @@ // compile-flags: --cfg "" -// error-pattern: expected identifier, found +// error-pattern: invalid `--cfg` argument: `""` (expected `key` or `key="value"`) pub fn main() { } diff --git a/src/test/ui/issues/issue-31495.rs b/src/test/ui/issues/issue-31495.rs deleted file mode 100644 index 794b8bb86bb5..000000000000 --- a/src/test/ui/issues/issue-31495.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags: --cfg foo(bar) -// error-pattern: invalid predicate in --cfg command line argument: `foo` -fn main() {}