Repository: ember-template-lint/ember-template-lint
Pull request #450 (2018-07-10 18:30:43)

Fix scope tracking


Patch:
diff --git a/lib/rules/base.js b/lib/rules/base.js
index 266a8250..eb883a84 100644
--- a/lib/rules/base.js
+++ b/lib/rules/base.js
@@ -96,6 +96,10 @@ module.exports = class BaseRule {
     // according to whether they will be called before/after the rule's AST
     // event handlers.
     let astHandlers = {
+      Program: {
+        enter: { before: [], after: [] },
+        exit: { before: [], after: [] }
+      },
       CommentStatement: {
         enter: { before: [], after: [] },
         exit: { before: [], after: [] }
@@ -148,6 +152,7 @@ module.exports = class BaseRule {
       visitor[key] = this._wrapVisitor(ruleVisitor[key], astHandlers[key]);
     }
 
+    visitor.Program = visitor.Program || this._wrapVisitor(null, astHandlers.Program);
     visitor.CommentStatement = visitor.CommentStatement || this._wrapVisitor(null, astHandlers.CommentStatement);
     visitor.MustacheCommentStatement = visitor.MustacheCommentStatement || this._wrapVisitor(null, astHandlers.MustacheCommentStatement);
     visitor.BlockStatement = visitor.BlockStatement || this._wrapVisitor(null, astHandlers.BlockStatement);
@@ -158,25 +163,19 @@ module.exports = class BaseRule {
     //
 
     // Manage scope
-    astHandlers.BlockStatement.enter.before.push(node => {
+    function pushFrame(node) {
       scope.pushFrame(node);
-    });
-    astHandlers.BlockStatement.exit.after.push(node => {
-      if (node !== scope.currentNode) {
-        throw new Error('Element stack out of sync with AST!');
-      }
-      scope.popFrame();
-    });
+    }
 
-    astHandlers.ElementNode.keys.children.enter.before.push(function(elementNode) {
-      scope.pushFrame(elementNode);
-    });
-    astHandlers.ElementNode.exit.after.push(function(elementNode) {
-      if (elementNode !== scope.currentNode) {
-        throw new Error('Element stack out of sync with AST!');
-      }
+    function popFrame() {
       scope.popFrame();
-    });
+    }
+
+    astHandlers.Program.enter.before.push(pushFrame);
+    astHandlers.Program.exit.after.push(popFrame);
+
+    astHandlers.ElementNode.keys.children.enter.before.push(pushFrame);
+    astHandlers.ElementNode.keys.children.exit.after.push(popFrame);
 
     // Manage inElementComments
     astHandlers.ElementNode.keys.comments.enter.before.push(function() {
diff --git a/lib/rules/internal/scope.js b/lib/rules/internal/scope.js
index bd5e1812..26bfb986 100644
--- a/lib/rules/internal/scope.js
+++ b/lib/rules/internal/scope.js
@@ -38,7 +38,7 @@ function getLocals(node) {
 
 class Frame {
   constructor(node) {
-    let locals =  getLocals(node);
+    let locals = getLocals(node);
 
     this.node = node;
     this.locals = locals;
diff --git a/lib/rules/lint-no-unused-block-params.js b/lib/rules/lint-no-unused-block-params.js
index 34dd4f98..06c62f0b 100644
--- a/lib/rules/lint-no-unused-block-params.js
+++ b/lib/rules/lint-no-unused-block-params.js
@@ -46,7 +46,7 @@ module.exports = class UnusedBlockParams extends Rule {
   visitor() {
 
     return {
-      BlockStatement: {
+      Program: {
         exit(node) {
           let unusedLocal = this.scope.frameHasUnusedBlockParams();
           if (unusedLocal) {


Test patch:
diff --git a/test/unit/rules/lint-no-unused-block-params-test.js b/test/unit/rules/lint-no-unused-block-params-test.js
index a8395243..b80b8e75 100644
--- a/test/unit/rules/lint-no-unused-block-params-test.js
+++ b/test/unit/rules/lint-no-unused-block-params-test.js
@@ -19,6 +19,7 @@ generateRuleTests({
         '{{index}}: {{life}}' +
       '{{/each}}' +
     '{{/each}}',
+    '{{#each cats as |cat|}}{{#meow-meow cat as |cat|}}{{cat}}{{/meow-meow}}{{/each}}',
     '{{! template-lint-disable unused-block-params}}{{#each cats as |cat|}}Dogs{{/each}}',
     '{{#with (component "foo-bar") as |FooBar|}}<FooBar />{{/with}}',
   ],
@@ -30,9 +31,9 @@ generateRuleTests({
       result: {
         message: '\'cat\' is defined but never used',
         moduleId: 'layout.hbs',
-        source: '{{#each cats as |cat|}}Dogs{{/each}}',
+        source: 'Dogs',
         line: 1,
-        column: 0
+        column: 23
       }
     },
     {
@@ -40,9 +41,9 @@ generateRuleTests({
       result: {
         message: '\'index\' is defined but never used',
         moduleId: 'layout.hbs',
-        source: '{{#each cats as |cat index|}}{{cat}}{{/each}}',
+        source: '{{cat}}',
         line: 1,
-        column: 0
+        column: 29
       }
     },
     {
@@ -55,9 +56,9 @@ generateRuleTests({
       result: {
         message: '\'index\' is defined but never used',
         moduleId: 'layout.hbs',
-        source: '{{#each cats as |cat index|}}{{#each cat.lives as |life index|}}{{index}}: {{life}}{{/each}}{{/each}}',
+        source: '{{#each cat.lives as |life index|}}{{index}}: {{life}}{{/each}}',
         line: 1,
-        column: 0
+        column: 29
       }
     },    {
       template:
@@ -68,9 +69,9 @@ generateRuleTests({
       result: {
         message: '\'life\' is defined but never used',
         moduleId: 'layout.hbs',
-        source: '{{#each cat.lives as |life|}}Life{{/each}}',
+        source: 'Life',
         line: 1,
-        column: 46,
+        column: 75,
       }
     }
   ]