Repository: vuejs/eslint-plugin-vue (js)

Issue:
Rule suggestion: `vue/next-tick-style`
**Please describe what the rule should do:**
Since Vue v2.1.0, [`Vue.nextTick`](https://vuejs.org/v2/api/#Vue-nextTick) and [`vm.$nextTick`](https://vuejs.org/v2/api/#vm-nextTick) return a Promise if no callback is provided. So both of the following are valid:

* `Vue.nextTick(callback)`
* `Vue.nextTick().then()` / `await Vue.nextTick()`

Or inside components:

* `this.$nextTick(callback)`
* `this.$nextTick().then()` / `await this.$nextTick()`

There should be a rule that enforces one of the styles to make the codebase more consistent. I suggest that the Promise-style is the default of this rule, because it makes the code more concise and is generally well supported as of now.

However, enforcing the `await` or `.then()` style is out of scope for this plugin, there is already the [`eslint-plugin-promise` plugin](https://github.com/xjamundx/eslint-plugin-promise) with the [`promise/prefer-await-to-then` rule](https://github.com/xjamundx/eslint-plugin-promise/blob/v4.0.1/docs/rules/prefer-await-to-then.md).

**What category should the rule belong to?**
[X] Enforces code style (layout)
[ &nbsp;] Warns about a potential error (problem)
[ &nbsp;] Suggests an alternate way of doing something (suggestion)
[ &nbsp;] Other (please specify:)

**Provide 2-3 code examples that this rule should warn about:**

```js
// "vue/next-tick-style": "warn"
// "vue/next-tick-style": ["warn", "promise"]

// BAD:
Vue.nextTick(() => callback())
Vue.nextTick(callback)
this.$nextTick(() => callback())
this.$nextTick(callback)

// GOOD:
Vue.nextTick().then(() => callback())
await Vue.nextTick(); callback()
this.$nextTick().then(() => callback())
await this.$nextTick(); callback()
```

```js
// "vue/next-tick-style": ["warn", "callback"]

// BAD:
Vue.nextTick().then(() => callback())
await Vue.nextTick(); callback()
this.$nextTick().then(() => callback())
await this.$nextTick(); callback()

// GOOD:
Vue.nextTick(() => callback())
Vue.nextTick(callback)
this.$nextTick(() => callback())
this.$nextTick(callback)
```

Pull request description:
Add `vue/next-tick-style` rule
Closes #1318.

Before reviewing, please have a look at #1399. Does it make sense to merge these two rules into one? If so, I'll try to implement that.

Patch:
diff --git a/docs/rules/README.md b/docs/rules/README.md
index 5f848467..92674e1f 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -64,7 +64,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | [vue/no-lifecycle-after-await](./no-lifecycle-after-await.md) | disallow asynchronously registered lifecycle hooks |  |
 | [vue/no-mutating-props](./no-mutating-props.md) | disallow mutation of component props |  |
 | [vue/no-parsing-error](./no-parsing-error.md) | disallow parsing errors in `<template>` |  |
-| [vue/no-ref-as-operand](./no-ref-as-operand.md) | disallow use of value wrapped by `ref()` (Composition API) as an operand |  |
+| [vue/no-ref-as-operand](./no-ref-as-operand.md) | disallow use of value wrapped by `ref()` (Composition API) as an operand | :wrench: |
 | [vue/no-reserved-keys](./no-reserved-keys.md) | disallow overwriting reserved keys |  |
 | [vue/no-setup-props-destructure](./no-setup-props-destructure.md) | disallow destructuring of `props` passed to `setup` |  |
 | [vue/no-shared-component-data](./no-shared-component-data.md) | enforce component's data property to be a function | :wrench: |
@@ -294,6 +294,7 @@ For example:
 | [vue/html-comment-indent](./html-comment-indent.md) | enforce consistent indentation in HTML comments | :wrench: |
 | [vue/match-component-file-name](./match-component-file-name.md) | require component name property to match its file name |  |
 | [vue/new-line-between-multi-line-property](./new-line-between-multi-line-property.md) | enforce new lines between multi-line properties in Vue components | :wrench: |
+| [vue/next-tick-style](./next-tick-style.md) | enforce Promise or callback style in `nextTick` | :wrench: |
 | [vue/no-bare-strings-in-template](./no-bare-strings-in-template.md) | disallow the use of bare strings in `<template>` |  |
 | [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: |
 | [vue/no-duplicate-attr-inheritance](./no-duplicate-attr-inheritance.md) | enforce `inheritAttrs` to be set to `false` when using `v-bind="$attrs"` |  |
@@ -347,6 +348,7 @@ The following rules extend the rules provided by ESLint itself and apply them to
 | [vue/key-spacing](./key-spacing.md) | enforce consistent spacing between keys and values in object literal properties | :wrench: |
 | [vue/keyword-spacing](./keyword-spacing.md) | enforce consistent spacing before and after keywords | :wrench: |
 | [vue/max-len](./max-len.md) | enforce a maximum line length |  |
+| [vue/no-constant-condition](./no-constant-condition.md) | disallow constant expressions in conditions |  |
 | [vue/no-empty-pattern](./no-empty-pattern.md) | disallow empty destructuring patterns |  |
 | [vue/no-extra-parens](./no-extra-parens.md) | disallow unnecessary parentheses | :wrench: |
 | [vue/no-irregular-whitespace](./no-irregular-whitespace.md) | disallow irregular whitespace |  |
diff --git a/docs/rules/next-tick-style.md b/docs/rules/next-tick-style.md
new file mode 100644
index 00000000..52188fd0
--- /dev/null
+++ b/docs/rules/next-tick-style.md
@@ -0,0 +1,103 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/next-tick-style
+description: enforce Promise or callback style in `nextTick`
+---
+# vue/next-tick-style
+
+> enforce Promise or callback style in `nextTick`
+
+- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
+- :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.
+
+## :book: Rule Details
+
+This rule enforces whether the callback version or Promise version (which was introduced in Vue v2.1.0) should be used in `Vue.nextTick` and `this.$nextTick`.
+
+<eslint-code-block fix :rules="{'vue/next-tick-style': ['error']}">
+
+```vue
+<script>
+import { nextTick as nt } from 'vue';
+
+export default {
+  async mounted() {
+    /* ✓ GOOD */
+    nt().then(() => callback());
+    await nt(); callback();
+    Vue.nextTick().then(() => callback());
+    await Vue.nextTick(); callback();
+    this.$nextTick().then(() => callback());
+    await this.$nextTick(); callback();
+
+    /* ✗ BAD */
+    nt(() => callback());
+    nt(callback);
+    Vue.nextTick(() => callback());
+    Vue.nextTick(callback);
+    this.$nextTick(() => callback());
+    this.$nextTick(callback);
+  }
+}
+</script>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+Default is set to `promise`.
+
+```json
+{
+  "vue/next-tick-style": ["error", "promise" | "callback"]
+}
+```
+
+- `"promise"` (default) ... requires using the promise version.
+- `"callback"` ... requires using the callback version. Use this if you use a Vue version below v2.1.0.
+
+### `"callback"`
+
+<eslint-code-block fix :rules="{'vue/next-tick-style': ['error', 'callback']}">
+
+```vue
+<script>
+import { nextTick as nt } from 'vue';
+
+export default {
+  async mounted() {
+    /* ✓ GOOD */
+    nt(() => callback());
+    nt(callback);
+    Vue.nextTick(() => callback());
+    Vue.nextTick(callback);
+    this.$nextTick(() => callback());
+    this.$nextTick(callback);
+
+    /* ✗ BAD */
+    nt().then(() => callback());
+    await nt(); callback();
+    Vue.nextTick().then(() => callback());
+    await Vue.nextTick(); callback();
+    this.$nextTick().then(() => callback());
+    await this.$nextTick(); callback();
+  }
+}
+</script>
+```
+
+</eslint-code-block>
+
+## :books: Further Reading
+
+- [`Vue.nextTick` API in Vue 2](https://vuejs.org/v2/api/#Vue-nextTick)
+- [`vm.$nextTick` API in Vue 2](https://vuejs.org/v2/api/#vm-nextTick)
+- [Global API Treeshaking](https://v3.vuejs.org/guide/migration/global-api-treeshaking.html)
+- [Global `nextTick` API in Vue 3](https://v3.vuejs.org/api/global-api.html#nexttick)
+- [Instance `$nextTick` API in Vue 3](https://v3.vuejs.org/api/instance-methods.html#nexttick)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/next-tick-style.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/next-tick-style.js)
diff --git a/docs/rules/no-async-in-computed-properties.md b/docs/rules/no-async-in-computed-properties.md
index 93e0ef04..549dc201 100644
--- a/docs/rules/no-async-in-computed-properties.md
+++ b/docs/rules/no-async-in-computed-properties.md
@@ -2,21 +2,21 @@
 pageClass: rule-details
 sidebarDepth: 0
 title: vue/no-async-in-computed-properties
-description: disallow asynchronous actions in computed properties
+description: disallow asynchronous actions in computed properties and functions
 since: v3.8.0
 ---
 # vue/no-async-in-computed-properties
 
-> disallow asynchronous actions in computed properties
+> disallow asynchronous actions in computed properties and functions
 
 - :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/essential"`, `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
 
-Computed properties should be synchronous. Asynchronous actions inside them may not work as expected and can lead to an unexpected behaviour, that's why you should avoid them.
+Computed properties and functions should be synchronous. Asynchronous actions inside them may not work as expected and can lead to an unexpected behaviour, that's why you should avoid them.
 If you need async computed properties you might want to consider using additional plugin [vue-async-computed]
 
 ## :book: Rule Details
 
-This rule is aimed at preventing asynchronous methods from being called in computed properties.
+This rule is aimed at preventing asynchronous methods from being called in computed properties and functions.
 
 <eslint-code-block :rules="{'vue/no-async-in-computed-properties': ['error']}">
 
@@ -62,6 +62,47 @@ export default {
 
 </eslint-code-block>
 
+<eslint-code-block :rules="{'vue/no-async-in-computed-properties': ['error']}">
+
+```vue
+<script>
+import {computed} from 'vue'
+export default {
+  setup() {
+    /* ✓ GOOD */
+    const foo = computed(() => {
+      var bar = 0
+      try {
+        bar = bar / this.a
+      } catch (e) {
+        return 0
+      } finally {
+        return bar
+      }
+    })
+
+    /* ✗ BAD */
+    const pro = computed(() => Promise.all([new Promise((resolve, reject) => {})]))
+    const foo1 = computed(async () => await someFunc())
+    const bar = computed(() => {
+      return fetch(url).then(response => {})
+    })
+    const tim = computed(() => {
+      setTimeout(() => { }, 0)
+    })
+    const inter = computed(() => {
+      setInterval(() => { }, 0)
+    })
+    const anim = computed(() => {
+      requestAnimationFrame(() => {})
+    })
+  }
+}
+</script>
+```
+
+</eslint-code-block>
+
 ## :wrench: Options
 
 Nothing.
diff --git a/docs/rules/no-constant-condition.md b/docs/rules/no-constant-condition.md
new file mode 100644
index 00000000..901b2a40
--- /dev/null
+++ b/docs/rules/no-constant-condition.md
@@ -0,0 +1,26 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-constant-condition
+description: disallow constant expressions in conditions
+---
+# vue/no-constant-condition
+
+> disallow constant expressions in conditions
+
+- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
+
+This rule is the same rule as core [no-constant-condition] rule but it applies to the expressions in `<template>`.
+
+## :books: Further Reading
+
+- [no-constant-condition]
+
+[no-constant-condition]: https://eslint.org/docs/rules/no-constant-condition
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-constant-condition.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-constant-condition.js)
+
+<sup>Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/no-constant-condition)</sup>
diff --git a/docs/rules/no-ref-as-operand.md b/docs/rules/no-ref-as-operand.md
index 3284fa7c..2610d462 100644
--- a/docs/rules/no-ref-as-operand.md
+++ b/docs/rules/no-ref-as-operand.md
@@ -10,13 +10,14 @@ since: v7.0.0
 > disallow use of value wrapped by `ref()` (Composition API) as an operand
 
 - :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+- :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.
 
 ## :book: Rule Details
 
 This rule reports cases where a ref is used incorrectly as an operand.  
 You must use `.value` to access the `Ref` value.
 
-<eslint-code-block :rules="{'vue/no-ref-as-operand': ['error']}">
+<eslint-code-block fix :rules="{'vue/no-ref-as-operand': ['error']}">
 
 ```vue
 <script>
diff --git a/docs/user-guide/README.md b/docs/user-guide/README.md
index 2457534f..a4e9acd9 100644
--- a/docs/user-guide/README.md
+++ b/docs/user-guide/README.md
@@ -338,4 +338,4 @@ Note that you cannot use angle-bracket type assertion style (`var x = <foo>bar;`
 
 You need to turn off Vetur's template validation by adding `vetur.validation.template: false` to your `.vscode/settings.json`.
 
-See also: "[Visual Studio Code](#editor-integrations)" section and [Vetur - Linting](https://vuejs.github.io/vetur/linting-error.html#linting).
+See also: "[Visual Studio Code](#editor-integrations)" section and [Vetur - Linting](https://vuejs.github.io/vetur/guide/linting-error.html#linting).
diff --git a/lib/index.js b/lib/index.js
index 21091a9f..ec311919 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -48,11 +48,13 @@ module.exports = {
     'mustache-interpolation-spacing': require('./rules/mustache-interpolation-spacing'),
     'name-property-casing': require('./rules/name-property-casing'),
     'new-line-between-multi-line-property': require('./rules/new-line-between-multi-line-property'),
+    'next-tick-style': require('./rules/next-tick-style'),
     'no-arrow-functions-in-watch': require('./rules/no-arrow-functions-in-watch'),
     'no-async-in-computed-properties': require('./rules/no-async-in-computed-properties'),
     'no-bare-strings-in-template': require('./rules/no-bare-strings-in-template'),
     'no-boolean-default': require('./rules/no-boolean-default'),
     'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'),
+    'no-constant-condition': require('./rules/no-constant-condition'),
     'no-custom-modifiers-on-v-model': require('./rules/no-custom-modifiers-on-v-model'),
     'no-deprecated-data-object-declaration': require('./rules/no-deprecated-data-object-declaration'),
     'no-deprecated-destroyed-lifecycle': require('./rules/no-deprecated-destroyed-lifecycle'),
diff --git a/lib/rules/next-tick-style.js b/lib/rules/next-tick-style.js
new file mode 100644
index 00000000..d015bb5b
--- /dev/null
+++ b/lib/rules/next-tick-style.js
@@ -0,0 +1,146 @@
+/**
+ * @fileoverview enforce Promise or callback style in `nextTick`
+ * @author Flo Edelmann
+ * @copyright 2020 Flo Edelmann. All rights reserved.
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+const { findVariable } = require('eslint-utils')
+
+// ------------------------------------------------------------------------------
+// Helpers
+// ------------------------------------------------------------------------------
+
+/**
+ * @param {Identifier} identifier
+ * @param {RuleContext} context
+ * @returns {CallExpression|undefined}
+ */
+function getVueNextTickCallExpression(identifier, context) {
+  // Instance API: this.$nextTick()
+  if (
+    identifier.name === '$nextTick' &&
+    identifier.parent.type === 'MemberExpression' &&
+    utils.isThis(identifier.parent.object, context) &&
+    identifier.parent.parent.type === 'CallExpression' &&
+    identifier.parent.parent.callee === identifier.parent
+  ) {
+    return identifier.parent.parent
+  }
+
+  // Vue 2 Global API: Vue.nextTick()
+  if (
+    identifier.name === 'nextTick' &&
+    identifier.parent.type === 'MemberExpression' &&
+    identifier.parent.object.type === 'Identifier' &&
+    identifier.parent.object.name === 'Vue' &&
+    identifier.parent.parent.type === 'CallExpression' &&
+    identifier.parent.parent.callee === identifier.parent
+  ) {
+    return identifier.parent.parent
+  }
+
+  // Vue 3 Global API: import { nextTick as nt } from 'vue'; nt()
+  if (
+    identifier.parent.type === 'CallExpression' &&
+    identifier.parent.callee === identifier
+  ) {
+    const variable = findVariable(context.getScope(), identifier)
+
+    if (variable != null && variable.defs.length === 1) {
+      const def = variable.defs[0]
+      if (
+        def.type === 'ImportBinding' &&
+        def.node.type === 'ImportSpecifier' &&
+        def.node.imported.type === 'Identifier' &&
+        def.node.imported.name === 'nextTick' &&
+        def.node.parent.type === 'ImportDeclaration' &&
+        def.node.parent.source.value === 'vue'
+      ) {
+        return identifier.parent
+      }
+    }
+  }
+
+  return undefined
+}
+
+/**
+ * @param {CallExpression} callExpression
+ * @returns {boolean}
+ */
+function isAwaitedPromise(callExpression) {
+  return (
+    callExpression.parent.type === 'AwaitExpression' ||
+    (callExpression.parent.type === 'MemberExpression' &&
+      callExpression.parent.property.type === 'Identifier' &&
+      callExpression.parent.property.name === 'then')
+  )
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'suggestion',
+    docs: {
+      description: 'enforce Promise or callback style in `nextTick`',
+      categories: undefined,
+      url: 'https://eslint.vuejs.org/rules/next-tick-style.html'
+    },
+    fixable: 'code',
+    schema: [{ enum: ['promise', 'callback'] }]
+  },
+  /** @param {RuleContext} context */
+  create(context) {
+    const preferredStyle =
+      /** @type {string|undefined} */ (context.options[0]) || 'promise'
+
+    return utils.defineVueVisitor(context, {
+      /** @param {Identifier} node */
+      Identifier(node) {
+        const callExpression = getVueNextTickCallExpression(node, context)
+        if (!callExpression) {
+          return
+        }
+
+        if (preferredStyle === 'callback') {
+          if (
+            callExpression.arguments.length !== 1 ||
+            isAwaitedPromise(callExpression)
+          ) {
+            context.report({
+              node,
+              message:
+                'Pass a callback function to `nextTick` instead of using the returned Promise.'
+            })
+          }
+
+          return
+        }
+
+        if (
+          callExpression.arguments.length !== 0 ||
+          !isAwaitedPromise(callExpression)
+        ) {
+          context.report({
+            node,
+            message:
+              'Use the Promise returned by `nextTick` instead of passing a callback function.',
+            fix(fixer) {
+              return fixer.insertTextAfter(node, '().then')
+            }
+          })
+        }
+      }
+    })
+  }
+}
diff --git a/lib/rules/no-async-in-computed-properties.js b/lib/rules/no-async-in-computed-properties.js
index 9812d61c..b12fa340 100644
--- a/lib/rules/no-async-in-computed-properties.js
+++ b/lib/rules/no-async-in-computed-properties.js
@@ -3,7 +3,7 @@
  * @author Armano
  */
 'use strict'
-
+const { ReferenceTracker } = require('eslint-utils')
 const utils = require('../utils')
 
 /**
@@ -11,16 +11,16 @@ const utils = require('../utils')
  * @typedef {import('../utils').ComponentComputedProperty} ComponentComputedProperty
  */
 
-const PROMISE_FUNCTIONS = ['then', 'catch', 'finally']
+const PROMISE_FUNCTIONS = new Set(['then', 'catch', 'finally'])
 
-const PROMISE_METHODS = ['all', 'race', 'reject', 'resolve']
+const PROMISE_METHODS = new Set(['all', 'race', 'reject', 'resolve'])
 
-const TIMED_FUNCTIONS = [
+const TIMED_FUNCTIONS = new Set([
   'setTimeout',
   'setInterval',
   'setImmediate',
   'requestAnimationFrame'
-]
+])
 
 /**
  * @param {CallExpression} node
@@ -28,16 +28,12 @@ const TIMED_FUNCTIONS = [
 function isTimedFunction(node) {
   const callee = utils.skipChainExpression(node.callee)
   return (
-    ((node.type === 'CallExpression' &&
-      callee.type === 'Identifier' &&
-      TIMED_FUNCTIONS.indexOf(callee.name) !== -1) ||
-      (node.type === 'CallExpression' &&
-        callee.type === 'MemberExpression' &&
+    ((callee.type === 'Identifier' && TIMED_FUNCTIONS.has(callee.name)) ||
+      (callee.type === 'MemberExpression' &&
         callee.object.type === 'Identifier' &&
         callee.object.name === 'window' &&
-        callee.property.type === 'Identifier' &&
-        TIMED_FUNCTIONS.indexOf(callee.property.name) !== -1)) &&
-    node.arguments.length
+        TIMED_FUNCTIONS.has(utils.getStaticPropertyName(callee) || ''))) &&
+    node.arguments.length > 0
   )
 }
 
@@ -46,15 +42,16 @@ function isTimedFunction(node) {
  */
 function isPromise(node) {
   const callee = utils.skipChainExpression(node.callee)
-  if (node.type === 'CallExpression' && callee.type === 'MemberExpression') {
+  if (callee.type === 'MemberExpression') {
+    const name = utils.getStaticPropertyName(callee)
     return (
+      name &&
       // hello.PROMISE_FUNCTION()
-      (callee.property.type === 'Identifier' &&
-        PROMISE_FUNCTIONS.indexOf(callee.property.name) !== -1) || // Promise.PROMISE_METHOD()
-      (callee.object.type === 'Identifier' &&
-        callee.object.name === 'Promise' &&
-        callee.property.type === 'Identifier' &&
-        PROMISE_METHODS.indexOf(callee.property.name) !== -1)
+      (PROMISE_FUNCTIONS.has(name) ||
+        // Promise.PROMISE_METHOD()
+        (callee.object.type === 'Identifier' &&
+          callee.object.name === 'Promise' &&
+          PROMISE_METHODS.has(name)))
     )
   }
   return false
@@ -77,13 +74,16 @@ module.exports = {
   },
   /** @param {RuleContext} context */
   create(context) {
+    /** @type {Map<ObjectExpression, ComponentComputedProperty[]>} */
+    const computedPropertiesMap = new Map()
+    /** @type {(FunctionExpression | ArrowFunctionExpression)[]} */
+    const computedFunctionNodes = []
+
     /**
      * @typedef {object} ScopeStack
      * @property {ScopeStack | null} upper
      * @property {BlockStatement | Expression} body
      */
-    /** @type {Map<ObjectExpression, ComponentComputedProperty[]>} */
-    const computedPropertiesMap = new Map()
     /** @type {ScopeStack | null} */
     let scopeStack = null
 
@@ -121,7 +121,7 @@ module.exports = {
      * @param {ComponentComputedProperty[]} computedProperties
      */
     function verify(node, targetBody, type, computedProperties = []) {
-      computedProperties.forEach((cp) => {
+      for (const cp of computedProperties) {
         if (
           cp.value &&
           node.loc.start.line >= cp.value.loc.start.line &&
@@ -137,65 +137,107 @@ module.exports = {
               propertyName: cp.key || 'unknown'
             }
           })
-        }
-      })
-    }
-    return utils.defineVueVisitor(context, {
-      onVueObjectEnter(node) {
-        computedPropertiesMap.set(node, utils.getComputedProperties(node))
-      },
-      ':function': onFunctionEnter,
-      ':function:exit': onFunctionExit,
-
-      NewExpression(node, { node: vueNode }) {
-        if (!scopeStack) {
           return
         }
+      }
+
+      for (const cf of computedFunctionNodes) {
         if (
-          node.callee.type === 'Identifier' &&
-          node.callee.name === 'Promise'
+          node.loc.start.line >= cf.body.loc.start.line &&
+          node.loc.end.line <= cf.body.loc.end.line &&
+          targetBody === cf.body
         ) {
-          verify(
+          context.report({
             node,
-            scopeStack.body,
-            'new',
-            computedPropertiesMap.get(vueNode)
-          )
+            message: 'Unexpected {{expressionName}} in computed function.',
+            data: {
+              expressionName: expressionTypes[type]
+            }
+          })
+          return
         }
-      },
+      }
+    }
+    return Object.assign(
+      {
+        Program() {
+          const tracker = new ReferenceTracker(context.getScope())
+          const traceMap = utils.createCompositionApiTraceMap({
+            [ReferenceTracker.ESM]: true,
+            computed: {
+              [ReferenceTracker.CALL]: true
+            }
+          })
 
-      CallExpression(node, { node: vueNode }) {
-        if (!scopeStack) {
-          return
+          for (const { node } of tracker.iterateEsmReferences(traceMap)) {
+            if (node.type !== 'CallExpression') {
+              continue
+            }
+
+            const getter = utils.getGetterBodyFromComputedFunction(node)
+            if (getter) {
+              computedFunctionNodes.push(getter)
+            }
+          }
         }
-        if (isPromise(node)) {
-          verify(
-            node,
-            scopeStack.body,
-            'promise',
-            computedPropertiesMap.get(vueNode)
-          )
-        } else if (isTimedFunction(node)) {
+      },
+      utils.defineVueVisitor(context, {
+        onVueObjectEnter(node) {
+          computedPropertiesMap.set(node, utils.getComputedProperties(node))
+        },
+        ':function': onFunctionEnter,
+        ':function:exit': onFunctionExit,
+
+        NewExpression(node, { node: vueNode }) {
+          if (!scopeStack) {
+            return
+          }
+          if (
+            node.callee.type === 'Identifier' &&
+            node.callee.name === 'Promise'
+          ) {
+            verify(
+              node,
+              scopeStack.body,
+              'new',
+              computedPropertiesMap.get(vueNode)
+            )
+          }
+        },
+
+        CallExpression(node, { node: vueNode }) {
+          if (!scopeStack) {
+            return
+          }
+          if (isPromise(node)) {
+            verify(
+              node,
+              scopeStack.body,
+              'promise',
+              computedPropertiesMap.get(vueNode)
+            )
+          } else if (isTimedFunction(node)) {
+            verify(
+              node,
+              scopeStack.body,
+              'timed',
+              computedPropertiesMap.get(vueNode)
+            )
+          }
+        },
+
+        AwaitExpression(node, { node: vueNode }) {
+          if (!scopeStack) {
+            return
+          }
           verify(
             node,
             scopeStack.body,
-            'timed',
+            'await',
             computedPropertiesMap.get(vueNode)
           )
         }
-      },
-
-      AwaitExpression(node, { node: vueNode }) {
-        if (!scopeStack) {
-          return
-        }
-        verify(
-          node,
-          scopeStack.body,
-          'await',
-          computedPropertiesMap.get(vueNode)
-        )
-      }
-    })
+      })
+    )
   }
 }
diff --git a/lib/rules/no-constant-condition.js b/lib/rules/no-constant-condition.js
new file mode 100644
index 00000000..a6b62431
--- /dev/null
+++ b/lib/rules/no-constant-condition.js
@@ -0,0 +1,29 @@
+/**
+ * @author Flo Edelmann
+ */
+'use strict'
+
+const { wrapCoreRule } = require('../utils')
+
+const conditionalDirectiveNames = new Set(['v-show', 'v-if', 'v-else-if'])
+
+// eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
+module.exports = wrapCoreRule('no-constant-condition', {
+  create(_context, { coreHandlers }) {
+    return {
+      VDirectiveKey(node) {
+        if (
+          conditionalDirectiveNames.has(`v-${node.name.name}`) &&
+          node.parent.value &&
+          node.parent.value.expression &&
+          coreHandlers.IfStatement
+        ) {
+          coreHandlers.IfStatement({
+            // @ts-expect-error -- Process expression of VExpressionContainer as IfStatement.
+            test: node.parent.value.expression
+          })
+        }
+      }
+    }
+  }
+})
diff --git a/lib/rules/no-ref-as-operand.js b/lib/rules/no-ref-as-operand.js
index fb5dc088..465ec50b 100644
--- a/lib/rules/no-ref-as-operand.js
+++ b/lib/rules/no-ref-as-operand.js
@@ -15,7 +15,7 @@ module.exports = {
       categories: ['vue3-essential'],
       url: 'https://eslint.vuejs.org/rules/no-ref-as-operand.html'
     },
-    fixable: null,
+    fixable: 'code',
     schema: [],
     messages: {
       requireDotValue:
@@ -46,6 +46,9 @@ module.exports = {
         messageId: 'requireDotValue',
         data: {
           method: data.method
+        },
+        fix(fixer) {
+          return fixer.insertTextAfter(node, '.value')
         }
       })
     }
@@ -131,8 +134,11 @@ module.exports = {
         reportIfRefWrapped(node)
       },
       // refValue+=1, refValue-=1, foo+=refValue, foo-=refValue
-      /** @param {Identifier} node */
+      /** @param {Identifier & {parent: AssignmentExpression}} node */
       'AssignmentExpression>Identifier'(node) {
+        if (node.parent.operator === '=' && node.parent.left !== node) {
+          return
+        }
         reportIfRefWrapped(node)
       },
       // refValue || other, refValue && other. ignore: other || refValue
diff --git a/lib/utils/index.js b/lib/utils/index.js
index b6ed0590..94d9994d 100644
--- a/lib/utils/index.js
+++ b/lib/utils/index.js
@@ -842,17 +842,11 @@ module.exports = {
         if (propValue.type === 'FunctionExpression') {
           value = propValue.body
         } else if (propValue.type === 'ObjectExpression') {
-          const get = propValue.properties.find(
-            /**
-             * @param {ESNode} p
-             * @returns { p is (Property & { value: FunctionExpression }) }
-             */
-            (p) =>
-              p.type === 'Property' &&
-              p.key.type === 'Identifier' &&
-              p.key.name === 'get' &&
-              p.value.type === 'FunctionExpression'
-          )
+          const get = /** @type {(Property & { value: FunctionExpression }) | null} */ (findProperty(
+            propValue,
+            'get',
+            (p) => p.value.type === 'FunctionExpression'
+          ))
           value = get ? get.value.body : null
         }
 
@@ -860,6 +854,39 @@ module.exports = {
       })
   },
 
+  /**
+   * Get getter body from computed function
+   * @param {CallExpression} callExpression call of computed function
+   * @return {FunctionExpression | ArrowFunctionExpression | null} getter function
+   */
+  getGetterBodyFromComputedFunction(callExpression) {
+    if (callExpression.arguments.length <= 0) {
+      return null
+    }
+
+    const arg = callExpression.arguments[0]
+
+    if (
+      arg.type === 'FunctionExpression' ||
+      arg.type === 'ArrowFunctionExpression'
+    ) {
+      return arg
+    }
+
+    if (arg.type === 'ObjectExpression') {
+      const getProperty = /** @type {(Property & { value: FunctionExpression | ArrowFunctionExpression }) | null} */ (findProperty(
+        arg,
+        'get',
+        (p) =>
+          p.value.type === 'FunctionExpression' ||
+          p.value.type === 'ArrowFunctionExpression'
+      ))
+      return getProperty ? getProperty.value : null
+    }
+
+    return null
+  },
+
   isVueFile,
 
   /**
diff --git a/package.json b/package.json
index b907051f..8467ccbf 100644
--- a/package.json
+++ b/package.json
@@ -56,7 +56,7 @@
     "eslint-utils": "^2.1.0",
     "natural-compare": "^1.4.0",
     "semver": "^7.3.2",
-    "vue-eslint-parser": "^7.3.0"
+    "vue-eslint-parser": "^7.4.1"
   },
   "devDependencies": {
     "@types/eslint": "^7.2.0",


Test patch:
diff --git a/tests/lib/rules/next-tick-style.js b/tests/lib/rules/next-tick-style.js
new file mode 100644
index 00000000..9f2d0ae5
--- /dev/null
+++ b/tests/lib/rules/next-tick-style.js
@@ -0,0 +1,304 @@
+/**
+ * @fileoverview enforce Promise or callback style in `nextTick`
+ * @author Flo Edelmann
+ * @copyright 2020 Flo Edelmann. All rights reserved.
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/next-tick-style')
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const tester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: {
+    ecmaVersion: 2017,
+    sourceType: 'module'
+  }
+})
+
+tester.run('next-tick-style', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: ''
+    },
+    {
+      filename: 'test.vue',
+      code: `<script>import { nextTick as nt } from 'vue';
+      export default {
+        async mounted() {
+          this.$nextTick().then(() => callback());
+          Vue.nextTick().then(() => callback());
+          nt().then(() => callback());
+
+          await this.$nextTick(); callback();
+          await Vue.nextTick(); callback();
+          await nt(); callback();
+        }
+      }</script>`
+    },
+    {
+      filename: 'test.vue',
+      options: ['promise'],
+      code: `<script>import { nextTick as nt } from 'vue';
+      export default {
+        async mounted() {
+          this.$nextTick().then(() => callback());
+          Vue.nextTick().then(() => callback());
+          nt().then(() => callback());
+
+          await this.$nextTick(); callback();
+          await Vue.nextTick(); callback();
+          await nt(); callback();
+        }
+      }</script>`
+    },
+    {
+      filename: 'test.vue',
+      options: ['callback'],
+      code: `<script>import { nextTick as nt } from 'vue';
+      export default {
+        mounted() {
+          this.$nextTick(() => callback());
+          Vue.nextTick(() => callback());
+          nt(() => callback());
+
+          this.$nextTick(callback);
+          Vue.nextTick(callback);
+          nt(callback);
+        }
+      }</script>`
+    },
+
+    // https://github.com/vuejs/eslint-plugin-vue/pull/1400#discussion_r550937977
+    {
+      filename: 'test.vue',
+      options: ['promise'],
+      code: `<script>import { nextTick as nt } from 'vue';
+      export default {
+        mounted() {
+          foo.then(this.$nextTick);
+          foo.then(Vue.nextTick);
+          foo.then(nt);
+
+          foo.then(nt, catchHandler);
+          foo.then(Vue.nextTick, catchHandler);
+          foo.then(this.$nextTick, catchHandler);
+        }
+      }</script>`
+    },
+    {
+      filename: 'test.vue',
+      options: ['callback'],
+      code: `<script>import { nextTick as nt } from 'vue';
+      export default {
+        mounted() {
+          foo.then(this.$nextTick);
+          foo.then(Vue.nextTick);
+          foo.then(nt);
+
+          foo.then(nt, catchHandler);
+          foo.then(Vue.nextTick, catchHandler);
+          foo.then(this.$nextTick, catchHandler);
+        }
+      }</script>`
+    }
+  ],
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: `<script>import { nextTick as nt } from 'vue';
+      export default {
+        mounted() {
+          this.$nextTick(() => callback());
+          Vue.nextTick(() => callback());
+          nt(() => callback());
+
+          this.$nextTick(callback);
+          Vue.nextTick(callback);
+          nt(callback);
+        }
+      }</script>`,
+      output: `<script>import { nextTick as nt } from 'vue';
+      export default {
+        mounted() {
+          this.$nextTick().then(() => callback());
+          Vue.nextTick().then(() => callback());
+          nt().then(() => callback());
+
+          this.$nextTick().then(callback);
+          Vue.nextTick().then(callback);
+          nt().then(callback);
+        }
+      }</script>`,
+      errors: [
+        {
+          message:
+            'Use the Promise returned by `nextTick` instead of passing a callback function.',
+          line: 4,
+          column: 16
+        },
+        {
+          message:
+            'Use the Promise returned by `nextTick` instead of passing a callback function.',
+          line: 5,
+          column: 15
+        },
+        {
+          message:
+            'Use the Promise returned by `nextTick` instead of passing a callback function.',
+          line: 6,
+          column: 11
+        },
+        {
+          message:
+            'Use the Promise returned by `nextTick` instead of passing a callback function.',
+          line: 8,
+          column: 16
+        },
+        {
+          message:
+            'Use the Promise returned by `nextTick` instead of passing a callback function.',
+          line: 9,
+          column: 15
+        },
+        {
+          message:
+            'Use the Promise returned by `nextTick` instead of passing a callback function.',
+          line: 10,
+          column: 11
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      options: ['promise'],
+      code: `<script>import { nextTick as nt } from 'vue';
+      export default {
+        mounted() {
+          this.$nextTick(() => callback());
+          Vue.nextTick(() => callback());
+          nt(() => callback());
+
+          this.$nextTick(callback);
+          Vue.nextTick(callback);
+          nt(callback);
+        }
+      }</script>`,
+      output: `<script>import { nextTick as nt } from 'vue';
+      export default {
+        mounted() {
+          this.$nextTick().then(() => callback());
+          Vue.nextTick().then(() => callback());
+          nt().then(() => callback());
+
+          this.$nextTick().then(callback);
+          Vue.nextTick().then(callback);
+          nt().then(callback);
+        }
+      }</script>`,
+      errors: [
+        {
+          message:
+            'Use the Promise returned by `nextTick` instead of passing a callback function.',
+          line: 4,
+          column: 16
+        },
+        {
+          message:
+            'Use the Promise returned by `nextTick` instead of passing a callback function.',
+          line: 5,
+          column: 15
+        },
+        {
+          message:
+            'Use the Promise returned by `nextTick` instead of passing a callback function.',
+          line: 6,
+          column: 11
+        },
+        {
+          message:
+            'Use the Promise returned by `nextTick` instead of passing a callback function.',
+          line: 8,
+          column: 16
+        },
+        {
+          message:
+            'Use the Promise returned by `nextTick` instead of passing a callback function.',
+          line: 9,
+          column: 15
+        },
+        {
+          message:
+            'Use the Promise returned by `nextTick` instead of passing a callback function.',
+          line: 10,
+          column: 11
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      options: ['callback'],
+      code: `<script>import { nextTick as nt } from 'vue';
+      export default {
+        async mounted() {
+          this.$nextTick().then(() => callback());
+          Vue.nextTick().then(() => callback());
+          nt().then(() => callback());
+
+          await this.$nextTick(); callback();
+          await Vue.nextTick(); callback();
+          await nt(); callback();
+        }
+      }</script>`,
+      output: null,
+      errors: [
+        {
+          message:
+            'Pass a callback function to `nextTick` instead of using the returned Promise.',
+          line: 4,
+          column: 16
+        },
+        {
+          message:
+            'Pass a callback function to `nextTick` instead of using the returned Promise.',
+          line: 5,
+          column: 15
+        },
+        {
+          message:
+            'Pass a callback function to `nextTick` instead of using the returned Promise.',
+          line: 6,
+          column: 11
+        },
+        {
+          message:
+            'Pass a callback function to `nextTick` instead of using the returned Promise.',
+          line: 8,
+          column: 22
+        },
+        {
+          message:
+            'Pass a callback function to `nextTick` instead of using the returned Promise.',
+          line: 9,
+          column: 21
+        },
+        {
+          message:
+            'Pass a callback function to `nextTick` instead of using the returned Promise.',
+          line: 10,
+          column: 17
+        }
+      ]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-async-in-computed-properties.js b/tests/lib/rules/no-async-in-computed-properties.js
index 8ea81b5c..2a15712c 100644
--- a/tests/lib/rules/no-async-in-computed-properties.js
+++ b/tests/lib/rules/no-async-in-computed-properties.js
@@ -231,6 +231,81 @@ ruleTester.run('no-async-in-computed-properties', rule, {
           data2: Promise.resolve(),
         })`,
       parserOptions
+    },
+    {
+      code: `
+        import {computed} from 'vue'
+        export default {
+          setup() {
+            const test1 = computed(() => {})
+            const test2 = computed(function () {
+              var bar = 0
+              try {
+                bar = bar / 0
+              } catch (e) {
+                return e
+              } finally {
+                return bar
+              }
+            })
+            const test3 = computed({
+              set() {
+                new Promise((resolve, reject) => {})
+              }
+            })
+            const test4 = computed(() => {
+              return {
+                async bar() {
+                  const data = await baz(this.a)
+                  return data
+                }
+              }
+            })
+            const test5 = computed(() => {
+              const a = 'test'
+              return [
+                async () => {
+                  const baz = await bar(a)
+                  return baz
+                },
+                'b',
+                {}
+              ]
+            })
+            const test6 = computed(() => function () {
+              return async () => await bar()
+            })
+            const test7 = computed(() => new Promise.resolve())
+            const test8 = computed(() => {
+              return new Bar(async () => await baz())
+            })
+            const test9 = computed(() => {
+              return someFunc.doSomething({
+                async bar() {
+                  return await baz()
+                }
+              })
+            })
+            const test10 = computed(() => {
+              return this.bar
+                      ? {
+                          baz:() => Promise.resolve(1)
+                        }
+                      : {}
+            })
+            const test11 = computed(() => {
+              return this.bar ? () => Promise.resolve(1) : null
+            })
+            const test12 = computed(() => {
+              return this.bar ? async () => 1 : null
+            })
+            const test13 = computed(() => {
+              bar()
+            })
+          }
+        }
+        `,
+      parserOptions
     }
   ],
 
@@ -639,6 +714,212 @@ ruleTester.run('no-async-in-computed-properties', rule, {
         'Unexpected timed function in "foo" computed property.',
         'Unexpected timed function in "foo" computed property.'
       ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      import {computed} from 'vue'
+      export default {
+        setup() {
+          const test1 = computed(async () => {
+            return await someFunc()
+          })
+          const test2 = computed(async () => await someFunc())
+          const test3 = computed(async function () {
+            return await someFunc()
+          })
+        }
+      }
+      `,
+      parserOptions,
+      errors: [
+        {
+          message:
+            'Unexpected async function declaration in computed function.',
+          line: 5
+        },
+        {
+          message: 'Unexpected await operator in computed function.',
+          line: 6
+        },
+        {
+          message:
+            'Unexpected async function declaration in computed function.',
+          line: 8
+        },
+        {
+          message: 'Unexpected await operator in computed function.',
+          line: 8
+        },
+        {
+          message:
+            'Unexpected async function declaration in computed function.',
+          line: 9
+        },
+        {
+          message: 'Unexpected await operator in computed function.',
+          line: 10
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      import {computed} from 'vue'
+      export default {
+        setup() {
+          const test = computed(async () => {
+            return new Promise((resolve, reject) => {})
+          })
+        }
+      }
+      `,
+      parserOptions,
+      errors: [
+        {
+          message:
+            'Unexpected async function declaration in computed function.',
+          line: 5
+        },
+        {
+          message: 'Unexpected Promise object in computed function.',
+          line: 6
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      import {computed} from 'vue'
+      export default {
+        setup() {
+          const test1 = computed(() => {
+            return bar.then(response => {})
+          })
+          const test2 = computed(() => {
+            return Promise.all([])
+          })
+        }
+      }
+      `,
+      parserOptions,
+      errors: [
+        {
+          message: 'Unexpected asynchronous action in computed function.',
+          line: 6
+        },
+        {
+          message: 'Unexpected asynchronous action in computed function.',
+          line: 9
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      import {computed} from 'vue'
+      export default {
+        setup() {
+          const test1 = computed({
+            get: () => {
+              return Promise.resolve([])
+            }
+          })
+          const test2 = computed({
+            get() {
+              return Promise.resolve([])
+            }
+          })
+        }
+      }
+      `,
+      parserOptions,
+      errors: [
+        {
+          message: 'Unexpected asynchronous action in computed function.',
+          line: 7
+        },
+        {
+          message: 'Unexpected asynchronous action in computed function.',
+          line: 12
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      import {computed} from 'vue'
+      export default {
+        setup() {
+          const test = computed(() => {
+            setTimeout(() => { }, 0)
+            window.setTimeout(() => { }, 0)
+            setInterval(() => { }, 0)
+            window.setInterval(() => { }, 0)
+            setImmediate(() => { })
+            window.setImmediate(() => { })
+            requestAnimationFrame(() => {})
+            window.requestAnimationFrame(() => {})
+          })
+        }
+      }
+      `,
+      parserOptions,
+      errors: [
+        {
+          message: 'Unexpected timed function in computed function.',
+          line: 6
+        },
+        {
+          message: 'Unexpected timed function in computed function.',
+          line: 7
+        },
+        {
+          message: 'Unexpected timed function in computed function.',
+          line: 8
+        },
+        {
+          message: 'Unexpected timed function in computed function.',
+          line: 9
+        },
+        {
+          message: 'Unexpected timed function in computed function.',
+          line: 10
+        },
+        {
+          message: 'Unexpected timed function in computed function.',
+          line: 11
+        },
+        {
+          message: 'Unexpected timed function in computed function.',
+          line: 12
+        },
+        {
+          message: 'Unexpected timed function in computed function.',
+          line: 13
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      import {computed} from 'vue'
+      export default {
+        setup() {
+          const test = computed(async () => {
+            bar()
+          })
+        }
+      }
+      `,
+      parserOptions,
+      errors: [
+        {
+          message:
+            'Unexpected async function declaration in computed function.',
+          line: 5
+        }
+      ]
     }
   ]
 })
diff --git a/tests/lib/rules/no-constant-condition.js b/tests/lib/rules/no-constant-condition.js
new file mode 100644
index 00000000..312a6ee0
--- /dev/null
+++ b/tests/lib/rules/no-constant-condition.js
@@ -0,0 +1,113 @@
+/**
+ * @author Flo Edelmann
+ */
+'use strict'
+
+const { RuleTester } = require('eslint')
+const rule = require('../../../lib/rules/no-constant-condition.js')
+
+const tester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 6 }
+})
+
+tester.run('no-constant-condition', rule, {
+  valid: [
+    '<template><CustomButton v-if="a" /></template>',
+    '<template><CustomButton v-if="a == 0" /></template>',
+    '<template><CustomButton v-if="a == f()" /></template>',
+    '<template><CustomButton v-other-directive="true" /></template>'
+  ],
+  invalid: [
+    {
+      code: '<template><CustomButton v-if="-2" /></template>',
+      errors: [
+        {
+          messageId: 'unexpected',
+          type: 'UnaryExpression',
+          column: 31,
+          endColumn: 33
+        }
+      ]
+    },
+    {
+      code: '<template><CustomButton v-else-if="true" /></template>',
+      errors: [
+        {
+          messageId: 'unexpected',
+          type: 'Literal',
+          column: 36,
+          endColumn: 40
+        }
+      ]
+    },
+    {
+      code: '<template><CustomButton v-if="1" /></template>',
+      errors: [
+        {
+          messageId: 'unexpected',
+          type: 'Literal',
+          column: 31,
+          endColumn: 32
+        }
+      ]
+    },
+    {
+      code: '<template><CustomButton v-show="{}" /></template>',
+      errors: [
+        {
+          messageId: 'unexpected',
+          type: 'ObjectExpression',
+          column: 33,
+          endColumn: 35
+        }
+      ]
+    },
+    {
+      code: '<template><CustomButton v-if="0 < 1" /></template>',
+      errors: [
+        {
+          messageId: 'unexpected',
+          type: 'BinaryExpression',
+          column: 31,
+          endColumn: 36
+        }
+      ]
+    },
+    {
+      code: '<template><CustomButton v-if="0 || 1" /></template>',
+      errors: [
+        {
+          messageId: 'unexpected',
+          type: 'LogicalExpression',
+          column: 31,
+          endColumn: 37
+        }
+      ]
+    }
+
+    // failing in Node.js v8, because template literals are not supported there:
+    // {
+    //   code: '<template><CustomButton v-if="`foo`" /></template>',
+    //   errors: [
+    //     {
+    //       messageId: 'unexpected',
+    //       type: 'TemplateLiteral',
+    //       column: 31,
+    //       endColumn: 36
+    //     }
+    //   ]
+    // },
+    // {
+    //   code: '<template><CustomButton v-if="``" /></template>',
+    //   errors: [
+    //     {
+    //       messageId: 'unexpected',
+    //       type: 'TemplateLiteral',
+    //       column: 31,
+    //       endColumn: 33
+    //     }
+    //   ]
+    // }
+  ]
+})
diff --git a/tests/lib/rules/no-parsing-error.js b/tests/lib/rules/no-parsing-error.js
index c0c1ce61..46733a3f 100644
--- a/tests/lib/rules/no-parsing-error.js
+++ b/tests/lib/rules/no-parsing-error.js
@@ -249,7 +249,10 @@ tester.run('no-parsing-error', rule, {
       filename: 'test.vue',
       code: '<template><div v-for="foo">hello</div></template>',
       errors: [
-        { message: 'Parsing error: Unexpected end of expression.', column: 26 }
+        {
+          message: 'Parsing error: Expected to be an alias, but got empty.',
+          column: 23
+        }
       ]
     },
     {
diff --git a/tests/lib/rules/no-ref-as-operand.js b/tests/lib/rules/no-ref-as-operand.js
index 50cfd324..fb564ce1 100644
--- a/tests/lib/rules/no-ref-as-operand.js
+++ b/tests/lib/rules/no-ref-as-operand.js
@@ -121,6 +121,16 @@ tester.run('no-ref-as-operand', rule, {
     const count = ref
     count++
     `,
+    `
+    import { ref } from 'vue'
+    const count = ref(0)
+    foo = count
+    `,
+    `
+    import { ref } from 'vue'
+    const count = ref(0)
+    const foo = count
+    `,
     {
       code: `
       <script>
@@ -150,6 +160,14 @@ tester.run('no-ref-as-operand', rule, {
       console.log(count + 1) // error
       console.log(1 + count) // error
       `,
+      output: `
+      import { ref } from 'vue'
+      let count = ref(0)
+
+      count.value++ // error
+      console.log(count.value + 1) // error
+      console.log(1 + count.value) // error
+      `,
       errors: [
         {
           message:
@@ -195,6 +213,23 @@ tester.run('no-ref-as-operand', rule, {
         }
       </script>
       `,
+      output: `
+      <script>
+        import { ref } from 'vue'
+        export default {
+          setup() {
+            let count = ref(0)
+
+            count.value++ // error
+            console.log(count.value + 1) // error
+            console.log(1 + count.value) // error
+            return {
+              count
+            }
+          }
+        }
+      </script>
+      `,
       errors: [
         {
           messageId: 'requireDotValue',
@@ -237,6 +272,23 @@ tester.run('no-ref-as-operand', rule, {
         }
       </script>
       `,
+      output: `
+      <script>
+        import { ref } from '@vue/composition-api'
+        export default {
+          setup() {
+            let count = ref(0)
+
+            count.value++ // error
+            console.log(count.value + 1) // error
+            console.log(1 + count.value) // error
+            return {
+              count
+            }
+          }
+        }
+      </script>
+      `,
       errors: [
         {
           messageId: 'requireDotValue',
@@ -269,6 +321,13 @@ tester.run('no-ref-as-operand', rule, {
         //
       }
       `,
+      output: `
+      import { ref } from 'vue'
+      const foo = ref(true)
+      if (foo.value) {
+        //
+      }
+      `,
       errors: [
         {
           messageId: 'requireDotValue',
@@ -284,6 +343,13 @@ tester.run('no-ref-as-operand', rule, {
         //
       }
       `,
+      output: `
+      import { ref } from 'vue'
+      const foo = ref(true)
+      switch (foo.value) {
+        //
+      }
+      `,
       errors: [
         {
           messageId: 'requireDotValue',
@@ -300,6 +366,14 @@ tester.run('no-ref-as-operand', rule, {
       var c = !foo
       var d = ~foo
       `,
+      output: `
+      import { ref } from 'vue'
+      const foo = ref(0)
+      var a = -foo.value
+      var b = +foo.value
+      var c = !foo.value
+      var d = ~foo.value
+      `,
       errors: [
         {
           messageId: 'requireDotValue',
@@ -328,6 +402,14 @@ tester.run('no-ref-as-operand', rule, {
       baz += foo
       baz -= foo
       `,
+      output: `
+      import { ref } from 'vue'
+      let foo = ref(0)
+      foo.value += 1
+      foo.value -= 1
+      baz += foo.value
+      baz -= foo.value
+      `,
       errors: [
         {
           messageId: 'requireDotValue',
@@ -354,6 +436,12 @@ tester.run('no-ref-as-operand', rule, {
       var a = foo || other
       var b = foo && other
       `,
+      output: `
+      import { ref } from 'vue'
+      const foo = ref(true)
+      var a = foo.value || other
+      var b = foo.value && other
+      `,
       errors: [
         {
           messageId: 'requireDotValue',
@@ -371,6 +459,11 @@ tester.run('no-ref-as-operand', rule, {
       let foo = ref(true)
       var a = foo ? x : y
       `,
+      output: `
+      import { ref } from 'vue'
+      let foo = ref(true)
+      var a = foo.value ? x : y
+      `,
       errors: [
         {
           messageId: 'requireDotValue',
@@ -395,6 +488,22 @@ tester.run('no-ref-as-operand', rule, {
         }
       </script>
       `,
+      output: `
+      <script>
+        import { ref } from 'vue'
+        let count = ref(0)
+        export default {
+          setup() {
+            count.value++ // error
+            console.log(count.value + 1) // error
+            console.log(1 + count.value) // error
+            return {
+              count
+            }
+          }
+        }
+      </script>
+      `,
       errors: [
         {
           messageId: 'requireDotValue',
@@ -451,6 +560,46 @@ tester.run('no-ref-as-operand', rule, {
         const n = foo + 1 // error
       </script>
       `,
+      output: `
+      <script>
+        import { ref, computed, toRef, customRef, shallowRef } from 'vue'
+        let count = ref(0)
+        let cntcnt = computed(()=>count.value+count.value)
+
+        const state = reactive({
+          foo: 1,
+          bar: 2
+        })
+
+        const fooRef = toRef(state, 'foo')
+
+        let value = 'hello'
+        const cref = customRef((track, trigger) => {
+          return {
+            get() {
+              track()
+              return value
+            },
+            set(newValue) {
+              clearTimeout(timeout)
+              timeout = setTimeout(() => {
+                value = newValue
+                trigger()
+              }, delay)
+            }
+          }
+        })
+
+        const foo = shallowRef({})
+
+        count.value++ // error
+        cntcnt.value++ // error
+
+        const s = \`\${fooRef.value} : \${cref.value}\` // error x 2
+
+        const n = foo.value + 1 // error
+      </script>
+      `,
       errors: [
         {
           message:
@@ -487,6 +636,13 @@ tester.run('no-ref-as-operand', rule, {
         foo.bar = 123
       </script>
       `,
+      output: `
+      <script>
+        import { ref, computed, toRef, customRef, shallowRef } from 'vue'
+        const foo = shallowRef({})
+        foo.value.bar = 123
+      </script>
+      `,
       errors: [
         {
           messageId: 'requireDotValue'
@@ -501,6 +657,13 @@ tester.run('no-ref-as-operand', rule, {
         const bar = foo?.bar
       </script>
       `,
+      output: `
+      <script>
+        import { ref } from 'vue'
+        const foo = ref(123)
+        const bar = foo.value?.bar
+      </script>
+      `,
       errors: [
         {
           messageId: 'requireDotValue'