Repository: vuejs/eslint-plugin-vue
Pull request #1565 (2021-07-14 07:28:02)

Improve report location of `vue/valid-v-xxx` rules


Patch:
diff --git a/docs/rules/valid-v-else-if.md b/docs/rules/valid-v-else-if.md
index 85c33e55..5b7b9f63 100644
--- a/docs/rules/valid-v-else-if.md
+++ b/docs/rules/valid-v-else-if.md
@@ -32,9 +32,10 @@ This rule reports `v-else-if` directives in the following cases:
   <div v-else-if="bar"/>
 
   <!-- ✗ BAD -->
-  <div v-else-if/>
-  <div v-else-if:aaa="foo"/>
-  <div v-else-if.bbb="foo"/>
+  <div /><div v-else-if="foo"/>
+  <div v-if="x"/><div v-else-if/>
+  <div v-if="x"/><div v-else-if:aaa="foo"/>
+  <div v-if="x"/><div v-else-if.bbb="foo"/>
 </template>
 ```
 
diff --git a/docs/rules/valid-v-else.md b/docs/rules/valid-v-else.md
index 35b26cee..b3424ad2 100644
--- a/docs/rules/valid-v-else.md
+++ b/docs/rules/valid-v-else.md
@@ -32,9 +32,10 @@ This rule reports `v-else` directives in the following cases:
   <div v-else/>
 
   <!-- ✗ BAD -->
-  <div v-else="foo"/>
-  <div v-else:aaa/>
-  <div v-else.bbb/>
+  <div v-else/>
+  <div v-if="x"/><div v-else="foo"/>
+  <div v-if="x"/><div v-else:aaa/>
+  <div v-if="x"/><div v-else.bbb/>
 </template>
 ```
 
