Repository: source-academy/js-slang
Pull request #310 (2019-07-25 14:03:54)

Disallow var statements
Let's disallow var statements and allow only const and let.

coveralls: ## Pull Request Test Coverage Report for [Build 1432](https://coveralls.io/builds/24792118)

* **17** of **17**   **(100.0%)**  changed or added relevant lines in **2** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.06%**) to **91.683%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/24792118/badge)](https://coveralls.io/builds/24792118) |
| :-- | --: |
| Change from base [Build 1426](https://coveralls.io/builds/24645152): |  0.06% |
| Covered Lines: | 1729 |
| Relevant Lines: | 1864 |

---
##### 💛  - [Coveralls](https://coveralls.io)

openorclose: Yeah. I wanted to combine the two also but it's not possible for now. 

Patch:
diff --git a/src/rules/index.ts b/src/rules/index.ts
index e29a0a16..bc699eb9 100644
--- a/src/rules/index.ts
+++ b/src/rules/index.ts
@@ -17,6 +17,7 @@ import noNull from './noNull'
 import noUnspecifiedLiteral from './noUnspecifiedLiteral'
 import noUnspecifiedOperator from './noUnspecifiedOperator'
 import noUpdateAssignment from './noUpdateAssignment'
+import noVar from './noVar'
 import singleVariableDeclaration from './singleVariableDeclaration'
 
 const rules: Array<Rule<es.Node>> = [
@@ -34,6 +35,7 @@ const rules: Array<Rule<es.Node>> = [
   noUnspecifiedLiteral,
   noUnspecifiedOperator,
   noUpdateAssignment,
+  noVar,
   singleVariableDeclaration,
   noEval
 ]
diff --git a/src/rules/noVar.ts b/src/rules/noVar.ts
new file mode 100644
index 00000000..16c7539c
--- /dev/null
+++ b/src/rules/noVar.ts
@@ -0,0 +1,42 @@
+import { generate } from 'astring'
+import * as es from 'estree'
+
+import { ErrorSeverity, ErrorType, Rule, SourceError } from '../types'
+
+export class NoVarError implements SourceError {
+  public type = ErrorType.SYNTAX
+  public severity = ErrorSeverity.ERROR
+
+  constructor(public node: es.VariableDeclaration) {}
+
+  get location() {
+    return this.node.loc!
+  }
+
+  public explain() {
+    return 'Variable declaration using "var" is not allowed.'
+  }
+
+  public elaborate() {
+    const name = (this.node.declarations[0].id as es.Identifier).name
+    const value = generate(this.node.declarations[0].init)
+
+    return `Use keyword "let" instead, to declare a variable:\n\n\tlet ${name} = ${value};`
+  }
+}
+
+const noVar: Rule<es.VariableDeclaration> = {
+  name: 'no-var',
+
+  checkers: {
+    VariableDeclaration(node: es.VariableDeclaration, ancestors: [es.Node]) {
+      if (node.kind === 'var') {
+        return [new NoVarError(node)]
+      } else {
+        return []
+      }
+    }
+  }
+}
+
+export default noVar


Test patch:
diff --git a/src/__tests__/__snapshots__/disallowed-syntax.ts.snap b/src/__tests__/__snapshots__/disallowed-syntax.ts.snap
index 0437de66..299402c2 100644
--- a/src/__tests__/__snapshots__/disallowed-syntax.ts.snap
+++ b/src/__tests__/__snapshots__/disallowed-syntax.ts.snap
@@ -11476,6 +11476,179 @@ typeof x;",
 }
 `;
 
+exports[`no var statements - verbose: expectParsedError 1`] = `
+Object {
+  "alertResult": Array [],
+  "code": "\\"enable verbose\\";
+ var x = 1;",
+  "displayResult": Array [],
+  "errors": Array [
+    NoVarError {
+      "node": Node {
+        "declarations": Array [
+          Node {
+            "end": 28,
+            "id": Node {
+              "end": 24,
+              "loc": SourceLocation {
+                "end": Position {
+                  "column": 6,
+                  "line": 2,
+                },
+                "start": Position {
+                  "column": 5,
+                  "line": 2,
+                },
+              },
+              "name": "x",
+              "start": 23,
+              "type": "Identifier",
+            },
+            "init": Node {
+              "end": 28,
+              "loc": SourceLocation {
+                "end": Position {
+                  "column": 10,
+                  "line": 2,
+                },
+                "start": Position {
+                  "column": 9,
+                  "line": 2,
+                },
+              },
+              "raw": "1",
+              "start": 27,
+              "type": "Literal",
+              "value": 1,
+            },
+            "loc": SourceLocation {
+              "end": Position {
+                "column": 10,
+                "line": 2,
+              },
+              "start": Position {
+                "column": 5,
+                "line": 2,
+              },
+            },
+            "start": 23,
+            "type": "VariableDeclarator",
+          },
+        ],
+        "end": 29,
+        "kind": "var",
+        "loc": SourceLocation {
+          "end": Position {
+            "column": 11,
+            "line": 2,
+          },
+          "start": Position {
+            "column": 1,
+            "line": 2,
+          },
+        },
+        "start": 19,
+        "type": "VariableDeclaration",
+      },
+      "severity": "Error",
+      "type": "Syntax",
+    },
+  ],
+  "parsedErrors": "Line 2, Column 1: Variable declaration using \\"var\\" is not allowed.
+Use keyword \\"let\\" instead, to declare a variable:
+
+	let x = 1;
+",
+  "result": undefined,
+  "resultStatus": "error",
+  "visualiseListResult": Array [],
+}
+`;
+
+exports[`no var statements: expectParsedError 1`] = `
+Object {
+  "alertResult": Array [],
+  "code": "var x = 1;",
+  "displayResult": Array [],
+  "errors": Array [
+    NoVarError {
+      "node": Node {
+        "declarations": Array [
+          Node {
+            "end": 9,
+            "id": Node {
+              "end": 5,
+              "loc": SourceLocation {
+                "end": Position {
+                  "column": 5,
+                  "line": 1,
+                },
+                "start": Position {
+                  "column": 4,
+                  "line": 1,
+                },
+              },
+              "name": "x",
+              "start": 4,
+              "type": "Identifier",
+            },
+            "init": Node {
+              "end": 9,
+              "loc": SourceLocation {
+                "end": Position {
+                  "column": 9,
+                  "line": 1,
+                },
+                "start": Position {
+                  "column": 8,
+                  "line": 1,
+                },
+              },
+              "raw": "1",
+              "start": 8,
+              "type": "Literal",
+              "value": 1,
+            },
+            "loc": SourceLocation {
+              "end": Position {
+                "column": 9,
+                "line": 1,
+              },
+              "start": Position {
+                "column": 4,
+                "line": 1,
+              },
+            },
+            "start": 4,
+            "type": "VariableDeclarator",
+          },
+        ],
+        "end": 10,
+        "kind": "var",
+        "loc": SourceLocation {
+          "end": Position {
+            "column": 10,
+            "line": 1,
+          },
+          "start": Position {
+            "column": 0,
+            "line": 1,
+          },
+        },
+        "start": 0,
+        "type": "VariableDeclaration",
+      },
+      "severity": "Error",
+      "type": "Syntax",
+    },
+  ],
+  "parsedErrors": "Line 1: Variable declaration using \\"var\\" is not allowed.",
+  "result": undefined,
+  "resultStatus": "error",
+  "visualiseListResult": Array [],
+}
+`;
+
 exports[`while needs braces - verbose: expectParsedError 1`] = `
 Object {
   "alertResult": Array [],
diff --git a/src/__tests__/disallowed-syntax.ts b/src/__tests__/disallowed-syntax.ts
index c758ad85..7b97d96e 100644
--- a/src/__tests__/disallowed-syntax.ts
+++ b/src/__tests__/disallowed-syntax.ts
@@ -435,6 +435,31 @@ For instance, to assign 20 to x, you can write:
 `)
 })
 
+test('no var statements', () => {
+  return expectParsedError(
+    stripIndent`
+  var x = 1;
+  `,
+    { chapter: 100 }
+  ).toMatchInlineSnapshot(`"Line 1: Variable declaration using \\"var\\" is not allowed."`)
+})
+
+test('no var statements - verbose', () => {
+  return expectParsedError(
+    stripIndent`
+	"enable verbose";
+  var x = 1;
+  `,
+    { chapter: 100 }
+  ).toMatchInlineSnapshot(`
+"Line 2, Column 1: Variable declaration using \\"var\\" is not allowed.
+Use keyword \\"let\\" instead, to declare a variable:
+
+	let x = 1;
+"
+`)
+})
+
 test('Cannot use update statements', () => {
   return expectParsedError(
     stripIndent`