Repository: vuejs/eslint-plugin-vue
Pull request #1576 (2021-07-18 08:41:25)

Update core wrapper rules to support style CSS vars injection
This PR updates the following core wrapper rules to support style CSS variable injection.

- [x] `vue/comma-spacing` rule
- [x] `vue/dot-notation` rule
- [x] `vue/eqeqeq` rule
- [x] `vue/func-call-spacing` rule
- [x] `vue/no-extra-parens` rule
- [x] `vue/no-restricted-syntax` rule
- [x] `vue/no-useless-concat` rule
- [x] `vue/prefer-template` rule
- [x] `vue/space-in-parens` rule
- [x] `vue/space-infix-ops` rule
- [x] `vue/space-unary-ops` rule
- [x] `vue/template-curly-spacing` rule

Related to #1248
Related to https://github.com/vuejs/vue-eslint-parser/pull/119


Patch:
diff --git a/docs/rules/README.md b/docs/rules/README.md
index 59e013e2..3021b972 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -320,6 +320,7 @@ For example:
 | [vue/no-unsupported-features](./no-unsupported-features.md) | disallow unsupported Vue.js syntax on the specified version | :wrench: |
 | [vue/no-unused-properties](./no-unused-properties.md) | disallow unused properties |  |
 | [vue/no-unused-refs](./no-unused-refs.md) | disallow unused refs |  |
+| [vue/no-use-computed-property-like-method](./no-use-computed-property-like-method.md) | disallow use computed property like method |  |
 | [vue/no-useless-mustaches](./no-useless-mustaches.md) | disallow unnecessary mustache interpolations | :wrench: |
 | [vue/no-useless-v-bind](./no-useless-v-bind.md) | disallow unnecessary `v-bind` directives | :wrench: |
 | [vue/padding-line-between-blocks](./padding-line-between-blocks.md) | require or disallow padding lines between blocks | :wrench: |
@@ -366,7 +367,7 @@ The following rules extend the rules provided by ESLint itself and apply them to
 | [vue/no-restricted-syntax](./no-restricted-syntax.md) | disallow specified syntax |  |
 | [vue/no-sparse-arrays](./no-sparse-arrays.md) | disallow sparse arrays |  |
 | [vue/no-useless-concat](./no-useless-concat.md) | disallow unnecessary concatenation of literals or template literals |  |
-| [vue/object-curly-newline](./object-curly-newline.md) | enforce consistent line breaks after opening and before closing braces | :wrench: |
+| [vue/object-curly-newline](./object-curly-newline.md) | enforce consistent line breaks inside braces | :wrench: |
 | [vue/object-curly-spacing](./object-curly-spacing.md) | enforce consistent spacing inside braces | :wrench: |
 | [vue/object-property-newline](./object-property-newline.md) | enforce placing object properties on separate lines | :wrench: |
 | [vue/operator-linebreak](./operator-linebreak.md) | enforce consistent linebreak style for operators | :wrench: |