diff --git a/lib/rules/valid-v-bind-sync.js b/lib/rules/valid-v-bind-sync.js
index 945ab0a7..2a42e1b3 100644
--- a/lib/rules/valid-v-bind-sync.js
+++ b/lib/rules/valid-v-bind-sync.js
@@ -119,7 +119,6 @@ module.exports = {
         if (!isValidElement(element)) {
           context.report({
             node,
-            loc: node.loc,
             messageId: 'unexpectedInvalidElement',
             data: { name }
           })
@@ -136,20 +135,17 @@ module.exports = {
         }
         if (isOptionalChainingMemberExpression(expression)) {
           context.report({
-            node,
-            loc: node.loc,
+            node: expression,
             messageId: 'unexpectedOptionalChaining'
           })
         } else if (!isLhs(expression)) {
           context.report({
-            node,
-            loc: node.loc,
+            node: expression,
             messageId: 'unexpectedNonLhsExpression'
           })
         } else if (maybeNullObjectMemberExpression(expression)) {
           context.report({
-            node,
-            loc: node.loc,
+            node: expression,
             messageId: 'unexpectedNullObject'
           })
         }
@@ -162,8 +158,7 @@ module.exports = {
           const variable = reference.variable
           if (variable) {
             context.report({
-              node,
-              loc: node.loc,
+              node: expression,
               messageId: 'unexpectedUpdateIterationVariable',
               data: { varName: id.name }
             })
diff --git a/lib/rules/valid-v-bind.js b/lib/rules/valid-v-bind.js
index 385272f7..5b9339c1 100644
--- a/lib/rules/valid-v-bind.js
+++ b/lib/rules/valid-v-bind.js
@@ -30,7 +30,12 @@ module.exports = {
       url: 'https://eslint.vuejs.org/rules/valid-v-bind.html'
     },
     fixable: null,
-    schema: []
+    schema: [],
+    messages: {
+      unsupportedModifier:
+        "'v-bind' directives don't support the modifier '{{name}}'.",
+      expectedValue: "'v-bind' directives require an attribute value."
+    }
   },
   /** @param {RuleContext} context */
   create(context) {
@@ -40,10 +45,8 @@ module.exports = {
         for (const modifier of node.key.modifiers) {
           if (!VALID_MODIFIERS.has(modifier.name)) {
             context.report({
-              node,
-              loc: node.key.loc,
-              message:
-                "'v-bind' directives don't support the modifier '{{name}}'.",
+              node: modifier,
+              messageId: 'unsupportedModifier',
               data: { name: modifier.name }
             })
           }
@@ -52,8 +55,7 @@ module.exports = {
         if (!node.value || utils.isEmptyValueDirective(node, context)) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-bind' directives require an attribute value."
+            messageId: 'expectedValue'
           })
         }
       }
diff --git a/lib/rules/valid-v-cloak.js b/lib/rules/valid-v-cloak.js
index 4fd51d78..efcaf694 100644
--- a/lib/rules/valid-v-cloak.js
+++ b/lib/rules/valid-v-cloak.js
@@ -24,7 +24,12 @@ module.exports = {
       url: 'https://eslint.vuejs.org/rules/valid-v-cloak.html'
     },
     fixable: null,
-    schema: []
+    schema: [],
+    messages: {
+      unexpectedArgument: "'v-cloak' directives require no argument.",
+      unexpectedModifier: "'v-cloak' directives require no modifier.",
+      unexpectedValue: "'v-cloak' directives require no attribute value."
+    }
   },
   /** @param {RuleContext} context */
   create(context) {
@@ -33,23 +38,24 @@ module.exports = {
       "VAttribute[directive=true][key.name.name='cloak']"(node) {
         if (node.key.argument) {
           context.report({
-            node,
-            loc: node.loc,
-            message: "'v-cloak' directives require no argument."
+            node: node.key.argument,
+            messageId: 'unexpectedArgument'
           })
         }
         if (node.key.modifiers.length > 0) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-cloak' directives require no modifier."
+            loc: {
+              start: node.key.modifiers[0].loc.start,
+              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
+            },
+            messageId: 'unexpectedModifier'
           })
         }
         if (node.value) {
           context.report({
-            node,
-            loc: node.loc,
-            message: "'v-cloak' directives require no attribute value."
+            node: node.value,
+            messageId: 'unexpectedValue'
           })
         }
       }
diff --git a/lib/rules/valid-v-else-if.js b/lib/rules/valid-v-else-if.js
index aafe97b1..c2c0c885 100644
--- a/lib/rules/valid-v-else-if.js
+++ b/lib/rules/valid-v-else-if.js
@@ -24,7 +24,18 @@ module.exports = {
       url: 'https://eslint.vuejs.org/rules/valid-v-else-if.html'
     },
     fixable: null,
-    schema: []
+    schema: [],
+    messages: {
+      missingVIf:
+        "'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive.",
+      withVIf:
+        "'v-else-if' and 'v-if' directives can't exist on the same element.",
+      withVElse:
+        "'v-else-if' and 'v-else' directives can't exist on the same element.",
+      unexpectedArgument: "'v-else-if' directives require no argument.",
+      unexpectedModifier: "'v-else-if' directives require no modifier.",
+      expectedValue: "'v-else-if' directives require that attribute value."
+    }
   },
   /** @param {RuleContext} context */
   create(context) {
@@ -36,46 +47,41 @@ module.exports = {
         if (!utils.prevElementHasIf(element)) {
           context.report({
             node,
-            loc: node.loc,
-            message:
-              "'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."
+            messageId: 'missingVIf'
           })
         }
         if (utils.hasDirective(element, 'if')) {
           context.report({
             node,
-            loc: node.loc,
-            message:
-              "'v-else-if' and 'v-if' directives can't exist on the same element."
+            messageId: 'withVIf'
           })
         }
         if (utils.hasDirective(element, 'else')) {
           context.report({
             node,
-            loc: node.loc,
-            message:
-              "'v-else-if' and 'v-else' directives can't exist on the same element."
+            messageId: 'withVElse'
           })
         }
         if (node.key.argument) {
           context.report({
-            node,
-            loc: node.loc,
-            message: "'v-else-if' directives require no argument."
+            node: node.key.argument,
+            messageId: 'unexpectedArgument'
           })
         }
         if (node.key.modifiers.length > 0) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-else-if' directives require no modifier."
+            loc: {
+              start: node.key.modifiers[0].loc.start,
+              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
+            },
+            messageId: 'unexpectedModifier'
           })
         }
         if (!node.value || utils.isEmptyValueDirective(node, context)) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-else-if' directives require that attribute value."
