Repository: w3c/webidl2.js
Pull request #558 (2021-04-07 12:40:26)

feat(productions/attribute): validate
This still needs to check generics inside unions but probably as a followup.

This is needed for Gecko since it still uses `attribute sequence<>`. The spec also doesn't block sequences in syntax level.

This patch closes #__ and includes:
- [x] A relevant test
- [x] A relevant documentation update



Patch:
diff --git a/README.md b/README.md
index 83ca37c..8ce14fb 100644
--- a/README.md
+++ b/README.md
@@ -186,6 +186,7 @@ properties:
 * `sourceName`: the source name you passed to `parse()`.
 * `level`: `"error"` by default, can be `"warning"` for some validations for e.g. potential future deprecations.
 * `ruleName`: Only for validations. Currently the followings are supported:
+   * `attr-invalid-type`: Attributes cannot have sequences, records, nor dictionaries.
    * `dict-arg-default`: Optional dictionary type arguments must have a default value of `{}`.
    * `dict-arg-optional`: Dictionary type arguments must be optional if the type does not include a required field.
    * `no-nullable-dict-arg`: Dictionary arguments cannot be nullable.
diff --git a/lib/productions/attribute.js b/lib/productions/attribute.js
index d38134d..b00d6f6 100644
--- a/lib/productions/attribute.js
+++ b/lib/productions/attribute.js
@@ -1,3 +1,5 @@
+import { validationError } from "../error.js";
+import { idlTypeIncludesDictionary } from "../validators/helpers.js";
 import { Base } from "./base.js";
 import { type_with_extended_attributes, unescape, autoParenter } from "./helpers.js";
 
@@ -25,10 +27,6 @@ export class Attribute extends Base {
       return;
     }
     ret.idlType = type_with_extended_attributes(tokeniser, "attribute-type") || tokeniser.error("Attribute lacks a type");
-    switch (ret.idlType.generic) {
-      case "sequence":
-      case "record": tokeniser.error(`Attributes cannot accept ${ret.idlType.generic} types`);
-    }
     tokens.name = tokeniser.consume("identifier", "async", "required") || tokeniser.error("Attribute lacks a name");
     tokens.termination = tokeniser.consume(";") || tokeniser.error("Unterminated attribute, expected `;`");
     return ret.this;
@@ -53,5 +51,22 @@ export class Attribute extends Base {
   *validate(defs) {
     yield* this.extAttrs.validate(defs);
     yield* this.idlType.validate(defs);
+
+    switch (this.idlType.generic) {
+      case "sequence":
+      case "record": {
+        const message = `Attributes cannot accept ${this.idlType.generic} types.`;
+        yield validationError(this.tokens.name, this, "attr-invalid-type", message);
+        break;
+      }
+      default: {
+        const { reference } = idlTypeIncludesDictionary(this.idlType, defs) || {};
+        if (reference) {
+          const targetToken = (this.union ? reference : this).tokens.base;
+          const message = "Attributes cannot accept dictionary types.";
+          yield validationError(targetToken, this, "attr-invalid-type", message);
+        }
+      }
+    }
   }
 }


Test patch:
diff --git a/test/invalid/baseline/invalid-attribute.txt b/test/invalid/baseline/invalid-attribute.txt
new file mode 100644
index 0000000..6b4d461
--- /dev/null
+++ b/test/invalid/baseline/invalid-attribute.txt
@@ -0,0 +1,12 @@
+(attr-invalid-type) Validation error at line 3 in invalid-attribute.webidl, inside `interface sequenceAsAttribute -> attribute invalid`:
+  attribute sequence<short> invalid;
+                            ^ Attributes cannot accept sequence types.
+(attr-invalid-type) Validation error at line 9 in invalid-attribute.webidl, inside `interface recordAsAttribute -> attribute invalid`:
+<DOMString, DOMString> invalid;
+                       ^ Attributes cannot accept record types.
+(attr-invalid-type) Validation error at line 17 in invalid-attribute.webidl, inside `interface dictionaryAsAttribute -> attribute dict`:
+  attribute Dict dict;
+  ^ Attributes cannot accept dictionary types.
+(attr-invalid-type) Validation error at line 18 in invalid-attribute.webidl, inside `interface dictionaryAsAttribute -> attribute dictUnion`:
+  attribute (Dict or boolean
+  ^ Attributes cannot accept dictionary types.
diff --git a/test/invalid/baseline/nullable-union-dictionary.txt b/test/invalid/baseline/nullable-union-dictionary.txt
index 0a71f47..e8efd35 100644
--- a/test/invalid/baseline/nullable-union-dictionary.txt
+++ b/test/invalid/baseline/nullable-union-dictionary.txt
@@ -28,6 +28,9 @@ boolean or Dict)? arg);
 (no-nullable-union-dict) Validation error at line 21 in nullable-union-dictionary.webidl:
   attribute (boolean or Dict)? attr;
                         ^ Nullable union cannot include a dictionary type.
+(attr-invalid-type) Validation error at line 21 in nullable-union-dictionary.webidl, inside `interface Interface -> attribute attr`:
+  attribute (boolean or Dict
+  ^ Attributes cannot accept dictionary types.
 (no-nullable-union-dict) Validation error at line 23 in nullable-union-dictionary.webidl:
   iterable<(boolean or Dict)?>;
                        ^ Nullable union cannot include a dictionary type.
diff --git a/test/invalid/baseline/sequenceAsAttribute.txt b/test/invalid/baseline/sequenceAsAttribute.txt
deleted file mode 100644
index 9d56385..0000000
--- a/test/invalid/baseline/sequenceAsAttribute.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Syntax error at line 2 in sequenceAsAttribute.webidl, since `interface sequenceAsAttribute`:
-  attribute sequence<short> invalid;
-                            ^ Attributes cannot accept sequence types
diff --git a/test/invalid/idl/invalid-attribute.webidl b/test/invalid/idl/invalid-attribute.webidl
new file mode 100644
index 0000000..7009e12
--- /dev/null
+++ b/test/invalid/idl/invalid-attribute.webidl
@@ -0,0 +1,19 @@
+[Exposed=Window]
+interface sequenceAsAttribute {
+  attribute sequence<short> invalid;
+  attribute (sequence<short> or boolean) invalidUnion; // TODO
+};
+
+[Exposed=Window]
+interface recordAsAttribute {
+  attribute record<DOMString, DOMString> invalid;
+  attribute (record<DOMString, DOMString> or boolean) invalidUnion; // TODO
+};
+
+dictionary Dict {};
+
+[Exposed=Window]
+interface dictionaryAsAttribute {
+  attribute Dict dict;
+  attribute (Dict or boolean) dictUnion;
+};
diff --git a/test/invalid/idl/sequenceAsAttribute.webidl b/test/invalid/idl/sequenceAsAttribute.webidl
deleted file mode 100644
index c23da82..0000000
--- a/test/invalid/idl/sequenceAsAttribute.webidl
+++ /dev/null
@@ -1,3 +0,0 @@
-interface sequenceAsAttribute {
-  attribute sequence<short> invalid;
-};
\ No newline at end of file