Repository: w3c/webidl2.js (js)

Issue:
Improve "No semicolon after enum" message
Would be great if we could improve "No semicolon after enum" to "Missing semicolon after ${identifier} enum".  

Pull request description:
Let error messages include the current definition name
Fixes #119 

This should be simple enough 😁 

Patch:
diff --git a/lib/webidl2.js b/lib/webidl2.js
index 3771031..45cccc3 100644
--- a/lib/webidl2.js
+++ b/lib/webidl2.js
@@ -45,6 +45,7 @@
     let line = 1;
     tokens = tokens.slice();
     const names = new Map();
+    let current = null;
 
     const FLOAT = "float";
     const INT = "integer";
@@ -69,7 +70,17 @@
         tok += tokens[numTokens].value;
         numTokens++;
       }
-      throw new WebIDLParseError(str, line, tok, tokens.slice(0, maxTokens));
+      
+      let message;
+      if (current) {
+        message = `Got an error during or right after parsing \`${current.partial ? "partial " : ""}${current.type} ${current.name}\`: ${str}`
+      }
+      else {
+        // throwing before any valid definition
+        message = `Got an error before parsing any named definition: ${str}`;
+      }
+
+      throw new WebIDLParseError(message, line, tok, tokens.slice(0, maxTokens));
     };
 
     function sanitize_name(name, type) {
@@ -484,12 +495,11 @@
       all_ws();
       const tok = consume(ID, "interface");
       if (tok) {
-        ret = interface_rest();
-        ret.type = "callback interface";
+        ret = interface_rest(false, store, "callback interface");
         return ret;
       }
       const name = consume(ID) || error("No name for callback");
-      ret = { type: "callback", name: sanitize_name(name.value, "callback") };
+      ret = current = { type: "callback", name: sanitize_name(name.value, "callback") };
       all_ws();
       consume(OTHER, "=") || error("No assignment in callback");
       all_ws();
@@ -675,14 +685,14 @@
       return ret;
     };
 