+            messageId: 'expectedValue'
           })
         }
       }
diff --git a/lib/rules/valid-v-else.js b/lib/rules/valid-v-else.js
index 7a854278..7c4bd28a 100644
--- a/lib/rules/valid-v-else.js
+++ b/lib/rules/valid-v-else.js
@@ -24,7 +24,18 @@ module.exports = {
       url: 'https://eslint.vuejs.org/rules/valid-v-else.html'
     },
     fixable: null,
-    schema: []
+    schema: [],
+    messages: {
+      missingVIf:
+        "'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive.",
+      withVIf:
+        "'v-else' and 'v-if' directives can't exist on the same element. You may want 'v-else-if' directives.",
+      withVElseIf:
+        "'v-else' and 'v-else-if' directives can't exist on the same element.",
+      unexpectedArgument: "'v-else' directives require no argument.",
+      unexpectedModifier: "'v-else' directives require no modifier.",
+      unexpectedValue: "'v-else' directives require no attribute value."
+    }
   },
   /** @param {RuleContext} context */
   create(context) {
@@ -36,46 +47,41 @@ module.exports = {
         if (!utils.prevElementHasIf(element)) {
           context.report({
             node,
-            loc: node.loc,
-            message:
-              "'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."
+            messageId: 'missingVIf'
           })
         }
         if (utils.hasDirective(element, 'if')) {
           context.report({
             node,
-            loc: node.loc,
-            message:
-              "'v-else' and 'v-if' directives can't exist on the same element. You may want 'v-else-if' directives."
+            messageId: 'withVIf'
           })
         }
         if (utils.hasDirective(element, 'else-if')) {
           context.report({
             node,
-            loc: node.loc,
-            message:
-              "'v-else' and 'v-else-if' directives can't exist on the same element."
+            messageId: 'withVElseIf'
           })
         }
         if (node.key.argument) {
           context.report({
-            node,
-            loc: node.loc,
-            message: "'v-else' directives require no argument."
+            node: node.key.argument,
+            messageId: 'unexpectedArgument'
           })
         }
         if (node.key.modifiers.length > 0) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-else' directives require no modifier."
+            loc: {
+              start: node.key.modifiers[0].loc.start,
+              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
+            },
+            messageId: 'unexpectedModifier'
           })
         }
         if (node.value) {
           context.report({
-            node,
-            loc: node.loc,
-            message: "'v-else' directives require no attribute value."
+            node: node.value,
+            messageId: 'unexpectedValue'
           })
         }
       }
diff --git a/lib/rules/valid-v-for.js b/lib/rules/valid-v-for.js
index 0d560aa9..0acf7dba 100644
--- a/lib/rules/valid-v-for.js
+++ b/lib/rules/valid-v-for.js
@@ -84,16 +84,13 @@ function checkKey(context, vFor, element) {
   if (utils.isCustomComponent(element) && vBindKey == null) {
     context.report({
       node: element.startTag,
-      loc: element.startTag.loc,
-      message: "Custom elements in iteration require 'v-bind:key' directives."
+      messageId: 'requireKey'
     })
   }
   if (vBindKey != null && !isUsingIterationVar(vFor, vBindKey)) {
     context.report({
       node: vBindKey,
-      loc: vBindKey.loc,
-      message:
-        "Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive."
+      messageId: 'keyUseFVorVars'
     })
   }
 }
@@ -111,7 +108,20 @@ module.exports = {
       url: 'https://eslint.vuejs.org/rules/valid-v-for.html'
     },
     fixable: null,
