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

Issue:
Rule Proposal: vue/no-constant-condition
<!--
  Before proposing new rule, please make sure it hasn't been posted already.
  You can see all open propositions here:
  https://github.com/vuejs/eslint-plugin-vue/issues?q=is%3Aopen+is%3Aissue+label%3A%22new+rule+proposition%22
-->

**Please describe what the rule should do:**
like [no-constant-condition](https://eslint.org/docs/rules/no-constant-condition) in eslint, but works with `v-if`/`v-show`.

**What category should the rule belong to?**
<!-- (place an "X" next to just one item) -->
[ ] Enforces code style (layout)
[x] Warns about a potential error (problem)
[ ] Suggests an alternate way of doing something (suggestion)
[ ] Other (please specify:)

**Provide 2-3 code examples that this rule should warn about:**

```vue
<div v-if="true"></div>
<div v-if="true || prop1"></div>
```

**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->


Pull request:
Add `vue/no-constant-condition` rule
Closes #1358.

Patch:
diff --git a/docs/rules/README.md b/docs/rules/README.md
index 5f848467..c96f44a0 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: |
@@ -347,6 +347,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/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/lib/index.js b/lib/index.js
index 21091a9f..699d2f40 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -53,6 +53,7 @@ module.exports = {
     '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/no-async-in-computed-properties.js b/lib/rules/no-async-in-computed-properties.js
index 9812d61c..6d853f52 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')
 
 /**
@@ -77,13 +77,16 @@ module.exports = {
   },
   /** @param {RuleContext} context */
   create(context) {
+    /** @type {Map<ObjectExpression, ComponentComputedProperty[]>} */
+    const computedPropertiesMap = new Map()
+    /** @type {Array<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
 
@@ -139,63 +142,103 @@ module.exports = {
           })
         }
       })
-    }
-    return utils.defineVueVisitor(context, {
-      onVueObjectEnter(node) {
-        computedPropertiesMap.set(node, utils.getComputedProperties(node))
-      },
-      ':function': onFunctionEnter,
-      ':function:exit': onFunctionExit,
 
-      NewExpression(node, { node: vueNode }) {
-        if (!scopeStack) {
-          return
-        }
+      computedFunctionNodes.forEach((c) => {
         if (
-          node.callee.type === 'Identifier' &&
-          node.callee.name === 'Promise'
+          node.loc.start.line >= c.loc.start.line &&
+          node.loc.end.line <= c.loc.end.line &&
+          targetBody === c.body
         ) {
-          verify(
+          context.report({
             node,
-            scopeStack.body,
-            'new',
-            computedPropertiesMap.get(vueNode)
-          )
+            message: 'Unexpected {{expressionName}} in computed function.',
+            data: {
+              expressionName: expressionTypes[type]
+            }
+          })
         }
-      },
+      })
+    }
+    return Object.assign(
+      {
+        Program() {
+          const tracker = new ReferenceTracker(context.getScope())
+          const traceMap = utils.createCompositionApiTraceMap({
+            [ReferenceTracker.ESM]: true,
+            computed: {
+              [ReferenceTracker.CALL]: true
+            }
+          })
+
+          for (const { node } of tracker.iterateEsmReferences(traceMap)) {
+            if (node.type !== 'CallExpression') {
+              continue
+            }
 
-      CallExpression(node, { node: vueNode }) {
-        if (!scopeStack) {
-          return
+            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..d93d0422 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')
         }
       })
     }
diff --git a/lib/utils/index.js b/lib/utils/index.js
index b6ed0590..6d38bb97 100644
--- a/lib/utils/index.js
+++ b/lib/utils/index.js
@@ -860,6 +860,44 @@ 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 = arg.properties.find(
+        /**
+         * @param {ESNode} p
+         * @returns { p is (Property & { value: FunctionExpression | ArrowFunctionExpression }) }
+         */
+        (p) =>
+          p.type === 'Property' &&
+          p.key.type === 'Identifier' &&
+          p.key.name === 'get' &&
+          (p.value.type === 'FunctionExpression' ||
+            p.value.type === 'ArrowFunctionExpression')
+      )
+      return getProperty ? getProperty.value : null
+    }
+
+    return null
+  },
+
   isVueFile,
 
   /**


Test patch:
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-ref-as-operand.js b/tests/lib/rules/no-ref-as-operand.js
index 50cfd324..7cdc61b7 100644
--- a/tests/lib/rules/no-ref-as-operand.js
+++ b/tests/lib/rules/no-ref-as-operand.js
@@ -150,6 +150,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 +203,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 +262,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 +311,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 +333,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 +356,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 +392,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 +426,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 +449,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 +478,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 +550,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 +626,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 +647,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'