diff --git a/docs/rules/README.md b/docs/rules/README.md
index 3577668f4..05565d3e6 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: |
@@ -365,7 +366,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 000000000..d776cd7eb
--- /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: ***This rule has not been released yet.***
+
+## :book: Rule Details
+
+This rule disallows to use computed property like method.
+
+
+
+```vue
+
+```
+
+
+
+This rule can't check if props is used as array:
+
+
+
+```vue
+
+```
+
+
+
+## :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 52e3d6ae4..cc4831c12 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/lib/index.js b/lib/index.js
index 8bf6cad45..88ea06c18 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/no-use-computed-property-like-method.js b/lib/rules/no-use-computed-property-like-method.js
new file mode 100644
index 000000000..5b0371b8f
--- /dev/null
+++ b/lib/rules/no-use-computed-property-like-method.js
@@ -0,0 +1,257 @@
+/**
+ * @author tyankatsu
+ * 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 {{[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} 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) {
+ const GROUP_NAMES = ['data', 'props', 'computed', 'methods']
+ const groups = new Set(GROUP_NAMES)
+
+ /** @type PropertyMap */
+ const propertyMap = {}
+
+ /**@type ObjectExpression */
+ let vueNodeMap = {}
+
+ return utils.defineVueVisitor(context, {
+ onVueObjectEnter(node) {
+ vueNodeMap = 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) {
+ addPropertyMap(groups, vueNodeMap, 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/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 000000000..b6e5e6958
--- /dev/null
+++ b/tests/lib/rules/no-use-computed-property-like-method.js
@@ -0,0 +1,703 @@
+/**
+ * @author tyankatsu
+ * 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: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ }
+ ],
+ invalid: [
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ 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: `
+
+ `,
+ 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: `
+
+ `,
+ 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: `
+
+ `,
+ 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: `
+
+ `,
+ errors: [
+ 'Use this.computedReturnComputedReturnString instead of this.computedReturnComputedReturnString().'
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ 'Use this.computedReturnObject instead of this.computedReturnObject().'
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ 'Use this.computedReturnNothing instead of this.computedReturnNothing().'
+ ]
+ }
+ ]
+})
diff --git a/typings/eslint-utils/index.d.ts b/typings/eslint-utils/index.d.ts
index 46d0f5abe..25e9ba8d4 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,