-    schema: []
+    schema: [],
+    messages: {
+      requireKey:
+        "Custom elements in iteration require 'v-bind:key' directives.",
+      keyUseFVorVars:
+        "Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive.",
+      unexpectedArgument: "'v-for' directives require no argument.",
+      unexpectedModifier: "'v-for' directives require no modifier.",
+      expectedValue: "'v-for' directives require that attribute value.",
+      unexpectedExpression:
+        "'v-for' directives require the special syntax '<alias> in <expression>'.",
+      invalidEmptyAlias: "Invalid alias ''.",
+      invalidAlias: "Invalid alias '{{text}}'."
+    }
   },
   /** @param {RuleContext} context */
   create(context) {
@@ -126,23 +136,24 @@ module.exports = {
 
         if (node.key.argument) {
           context.report({
-            node,
-            loc: node.loc,
-            message: "'v-for' directives require no argument."
+            node: node.key.argument,
+            messageId: 'unexpectedArgument'
           })
         }
         if (node.key.modifiers.length > 0) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-for' directives require no modifier."
+            loc: {
+              start: node.key.modifiers[0].loc.start,
+              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
+            },
+            messageId: 'unexpectedModifier'
           })
         }
         if (!node.value || utils.isEmptyValueDirective(node, context)) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-for' directives require that attribute value."
+            messageId: 'expectedValue'
           })
           return
         }
@@ -154,9 +165,7 @@ module.exports = {
         if (expr.type !== 'VForExpression') {
           context.report({
             node: node.value,
-            loc: node.value.loc,
-            message:
-              "'v-for' directives require the special syntax '<alias> in <expression>'."
+            messageId: 'unexpectedExpression'
           })
           return
         }
@@ -169,22 +178,20 @@ module.exports = {
         if (value === null) {
           context.report({
             node: expr,
-            message: "Invalid alias ''."
+            messageId: 'invalidEmptyAlias'
           })
         }
         if (key !== undefined && (!key || key.type !== 'Identifier')) {
           context.report({
             node: key || expr,
-            loc: key && key.loc,
-            message: "Invalid alias '{{text}}'.",
+            messageId: 'invalidAlias',
             data: { text: key ? sourceCode.getText(key) : '' }
           })
         }
         if (index !== undefined && (!index || index.type !== 'Identifier')) {
           context.report({
             node: index || expr,
-            loc: index && index.loc,
-            message: "Invalid alias '{{text}}'.",
+            messageId: 'invalidAlias',
             data: { text: index ? sourceCode.getText(index) : '' }
           })
         }
diff --git a/lib/rules/valid-v-html.js b/lib/rules/valid-v-html.js
index d8899768..be39026d 100644
--- a/lib/rules/valid-v-html.js
+++ b/lib/rules/valid-v-html.js
@@ -24,7 +24,12 @@ module.exports = {
       url: 'https://eslint.vuejs.org/rules/valid-v-html.html'
     },
     fixable: null,
-    schema: []
+    schema: [],
+    messages: {
+      unexpectedArgument: "'v-html' directives require no argument.",
+      unexpectedModifier: "'v-html' directives require no modifier.",
+      expectedValue: "'v-html' directives require that attribute value."
+    }
   },
   /** @param {RuleContext} context */
   create(context) {
@@ -33,23 +38,24 @@ module.exports = {
       "VAttribute[directive=true][key.name.name='html']"(node) {
         if (node.key.argument) {
           context.report({
-            node,
-            loc: node.loc,
-            message: "'v-html' directives require no argument."
+            node: node.key.argument,
+            messageId: 'unexpectedArgument'
           })
         }
         if (node.key.modifiers.length > 0) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-html' directives require no modifier."
+            loc: {
+              start: node.key.modifiers[0].loc.start,
+              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
+            },
+            messageId: 'unexpectedModifier'
           })
         }
         if (!node.value || utils.isEmptyValueDirective(node, context)) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-html' directives require that attribute value."
+            messageId: 'expectedValue'
           })
         }
       }
diff --git a/lib/rules/valid-v-if.js b/lib/rules/valid-v-if.js
index b61435ab..7ac4287b 100644
--- a/lib/rules/valid-v-if.js
+++ b/lib/rules/valid-v-if.js
@@ -24,7 +24,16 @@ module.exports = {
       url: 'https://eslint.vuejs.org/rules/valid-v-if.html'
     },
     fixable: null,
