diff --git a/docs/rules/no-template-shadow.md b/docs/rules/no-template-shadow.md
new file mode 100644
index 000000000..09b079334
--- /dev/null
+++ b/docs/rules/no-template-shadow.md
@@ -0,0 +1,56 @@
+# Disallow variable declarations from shadowing variables declared in the outer scope. (no-template-shadow)
+
+`no-template-shadow` should report variable definitions of v-for directives or scope attributes if those shadows the variables in parent scopes.
+
+## :book: Rule Details
+
+This rule aims to eliminate shadowed variable declarations of v-for directives or scope attributes.
+
+:-1: Examples of **incorrect** code for this rule:
+
+```html
+
+
+
+ ```
+
+ ```html
+
+
+
+
+ ```
+
+:+1: Examples of **correct** code for this rule:
+
+```html
+
+
+
+
+
+```
+
+## :wrench: Options
+
+Nothing.
diff --git a/lib/rules/no-template-shadow.js b/lib/rules/no-template-shadow.js
new file mode 100644
index 000000000..6a476d67d
--- /dev/null
+++ b/lib/rules/no-template-shadow.js
@@ -0,0 +1,76 @@
+/**
+ * @fileoverview Disallow variable declarations from shadowing variables declared in the outer scope.
+ * @author Armano
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const GROUP_NAMES = ['props', 'computed', 'data', 'methods']
+
+module.exports = {
+ meta: {
+ docs: {
+ description: 'disallow variable declarations from shadowing variables declared in the outer scope',
+ category: 'strongly-recommended',
+ url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.1/docs/rules/no-template-shadow.md'
+ },
+ fixable: null,
+ schema: []
+ },
+
+ create (context) {
+ const jsVars = new Set()
+ let scope = {
+ parent: null,
+ nodes: []
+ }
+
+ // ----------------------------------------------------------------------
+ // Public
+ // ----------------------------------------------------------------------
+
+ return utils.defineTemplateBodyVisitor(context, {
+ VElement (node) {
+ scope = {
+ parent: scope,
+ nodes: scope.nodes.slice() // make copy
+ }
+ if (node.variables) {
+ for (const variable of node.variables) {
+ const varNode = variable.id
+ const name = varNode.name
+ if (scope.nodes.some(node => node.name === name) || jsVars.has(name)) {
+ context.report({
+ node: varNode,
+ loc: varNode.loc,
+ message: "Variable '{{name}}' is already declared in the upper scope.",
+ data: {
+ name
+ }
+ })
+ } else {
+ scope.nodes.push(varNode)
+ }
+ }
+ }
+ },
+ 'VElement:exit' (node) {
+ scope = scope.parent
+ }
+ }, utils.executeOnVue(context, (obj) => {
+ const properties = Array.from(utils.iterateProperties(obj, new Set(GROUP_NAMES)))
+ for (const node of properties) {
+ jsVars.add(node.name)
+ }
+ }))
+ }
+}
diff --git a/lib/utils/index.js b/lib/utils/index.js
index f45950fcd..e0279636c 100644
--- a/lib/utils/index.js
+++ b/lib/utils/index.js
@@ -26,7 +26,7 @@ module.exports = {
*
* @param {RuleContext} context The rule context to use parser services.
* @param {Object} templateBodyVisitor The visitor to traverse the template body.
- * @param {Object} scriptVisitor The visitor to traverse the script.
+ * @param {Object} [scriptVisitor] The visitor to traverse the script.
* @returns {Object} The merged visitor.
*/
defineTemplateBodyVisitor (context, templateBodyVisitor, scriptVisitor) {
diff --git a/tests/lib/rules/no-template-shadow.js b/tests/lib/rules/no-template-shadow.js
new file mode 100644
index 000000000..c52f4ddb4
--- /dev/null
+++ b/tests/lib/rules/no-template-shadow.js
@@ -0,0 +1,213 @@
+/**
+ * @fileoverview Disallow variable declarations from shadowing variables declared in the outer scope.
+ * @author Armano
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-template-shadow')
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+ parser: 'vue-eslint-parser',
+ parserOptions: {
+ ecmaVersion: 2018,
+ sourceType: 'module'
+ }
+})
+
+ruleTester.run('no-template-shadow', rule, {
+
+ valid: [
+ '',
+ '',
+ '',
+ '',
+ '',
+ ' ',
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+ `
+ }
+ ],
+
+ invalid: [
+ {
+ filename: 'test.vue',
+ code: '',
+ errors: [{
+ message: "Variable 'i' is already declared in the upper scope.",
+ type: 'Identifier'
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+ `,
+ errors: [{
+ message: "Variable 'i' is already declared in the upper scope.",
+ type: 'Identifier',
+ line: 2
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+ `,
+ errors: [{
+ message: "Variable 'i' is already declared in the upper scope.",
+ type: 'Identifier',
+ line: 2
+ }, {
+ message: "Variable 'i' is already declared in the upper scope.",
+ type: 'Identifier',
+ line: 3
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+ `,
+ errors: [{
+ message: "Variable 'i' is already declared in the upper scope.",
+ type: 'Identifier',
+ line: 2
+ }, {
+ message: "Variable 'i' is already declared in the upper scope.",
+ type: 'Identifier',
+ line: 3
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+ `,
+ errors: [{
+ message: "Variable 'i' is already declared in the upper scope.",
+ type: 'Identifier',
+ line: 2
+ }, {
+ message: "Variable 'f' is already declared in the upper scope.",
+ type: 'Identifier',
+ line: 3
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+
+
+
+ `,
+ errors: [{
+ message: "Variable 'e' is already declared in the upper scope.",
+ type: 'Identifier',
+ line: 6
+ }, {
+ message: "Variable 'f' is already declared in the upper scope.",
+ type: 'Identifier',
+ line: 7
+ }]
+ }
+ ]
+})