]> git.lizzy.rs Git - rust.git/commitdiff
Validate syntax of `cfg` attributes
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Sat, 1 Sep 2018 21:13:22 +0000 (00:13 +0300)
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Wed, 5 Sep 2018 22:18:30 +0000 (01:18 +0300)
src/libsyntax/attr/builtin.rs
src/libsyntax/config.rs
src/test/ui/cfg-attr-syntax-validation.rs [new file with mode: 0644]
src/test/ui/cfg-attr-syntax-validation.stderr [new file with mode: 0644]

index 3eecdf14a4e50029eb62d7a161e310bcbd71f905..5fc9c5578e1f202cd445fdd1d4693641e3f6b9c4 100644 (file)
@@ -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()))
+            }
+        }
     })
 }
 
index 5233267e3a95af043a0fb1165b3b5e3762f5fac8..63b70b1224840a6afb70c42754e0bec111d7feda 100644 (file)
@@ -116,25 +116,45 @@ fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
     // 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 (file)
index 0000000..06a22ef
--- /dev/null
@@ -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 (file)
index 0000000..7773fdb
--- /dev/null
@@ -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`.