diff --git a/docs/rules/no-use-computed-property-like-method.md b/docs/rules/no-use-computed-property-like-method.md
new file mode 100644
index 00000000..d776cd7e
--- /dev/null
+++ b/docs/rules/no-use-computed-property-like-method.md
@@ -0,0 +1,340 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-use-computed-property-like-method
+description: disallow use computed property like method
+---
+# vue/no-use-computed-property-like-method
+
+> disallow use computed property like method
+
+- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
+
+## :book: Rule Details
+
+This rule disallows to use computed property like method.
+
+<eslint-code-block :rules="{'vue/no-use-computed-property-like-method': ['error']}">
+
+```vue
+<script>
+export default {
+  data() {
+    return {
+      dataString: 'dataString',
+      dataNumber: 10,
+      dataObject: {
+        inside: 'inside'
+      },
+      dataArray: [1, 2, 3, 4, 5],
+      dataBoolean: true,
+      dataFunction() {
+        alert('dataFunction')
+      }
+    }
+  },
+  props: {
+    propsString: String,
+    propsNumber: Number,
+    propsObject: Object,
+    propsArray: Array,
+    propsBoolean: Boolean,
+    propsFunction: Function,
+
+    objectPropsString: {
+      type: String
+    },
+    objectPropsNumber: {
+      type: Number
+    },
+    objectPropsObject: {
+      type: Object
+    },
+    objectPropsArray: {
+      type: Array
+    },
+    objectPropsBoolean: {
+      type: Boolean
+    },
+    objectPropsFunction: {
+      type: Function
+    }
+  },
+  computed: {
+    computedReturnDataString() {
+      return this.dataString
+    },
+    computedReturnDataNumber() {
+      return this.dataNumber
+    },
+    computedReturnDataObject() {
+      return this.dataObject
+    },
+    computedReturnDataArray() {
+      return this.dataArray
+    },
+    computedReturnDataBoolean() {
+      return this.dataBoolean
+    },
+    computedReturnDataFunction() {
+      return this.dataFunction
+    },
+
+    computedReturnPropsString() {
+      return this.propsString
+    },
+    computedReturnPropsNumber() {
+      return this.propsNumber
+    },
+    computedReturnPropsObject() {
+      return this.propsObject
+    },
+    computedReturnPropsArray() {
+      return this.propsArray
+    },
+    computedReturnPropsBoolean() {
+      return this.propsBoolean
+    },
+    computedReturnPropsFunction() {
+      return this.propsFunction
+    },
+
+    computedReturnObjectPropsString() {
+      return this.objectPropsString
+    },
+    computedReturnObjectPropsNumber() {
+      return this.objectPropsNumber
+    },
+    computedReturnObjectPropsObject() {
+      return this.objectPropsObject
+    },
+    computedReturnObjectPropsArray() {
+      return this.objectPropsArray
+    },
+    computedReturnObjectPropsBoolean() {
+      return this.objectPropsBoolean
+    },
+    computedReturnObjectPropsFunction() {
+      return this.objectPropsFunction
+    },
+
+    computedReturnString() {
+      return 'computedReturnString'
+    },
+    computedReturnNumber() {
+      return 10
+    },
+    computedReturnObject() {
+      return {
+        inside: 'inside'
+      }
+    },
+    computedReturnArray() {
+      return [1, 2, 3, 4, 5]
+    },
+    computedReturnBoolean() {
+      return true
+    },
+    computedReturnFunction() {
+      const fn = () => alert('computedReturnFunction')
+      return fn
+    },
+
+    computedReturnMethodsReturnString() {
+      return this.methodsReturnString
+    },
+    computedReturnMethodsReturnNumber() {
+      return this.methodsReturnNumber
+    },
+    computedReturnMethodsReturnObject() {
+      return this.methodsReturnObject
+    },
+    computedReturnMethodsReturnArray() {
+      return this.methodsReturnArray
+    },
+    computedReturnMethodsReturnBoolean() {
+      return this.methodsReturnBoolean
+    },
+    computedReturnMethodsReturnFunction() {
+      return this.methodsReturnFunction
+    }
+  },
+  methods: {
+    methodsReturnString() {
+      return 'methodsReturnString'
+    },
+    methodsReturnNumber() {
+      return 'methodsReturnNumber'
+    },
+    methodsReturnObject() {
+      return {
+        inside: 'inside'
+      }
+    },
+    methodsReturnArray() {
+      return [1, 2, 3, 4, 5]
+    },
+    methodsReturnBoolean() {
+      return true
+    },
+    methodsReturnFunction() {
+      const fn = () => alert('methodsReturnFunction')
+      return fn
+    },
+
+    fn() {
+      /* Reference data */
+      /* ✓ GOOD */
+      this.computedReturnDataString
+      this.computedReturnDataNumber
+      this.computedReturnDataObject
+      this.computedReturnDataArray
+      this.computedReturnDataBoolean
+      this.computedReturnDataFunction
+      this.computedReturnDataFunction()
+      /* ✗ BAD */
+      this.computedReturnDataString()
+      this.computedReturnDataNumber()
+      this.computedReturnDataObject()
+      this.computedReturnDataArray()
+      this.computedReturnDataBoolean()
+
+      /* Reference props */
+      /* ✓ GOOD */
+      this.computedReturnPropsString
+      this.computedReturnPropsNumber
+      this.computedReturnPropsObject
+      this.computedReturnPropsArray
+      this.computedReturnPropsBoolean
+      this.computedReturnPropsFunction
+      this.computedReturnPropsFunction()
+      /* ✗ BAD */
+      this.computedReturnPropsString()
+      this.computedReturnPropsNumber()
+      this.computedReturnPropsObject()
+      this.computedReturnPropsArray()
+      this.computedReturnPropsBoolean()
+
+      /* ✓ GOOD */
+      this.computedReturnObjectPropsString
+      this.computedReturnObjectPropsNumber
+      this.computedReturnObjectPropsObject
+      this.computedReturnObjectPropsArray
+      this.computedReturnObjectPropsBoolean
+      this.computedReturnObjectPropsFunction
+      this.computedReturnObjectPropsFunction()
+      /* ✗ BAD */
+      this.computedReturnObjectPropsString()
+      this.computedReturnObjectPropsNumber()
+      this.computedReturnObjectPropsObject()
+      this.computedReturnObjectPropsArray()
+      this.computedReturnObjectPropsBoolean()
+
+      /* Reference computed */
+      /* ✓ GOOD */
+      this.computedReturnString
+      this.computedReturnNumber
+      this.computedReturnObject
+      this.computedReturnArray
+      this.computedReturnBoolean
+      this.computedReturnFunction
+      this.computedReturnFunction()
+      /* ✗ BAD */
+      this.computedReturnString()
+      this.computedReturnNumber()
+      this.computedReturnObject()
+      this.computedReturnArray()
+      this.computedReturnBoolean()
+
+      /* Reference methods */
+      /* ✓ GOOD */
+      this.computedReturnMethodsReturnString
+      this.computedReturnMethodsReturnNumber
+      this.computedReturnMethodsReturnObject
+      this.computedReturnMethodsReturnArray
+      this.computedReturnMethodsReturnBoolean
+      this.computedReturnMethodsReturnFunction
+      this.computedReturnMethodsReturnFunction()
+      /* ✗ BAD */
+      this.computedReturnMethodsReturnString()
+      this.computedReturnMethodsReturnNumber()
+      this.computedReturnMethodsReturnObject()
+      this.computedReturnMethodsReturnArray()
+      this.computedReturnMethodsReturnBoolean()
+    }
+  }
+}
+</script>
+```
+
+</eslint-code-block>
+
+This rule can't check if props is used as array:
+
+<eslint-code-block :rules="{'vue/no-use-computed-property-like-method': ['error']}">
+
+```vue
+<script>
+export default {
+  props: [
+    'propsString',
+    'propsNumber',
+    'propsObject',
+    'propsArray',
+    'propsBoolean',
+    'propsFunction'
+  ],
+  computed: {
+    computedReturnPropsString() {
+      return this.propsString
+    },
+    computedReturnPropsNumber() {
+      return this.propsNumber
+    },
+    computedReturnPropsObject() {
+      return this.propsObject
+    },
+    computedReturnPropsArray() {
+      return this.propsArray
+    },
+    computedReturnPropsBoolean() {
+      return this.propsBoolean
+    },
+    computedReturnPropsFunction() {
+      return this.propsFunction
+    }
+  },
+  methods: {
+    fn() {
+      /* Reference props */
+      /* ✓ GOOD */
+      this.computedReturnPropsString
+      this.computedReturnPropsString()
+      this.computedReturnPropsNumber
+      this.computedReturnPropsNumber()
+      this.computedReturnPropsObject
+      this.computedReturnPropsObject()
+      this.computedReturnPropsArray
+      this.computedReturnPropsArray()
+      this.computedReturnPropsBoolean
+      this.computedReturnPropsBoolean()
+      this.computedReturnPropsFunction
+      this.computedReturnPropsFunction()
+      /* ✗ BAD */
+      /* Nope. everything is GOOD!! */
+    }
+  }
+}
+</script>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+Nothing.
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-use-computed-property-like-method.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-use-computed-property-like-method.js)
diff --git a/docs/rules/object-curly-newline.md b/docs/rules/object-curly-newline.md
index 52e3d6ae..cc4831c1 100644
--- a/docs/rules/object-curly-newline.md
+++ b/docs/rules/object-curly-newline.md
@@ -2,12 +2,12 @@
 pageClass: rule-details
 sidebarDepth: 0
 title: vue/object-curly-newline
-description: enforce consistent line breaks after opening and before closing braces
+description: enforce consistent line breaks inside braces
 since: v7.0.0
 ---
 # vue/object-curly-newline
 
-> enforce consistent line breaks after opening and before closing braces
+> enforce consistent line breaks inside braces
 
 - :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
 
diff --git a/docs/rules/require-expose.md b/docs/rules/require-expose.md
index f402c3cd..01baf5d4 100644
--- a/docs/rules/require-expose.md
+++ b/docs/rules/require-expose.md
@@ -3,13 +3,12 @@ pageClass: rule-details
 sidebarDepth: 0
 title: vue/require-expose
 description: require declare public properties using `expose`
+since: v7.14.0
 ---
 # vue/require-expose
 
 > require declare public properties using `expose`
 
-- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
-
 ## :book: Rule Details
 
 This rule enforces the component to explicitly declare the exposed properties to the component using `expose`. You can use `expose` to control the internal properties of a component so that they cannot be referenced externally.
@@ -114,6 +113,10 @@ Nothing.
 
 - [Vue RFCs - 0042-expose-api](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0042-expose-api.md)
 
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v7.14.0
+
 ## :mag: Implementation
 
 - [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-expose.js)
diff --git a/lib/index.js b/lib/index.js
index 752a52dd..87f80c9a 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -122,6 +122,7 @@ module.exports = {
     'no-unused-properties': require('./rules/no-unused-properties'),
     'no-unused-refs': require('./rules/no-unused-refs'),
     'no-unused-vars': require('./rules/no-unused-vars'),
+    'no-use-computed-property-like-method': require('./rules/no-use-computed-property-like-method'),
     'no-use-v-if-with-v-for': require('./rules/no-use-v-if-with-v-for'),
     'no-useless-concat': require('./rules/no-useless-concat'),
     'no-useless-mustaches': require('./rules/no-useless-mustaches'),
diff --git a/lib/rules/comma-spacing.js b/lib/rules/comma-spacing.js
index 7ac0245e..6bad7e4b 100644
--- a/lib/rules/comma-spacing.js
+++ b/lib/rules/comma-spacing.js
@@ -8,5 +8,6 @@ const { wrapCoreRule } = require('../utils')
 // eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
 module.exports = wrapCoreRule('comma-spacing', {
   skipDynamicArguments: true,
-  skipDynamicArgumentsReport: true
+  skipDynamicArgumentsReport: true,
+  applyDocument: true
 })
diff --git a/lib/rules/dot-notation.js b/lib/rules/dot-notation.js
index 71265896..bd72bd82 100644
--- a/lib/rules/dot-notation.js
+++ b/lib/rules/dot-notation.js
@@ -6,4 +6,6 @@
 const { wrapCoreRule } = require('../utils')
 
 // eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
-module.exports = wrapCoreRule('dot-notation')
+module.exports = wrapCoreRule('dot-notation', {
+  applyDocument: true
+})
diff --git a/lib/rules/eqeqeq.js b/lib/rules/eqeqeq.js
index 39894e1f..d918b4d8 100644
--- a/lib/rules/eqeqeq.js
+++ b/lib/rules/eqeqeq.js
@@ -6,4 +6,6 @@
 const { wrapCoreRule } = require('../utils')
 
 // eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
-module.exports = wrapCoreRule('eqeqeq')
+module.exports = wrapCoreRule('eqeqeq', {
+  applyDocument: true
+})
diff --git a/lib/rules/func-call-spacing.js b/lib/rules/func-call-spacing.js
index 9fb96ab3..2054376c 100644
--- a/lib/rules/func-call-spacing.js
+++ b/lib/rules/func-call-spacing.js
@@ -7,5 +7,6 @@ const { wrapCoreRule } = require('../utils')
 
 // eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
 module.exports = wrapCoreRule('func-call-spacing', {
-  skipDynamicArguments: true
+  skipDynamicArguments: true,
+  applyDocument: true
 })
diff --git a/lib/rules/no-extra-parens.js b/lib/rules/no-extra-parens.js
index 20c25938..223bd630 100644
--- a/lib/rules/no-extra-parens.js
+++ b/lib/rules/no-extra-parens.js
@@ -9,6 +9,7 @@ const { wrapCoreRule } = require('../utils')
 // eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
 module.exports = wrapCoreRule('no-extra-parens', {
   skipDynamicArguments: true,
+  applyDocument: true,
   create: createForVueSyntax
 })
 
diff --git a/lib/rules/no-restricted-syntax.js b/lib/rules/no-restricted-syntax.js
index a09ff666..19feb14e 100644
--- a/lib/rules/no-restricted-syntax.js
+++ b/lib/rules/no-restricted-syntax.js
@@ -6,4 +6,6 @@
 const { wrapCoreRule } = require('../utils')
 
 // eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
-module.exports = wrapCoreRule('no-restricted-syntax')
+module.exports = wrapCoreRule('no-restricted-syntax', {
+  applyDocument: true
+})
diff --git a/lib/rules/no-use-computed-property-like-method.js b/lib/rules/no-use-computed-property-like-method.js
new file mode 100644
index 00000000..1add2c34
--- /dev/null
+++ b/lib/rules/no-use-computed-property-like-method.js
@@ -0,0 +1,258 @@
+/**
+ * @author tyankatsu <https://github.com/tyankatsu0105>
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+const eslintUtils = require('eslint-utils')
+const utils = require('../utils')
+
+/**
+ * @typedef {import('../utils').ComponentPropertyData} ComponentPropertyData
+ * @typedef {import('../utils').ComponentObjectPropertyData} ComponentObjectPropertyData
+ * @typedef {import('../utils').GroupName} GroupName
+ *
+ * @typedef {{[key: string]: ComponentPropertyData & { valueType: { type: string | null } }}} PropertyMap
+ */
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+/**
+ * Get type of props item.
+ * Can't consider array props like: props: {propsA: [String, Number, Function]}
+ * @param {ComponentObjectPropertyData} property
+ * @return {string | null}
+ *
+ * @example
+ * props: {
+ *   propA: String, // => String
+ *   propB: {
+ *     type: Number // => String
+ *   },
+ * }
+ */
+const getComponentPropsType = (property) => {
+  /**
+   * Check basic props `props: { basicProps: ... }`
+   */
+  if (property.property.value.type === 'Identifier') {
+    return property.property.value.name
+  }
+  /**
+   * Check object props `props: { objectProps: {...} }`
+   */
+  if (property.property.value.type === 'ObjectExpression') {
+    const typeProperty = utils.findProperty(property.property.value, 'type')
+    if (typeProperty == null) return null
+
+    if (typeProperty.value.type === 'Identifier') return typeProperty.value.name
+  }
+  return null
+}
+
+/**
+ *
+ * @param {any} obj
+ */
+const getPrototypeType = (obj) =>
+  Object.prototype.toString.call(obj).slice(8, -1)
+
+/**
+ * Get return type of property.
+ * @param {{ property: ComponentPropertyData, propertyMap?: PropertyMap }} args
+ * @returns {{type: string | null | 'ReturnStatementHasNotArgument'}}
+ */
+const getValueType = ({ property, propertyMap }) => {
+  if (property.type === 'array') {
+    return {
+      type: null
+    }
+  }
+
+  if (property.type === 'object') {
+    if (property.groupName === 'props') {
+      return {
+        type: getComponentPropsType(property)
+      }
+    }
+
+    if (property.groupName === 'computed' || property.groupName === 'methods') {
+      if (
+        property.property.value.type === 'FunctionExpression' &&
+        property.property.value.body.type === 'BlockStatement'
+      ) {
+        const blockStatement = property.property.value.body
+
+        /**
+         * Only check return statement inside computed and methods
+         */
+        const returnStatement = blockStatement.body.find(
+          (b) => b.type === 'ReturnStatement'
+        )
+
+        if (!returnStatement || returnStatement.type !== 'ReturnStatement')
+          return {
+            type: null
+          }
+
+        if (returnStatement.argument === null)
+          return {
+            type: 'ReturnStatementHasNotArgument'
+          }
+
+        if (
+          property.groupName === 'computed' &&
+          propertyMap &&
+          propertyMap[property.name] &&
+          returnStatement.argument
+        ) {
+          if (
+            returnStatement.argument.type === 'MemberExpression' &&
+            returnStatement.argument.object.type === 'ThisExpression' &&
+            returnStatement.argument.property.type === 'Identifier'
+          )
+            return {
+              type: propertyMap[returnStatement.argument.property.name]
+                .valueType.type
+            }
+
+          if (
+            returnStatement.argument.type === 'CallExpression' &&
+            returnStatement.argument.callee.type === 'MemberExpression' &&
+            returnStatement.argument.callee.object.type === 'ThisExpression' &&
+            returnStatement.argument.callee.property.type === 'Identifier'
+          )
+            return {
+              type: propertyMap[returnStatement.argument.callee.property.name]
+                .valueType.type
+            }
+        }
+
+        /**
+         * Use value as Object even if object includes method
+         */
+        if (
+          property.groupName === 'computed' &&
+          returnStatement.argument.type === 'ObjectExpression'
+        ) {
+          return {
+            type: 'Object'
+          }
+        }
+
+        const evaluated = eslintUtils.getStaticValue(returnStatement.argument)
+
+        if (evaluated) {
+          return {
+            type: getPrototypeType(evaluated.value)
+          }
+        }
+      }
+    }
+
+    const evaluated = eslintUtils.getStaticValue(property.property.value)
+
+    if (evaluated) {
+      return {
+        type: getPrototypeType(evaluated.value)
+      }
+    }
+  }
+  return {
+    type: null
+  }
+}
+
+/**
+ * @param {Set<GroupName>} groups
+ * @param {ObjectExpression} vueNodeMap
+ * @param {PropertyMap} propertyMap
+ */
+const addPropertyMap = (groups, vueNodeMap, propertyMap) => {
+  const properties = utils.iterateProperties(vueNodeMap, groups)
+  for (const property of properties) {
+    propertyMap[property.name] = {
+      ...propertyMap[property.name],
+      ...property,
+      valueType: getValueType({ property, propertyMap })
+    }
+  }
+}
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow use computed property like method',
+      categories: undefined,
+      url: 'https://eslint.vuejs.org/rules/no-use-computed-property-like-method.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      unexpected: 'Use {{ likeProperty }} instead of {{ likeMethod }}.'
+    }
+  },
+  /** @param {RuleContext} context */
+  create(context) {
+    /** @type {GroupName[]} */
+    const GROUP_NAMES = ['data', 'props', 'computed', 'methods']
+    const groups = new Set(GROUP_NAMES)
+
+    /** @type {PropertyMap} */
+    const propertyMap = Object.create(null)
+
+    return utils.defineVueVisitor(context, {
+      onVueObjectEnter(node) {
+        const properties = utils.iterateProperties(node, groups)
+
+        for (const property of properties) {
+          propertyMap[property.name] = {
+            ...propertyMap[property.name],
+            ...property,
+            valueType: getValueType({ property })
+          }
+        }
+      },
+
+      /** @param {ThisExpression} node */
+      'CallExpression > MemberExpression > ThisExpression'(
+        node,
+        { node: vueNode }
+      ) {
+        addPropertyMap(groups, vueNode, propertyMap)
+
+        if (node.parent.type !== 'MemberExpression') return
+        if (node.parent.property.type !== 'Identifier') return
+
+        const thisMember = node.parent.property.name
+
+        if (
+          !propertyMap[thisMember] ||
+          !propertyMap[thisMember].valueType ||
+          !propertyMap[thisMember].valueType.type
+        )
+          return
+
+        if (
+          propertyMap[thisMember].groupName === 'computed' &&
+          propertyMap[thisMember].valueType.type !== 'Function'
+        ) {
+          context.report({
+            node: node.parent.parent,
+            loc: node.parent.parent.loc,
+            messageId: 'unexpected',
+            data: {
+              likeProperty: `this.${thisMember}`,
+              likeMethod: `this.${thisMember}()`
+            }
+          })
+        }
+      }
+    })
+  }
+}
diff --git a/lib/rules/no-useless-concat.js b/lib/rules/no-useless-concat.js
index 7379a1ee..f2c51b90 100644
--- a/lib/rules/no-useless-concat.js
+++ b/lib/rules/no-useless-concat.js
@@ -6,4 +6,6 @@
 const { wrapCoreRule } = require('../utils')
 
 // eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
-module.exports = wrapCoreRule('no-useless-concat')
+module.exports = wrapCoreRule('no-useless-concat', {
+  applyDocument: true
+})
diff --git a/lib/rules/prefer-template.js b/lib/rules/prefer-template.js
index 5cc11e2a..6fac1a05 100644
--- a/lib/rules/prefer-template.js
+++ b/lib/rules/prefer-template.js
@@ -6,4 +6,6 @@
 const { wrapCoreRule } = require('../utils')
 
 // eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
-module.exports = wrapCoreRule('prefer-template')
+module.exports = wrapCoreRule('prefer-template', {
+  applyDocument: true
+})
diff --git a/lib/rules/space-in-parens.js b/lib/rules/space-in-parens.js
index 29ba0403..3a9a7e0d 100644
--- a/lib/rules/space-in-parens.js
+++ b/lib/rules/space-in-parens.js
@@ -8,5 +8,6 @@ const { wrapCoreRule } = require('../utils')
 // eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
 module.exports = wrapCoreRule('space-in-parens', {
   skipDynamicArguments: true,
-  skipDynamicArgumentsReport: true
+  skipDynamicArgumentsReport: true,
+  applyDocument: true
 })
diff --git a/lib/rules/space-infix-ops.js b/lib/rules/space-infix-ops.js
index 650b215e..dd0a8af2 100644
--- a/lib/rules/space-infix-ops.js
+++ b/lib/rules/space-infix-ops.js
@@ -7,5 +7,6 @@ const { wrapCoreRule } = require('../utils')
 
 // eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
 module.exports = wrapCoreRule('space-infix-ops', {
-  skipDynamicArguments: true
+  skipDynamicArguments: true,
+  applyDocument: true
 })
diff --git a/lib/rules/space-unary-ops.js b/lib/rules/space-unary-ops.js
index 4920ca9e..e3ece4c2 100644
--- a/lib/rules/space-unary-ops.js
+++ b/lib/rules/space-unary-ops.js
@@ -7,5 +7,6 @@ const { wrapCoreRule } = require('../utils')
 
 // eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
 module.exports = wrapCoreRule('space-unary-ops', {
-  skipDynamicArguments: true
+  skipDynamicArguments: true,
+  applyDocument: true
 })
diff --git a/lib/rules/template-curly-spacing.js b/lib/rules/template-curly-spacing.js
index acefea6b..549c790b 100644
--- a/lib/rules/template-curly-spacing.js
+++ b/lib/rules/template-curly-spacing.js
@@ -7,5 +7,6 @@ const { wrapCoreRule } = require('../utils')
 
 // eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
 module.exports = wrapCoreRule('template-curly-spacing', {
-  skipDynamicArguments: true
+  skipDynamicArguments: true,
+  applyDocument: true
 })
diff --git a/lib/utils/index.js b/lib/utils/index.js
index b0c18959..510e016a 100644
--- a/lib/utils/index.js
+++ b/lib/utils/index.js
@@ -78,19 +78,24 @@ function getCoreRule(name) {
  * Wrap the rule context object to override methods which access to tokens (such as getTokenAfter).
  * @param {RuleContext} context The rule context object.
  * @param {ParserServices.TokenStore} tokenStore The token store object for template.
+ * @param {Object} options The option of this rule.
+ * @param {boolean} [options.applyDocument] If `true`, apply check to document fragment.
  * @returns {RuleContext}
  */
-function wrapContextToOverrideTokenMethods(context, tokenStore) {
+function wrapContextToOverrideTokenMethods(context, tokenStore, options) {
   const eslintSourceCode = context.getSourceCode()
+  const rootNode = options.applyDocument
+    ? context.parserServices.getDocumentFragment &&
+      context.parserServices.getDocumentFragment()
+    : eslintSourceCode.ast.templateBody
   /** @type {Token[] | null} */
   let tokensAndComments = null
   function getTokensAndComments() {
     if (tokensAndComments) {
       return tokensAndComments
     }
-    const templateBody = eslintSourceCode.ast.templateBody
-    tokensAndComments = templateBody
-      ? tokenStore.getTokens(templateBody, {
+    tokensAndComments = rootNode
+      ? tokenStore.getTokens(rootNode, {
           includeComments: true
         })
       : []
@@ -99,8 +104,7 @@ function wrapContextToOverrideTokenMethods(context, tokenStore) {
 
   /** @param {number} index */
   function getNodeByRangeIndex(index) {
-    const templateBody = eslintSourceCode.ast.templateBody
-    if (!templateBody) {
+    if (!rootNode) {
       return eslintSourceCode.ast
     }
 
@@ -110,7 +114,7 @@ function wrapContextToOverrideTokenMethods(context, tokenStore) {
     const skipNodes = []
     let breakFlag = false
 
-    traverseNodes(templateBody, {
+    traverseNodes(rootNode, {
       enterNode(node, parent) {
         if (breakFlag) {
           return
@@ -242,6 +246,7 @@ module.exports = {
    * @param {string[]} [options.categories] The categories of this rule.
    * @param {boolean} [options.skipDynamicArguments] If `true`, skip validation within dynamic arguments.
    * @param {boolean} [options.skipDynamicArgumentsReport] If `true`, skip report within dynamic arguments.
+   * @param {boolean} [options.applyDocument] If `true`, apply check to document fragment.
    * @param { (context: RuleContext, options: { coreHandlers: RuleListener }) => TemplateListener } [options.create] If define, extend core rule.
    * @returns {RuleModule} The wrapped rule implementation.
    */
@@ -251,6 +256,7 @@ module.exports = {
       categories,
       skipDynamicArguments,
       skipDynamicArgumentsReport,
+      applyDocument,
       create
     } = options || {}
     return {
@@ -262,7 +268,9 @@ module.exports = {
         // The `context.getSourceCode()` cannot access the tokens of templates.
         // So override the methods which access to tokens by the `tokenStore`.
         if (tokenStore) {
-          context = wrapContextToOverrideTokenMethods(context, tokenStore)
+          context = wrapContextToOverrideTokenMethods(context, tokenStore, {
+            applyDocument
+          })
         }
 
         if (skipDynamicArgumentsReport) {
@@ -277,12 +285,19 @@ module.exports = {
           Object.assign({}, coreHandlers)
         )
         if (handlers.Program) {
-          handlers["VElement[parent.type!='VElement']"] = handlers.Program
+          handlers[
+            applyDocument
+              ? 'VDocumentFragment'
+              : "VElement[parent.type!='VElement']"
+          ] = /** @type {any} */ (handlers.Program)
           delete handlers.Program
         }
         if (handlers['Program:exit']) {
-          handlers["VElement[parent.type!='VElement']:exit"] =
-            handlers['Program:exit']
+          handlers[
+            applyDocument
+              ? 'VDocumentFragment:exit'
+              : "VElement[parent.type!='VElement']:exit"
+          ] = /** @type {any} */ (handlers['Program:exit'])
           delete handlers['Program:exit']
         }
 
@@ -309,6 +324,10 @@ module.exports = {
           compositingVisitors(handlers, create(context, { coreHandlers }))
         }
 
+        if (applyDocument) {
+          // Apply the handlers to document.
+          return defineDocumentVisitor(context, handlers)
+        }
         // Apply the handlers to templates.
         return defineTemplateBodyVisitor(context, handlers)
       },
@@ -1812,6 +1831,30 @@ function defineTemplateBodyVisitor(
     options
   )
 }
+/**
+ * Register the given visitor to parser services.
+ * If the parser service of `vue-eslint-parser` was not found,
+ * this generates a warning.
+ *
+ * @param {RuleContext} context The rule context to use parser services.
+ * @param {TemplateListener} documentVisitor The visitor to traverse the document.
+ * @param { { triggerSelector: "Program" | "Program:exit" } } [options] The options.
+ * @returns {RuleListener} The merged visitor.
+ */
+function defineDocumentVisitor(context, documentVisitor, options) {
+  if (context.parserServices.defineDocumentVisitor == null) {
+    const filename = context.getFilename()
+    if (path.extname(filename) === '.vue') {
+      context.report({
+        loc: { line: 1, column: 0 },
+        message:
+          'Use the latest vue-eslint-parser. See also https://eslint.vuejs.org/user-guide/#what-is-the-use-the-latest-vue-eslint-parser-error.'
+      })
+    }
+    return {}
+  }
+  return context.parserServices.defineDocumentVisitor(documentVisitor, options)
+}
 
 /**
  * @template T
diff --git a/lib/utils/style-variables/index.js b/lib/utils/style-variables/index.js
index e2016a63..63caf208 100644
--- a/lib/utils/style-variables/index.js
+++ b/lib/utils/style-variables/index.js
@@ -31,7 +31,7 @@ class StyleVariablesContext {
   }
 }
 
-/** @type {Map<VElement, StyleVariablesContext} */
+/** @type {Map<VElement, StyleVariablesContext>} */
 const cache = new Map()
 /**
  * Get the style vars context
diff --git a/package.json b/package.json
index 299296e1..7ceec76c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint-plugin-vue",
-  "version": "7.13.0",
+  "version": "7.14.0",
   "description": "Official ESLint plugin for Vue.js",
   "main": "lib/index.js",
   "scripts": {
@@ -56,7 +56,7 @@
     "eslint-utils": "^2.1.0",
     "natural-compare": "^1.4.0",
     "semver": "^6.3.0",
-    "vue-eslint-parser": "^7.9.0"
+    "vue-eslint-parser": "^7.10.0"
   },
   "devDependencies": {
     "@types/eslint": "^7.2.0",
diff --git a/typings/eslint-plugin-vue/util-types/ast/ast.ts b/typings/eslint-plugin-vue/util-types/ast/ast.ts
index e29c199f..4356ebc5 100644
--- a/typings/eslint-plugin-vue/util-types/ast/ast.ts
+++ b/typings/eslint-plugin-vue/util-types/ast/ast.ts
@@ -145,6 +145,8 @@ export type VNodeListenerMap = {
   'VFilterSequenceExpression:exit': V.VFilterSequenceExpression
   VFilter: V.VFilter
   'VFilter:exit': V.VFilter
+  VDocumentFragment: V.VDocumentFragment
+  'VDocumentFragment:exit': V.VDocumentFragment
 } & ESNodeListenerMap
 export type NodeListenerMap = {
   JSXAttribute: JSX.JSXAttribute
diff --git a/typings/eslint-plugin-vue/util-types/parser-services.ts b/typings/eslint-plugin-vue/util-types/parser-services.ts
index 18351677..4c938f33 100644
--- a/typings/eslint-plugin-vue/util-types/parser-services.ts
+++ b/typings/eslint-plugin-vue/util-types/parser-services.ts
@@ -20,6 +20,12 @@ export interface ParserServices {
       templateBodyTriggerSelector: 'Program' | 'Program:exit'
     }
   ) => eslint.Rule.RuleListener
+  defineDocumentVisitor?: (
+    documentVisitor: TemplateListener,
+    options?: {
+      triggerSelector: 'Program' | 'Program:exit'
+    }
+  ) => eslint.Rule.RuleListener
   getDocumentFragment?: () => VAST.VDocumentFragment | null
 }
 export namespace ParserServices {
diff --git a/typings/eslint-utils/index.d.ts b/typings/eslint-utils/index.d.ts
index 46d0f5ab..25e9ba8d 100644
--- a/typings/eslint-utils/index.d.ts
+++ b/typings/eslint-utils/index.d.ts
@@ -8,6 +8,11 @@ export function findVariable(
   nameOrNode: VAST.Identifier | string
 ): eslint.Scope.Variable
 
+export function getStaticValue(
+  node: VAST.ESNode,
+  initialScope?: eslint.Scope.Scope
+): { value: any } | null
+
 export function isParenthesized(
   num: number,
   node: VAST.ESNode,


Test patch:
diff --git a/tests/lib/rules/comma-spacing.js b/tests/lib/rules/comma-spacing.js
index e5b74407..68b70b32 100644
--- a/tests/lib/rules/comma-spacing.js
+++ b/tests/lib/rules/comma-spacing.js
@@ -61,7 +61,14 @@ tester.run('comma-spacing', rule, {
     `<script>
     fn = (a,b) => {}
     </script>`,
-    `fn = (a,b) => {}`
+    `fn = (a,b) => {}`,
+    // CSS vars injection
+    `
+    <style>
+    .text {
+      color: v-bind('foo(a, b)')
+    }
+    </style>`
   ],
   invalid: [
     {
@@ -278,6 +285,26 @@ tester.run('comma-spacing', rule, {
           line: 4
         }
       ]
+    },
+    {
+      code: `
+      <style>
+      .text {
+        color: v-bind('foo(a,b)')
+      }
+      </style>`,
+      output: `
+      <style>
+      .text {
+        color: v-bind('foo(a, b)')
+      }
+      </style>`,
+      errors: [
+        {
+          message: "A space is required after ','.",
+          line: 4
+        }
+      ]
     }
   ]
 })
diff --git a/tests/lib/rules/dot-notation.js b/tests/lib/rules/dot-notation.js
index eea4c192..ff32b85e 100644
--- a/tests/lib/rules/dot-notation.js
+++ b/tests/lib/rules/dot-notation.js
@@ -17,7 +17,14 @@ tester.run('dot-notation', rule, {
     '<template><div attr="foo[\'bar\']" /></template>',
     `<template><div :[foo.bar]="a" /></template>`,
     `<template><div :attr="foo[bar]" /></template>`,
-    `<template><div :[foo[bar]]="a" /></template>`
+    `<template><div :[foo[bar]]="a" /></template>`,
+    // CSS vars injection
+    `
+    <style>
+    .text {
+      color: v-bind(foo.bar)
+    }
+    </style>`
   ],
   invalid: [
     {
@@ -29,6 +36,37 @@ tester.run('dot-notation', rule, {
       code: `<template><div :[foo[\`bar\`]]="a" /></template>`,
       output: `<template><div :[foo.bar]="a" /></template>`,
       errors: ['[`bar`] is better written in dot notation.']
+    },
+    // CSS vars injection
+    {
+      code: `
+      <style>
+      .text {
+        color: v-bind(foo[\`bar\`])
+      }
+      </style>`,
+      output: `
+      <style>
+      .text {
+        color: v-bind(foo.bar)
+      }
+      </style>`,
+      errors: ['[`bar`] is better written in dot notation.']
+    },
+    {
+      code: `
+      <style>
+      .text {
+        color: v-bind("foo[\`bar\`]")
+      }
+      </style>`,
+      output: `
+      <style>
+      .text {
+        color: v-bind("foo.bar")
+      }
+      </style>`,
+      errors: ['[`bar`] is better written in dot notation.']
     }
   ]
 })