-    schema: []
+    schema: [],
+    messages: {
+      withVElse:
+        "'v-if' and 'v-else' directives can't exist on the same element. You may want 'v-else-if' directives.",
+      withVElseIf:
+        "'v-if' and 'v-else-if' directives can't exist on the same element.",
+      unexpectedArgument: "'v-if' directives require no argument.",
+      unexpectedModifier: "'v-if' directives require no modifier.",
+      expectedValue: "'v-if' directives require that attribute value."
+    }
   },
   /** @param {RuleContext} context */
   create(context) {
@@ -36,38 +45,35 @@ module.exports = {
         if (utils.hasDirective(element, 'else')) {
           context.report({
             node,
-            loc: node.loc,
-            message:
-              "'v-if' and 'v-else' directives can't exist on the same element. You may want 'v-else-if' directives."
+            messageId: 'withVElse'
           })
         }
         if (utils.hasDirective(element, 'else-if')) {
           context.report({
             node,
-            loc: node.loc,
-            message:
-              "'v-if' and 'v-else-if' directives can't exist on the same element."
+            messageId: 'withVElseIf'
           })
         }
         if (node.key.argument) {
           context.report({
-            node,
-            loc: node.loc,
-            message: "'v-if' directives require no argument."
+            node: node.key.argument,
+            messageId: 'unexpectedArgument'
           })
         }
         if (node.key.modifiers.length > 0) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-if' directives require no modifier."
+            loc: {
+              start: node.key.modifiers[0].loc.start,
+              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
+            },
+            messageId: 'unexpectedModifier'
           })
         }
         if (!node.value || utils.isEmptyValueDirective(node, context)) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-if' directives require that attribute value."
+            messageId: 'expectedValue'
           })
         }
       }