-    function interface_rest(isPartial, store) {
+    function interface_rest(isPartial, store, typeName = "interface") {
       all_ws();
       const name = consume(ID) || error("No name for interface");
       const mems = [];
-      const ret = {
-        type: "interface",
+      const ret = current = {
+        type: typeName,
         name: isPartial ? name.value : sanitize_name(name.value, "interface"),
-        partial: false,
+        partial: isPartial,
         members: mems
       };
       if (!isPartial) ret.inheritance = inheritance() || null;
@@ -721,10 +731,10 @@
       all_ws();
       const name = consume(ID) || error("No name for interface mixin");
       const mems = [];
-      const ret = {
+      const ret = current = {
         type: "interface mixin",
         name: isPartial ? name.value : sanitize_name(name.value, "interface mixin"),
-        partial: false,
+        partial: isPartial,
         members: mems
       };
       all_ws();
@@ -767,7 +777,7 @@
       all_ws();
       const name = consume(ID) || error("No name for namespace");
       const mems = [];
-      const ret = {
+      const ret = current = {
         type: "namespace",
         name: isPartial ? name.value : sanitize_name(name.value, "namespace"),
         partial: isPartial,
@@ -836,7 +846,6 @@
         interface_(true, store) ||
         namespace(true, store) ||
         error("Partial doesn't apply to anything");
-      thing.partial = true;
       return thing;
     };
 
@@ -846,10 +855,10 @@
       all_ws();
       const name = consume(ID) || error("No name for dictionary");
       const mems = [];
-      const ret = {
+      const ret = current = {
         type: "dictionary",
         name: isPartial ? name.value : sanitize_name(name.value, "dictionary"),
-        partial: false,
+        partial: isPartial,
         members: mems
       };
       if (!isPartial) ret.inheritance = inheritance() || null;
@@ -892,7 +901,7 @@
       all_ws();
       const name = consume(ID) || error("No name for enum");
       const vals = [];
-      const ret = {
+      const ret = current = {
         type: "enum",
         name: sanitize_name(name.value, "enum"),
         values: vals
@@ -932,6 +941,7 @@
       all_ws();
       const name = consume(ID) || error("No name in typedef");
       ret.name = sanitize_name(name.value, "typedef");
+      current = ret;
       all_ws();
       consume(OTHER, ";") || error("Unterminated typedef");
       return ret;


Test patch:
diff --git a/test/invalid/idl/no-semicolon-callback.widl b/test/invalid/idl/no-semicolon-callback.widl
new file mode 100644
index 0000000..cb20557
--- /dev/null
+++ b/test/invalid/idl/no-semicolon-callback.widl
@@ -0,0 +1,7 @@
+callback interface NoSemicolon {
+  attribute boolean noSemiColon;
+}
+
+enum YouNeedOne {
+  "really"
+}
diff --git a/test/invalid/idl/no-semicolon.widl b/test/invalid/idl/no-semicolon.widl
new file mode 100644
index 0000000..10bc716
--- /dev/null
+++ b/test/invalid/idl/no-semicolon.widl
@@ -0,0 +1,7 @@
+partial interface NoSemicolon {
+  attribute boolean noSemiColon;
+}
+
+enum YouNeedOne {
+  "really"
+}
diff --git a/test/invalid/json/array.json b/test/invalid/json/array.json
index 30b377f..898b2d8 100644
--- a/test/invalid/json/array.json
+++ b/test/invalid/json/array.json
@@ -1,4 +1,4 @@
 {
-    "message": "No name in attribute",
+    "message": "Got an error during or right after parsing `interface LotteryResults`: No name in attribute",
     "line": 5
-}
\ No newline at end of file
+}
diff --git a/test/invalid/json/caller.json b/test/invalid/json/caller.json
index 79b8eca..567fa33 100644
--- a/test/invalid/json/caller.json
+++ b/test/invalid/json/caller.json
@@ -1,4 +1,4 @@
 {
-    "message": "Invalid operation",
+    "message": "Got an error during or right after parsing `interface NumberQuadrupler`: Invalid operation",
     "line": 6
 }
diff --git a/test/invalid/json/dict-required-default.json b/test/invalid/json/dict-required-default.json
index c5afeca..82b6b2a 100644
--- a/test/invalid/json/dict-required-default.json
+++ b/test/invalid/json/dict-required-default.json
@@ -1,4 +1,4 @@
 {
-    "message":  "Required member must not have a default"
+    "message":  "Got an error during or right after parsing `dictionary Dict`: Required member must not have a default"
 ,   "line":     4
 }
diff --git a/test/invalid/json/duplicate.json b/test/invalid/json/duplicate.json
index cef3358..e88a715 100644
--- a/test/invalid/json/duplicate.json
+++ b/test/invalid/json/duplicate.json
@@ -1,4 +1,4 @@
 {
-    "message": "The name \"Test\" of type \"typedef\" is already seen",
+    "message": "Got an error during or right after parsing `typedef Test`: The name \"Test\" of type \"typedef\" is already seen",
     "line": 3
-}
\ No newline at end of file
+}
diff --git a/test/invalid/json/enum.json b/test/invalid/json/enum.json
index 1661158..073ff6c 100644
--- a/test/invalid/json/enum.json
+++ b/test/invalid/json/enum.json
@@ -1,4 +1,4 @@
 {
-    "message":  "Unexpected value in enum"
+    "message":  "Got an error during or right after parsing `enum foo`: Unexpected value in enum"
 ,   "line":     1
 }
diff --git a/test/invalid/json/exception.json b/test/invalid/json/exception.json
index f52cda1..ad9fac6 100644
--- a/test/invalid/json/exception.json
+++ b/test/invalid/json/exception.json
@@ -1,4 +1,4 @@
 {
-    "message": "Unrecognised tokens",
+    "message": "Got an error before parsing any named definition: Unrecognised tokens",
     "line": 4
 }
diff --git a/test/invalid/json/iterator.json b/test/invalid/json/iterator.json
index 7cc2f94..e46d653 100644
--- a/test/invalid/json/iterator.json
+++ b/test/invalid/json/iterator.json
@@ -1,4 +1,4 @@
 {
-    "message": "Invalid operation",
+    "message": "Got an error during or right after parsing `interface SessionManager`: Invalid operation",
     "line": 5
 }
diff --git a/test/invalid/json/maplike-1type.json b/test/invalid/json/maplike-1type.json
index 859a820..75e7a35 100644
--- a/test/invalid/json/maplike-1type.json
+++ b/test/invalid/json/maplike-1type.json
@@ -1,4 +1,4 @@
 {
-    "message": "Missing second type argument in maplike declaration",
+    "message": "Got an error during or right after parsing `interface MapLikeOneType`: Missing second type argument in maplike declaration",
     "line": 2
-}
\ No newline at end of file
+}
diff --git a/test/invalid/json/module.json b/test/invalid/json/module.json
index 3b0984d..9c071cd 100644
--- a/test/invalid/json/module.json
+++ b/test/invalid/json/module.json
@@ -1,4 +1,4 @@
 {
-    "message":  "Unrecognised tokens"
+    "message":  "Got an error before parsing any named definition: Unrecognised tokens"
 ,   "line":     2
 }
diff --git a/test/invalid/json/no-semicolon-callback.json b/test/invalid/json/no-semicolon-callback.json
new file mode 100644
index 0000000..1db9d14
--- /dev/null
+++ b/test/invalid/json/no-semicolon-callback.json
@@ -0,0 +1,4 @@
+{
+    "message": "Got an error during or right after parsing `callback interface NoSemicolon`: Missing semicolon after interface",
+    "line": 5
+}
diff --git a/test/invalid/json/no-semicolon.json b/test/invalid/json/no-semicolon.json
new file mode 100644
index 0000000..087532a
--- /dev/null
+++ b/test/invalid/json/no-semicolon.json
@@ -0,0 +1,4 @@
+{
+    "message": "Got an error during or right after parsing `partial interface NoSemicolon`: Missing semicolon after interface",
+    "line": 5
+}
diff --git a/test/invalid/json/nonnullableany.json b/test/invalid/json/nonnullableany.json
index cf5229e..8a1f900 100644
--- a/test/invalid/json/nonnullableany.json
+++ b/test/invalid/json/nonnullableany.json
@@ -1,4 +1,4 @@
 {
-    "message":  "Type any cannot be made nullable"
+    "message":  "Got an error during or right after parsing `interface NonNullable`: Type any cannot be made nullable"
 ,   "line":     2
 }
diff --git a/test/invalid/json/nonnullableobjects.json b/test/invalid/json/nonnullableobjects.json
index 23cbb3e..52bd8da 100644
--- a/test/invalid/json/nonnullableobjects.json
+++ b/test/invalid/json/nonnullableobjects.json
@@ -1,4 +1,4 @@
 {
-    "message":  "Can't nullable more than once"
+    "message":  "Got an error during or right after parsing `interface NonNullable`: Can't nullable more than once"
 ,   "line":     4
 }
diff --git a/test/invalid/json/promise-with-extended-attribute.json b/test/invalid/json/promise-with-extended-attribute.json
index e9623ce..71212d4 100644
--- a/test/invalid/json/promise-with-extended-attribute.json
+++ b/test/invalid/json/promise-with-extended-attribute.json
@@ -1,4 +1,4 @@
 {
-    "message": "Promise type cannot have extended attribute",
+    "message": "Got an error during or right after parsing `interface Foo`: Promise type cannot have extended attribute",
     "line": 2
 }
diff --git a/test/invalid/json/raises.json b/test/invalid/json/raises.json
index 8b67afe..3165b87 100644
--- a/test/invalid/json/raises.json
+++ b/test/invalid/json/raises.json
@@ -1,4 +1,4 @@
 {
-    "message":  "Unterminated attribute"
+    "message":  "Got an error during or right after parsing `interface Person`: Unterminated attribute"
 ,   "line":     5
 }
diff --git a/test/invalid/json/readonly-iterable.json b/test/invalid/json/readonly-iterable.json
index c6f52a2..1a09264 100644
--- a/test/invalid/json/readonly-iterable.json
+++ b/test/invalid/json/readonly-iterable.json
@@ -1,4 +1,4 @@
 {
-    "message": "Invalid operation",
+    "message": "Got an error during or right after parsing `interface ReadonlyIterable`: Invalid operation",
     "line": 2
-}
\ No newline at end of file
+}
diff --git a/test/invalid/json/record-key-with-extended-attribute.json b/test/invalid/json/record-key-with-extended-attribute.json
index 3945825..4002e7f 100644
--- a/test/invalid/json/record-key-with-extended-attribute.json
+++ b/test/invalid/json/record-key-with-extended-attribute.json
@@ -1,4 +1,4 @@
 {
-    "message": "Record key cannot have extended attribute",
+    "message": "Got an error during or right after parsing `interface Foo`: Record key cannot have extended attribute",
     "line": 2
 }
diff --git a/test/invalid/json/record-key.json b/test/invalid/json/record-key.json
index 3b929b9..179d645 100644
--- a/test/invalid/json/record-key.json
+++ b/test/invalid/json/record-key.json
@@ -1,4 +1,4 @@
 {
-    "message": "Record key must be DOMString, USVString, or ByteString",
+    "message": "Got an error during or right after parsing `interface Foo`: Record key must be DOMString, USVString, or ByteString",
     "line": 2
 }
diff --git a/test/invalid/json/scopedname.json b/test/invalid/json/scopedname.json
index 8e2cd80..4620d2d 100644
--- a/test/invalid/json/scopedname.json
+++ b/test/invalid/json/scopedname.json
@@ -1,4 +1,4 @@
 {
-    "message":  "No name in typedef"
+    "message":  "Got an error before parsing any named definition: No name in typedef"
 ,   "line":     2
 }
diff --git a/test/invalid/json/sequenceAsAttribute.json b/test/invalid/json/sequenceAsAttribute.json
index b714f5d..5b4314a 100644
--- a/test/invalid/json/sequenceAsAttribute.json
+++ b/test/invalid/json/sequenceAsAttribute.json
@@ -1,4 +1,4 @@
 {
-    "message":  "Attributes cannot accept sequence types"
+    "message":  "Got an error during or right after parsing `interface sequenceAsAttribute`: Attributes cannot accept sequence types"
 ,   "line":     2
 }
diff --git a/test/invalid/json/setlike-2types.json b/test/invalid/json/setlike-2types.json
index c9e49b6..2900e1b 100644
--- a/test/invalid/json/setlike-2types.json
+++ b/test/invalid/json/setlike-2types.json
@@ -1,4 +1,4 @@
 {
-    "message": "Unterminated setlike declaration",
+    "message": "Got an error during or right after parsing `interface SetLikeTwoTypes`: Unterminated setlike declaration",
     "line": 2
-}
\ No newline at end of file
+}
diff --git a/test/invalid/json/setter-creator.json b/test/invalid/json/setter-creator.json
index 07b9f1f..25decb3 100644
--- a/test/invalid/json/setter-creator.json
+++ b/test/invalid/json/setter-creator.json
@@ -1,4 +1,4 @@
 {
-    "message": "Invalid operation",
+    "message": "Got an error during or right after parsing `interface OrderedMap`: Invalid operation",
     "line": 3
 }
diff --git a/test/invalid/json/special-omittable.json b/test/invalid/json/special-omittable.json
index 7acb088..c20b28e 100644
--- a/test/invalid/json/special-omittable.json
+++ b/test/invalid/json/special-omittable.json
@@ -1,4 +1,4 @@
 {
-    "message":  "Invalid operation"
+    "message":  "Got an error during or right after parsing `interface Dictionary`: Invalid operation"
 ,   "line":     6
 }
diff --git a/test/invalid/json/stringconstants.json b/test/invalid/json/stringconstants.json
index d5bf1a8..1eeb31c 100644
--- a/test/invalid/json/stringconstants.json
+++ b/test/invalid/json/stringconstants.json
@@ -1,4 +1,4 @@
 {
-    "message":  "No value for const"
+    "message":  "Got an error during or right after parsing `interface Util`: No value for const"
 ,   "line":     2
 }
diff --git a/test/invalid/json/typedef-nested.json b/test/invalid/json/typedef-nested.json
index d7fb918..8c60814 100644
--- a/test/invalid/json/typedef-nested.json
+++ b/test/invalid/json/typedef-nested.json
@@ -1,4 +1,4 @@
 {
-    "message":  "Invalid operation"
+    "message":  "Got an error during or right after parsing `interface Widget`: Invalid operation"
 ,   "line":     14
-}
\ No newline at end of file
+}