diff --git a/tests/lib/rules/eqeqeq.js b/tests/lib/rules/eqeqeq.js
index baa6d4b6..092c6f0d 100644
--- a/tests/lib/rules/eqeqeq.js
+++ b/tests/lib/rules/eqeqeq.js
@@ -12,11 +12,30 @@ const tester = new RuleTester({
 })
 
 tester.run('eqeqeq', rule, {
-  valid: ['<template><div :attr="a === 1" /></template>'],
+  valid: [
+    '<template><div :attr="a === 1" /></template>',
+    // CSS vars injection
+    `
+    <style>
+    .text {
+      color: v-bind(a === 1 ? 'red' : 'blue')
+    }
+    </style>`
+  ],
   invalid: [
     {
       code: '<template><div :attr="a == 1" /></template>',
       errors: ["Expected '===' and instead saw '=='."]
+    },
+    // CSS vars injection
+    {
+      code: `
+      <style>
+      .text {
+        color: v-bind(a == 1 ? 'red' : 'blue')
+      }
+      </style>`,
+      errors: ["Expected '===' and instead saw '=='."]
     }
   ]
 })
diff --git a/tests/lib/rules/func-call-spacing.js b/tests/lib/rules/func-call-spacing.js
index 93df1577..d9250200 100644
--- a/tests/lib/rules/func-call-spacing.js
+++ b/tests/lib/rules/func-call-spacing.js
@@ -39,7 +39,14 @@ tester.run('func-call-spacing', rule, {
       </template>
       `,
       options: ['always']
+    },
+    // CSS vars injection
+    `
+    <style>
+    .text {
+      color: v-bind('foo()')
     }
+    </style>`
   ],
   invalid: [
     {
@@ -80,6 +87,30 @@ tester.run('func-call-spacing', rule, {
           line: 3
         }
       ]
+    },
+
+    // CSS vars injection
+    {
+      code: `
+      <style>
+      .text {
+        color: v-bind('foo ()')
+      }
+      </style>`,
+      output: `
+      <style>
+      .text {
+        color: v-bind('foo()')
+      }
+      </style>`,
+      errors: [
+        {
+          message: semver.lt(CLIEngine.version, '7.0.0')
+            ? 'Unexpected newline between function name and paren.'
+            : 'Unexpected whitespace between function name and paren.',
+          line: 4
+        }
+      ]
     }
   ]
 })
diff --git a/tests/lib/rules/no-extra-parens.js b/tests/lib/rules/no-extra-parens.js
index 1bb704ba..28f644a9 100644
--- a/tests/lib/rules/no-extra-parens.js
+++ b/tests/lib/rules/no-extra-parens.js
@@ -41,7 +41,14 @@ tester.run('no-extra-parens', rule, {
     '<template><button :class="(a+b | bitwise)" /></template>',
     '<template><button>{{ (foo + bar | bitwise) }}</button></template>',
     '<template><button>{{ (foo | bitwise) | filter }}</button></template>',
-    '<template><button>{{ (function () {} ()) }}</button></template>'
+    '<template><button>{{ (function () {} ()) }}</button></template>',
+    // CSS vars injection
+    `
+    <style>
+    .text {
+      color: v-bind('a')
+    }
+    </style>`
   ],
   invalid: [
     {
@@ -192,6 +199,22 @@ tester.run('no-extra-parens', rule, {
       code: '<template><button>{{ ((function () {})()) }}</button></template>',
       output: '<template><button>{{ (function () {})() }}</button></template>',
       errors: [{ messageId: 'unexpected' }]
+    },
+    // CSS vars injection
+    {
+      code: `
+      <style>
+      .text {
+        color: v-bind('(a)')
+      }
+      </style>`,
+      output: `
+      <style>
+      .text {
+        color: v-bind('a')
+      }
+      </style>`,
+      errors: [{ messageId: 'unexpected' }]
     }
   ]
 })
diff --git a/tests/lib/rules/no-restricted-syntax.js b/tests/lib/rules/no-restricted-syntax.js
index 662c2296..647bdc4b 100644
--- a/tests/lib/rules/no-restricted-syntax.js
+++ b/tests/lib/rules/no-restricted-syntax.js
@@ -50,6 +50,21 @@ tester.run('no-restricted-syntax', rule, {
           message: 'Third argument of interpolate must be true'
         }
       ]
+    },
+    // CSS vars injection
+    {
+      code: `
+      <style>
+      .text {
+        color: v-bind('foo')
+      }
+      </style>`,
+      options: [
+        {
+          selector: 'CallExpression',
+          message: 'Call expressions are not allowed.'
+        }
+      ]
     }
   ],
   invalid: [
@@ -183,6 +198,28 @@ tester.run('no-restricted-syntax', rule, {
           column: 48
         }
       ]
+    },
+    // CSS vars injection
+    {
+      code: `
+      <style>
+      .text {
+        color: v-bind('foo()')
+      }
+      </style>`,
+      options: [
+        {
+          selector: 'CallExpression',
+          message: 'Call expressions are not allowed.'
+        }
+      ],
+      errors: [
+        {
+          message: 'Call expressions are not allowed.',
+          line: 4,
+          column: 24
+        }
+      ]
     }
   ]
 })
diff --git a/tests/lib/rules/no-use-computed-property-like-method.js b/tests/lib/rules/no-use-computed-property-like-method.js
new file mode 100644
index 00000000..b6e5e695
--- /dev/null
+++ b/tests/lib/rules/no-use-computed-property-like-method.js
@@ -0,0 +1,703 @@
+/**
+ * @author tyankatsu <https://github.com/tyankatsu0105>
+ * See LICENSE file in root directory for full license.
+ */
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/no-use-computed-property-like-method')
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const tester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2015, sourceType: 'module' }
+})
+
+tester.run('no-use-computed-property-like-method', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          data() {
+            return {
+              dataString: 'dataString',
+              dataNumber: 10,
+              dataObject: {
+                inside: "inside"
+              },
+              dataArray: [1,2,3,4,5],
+              dataBoolean: true,
+            }
+          },
+          computed: {
+            computedReturnDataString() {
+              return this.dataString
+            },
+            computedReturnDataNumber() {
+              return this.dataNumber
+            },
+            computedReturnDataObject() {
+              return this.dataObject
+            },
+            computedReturnDataArray() {
+              return this.dataArray
+            },
+            computedReturnDataBoolean() {
+              return this.dataBoolean
+            }
+          },
+          methods: {
+            fn() {
+              this.computedReturnDataString
+              this.computedReturnDataNumber
+              this.computedReturnDataObject
+              this.computedReturnDataArray
+              this.computedReturnDataBoolean
+            }
+          }
+        }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          data() {
+            return {
+              dataFunction() {
+                alert('dataFunction')
+              }
+            }
+          },
+          computed: {
+            computedReturnDataFunction() {
+              return this.dataFunction
+            }
+          },
+          methods: {
+            fn() {
+              this.computedReturnDataFunction
+              this.computedReturnDataFunction()
+            }
+          }
+        }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          props: {
+            propsString: String,
+            propsNumber: Number,
+            propsObject: Object,
+            propsArray: Array,
+            propsBoolean: Boolean
+          },
+          computed: {
+            computedReturnPropsString() {
+              return this.propsString
+            },
+            computedReturnPropsNumber() {
+              return this.propsNumber
+            },
+            computedReturnPropsObject() {
+              return this.propsObject
+            },
+            computedReturnPropsArray() {
+              return this.propsArray
+            },
+            computedReturnPropsBoolean() {
+              return this.propsBoolean
+            }
+          },
+          methods: {
+            fn() {
+              this.computedReturnPropsString
+              this.computedReturnPropsNumber
+              this.computedReturnPropsObject
+              this.computedReturnPropsArray
+              this.computedReturnPropsBoolean
+            }
+          }
+        }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          props: {
+            propsString: {
+              type: String
+            },
+            propsNumber: {
+              type: Number
+            },
+            propsObject: {
+              type: Object
+            },
+            propsArray: {
+              type: Array
+            },
+            propsBoolean: {
+              type: Boolean
+            }
+          },
+          computed: {
+            computedReturnPropsString() {
+              return this.propsString
+            },
+            computedReturnPropsNumber() {
+              return this.propsNumber
+            },
+            computedReturnPropsObject() {
+              return this.propsObject
+            },
+            computedReturnPropsArray() {
+              return this.propsArray
+            },
+            computedReturnPropsBoolean() {
+              return this.propsBoolean
+            }
+          },
+          methods: {
+            fn() {
+              this.computedReturnPropsString
+              this.computedReturnPropsNumber
+              this.computedReturnPropsObject
+              this.computedReturnPropsArray
+              this.computedReturnPropsBoolean
+            }
+          }
+        }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          props: {
+            propsFunction: {
+              type: Function
+            },
+          },
+          computed: {
+            computedReturnPropsFunction() {
+              return this.propsFunction
+            }
+          },
+          methods: {
+            fn() {
+              this.computedReturnPropsFunction
+              this.computedReturnPropsFunction()
+            }
+          }
+        }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          props: [
+            "propsString",
+            "propsNumber",
+            "propsObject",
+            "propsArray",
+            "propsBoolean",
+            "propsFunction"
+          ],
+          computed: {
+            computedReturnPropsString() {
+              return this.propsString
+            },
+            computedReturnPropsNumber() {
+              return this.propsNumber
+            },
+            computedReturnPropsObject() {
+              return this.propsObject
+            },
+            computedReturnPropsArray() {
+              return this.propsArray
+            },
+            computedReturnPropsBoolean() {
+              return this.propsBoolean
+            },
+            computedReturnPropsFunction() {
+              return this.propsFunction
+            }
+          },
+          methods: {
+            fn() {
+              this.computedReturnPropsString
+              this.computedReturnPropsString()
+
+              this.computedReturnPropsNumber
+              this.computedReturnPropsNumber()
+
+              this.computedReturnPropsObject
+              this.computedReturnPropsObject()
+
+              this.computedReturnPropsArray
+              this.computedReturnPropsArray()
+
+              this.computedReturnPropsBoolean
+              this.computedReturnPropsBoolean()
+
+              this.computedReturnPropsFunction
+              this.computedReturnPropsFunction()
+            }
+          }
+        }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          computed: {
+            computedReturnString() {
+              return 'computedReturnString'
+            },
+            computedReturnNumber() {
+              return 10
+            },
+            computedReturnObject() {
+              return {
+                inside: "inside"
+              }
+            },
+            computedReturnArray() {
+              return [1,2,3,4,5]
+            },
+            computedReturnBoolean() {
+              return true
+            }
+          },
+          methods: {
+            fn() {
+              this.computedReturnString
+              this.computedReturnNumber
+              this.computedReturnObject
+              this.computedReturnArray
+              this.computedReturnBoolean
+            }
+          }
+        }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          computed: {
+            computedReturnFunction() {
+              const fn = () => alert('computedReturnFunction')
+              return fn
+            }
+          },
+          methods: {
+            fn() {
+              this.computedReturnFunction
+              this.computedReturnFunction()
+            }
+          }
+        }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          computed: {
+            computedReturnMethodsReturnString() {
+              return this.methodsReturnString
+            },
+            computedReturnMethodsReturnNumber() {
+              return this.methodsReturnNumber
+            },
+            computedReturnMethodsReturnObject() {
+              return this.methodsReturnObject
+            },
+            computedReturnMethodsReturnArray() {
+              return this.methodsReturnArray
+            },
+            computedReturnMethodsReturnBoolean() {
+              return this.methodsReturnBoolean
+            }
+          },
+          methods: {
+            methodsReturnString() {
+              return 'methodsReturnString'
+            },
+            methodsReturnNumber() {
+              return 'methodsReturnNumber'
+            },
+            methodsReturnObject() {
+              return {
+                inside: "inside"
+              }
+            },
+            methodsReturnArray() {
+              return [1,2,3,4,5]
+            },
+            methodsReturnBoolean() {
+              return true
+            },
+            fn() {
+              this.computedReturnMethodsReturnString
+              this.computedReturnMethodsReturnNumber
+              this.computedReturnMethodsReturnObject
+              this.computedReturnMethodsReturnArray
+              this.computedReturnMethodsReturnBoolean
+            }
+          }
+        }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          computed: {
+            computedReturnMethodsReturnFunction() {
+              return this.methodsReturnFunction
+            }
+          },
+          methods: {
+            methodsReturnFunction() {
+              const fn = () => alert('methodsReturnFunction')
+              return fn
+            },
+            fn() {
+              this.computedReturnMethodsReturnFunction
+              this.computedReturnMethodsReturnFunction()
+            }
+          }
+        }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          methods: {
+            fn() {
+              this.foo()
+            }
+          }
+        }
+      </script>
+      `
+    }
+  ],
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          data() {
+            return {
+              dataString: 'dataString',
+              dataNumber: 10,
+              dataObject: {
+                inside: "inside"
+              },
+              dataArray: [1,2,3,4,5],
+              dataBoolean: true,
+            }
+          },
+          computed: {
+            computedReturnDataString() {
+              return this.dataString
+            },
+            computedReturnDataNumber() {
+              return this.dataNumber
+            },
+            computedReturnDataObject() {
+              return this.dataObject
+            },
+            computedReturnDataArray() {
+              return this.dataArray
+            },
+            computedReturnDataBoolean() {
+              return this.dataBoolean
+            }
+          },
+          methods: {
+            fn() {
+              this.computedReturnDataString()
+              this.computedReturnDataNumber()
+              this.computedReturnDataObject()
+              this.computedReturnDataArray()
+              this.computedReturnDataBoolean()
+            }
+          }
+        }
+      </script>
+      `,
+      errors: [
+        'Use this.computedReturnDataString instead of this.computedReturnDataString().',
+        'Use this.computedReturnDataNumber instead of this.computedReturnDataNumber().',
+        'Use this.computedReturnDataObject instead of this.computedReturnDataObject().',
+        'Use this.computedReturnDataArray instead of this.computedReturnDataArray().',
+        'Use this.computedReturnDataBoolean instead of this.computedReturnDataBoolean().'
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          props: {
+            propsString: String,
+            propsNumber: Number,
+            propsObject: Object,
+            propsArray: Array,
+            propsBoolean: Boolean
+          },
+          computed: {
+            computedReturnPropsString() {
+              return this.propsString
+            },
+            computedReturnPropsNumber() {
+              return this.propsNumber
+            },
+            computedReturnPropsObject() {
+              return this.propsObject
+            },
+            computedReturnPropsArray() {
+              return this.propsArray
+            },
+            computedReturnPropsBoolean() {
+              return this.propsBoolean
+            }
+          },
+          methods: {
+            fn() {
+              this.computedReturnPropsString()
+              this.computedReturnPropsNumber()
+              this.computedReturnPropsObject()
+              this.computedReturnPropsArray()
+              this.computedReturnPropsBoolean()
+            }
+          }
+        }
+      </script>
+      `,
+      errors: [
+        'Use this.computedReturnPropsString instead of this.computedReturnPropsString().',
+        'Use this.computedReturnPropsNumber instead of this.computedReturnPropsNumber().',
+        'Use this.computedReturnPropsObject instead of this.computedReturnPropsObject().',
+        'Use this.computedReturnPropsArray instead of this.computedReturnPropsArray().',
+        'Use this.computedReturnPropsBoolean instead of this.computedReturnPropsBoolean().'
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          props: {
+            propsString: {
+              type: String
+            },
+            propsNumber: {
+              type: Number
+            },
+            propsObject: {
+              type: Object
+            },
+            propsArray: {
+              type: Array
+            },
+            propsBoolean: {
+              type: Boolean
+            }
+          },
+          computed: {
+            computedReturnPropsString() {
+              return this.propsString
+            },
+            computedReturnPropsNumber() {
+              return this.propsNumber
+            },
+            computedReturnPropsObject() {
+              return this.propsObject
+            },
+            computedReturnPropsArray() {
+              return this.propsArray
+            },
+            computedReturnPropsBoolean() {
+              return this.propsBoolean
+            }
+          },
+          methods: {
+            fn() {
+              this.computedReturnPropsString()
+              this.computedReturnPropsNumber()
+              this.computedReturnPropsObject()
+              this.computedReturnPropsArray()
+              this.computedReturnPropsBoolean()
+            }
+          }
+        }
+      </script>
+      `,
+      errors: [
+        'Use this.computedReturnPropsString instead of this.computedReturnPropsString().',
+        'Use this.computedReturnPropsNumber instead of this.computedReturnPropsNumber().',
+        'Use this.computedReturnPropsObject instead of this.computedReturnPropsObject().',
+        'Use this.computedReturnPropsArray instead of this.computedReturnPropsArray().',
+        'Use this.computedReturnPropsBoolean instead of this.computedReturnPropsBoolean().'
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          computed: {
+            computedReturnString() {
+              return 'computedReturnString'
+            },
+            computedReturnNumber() {
+              return 10
+            },
+            computedReturnObject() {
+              return {
+                inside: "inside"
+              }
+            },
+            computedReturnArray() {
+              return [1,2,3,4,5]
+            },
+            computedReturnBoolean() {
+              return true
+            }
+          },
+          methods: {
+            fn() {
+              this.computedReturnString()
+              this.computedReturnNumber()
+              this.computedReturnObject()
+              this.computedReturnArray()
+              this.computedReturnBoolean()
+            }
+          }
+        }
+      </script>
+      `,
+      errors: [
+        'Use this.computedReturnString instead of this.computedReturnString().',
+        'Use this.computedReturnNumber instead of this.computedReturnNumber().',
+        'Use this.computedReturnObject instead of this.computedReturnObject().',
+        'Use this.computedReturnArray instead of this.computedReturnArray().',
+        'Use this.computedReturnBoolean instead of this.computedReturnBoolean().'
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          computed: {
+            computedReturnString() {
+              return 'computedReturnString'
+            },
+            computedReturnComputedReturnString() {
+              return this.computedReturnString
+            }
+          },
+          methods: {
+            fn() {
+              this.computedReturnComputedReturnString()
+            }
+          }
+        }
+      </script>
+      `,
+      errors: [
+        'Use this.computedReturnComputedReturnString instead of this.computedReturnComputedReturnString().'
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          computed: {
+            computedReturnObject() {
+              return {
+                insideFn() {
+                  return "insideFn"
+                }
+              }
+            },
+          },
+          methods: {
+            fn() {
+              this.computedReturnObject().insideFn
+            }
+          }
+        }
+      </script>
+      `,
+      errors: [
+        'Use this.computedReturnObject instead of this.computedReturnObject().'
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+        export default {
+          computed: {
+            computedReturnNothing() {
+              return
+            },
+          },
+          methods: {
+            fn() {
+              this.computedReturnNothing()
+            }
+          }
+        }
+      </script>
+      `,
+      errors: [
+        'Use this.computedReturnNothing instead of this.computedReturnNothing().'
+      ]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-useless-concat.js b/tests/lib/rules/no-useless-concat.js
index 72e1ca55..28ac8240 100644
--- a/tests/lib/rules/no-useless-concat.js
+++ b/tests/lib/rules/no-useless-concat.js
@@ -15,7 +15,14 @@ tester.run('no-useless-concat', rule, {
   valid: [
     `<template><div :attr="'foo-bar'" /></template>`,
     '<template><div attr="foo-bar" /></template>',
-    `<template><div :[\`foo-bar\`]="a" /></template>`
+    `<template><div :[\`foo-bar\`]="a" /></template>`,
+    // CSS vars injection
+    `
+    <style>
+    .text {
+      color: v-bind('"red"')
+    }
+    </style>`
   ],
   invalid: [
     {
@@ -25,6 +32,16 @@ tester.run('no-useless-concat', rule, {
     {
       code: `<template><div :[\`foo\`+\`bar\`]="a" /></template>`,
       errors: ['Unexpected string concatenation of literals.']
+    },
+    // CSS vars injection
+    {
+      code: `
+      <style>
+      .text {
+        color: v-bind('"re" + "d"')
+      }
+      </style>`,
+      errors: ['Unexpected string concatenation of literals.']
     }
   ]
 })
diff --git a/tests/lib/rules/prefer-template.js b/tests/lib/rules/prefer-template.js
index 73ad5e5b..e35fa6d1 100644
--- a/tests/lib/rules/prefer-template.js
+++ b/tests/lib/rules/prefer-template.js
@@ -22,7 +22,14 @@ tester.run('prefer-template', rule, {
     <template>
       <div :[\`foo\${bar}\`]="value" />
     </template>
+    `,
+    // CSS vars injection
     `
+    <style>
+    .text {
+      color: v-bind('\`#\${hex}\`')
+    }
+    </style>`
   ],
   invalid: [
     {
@@ -58,6 +65,27 @@ tester.run('prefer-template', rule, {
           line: 3
         }
       ]
+    },
+    // CSS vars injection
+    {
+      code: `
+      <style>
+      .text {
+        color: v-bind('"#"+hex')
+      }
+      </style>`,
+      output: `
+      <style>
+      .text {
+        color: v-bind('\`#\${hex}\`')
+      }
+      </style>`,
+      errors: [
+        {
+          message: 'Unexpected string concatenation.',
+          line: 4
+        }
+      ]
     }
   ]
 })
diff --git a/tests/lib/rules/space-in-parens.js b/tests/lib/rules/space-in-parens.js
index 421fb4e9..57be0b52 100644
--- a/tests/lib/rules/space-in-parens.js
+++ b/tests/lib/rules/space-in-parens.js
@@ -53,7 +53,14 @@ tester.run('space-in-parens', rule, {
         />
       </template>`,
       options: ['always']
+    },
+    // CSS vars injection
+    `
+    <style>
+    .text {
+      color: v-bind('foo(arg)')
     }
+    </style>`
   ],
   invalid: [
     {
@@ -202,6 +209,32 @@ tester.run('space-in-parens', rule, {
           line: 4
         })
       ]
+    },
+
+    // CSS vars injection
+    {
+      code: `
+      <style>
+      .text {
+        color: v-bind('foo( arg )')
+      }
+      </style>`,
+      output: `
+      <style>
+      .text {
+        color: v-bind('foo(arg)')
+      }
+      </style>`,
+      errors: [
+        errorMessage({
+          messageId: 'rejectedOpeningSpace',
+          line: 4
+        }),
+        errorMessage({
+          messageId: 'rejectedClosingSpace',
+          line: 4
+        })
+      ]
     }
   ]
 })
diff --git a/tests/lib/rules/space-infix-ops.js b/tests/lib/rules/space-infix-ops.js
index 427a8fdc..26b008d8 100644
--- a/tests/lib/rules/space-infix-ops.js
+++ b/tests/lib/rules/space-infix-ops.js
@@ -20,7 +20,15 @@ tester.run('space-infix-ops', rule, {
   valid: [
     '<template><div :attr="a + 1" /></template>',
     '<template><div :attr="a ? 1 : 2" /></template>',
-    '<template><div :[1+2]="a" /></template>'
+    '<template><div :[1+2]="a" /></template>',
+
+    // CSS vars injection
+    `
+    <style>
+    .text {
+      padding: v-bind('a + b + "px"')
+    }
+    </style>`
   ],
   invalid: [
     {
@@ -42,6 +50,23 @@ tester.run('space-infix-ops', rule, {
       code: '<template><div :[1+2]="1+2" /></template>',
       output: '<template><div :[1+2]="1 + 2" /></template>',
       errors: [message('+')]
+    },
+
+    // CSS vars injection
+    {
+      code: `
+      <style>
+      .text {
+        padding: v-bind('a+b+"px"')
+      }
+      </style>`,
+      output: `
+      <style>
+      .text {
+        padding: v-bind('a + b + "px"')
+      }
+      </style>`,
+      errors: [message('+'), message('+')]
     }
   ]
 })
diff --git a/tests/lib/rules/space-unary-ops.js b/tests/lib/rules/space-unary-ops.js
index 9d74207c..6b3e7c25 100644
--- a/tests/lib/rules/space-unary-ops.js
+++ b/tests/lib/rules/space-unary-ops.js
@@ -23,7 +23,14 @@ tester.run('space-unary-ops', rule, {
     {
       code: '<template><div :[!a]="a" /></template>',
       options: [{ nonwords: true }]
+    },
+    // CSS vars injection
+    `
+    <style>
+    .text {
+      padding: v-bind(\`\${-num}px\`)
     }
+    </style>`
   ],
   invalid: [
     {
@@ -46,6 +53,23 @@ tester.run('space-unary-ops', rule, {
       options: [{ nonwords: true }],
       output: '<template><div :[!a]="! a" /></template>',
       errors: ["Unary operator '!' must be followed by whitespace."]
+    },
+
+    // CSS vars injection
+    {
+      code: `
+      <style>
+      .text {
+        padding: v-bind(\`\${- num}px\`)
+      }
+      </style>`,
+      output: `
+      <style>
+      .text {
+        padding: v-bind(\`\${-num}px\`)
+      }
+      </style>`,
+      errors: ["Unexpected space after unary operator '-'."]
     }
   ]
 })
diff --git a/tests/lib/rules/template-curly-spacing.js b/tests/lib/rules/template-curly-spacing.js
index 02aeb43e..6c437696 100644
--- a/tests/lib/rules/template-curly-spacing.js
+++ b/tests/lib/rules/template-curly-spacing.js
@@ -38,6 +38,16 @@ tester.run('template-curly-spacing', rule, {
       </template>
       `,
       options: ['always']
+    },
+
+    // CSS vars injection
+    {
+      code: `
+      <style>
+      .text {
+        padding: v-bind(\`\${a}px\`)
+      }
+      </style>`
     }
   ],
   invalid: [
@@ -85,6 +95,32 @@ tester.run('template-curly-spacing', rule, {
           line: 3
         }
       ]
+    },
+
+    // CSS vars injection
+    {
+      code: `
+      <style>
+      .text {
+        padding: v-bind(\`\${ a }px\`)
+      }
+      </style>`,
+      output: `
+      <style>
+      .text {
+        padding: v-bind(\`\${a}px\`)
+      }
+      </style>`,
+      errors: [
+        {
+          message: "Unexpected space(s) after '${'.",
+          line: 4
+        },
+        {
+          message: "Unexpected space(s) before '}'.",
+          line: 4
+        }
+      ]
     }
   ]
 })