diff --git a/lib/rules/valid-v-is.js b/lib/rules/valid-v-is.js
index 5c430ce1..584d7574 100644
--- a/lib/rules/valid-v-is.js
+++ b/lib/rules/valid-v-is.js
@@ -60,22 +60,23 @@ module.exports = {
       "VAttribute[directive=true][key.name.name='is']"(node) {
         if (node.key.argument) {
           context.report({
-            node,
-            loc: node.loc,
+            node: node.key.argument,
             messageId: 'unexpectedArgument'
           })
         }
         if (node.key.modifiers.length > 0) {
           context.report({
             node,
-            loc: node.loc,
+            loc: {
+              start: node.key.modifiers[0].loc.start,
+              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
+            },
             messageId: 'unexpectedModifier'
           })
         }
         if (!node.value || utils.isEmptyValueDirective(node, context)) {
           context.report({
             node,
-            loc: node.loc,
             messageId: 'expectedValue'
           })
         }
@@ -86,7 +87,6 @@ module.exports = {
           const name = element.name
           context.report({
             node,
-            loc: node.loc,
             messageId: 'ownerMustBeHTMLElement',
             data: { name }
           })
diff --git a/lib/rules/valid-v-model.js b/lib/rules/valid-v-model.js
index adfe9c2d..eb8b63b2 100644
--- a/lib/rules/valid-v-model.js
+++ b/lib/rules/valid-v-model.js
@@ -159,7 +159,6 @@ module.exports = {
         if (!isValidElement(element)) {
           context.report({
             node,
-            loc: node.loc,
             messageId: 'unexpectedInvalidElement',
             data: { name }
           })
@@ -168,7 +167,6 @@ module.exports = {
         if (name === 'input' && utils.hasAttribute(element, 'type', 'file')) {
           context.report({
             node,
-            loc: node.loc,
             messageId: 'unexpectedInputFile'
           })
         }
@@ -176,8 +174,7 @@ module.exports = {
         if (!utils.isCustomComponent(element)) {
           if (node.key.argument) {
             context.report({
-              node,
-              loc: node.loc,
+              node: node.key.argument,
               messageId: 'unexpectedArgument'
             })
           }
@@ -185,8 +182,7 @@ module.exports = {
           for (const modifier of node.key.modifiers) {
             if (!VALID_MODIFIERS.has(modifier.name)) {
               context.report({
-                node,
-                loc: node.loc,
+                node: modifier,
                 messageId: 'unexpectedModifier',
                 data: { name: modifier.name }
               })
@@ -197,7 +193,6 @@ module.exports = {
         if (!node.value || utils.isEmptyValueDirective(node, context)) {
           context.report({
             node,
-            loc: node.loc,
             messageId: 'missingValue'
           })
           return
@@ -209,20 +204,17 @@ module.exports = {
         }
         if (isOptionalChainingMemberExpression(expression)) {
           context.report({
-            node,
-            loc: node.loc,
+            node: expression,
             messageId: 'unexpectedOptionalChaining'
           })
         } else if (!isLhs(expression)) {
           context.report({
-            node,
-            loc: node.loc,
+            node: expression,
             messageId: 'unexpectedNonLhsExpression'
           })
         } else if (maybeNullObjectMemberExpression(expression)) {
           context.report({
-            node,
-            loc: node.loc,
+            node: expression,
             messageId: 'unexpectedNullObject'
           })
         }
@@ -236,8 +228,7 @@ module.exports = {
           const variable = getVariable(id.name, element)
           if (variable != null) {
             context.report({
-              node,
-              loc: node.loc,
+              node: expression,
               messageId: 'unexpectedUpdateIterationVariable',
 
               data: { varName: id.name }
diff --git a/lib/rules/valid-v-on.js b/lib/rules/valid-v-on.js
index 4d55d7f1..864a5ccf 100644
--- a/lib/rules/valid-v-on.js
+++ b/lib/rules/valid-v-on.js
@@ -89,7 +89,15 @@ module.exports = {
         },
         additionalProperties: false
       }
-    ]
+    ],
+    messages: {
+      unsupportedModifier:
+        "'v-on' directives don't support the modifier '{{modifier}}'.",
+      avoidKeyword:
+        'Avoid using JavaScript keyword as "v-on" value: {{value}}.',
+      expectedValueOrVerb:
+        "'v-on' directives require a value or verb modifier (like 'stop' or 'prevent')."
+    }
   },
   /** @param {RuleContext} context */
   create(context) {
@@ -104,10 +112,8 @@ module.exports = {
         for (const modifier of node.key.modifiers) {
           if (!isValidModifier(modifier, customModifiers)) {
             context.report({
-              node,
-              loc: node.loc,
-              message:
-                "'v-on' directives don't support the modifier '{{modifier}}'.",
+              node: modifier,
+              messageId: 'unsupportedModifier',
               data: { modifier: modifier.name }
             })
           }
@@ -131,19 +137,15 @@ module.exports = {
             }
             if (/^\w+$/.test(innerText)) {
               context.report({
-                node,
-                loc: node.loc,
-                message:
-                  'Avoid using JavaScript keyword as "v-on" value: {{value}}.',
+                node: node.value,
+                messageId: 'avoidKeyword',
                 data: { value: valueText }
               })
             }
           } else {
             context.report({
               node,
-              loc: node.loc,
-              message:
-                "'v-on' directives require a value or verb modifier (like 'stop' or 'prevent')."
+              messageId: 'expectedValueOrVerb'
             })
           }
         }
diff --git a/lib/rules/valid-v-once.js b/lib/rules/valid-v-once.js
index 83ff3ddc..bacf8330 100644
--- a/lib/rules/valid-v-once.js
+++ b/lib/rules/valid-v-once.js
@@ -24,7 +24,12 @@ module.exports = {
       url: 'https://eslint.vuejs.org/rules/valid-v-once.html'
     },
     fixable: null,
-    schema: []
+    schema: [],
+    messages: {
+      unexpectedArgument: "'v-once' directives require no argument.",
+      unexpectedModifier: "'v-once' directives require no modifier.",
+      unexpectedValue: "'v-once' directives require no attribute value."
+    }
   },
   /** @param {RuleContext} context */
   create(context) {
@@ -33,23 +38,24 @@ module.exports = {
       "VAttribute[directive=true][key.name.name='once']"(node) {
         if (node.key.argument) {
           context.report({
-            node,
-            loc: node.loc,
-            message: "'v-once' directives require no argument."
+            node: node.key.argument,
+            messageId: 'unexpectedArgument'
           })
         }
         if (node.key.modifiers.length > 0) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-once' directives require no modifier."
+            loc: {
+              start: node.key.modifiers[0].loc.start,
+              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
+            },
+            messageId: 'unexpectedModifier'
           })
         }
         if (node.value) {
           context.report({
-            node,
-            loc: node.loc,
-            message: "'v-once' directives require no attribute value."
+            node: node.value,
+            messageId: 'unexpectedValue'
           })
         }
       }
diff --git a/lib/rules/valid-v-pre.js b/lib/rules/valid-v-pre.js
index 90174db1..0112ebde 100644
--- a/lib/rules/valid-v-pre.js
+++ b/lib/rules/valid-v-pre.js
@@ -24,7 +24,12 @@ module.exports = {
       url: 'https://eslint.vuejs.org/rules/valid-v-pre.html'
     },
     fixable: null,
-    schema: []
+    schema: [],
+    messages: {
+      unexpectedArgument: "'v-pre' directives require no argument.",
+      unexpectedModifier: "'v-pre' directives require no modifier.",
+      unexpectedValue: "'v-pre' directives require no attribute value."
+    }
   },
   /** @param {RuleContext} context */
   create(context) {
@@ -33,23 +38,24 @@ module.exports = {
       "VAttribute[directive=true][key.name.name='pre']"(node) {
         if (node.key.argument) {
           context.report({
-            node,
-            loc: node.loc,
-            message: "'v-pre' directives require no argument."
+            node: node.key.argument,
+            messageId: 'unexpectedArgument'
           })
         }
         if (node.key.modifiers.length > 0) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-pre' directives require no modifier."
+            loc: {
+              start: node.key.modifiers[0].loc.start,
+              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
+            },
+            messageId: 'unexpectedModifier'
           })
         }
         if (node.value) {
           context.report({
-            node,
-            loc: node.loc,
-            message: "'v-pre' directives require no attribute value."
+            node: node.value,
+            messageId: 'unexpectedValue'
           })
         }
       }
diff --git a/lib/rules/valid-v-show.js b/lib/rules/valid-v-show.js
index 721904f4..59ba9c43 100644
--- a/lib/rules/valid-v-show.js
+++ b/lib/rules/valid-v-show.js
@@ -24,7 +24,14 @@ module.exports = {
       url: 'https://eslint.vuejs.org/rules/valid-v-show.html'
     },
     fixable: null,
-    schema: []
+    schema: [],
+    messages: {
+      unexpectedArgument: "'v-show' directives require no argument.",
+      unexpectedModifier: "'v-show' directives require no modifier.",
+      expectedValue: "'v-show' directives require that attribute value.",
+      unexpectedTemplate:
+        "'v-show' directives cannot be put on <template> tags."
+    }
   },
   /** @param {RuleContext} context */
   create(context) {
@@ -33,30 +40,30 @@ module.exports = {
       "VAttribute[directive=true][key.name.name='show']"(node) {
         if (node.key.argument) {
           context.report({
-            node,
-            loc: node.loc,
-            message: "'v-show' directives require no argument."
+            node: node.key.argument,
+            messageId: 'unexpectedArgument'
           })
         }
         if (node.key.modifiers.length > 0) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-show' directives require no modifier."
+            loc: {
+              start: node.key.modifiers[0].loc.start,
+              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
+            },
+            messageId: 'unexpectedModifier'
           })
         }
         if (!node.value || utils.isEmptyValueDirective(node, context)) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-show' directives require that attribute value."
+            messageId: 'expectedValue'
           })
         }
         if (node.parent.parent.name === 'template') {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-show' directives cannot be put on <template> tags."
+            messageId: 'unexpectedTemplate'
           })
         }
       }
diff --git a/lib/rules/valid-v-slot.js b/lib/rules/valid-v-slot.js
index 8d484d25..df0fa7c6 100644
--- a/lib/rules/valid-v-slot.js
+++ b/lib/rules/valid-v-slot.js
@@ -378,6 +378,10 @@ module.exports = {
           // E.g., <template v-slot.foo>
           context.report({
             node,
+            loc: {
+              start: node.key.modifiers[0].loc.start,
+              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
+            },
             messageId: 'disallowAnyModifier'
           })
         }
diff --git a/lib/rules/valid-v-text.js b/lib/rules/valid-v-text.js
index 1e90ec68..1e93c14e 100644
--- a/lib/rules/valid-v-text.js
+++ b/lib/rules/valid-v-text.js
@@ -24,7 +24,12 @@ module.exports = {
       url: 'https://eslint.vuejs.org/rules/valid-v-text.html'
     },
     fixable: null,
-    schema: []
+    schema: [],
+    messages: {
+      unexpectedArgument: "'v-text' directives require no argument.",
+      unexpectedModifier: "'v-text' directives require no modifier.",
+      expectedValue: "'v-text' directives require that attribute value."
+    }
   },
   /** @param {RuleContext} context */
   create(context) {
@@ -33,23 +38,24 @@ module.exports = {
       "VAttribute[directive=true][key.name.name='text']"(node) {
         if (node.key.argument) {
           context.report({
-            node,
-            loc: node.loc,
-            message: "'v-text' directives require no argument."
+            node: node.key.argument,
+            messageId: 'unexpectedArgument'
           })
         }
         if (node.key.modifiers.length > 0) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-text' directives require no modifier."
+            loc: {
+              start: node.key.modifiers[0].loc.start,
+              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
+            },
+            messageId: 'unexpectedModifier'
           })
         }
         if (!node.value || utils.isEmptyValueDirective(node, context)) {
           context.report({
             node,
-            loc: node.loc,
-            message: "'v-text' directives require that attribute value."
+            messageId: 'expectedValue'
           })
         }
       }


Test patch:
diff --git a/tests/lib/rules/valid-v-bind-sync.js b/tests/lib/rules/valid-v-bind-sync.js
index a2e47a6a..32f99bf5 100644
--- a/tests/lib/rules/valid-v-bind-sync.js
+++ b/tests/lib/rules/valid-v-bind-sync.js
@@ -190,8 +190,8 @@ tester.run('valid-v-bind-sync', rule, {
           message:
             "'.sync' modifiers require the attribute value which is valid as LHS.",
           line: 3,
-          column: 24,
-          endColumn: 41
+          column: 35,
+          endColumn: 40
         }
       ]
     },
@@ -207,8 +207,8 @@ tester.run('valid-v-bind-sync', rule, {
           message:
             "'.sync' modifiers require the attribute value which is valid as LHS.",
           line: 3,
-          column: 24,
-          endColumn: 47
+          column: 41,
+          endColumn: 46
         }
       ]
     },
@@ -241,8 +241,8 @@ tester.run('valid-v-bind-sync', rule, {
           message:
             "'.sync' modifiers require the attribute value which is valid as LHS.",
           line: 3,
-          column: 24,
-          endColumn: 41
+          column: 35,
+          endColumn: 40
         }
       ]
     },
@@ -258,8 +258,8 @@ tester.run('valid-v-bind-sync', rule, {
           message:
             "'.sync' modifiers require the attribute value which is valid as LHS.",
           line: 3,
-          column: 24,
-          endColumn: 46
+          column: 35,
+          endColumn: 45
         }
       ]
     },
@@ -294,8 +294,8 @@ tester.run('valid-v-bind-sync', rule, {
           message:
             "'.sync' modifiers cannot update the iteration variable 'x' itself.",
           line: 4,
-          column: 26,
-          endColumn: 39
+          column: 37,
+          endColumn: 38
         }
       ]
     },
@@ -313,8 +313,8 @@ tester.run('valid-v-bind-sync', rule, {
           message:
             "'.sync' modifiers cannot update the iteration variable 'e' itself.",
           line: 4,
-          column: 26,
-          endColumn: 45
+          column: 43,
+          endColumn: 44
         }
       ]
     },
diff --git a/tests/lib/rules/valid-v-is.js b/tests/lib/rules/valid-v-is.js
index 1a3a45e5..dd55ef7b 100644
--- a/tests/lib/rules/valid-v-is.js
+++ b/tests/lib/rules/valid-v-is.js
@@ -55,8 +55,8 @@ tester.run('valid-v-is', rule, {
       errors: [
         {
           message: "'v-is' directives require no argument.",
-          column: 16,
-          endColumn: 28
+          column: 21,
+          endColumn: 22
         }
       ]
     },
@@ -66,8 +66,8 @@ tester.run('valid-v-is', rule, {
       errors: [
         {
           message: "'v-is' directives require no modifier.",
-          column: 16,
-          endColumn: 28
+          column: 21,
+          endColumn: 22
         }
       ]
     },