Repository: vuejs/eslint-plugin-vue
Pull request #1065 (2020-03-06 04:00:14)

New: Add `vue/no-ref-as-operand` rule
This PR adds the `vue/no-ref-as-operand` rule.

The `vue/no-ref-as-operand` rule reports cases where a ref is used incorrectly as an operand.

---

ref #1035


Patch:
diff --git a/docs/rules/README.md b/docs/rules/README.md
index ab1f5b22..8bf02045 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -39,8 +39,10 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | Rule ID | Description |    |
 |:--------|:------------|:---|
 | [vue/no-async-in-computed-properties](./no-async-in-computed-properties.md) | disallow asynchronous actions in computed properties |  |
+| [vue/no-custom-modifiers-on-v-model](./no-custom-modifiers-on-v-model.md) | disallow custom modifiers on v-model used on the component |  |
 | [vue/no-dupe-keys](./no-dupe-keys.md) | disallow duplication of field names |  |
 | [vue/no-duplicate-attributes](./no-duplicate-attributes.md) | disallow duplication of attributes |  |
+| [vue/no-multiple-template-root](./no-multiple-template-root.md) | disallow adding multiple root nodes to the template |  |
 | [vue/no-parsing-error](./no-parsing-error.md) | disallow parsing errors in `<template>` |  |
 | [vue/no-reserved-keys](./no-reserved-keys.md) | disallow overwriting reserved keys |  |
 | [vue/no-shared-component-data](./no-shared-component-data.md) | enforce component's data property to be a function | :wrench: |
@@ -50,6 +52,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | [vue/no-unused-components](./no-unused-components.md) | disallow registering components that are not used inside templates |  |
 | [vue/no-unused-vars](./no-unused-vars.md) | disallow unused variable definitions of v-for directives or scope attributes |  |
 | [vue/no-use-v-if-with-v-for](./no-use-v-if-with-v-for.md) | disallow use v-if on the same element as v-for |  |
+| [vue/no-v-model-argument](./no-v-model-argument.md) | disallow adding an argument to `v-model` used in custom component |  |
 | [vue/require-component-is](./require-component-is.md) | require `v-bind:is` of `<component>` elements |  |
 | [vue/require-prop-type-constructor](./require-prop-type-constructor.md) | require prop type to be a constructor | :wrench: |
 | [vue/require-render-return](./require-render-return.md) | enforce render function to always return value |  |
@@ -58,6 +61,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | [vue/return-in-computed-property](./return-in-computed-property.md) | enforce that a return statement is present in computed property |  |
 | [vue/use-v-on-exact](./use-v-on-exact.md) | enforce usage of `exact` modifier on `v-on` |  |
 | [vue/valid-template-root](./valid-template-root.md) | enforce valid template root |  |
+| [vue/valid-v-bind-sync](./valid-v-bind-sync.md) | enforce valid `.sync` modifier on `v-bind` directives |  |
 | [vue/valid-v-bind](./valid-v-bind.md) | enforce valid `v-bind` directives |  |
 | [vue/valid-v-cloak](./valid-v-cloak.md) | enforce valid `v-cloak` directives |  |
 | [vue/valid-v-else-if](./valid-v-else-if.md) | enforce valid `v-else-if` directives |  |
@@ -70,6 +74,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | [vue/valid-v-once](./valid-v-once.md) | enforce valid `v-once` directives |  |
 | [vue/valid-v-pre](./valid-v-pre.md) | enforce valid `v-pre` directives |  |
 | [vue/valid-v-show](./valid-v-show.md) | enforce valid `v-show` directives |  |
+| [vue/valid-v-slot](./valid-v-slot.md) | enforce valid `v-slot` directives |  |
 | [vue/valid-v-text](./valid-v-text.md) | enforce valid `v-text` directives |  |
 
 ## Priority B: Strongly Recommended (Improving Readability)
@@ -85,6 +90,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | Rule ID | Description |    |
 |:--------|:------------|:---|
 | [vue/attribute-hyphenation](./attribute-hyphenation.md) | enforce attribute naming style on custom components in template | :wrench: |
+| [vue/component-definition-name-casing](./component-definition-name-casing.md) | enforce specific casing for component definition name | :wrench: |
 | [vue/html-closing-bracket-newline](./html-closing-bracket-newline.md) | require or disallow a line break before tag's closing brackets | :wrench: |
 | [vue/html-closing-bracket-spacing](./html-closing-bracket-spacing.md) | require or disallow a space before tag's closing brackets | :wrench: |
 | [vue/html-end-tags](./html-end-tags.md) | enforce end tag style | :wrench: |
@@ -94,7 +100,6 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | [vue/max-attributes-per-line](./max-attributes-per-line.md) | enforce the maximum number of attributes per line | :wrench: |
 | [vue/multiline-html-element-content-newline](./multiline-html-element-content-newline.md) | require a line break before and after the contents of a multiline element | :wrench: |
 | [vue/mustache-interpolation-spacing](./mustache-interpolation-spacing.md) | enforce unified spacing in mustache interpolations | :wrench: |
-| [vue/name-property-casing](./name-property-casing.md) | enforce specific casing for the name property in Vue components | :wrench: |
 | [vue/no-multi-spaces](./no-multi-spaces.md) | disallow multiple spaces | :wrench: |
 | [vue/no-spaces-around-equal-signs-in-attribute](./no-spaces-around-equal-signs-in-attribute.md) | disallow spaces around equal signs in attribute | :wrench: |
 | [vue/no-template-shadow](./no-template-shadow.md) | disallow variable declarations from shadowing variables declared in the outer scope |  |
@@ -104,6 +109,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | [vue/singleline-html-element-content-newline](./singleline-html-element-content-newline.md) | require a line break before and after the contents of a singleline element | :wrench: |
 | [vue/v-bind-style](./v-bind-style.md) | enforce `v-bind` directive style | :wrench: |
 | [vue/v-on-style](./v-on-style.md) | enforce `v-on` directive style | :wrench: |
+| [vue/v-slot-style](./v-slot-style.md) | enforce `v-slot` directive style | :wrench: |
 
 ## Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
 
@@ -118,6 +124,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | Rule ID | Description |    |
 |:--------|:------------|:---|
 | [vue/attributes-order](./attributes-order.md) | enforce order of attributes | :wrench: |
+| [vue/component-tags-order](./component-tags-order.md) | enforce order of component top-level elements |  |
 | [vue/no-v-html](./no-v-html.md) | disallow use of v-html to prevent XSS attack |  |
 | [vue/order-in-components](./order-in-components.md) | enforce order of properties in components | :wrench: |
 | [vue/this-in-template](./this-in-template.md) | disallow usage of `this` in template |  |
@@ -145,9 +152,7 @@ For example:
 | [vue/brace-style](./brace-style.md) | enforce consistent brace style for blocks | :wrench: |
 | [vue/camelcase](./camelcase.md) | enforce camelcase naming convention |  |
 | [vue/comma-dangle](./comma-dangle.md) | require or disallow trailing commas | :wrench: |
-| [vue/component-definition-name-casing](./component-definition-name-casing.md) | enforce specific casing for component definition name | :wrench: |
 | [vue/component-name-in-template-casing](./component-name-in-template-casing.md) | enforce specific casing for the component naming style in template | :wrench: |
-| [vue/component-tags-order](./component-tags-order.md) | enforce order of component top-level elements |  |
 | [vue/dot-location](./dot-location.md) | enforce consistent newlines before and after dots | :wrench: |
 | [vue/eqeqeq](./eqeqeq.md) | require the use of `===` and `!==` | :wrench: |
 | [vue/key-spacing](./key-spacing.md) | enforce consistent spacing between keys and values in object literal properties | :wrench: |
@@ -155,11 +160,14 @@ For example:
 | [vue/match-component-file-name](./match-component-file-name.md) | require component name property to match its file name |  |
 | [vue/max-len](./max-len.md) | enforce a maximum line length |  |
 | [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: |
+| [vue/no-deprecated-filter](./no-deprecated-filter.md) | disallow using deprecated filters syntax |  |
 | [vue/no-deprecated-scope-attribute](./no-deprecated-scope-attribute.md) | disallow deprecated `scope` attribute (in Vue.js 2.5.0+) | :wrench: |
 | [vue/no-deprecated-slot-attribute](./no-deprecated-slot-attribute.md) | disallow deprecated `slot` attribute (in Vue.js 2.6.0+) | :wrench: |
 | [vue/no-deprecated-slot-scope-attribute](./no-deprecated-slot-scope-attribute.md) | disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+) | :wrench: |
+| [vue/no-deprecated-v-bind-sync](./no-deprecated-v-bind-sync.md) | disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+) | :wrench: |
 | [vue/no-empty-pattern](./no-empty-pattern.md) | disallow empty destructuring patterns |  |
 | [vue/no-irregular-whitespace](./no-irregular-whitespace.md) | disallow irregular whitespace |  |
+| [vue/no-ref-as-operand](./no-ref-as-operand.md) | disallow use of value wrapped by `ref()` (Composition API) as an operand |  |
 | [vue/no-reserved-component-names](./no-reserved-component-names.md) | disallow the use of reserved names in component definitions |  |
 | [vue/no-restricted-syntax](./no-restricted-syntax.md) | disallow specified syntax |  |
 | [vue/no-static-inline-styles](./no-static-inline-styles.md) | disallow static inline `style` attributes |  |
@@ -174,9 +182,6 @@ For example:
 | [vue/space-unary-ops](./space-unary-ops.md) | enforce consistent spacing before or after unary operators | :wrench: |
 | [vue/static-class-names-order](./static-class-names-order.md) | enforce static class names order | :wrench: |
 | [vue/v-on-function-call](./v-on-function-call.md) | enforce or forbid parentheses after method calls without arguments in `v-on` directives | :wrench: |
-| [vue/v-slot-style](./v-slot-style.md) | enforce `v-slot` directive style | :wrench: |
-| [vue/valid-v-bind-sync](./valid-v-bind-sync.md) | enforce valid `.sync` modifier on `v-bind` directives |  |
-| [vue/valid-v-slot](./valid-v-slot.md) | enforce valid `v-slot` directives |  |
 
 ## Deprecated
 
@@ -185,4 +190,5 @@ For example:
 
 | Rule ID | Replaced by |
 |:--------|:------------|
+| [vue/name-property-casing](./name-property-casing.md) | [vue/component-definition-name-casing](./component-definition-name-casing.md) |
 | [vue/no-confusing-v-for-v-if](./no-confusing-v-for-v-if.md) | [vue/no-use-v-if-with-v-for](./no-use-v-if-with-v-for.md) |
diff --git a/docs/rules/component-definition-name-casing.md b/docs/rules/component-definition-name-casing.md
index d43d0ded..d5566ca9 100644
--- a/docs/rules/component-definition-name-casing.md
+++ b/docs/rules/component-definition-name-casing.md
@@ -7,6 +7,7 @@ description: enforce specific casing for component definition name
 # vue/component-definition-name-casing
 > enforce specific casing for component definition name
 
+- :gear: This rule is included in `"plugin:vue/strongly-recommended"` and `"plugin:vue/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.
 
 Define a style for component definition name casing for consistency purposes.
diff --git a/docs/rules/component-tags-order.md b/docs/rules/component-tags-order.md
index d8c45447..e66cb665 100644
--- a/docs/rules/component-tags-order.md
+++ b/docs/rules/component-tags-order.md
@@ -7,6 +7,8 @@ description: enforce order of component top-level elements
 # vue/component-tags-order
 > enforce order of component top-level elements
 
+- :gear: This rule is included in `"plugin:vue/recommended"`.
+
 ## :book: Rule Details
 
 This rule warns about the order of the `<script>`, `<template>` & `<style>` tags.
diff --git a/docs/rules/name-property-casing.md b/docs/rules/name-property-casing.md
index 98aa4a26..852d4e71 100644
--- a/docs/rules/name-property-casing.md
+++ b/docs/rules/name-property-casing.md
@@ -7,7 +7,7 @@ description: enforce specific casing for the name property in Vue components
 # vue/name-property-casing
 > enforce specific casing for the name property in Vue components
 
-- :gear: This rule is included in `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+- :warning: This rule was **deprecated** and replaced by [vue/component-definition-name-casing](component-definition-name-casing.md) rule.
 - :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
diff --git a/docs/rules/no-custom-modifiers-on-v-model.md b/docs/rules/no-custom-modifiers-on-v-model.md
new file mode 100644
index 00000000..f204294b
--- /dev/null
+++ b/docs/rules/no-custom-modifiers-on-v-model.md
@@ -0,0 +1,53 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-custom-modifiers-on-v-model
+description: disallow custom modifiers on v-model used on the component
+---
+# vue/no-custom-modifiers-on-v-model
+> disallow custom modifiers on v-model used on the component
+
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+
+This rule checks whether `v-model `used on the component do not have custom modifiers.
+
+## Rule Details
+
+This rule reports `v-model` directives in the following cases:
+
+- The directive used on the component has custom modifiers. E.g. `<MyComponent v-model.aaa="foo" />`
+
+<eslint-code-block :rules="{'vue/no-custom-modifiers-on-v-model': ['error']}">
+
+```vue
+<template>
+  <!-- ✓ GOOD -->
+  <MyComponent v-model="foo" />
+  <MyComponent v-model.trim="foo" />
+  <MyComponent v-model.lazy="foo" />
+  <MyComponent v-model.number="foo" />
+
+
+  <!-- ✗ BAD -->
+  <MyComponent v-model.aaa="foo" />
+  <MyComponent v-model.aaa.bbb="foo" />
+
+</template>
+```
+
+</eslint-code-block>
+
+### Options
+
+Nothing.
+
+## :couple: Related rules
+
+- [valid-v-model]
+
+[valid-v-model]: valid-v-model.md
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-custom-modifiers-on-v-model.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-custom-modifiers-on-v-model.js)
diff --git a/docs/rules/no-deprecated-filter.md b/docs/rules/no-deprecated-filter.md
new file mode 100644
index 00000000..f72a8ee8
--- /dev/null
+++ b/docs/rules/no-deprecated-filter.md
@@ -0,0 +1,50 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-deprecated-filter
+description: disallow using deprecated filters syntax
+---
+# vue/no-deprecated-filter
+> disallow using deprecated filters syntax
+
+
+## :book: Rule Details
+
+This rule reports deprecated `filters` syntax (removed in Vue.js v3.0.0+)
+
+<eslint-code-block :rules="{'vue/no-deprecated-filter': ['error']}">
+
+```vue
+<template>
+  <!-- ✓ GOOD -->
+  {{ filter(msg) }}
+  {{ filter(msg, '€') }}
+  {{ filterB(filterA(msg)) }}
+  <div v-bind:id="filter(msg)"></div>
+  <div v-bind:id="filter(msg, '€')"></div>
+  <div v-bind:id="filterB(filterA(msg))"></div>
+
+  <!-- ✗ BAD -->
+  {{ msg | filter }}
+  {{ msg | filter('€') }}
+  {{ msg | filterA | filterB }}
+  <div v-bind:id="msg | filter"></div>
+  <div v-bind:id="msg | filter('€')"></div>
+  <div v-bind:id="msg | filterA | filterB"></div>
+</template>
+```
+
+</eslint-code-block>
+
+### :wrench: Options
+
+Nothing.
+
+## :books: Further Reading
+
+- [Vue RFCs - Remove support for filters.](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0015-remove-filters.md)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-filter.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-filter.js)
diff --git a/docs/rules/no-deprecated-v-bind-sync.md b/docs/rules/no-deprecated-v-bind-sync.md
new file mode 100644
index 00000000..056b10a9
--- /dev/null
+++ b/docs/rules/no-deprecated-v-bind-sync.md
@@ -0,0 +1,52 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-deprecated-v-bind-sync
+description: disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)
+---
+# vue/no-deprecated-v-bind-sync
+> disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)
+
+- :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 use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)
+
+<eslint-code-block fix :rules="{'vue/no-deprecated-v-bind-sync': ['error']}">
+
+```vue
+<template>
+  <!-- ✓ GOOD -->
+  <MyComponent v-bind:propName="foo"/>
+  <MyComponent :propName="foo"/>
+
+
+  <!-- ✗ BAD -->
+  <MyComponent v-bind:propName.sync="foo"/>
+  <MyComponent v-bind:[dynamiArg].sync="foo"/>
+  <MyComponent v-bind.sync="foo"/>
+  <MyComponent :propName.sync="foo"/>
+</template>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+Nothing.
+
+## :couple: Related rules
+
+- [valid-v-bind]
+
+[valid-v-bind]: valid-v-bind.md
+
+## :books: Further reading
+
+- [RFC: Replace v-bind.sync with v-model argument](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0005-replace-v-bind-sync-with-v-model-argument.md)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-v-bind-sync.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-v-bind-sync.js)
diff --git a/docs/rules/no-multiple-template-root.md b/docs/rules/no-multiple-template-root.md
new file mode 100644
index 00000000..e22cb0f4
--- /dev/null
+++ b/docs/rules/no-multiple-template-root.md
@@ -0,0 +1,65 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-multiple-template-root
+description: disallow adding multiple root nodes to the template
+---
+# vue/no-multiple-template-root
+> disallow adding multiple root nodes to the template
+
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+
+This rule checks whether template contains single root element valid for Vue 2.
+
+
+<eslint-code-block :rules="{'vue/no-multiple-template-root': ['error']}">
+
+```vue
+<!-- The root is text -->
+<template>Lorem ipsum</template>
+```
+
+</eslint-code-block>
+
+<eslint-code-block :rules="{'vue/no-multiple-template-root': ['error']}">
+
+```vue
+<!-- There are multiple root elements -->
+<template>
+  <div>hello</div>
+  <div>hello</div>
+</template>
+```
+
+</eslint-code-block>
+
+<eslint-code-block :rules="{'vue/no-multiple-template-root': ['error']}">
+
+```vue
+<!-- The root element has `v-for` directives -->
+<template>
+  <div v-for="item in items"/>
+</template>
+```
+
+</eslint-code-block>
+
+<eslint-code-block :rules="{'vue/no-multiple-template-root': ['error']}">
+
+```vue
+<!-- The root element is `<template>` or `<slot>` -->
+<template>
+  <slot />
+</template>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+Nothing.
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-multiple-template-root.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-multiple-template-root.js)
diff --git a/docs/rules/no-ref-as-operand.md b/docs/rules/no-ref-as-operand.md
new file mode 100644
index 00000000..e6d0b94b
--- /dev/null
+++ b/docs/rules/no-ref-as-operand.md
@@ -0,0 +1,58 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-ref-as-operand
+description: disallow use of value wrapped by `ref()` (Composition API) as an operand
+---
+# vue/no-ref-as-operand
+> disallow use of value wrapped by `ref()` (Composition API) as an operand
+
+## :book: Rule Details
+
+This rule reports cases where a ref is used incorrectly as an operand.
+
+<eslint-code-block :rules="{'vue/no-ref-as-operand': ['error']}">
+
+```vue
+<script>
+import { ref } from 'vue'
+
+export default {
+  setup () {
+    const count = ref(0)
+    const ok = ref(true)
+
+    /* ✓ GOOD */
+    count.value++
+    count.value + 1
+    1 + count.value
+    var msg = ok.value ? 'yes' : 'no'
+
+    /* ✗ BAD */
+    count++
+    count + 1
+    1 + count
+    var msg = ok ? 'yes' : 'no'
+
+    return {
+      count
+    }
+  }
+}
+</script>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+Nothing.
+
+## :books: Further reading
+
+- [Vue RFCs - 0013-composition-api](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0013-composition-api.md)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-ref-as-operand.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-ref-as-operand.js)
diff --git a/docs/rules/no-v-model-argument.md b/docs/rules/no-v-model-argument.md
new file mode 100644
index 00000000..f67b3a3b
--- /dev/null
+++ b/docs/rules/no-v-model-argument.md
@@ -0,0 +1,49 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-v-model-argument
+description: disallow adding an argument to `v-model` used in custom component
+---
+# vue/no-v-model-argument
+> disallow adding an argument to `v-model` used in custom component
+
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+
+This rule checks whether `v-model` used on custom component do not have an argument.
+
+## :book: Rule Details
+
+This rule reports `v-model` directives in the following cases:
+
+- The directive used on component has an argument. E.g. `<MyComponent v-model:aaa="foo" />`
+
+<eslint-code-block :rules="{'vue/no-v-model-argument': ['error']}">
+
+```vue
+<template>
+  <!-- ✓ GOOD -->
+  <MyComponent v-model="foo" />
+
+
+  <!-- ✗ BAD -->
+  <MyComponent v-model:aaa="foo" />
+</template>
+```
+
+</eslint-code-block>
+
+
+## :wrench: Options
+
+Nothing.
+
+## :couple: Related rules
+
+- [valid-v-model]
+
+[valid-v-model]: valid-v-model.md
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-v-model-argument.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-v-model-argument.js)
diff --git a/docs/rules/v-slot-style.md b/docs/rules/v-slot-style.md
index 41b5ea6b..9fdddaa7 100644
--- a/docs/rules/v-slot-style.md
+++ b/docs/rules/v-slot-style.md
@@ -7,6 +7,7 @@ description: enforce `v-slot` directive style
 # vue/v-slot-style
 > enforce `v-slot` directive style
 
+- :gear: This rule is included in `"plugin:vue/strongly-recommended"` and `"plugin:vue/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
diff --git a/docs/rules/valid-template-root.md b/docs/rules/valid-template-root.md
index 777a674d..7d6e0778 100644
--- a/docs/rules/valid-template-root.md
+++ b/docs/rules/valid-template-root.md
@@ -27,42 +27,8 @@ This rule reports the template root in the following cases:
 <eslint-code-block :rules="{'vue/valid-template-root': ['error']}">
 
 ```vue
-<!-- The root is text -->
-<template>Lorem ipsum</template>
-```
-
-</eslint-code-block>
-
-<eslint-code-block :rules="{'vue/valid-template-root': ['error']}">
-
-```vue
-<!-- There are multiple root elements -->
-<template>
-  <div>hello</div>
-  <div>hello</div>
-</template>
-```
-
-</eslint-code-block>
-
-<eslint-code-block :rules="{'vue/valid-template-root': ['error']}">
-
-```vue
-<!-- The root element has `v-for` directives -->
-<template>
-  <div v-for="item in items"/>
-</template>
-```
-
-</eslint-code-block>
-
-<eslint-code-block :rules="{'vue/valid-template-root': ['error']}">
-
-```vue
-<!-- The root element is `<template>` or `<slot>` -->
-<template>
-  <slot />
-</template>
+<!-- The root with src attribute is not empty -->
+<template src="foo.html"><div></div></template>
 ```
 
 </eslint-code-block>
diff --git a/docs/rules/valid-v-bind-sync.md b/docs/rules/valid-v-bind-sync.md
index 6e8d9504..9ebd51fd 100644
--- a/docs/rules/valid-v-bind-sync.md
+++ b/docs/rules/valid-v-bind-sync.md
@@ -7,6 +7,8 @@ description: enforce valid `.sync` modifier on `v-bind` directives
 # vue/valid-v-bind-sync
 > enforce valid `.sync` modifier on `v-bind` directives
 
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+
 This rule checks whether every `.sync` modifier on `v-bind` directives is valid.
 
 ## :book: Rule Details
@@ -62,7 +64,7 @@ Nothing.
 
 ## :books: Further reading
 
-- [Guide - `.sync` Modifier]([https://vuejs.org/v2/guide/list.html#v-for-with-a-Component](https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier))
+- [Guide - `.sync` Modifier](https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier)
 
 ## :mag: Implementation
 
diff --git a/docs/rules/valid-v-bind.md b/docs/rules/valid-v-bind.md
index 30d096e4..03c550f4 100644
--- a/docs/rules/valid-v-bind.md
+++ b/docs/rules/valid-v-bind.md
@@ -54,6 +54,14 @@ Nothing.
 
 [no-parsing-error]: no-parsing-error.md
 
+- [no-deprecated-v-bind-sync]
+
+[no-deprecated-v-bind-sync]: no-deprecated-v-bind-sync.md
+
+- [valid-v-bind-sync]
+
+[valid-v-bind-sync]: valid-v-bind-sync.md
+
 ## :mag: Implementation
 
 - [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/valid-v-bind.js)
diff --git a/docs/rules/valid-v-model.md b/docs/rules/valid-v-model.md
index 0aa8a753..934b5edf 100644
--- a/docs/rules/valid-v-model.md
+++ b/docs/rules/valid-v-model.md
@@ -15,8 +15,8 @@ This rule checks whether every `v-model` directive is valid.
 
 This rule reports `v-model` directives in the following cases:
 
-- The directive has that argument. E.g. `<input v-model:aaa="foo">`
-- The directive has the modifiers which are not supported. E.g. `<input v-model.bbb="foo">`
+- The directive used on HTMLElement has an argument. E.g. `<input v-model:aaa="foo">`
+- The directive used on HTMLElement has modifiers which are not supported. E.g. `<input v-model.bbb="foo">`
 - The directive does not have that attribute value. E.g. `<input v-model>`
 - The directive does not have the attribute value which is valid as LHS. E.g. `<input v-model="foo() + bar()">`
 - The directive is on unsupported elements. E.g. `<div v-model="foo"></div>`
@@ -32,6 +32,9 @@ This rule reports `v-model` directives in the following cases:
   <input v-model.lazy="foo">
   <textarea v-model="foo"/>
   <MyComponent v-model="foo"/>
+  <MyComponent v-model:propName="foo"/>
+  <MyComponent v-model.modifier="foo"/>
+  <MyComponent v-model:propName.modifier="foo"/>
   <div v-for="todo in todos">
     <input v-model="todo.name">
   </div>
diff --git a/docs/rules/valid-v-slot.md b/docs/rules/valid-v-slot.md
index edb17c8d..df3fce52 100644
--- a/docs/rules/valid-v-slot.md
+++ b/docs/rules/valid-v-slot.md
@@ -7,6 +7,8 @@ description: enforce valid `v-slot` directives
 # vue/valid-v-slot
 > enforce valid `v-slot` directives
 
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+
 This rule checks whether every `v-slot` directive is valid.
 
 ## :book: Rule Details
diff --git a/lib/configs/essential.js b/lib/configs/essential.js
index a335310f..21335f19 100644
--- a/lib/configs/essential.js
+++ b/lib/configs/essential.js
@@ -7,8 +7,10 @@ module.exports = {
   extends: require.resolve('./base'),
   rules: {
     'vue/no-async-in-computed-properties': 'error',
+    'vue/no-custom-modifiers-on-v-model': 'error',
     'vue/no-dupe-keys': 'error',
     'vue/no-duplicate-attributes': 'error',
+    'vue/no-multiple-template-root': 'error',
     'vue/no-parsing-error': 'error',
     'vue/no-reserved-keys': 'error',
     'vue/no-shared-component-data': 'error',
@@ -18,6 +20,7 @@ module.exports = {
     'vue/no-unused-components': 'error',
     'vue/no-unused-vars': 'error',
     'vue/no-use-v-if-with-v-for': 'error',
+    'vue/no-v-model-argument': 'error',
     'vue/require-component-is': 'error',
     'vue/require-prop-type-constructor': 'error',
     'vue/require-render-return': 'error',
@@ -26,6 +29,7 @@ module.exports = {
     'vue/return-in-computed-property': 'error',
     'vue/use-v-on-exact': 'error',
     'vue/valid-template-root': 'error',
+    'vue/valid-v-bind-sync': 'error',
     'vue/valid-v-bind': 'error',
     'vue/valid-v-cloak': 'error',
     'vue/valid-v-else-if': 'error',
@@ -38,6 +42,7 @@ module.exports = {
     'vue/valid-v-once': 'error',
     'vue/valid-v-pre': 'error',
     'vue/valid-v-show': 'error',
+    'vue/valid-v-slot': 'error',
     'vue/valid-v-text': 'error'
   }
 }
diff --git a/lib/configs/recommended.js b/lib/configs/recommended.js
index 51bcc2e8..e3ca8eba 100644
--- a/lib/configs/recommended.js
+++ b/lib/configs/recommended.js
@@ -7,6 +7,7 @@ module.exports = {
   extends: require.resolve('./strongly-recommended'),
   rules: {
     'vue/attributes-order': 'warn',
+    'vue/component-tags-order': 'warn',
     'vue/no-v-html': 'warn',
     'vue/order-in-components': 'warn',
     'vue/this-in-template': 'warn'
diff --git a/lib/configs/strongly-recommended.js b/lib/configs/strongly-recommended.js
index 7c32816c..22018866 100644
--- a/lib/configs/strongly-recommended.js
+++ b/lib/configs/strongly-recommended.js
@@ -7,6 +7,7 @@ module.exports = {
   extends: require.resolve('./essential'),
   rules: {
     'vue/attribute-hyphenation': 'warn',
+    'vue/component-definition-name-casing': 'warn',
     'vue/html-closing-bracket-newline': 'warn',
     'vue/html-closing-bracket-spacing': 'warn',
     'vue/html-end-tags': 'warn',
@@ -16,7 +17,6 @@ module.exports = {
     'vue/max-attributes-per-line': 'warn',
     'vue/multiline-html-element-content-newline': 'warn',
     'vue/mustache-interpolation-spacing': 'warn',
-    'vue/name-property-casing': 'warn',
     'vue/no-multi-spaces': 'warn',
     'vue/no-spaces-around-equal-signs-in-attribute': 'warn',
     'vue/no-template-shadow': 'warn',
@@ -25,6 +25,7 @@ module.exports = {
     'vue/require-prop-types': 'warn',
     'vue/singleline-html-element-content-newline': 'warn',
     'vue/v-bind-style': 'warn',
-    'vue/v-on-style': 'warn'
+    'vue/v-on-style': 'warn',
+    'vue/v-slot-style': 'warn'
   }
 }
diff --git a/lib/index.js b/lib/index.js
index 02e65ba9..4d3357cb 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -39,15 +39,20 @@ module.exports = {
     'no-async-in-computed-properties': require('./rules/no-async-in-computed-properties'),
     'no-boolean-default': require('./rules/no-boolean-default'),
     'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'),
+    'no-deprecated-filter': require('./rules/no-deprecated-filter'),
+    'no-custom-modifiers-on-v-model': require('./rules/no-custom-modifiers-on-v-model'),
     'no-deprecated-scope-attribute': require('./rules/no-deprecated-scope-attribute'),
     'no-deprecated-slot-attribute': require('./rules/no-deprecated-slot-attribute'),
     'no-deprecated-slot-scope-attribute': require('./rules/no-deprecated-slot-scope-attribute'),
+    'no-deprecated-v-bind-sync': require('./rules/no-deprecated-v-bind-sync'),
     'no-dupe-keys': require('./rules/no-dupe-keys'),
     'no-duplicate-attributes': require('./rules/no-duplicate-attributes'),
     'no-empty-pattern': require('./rules/no-empty-pattern'),
     'no-irregular-whitespace': require('./rules/no-irregular-whitespace'),
     'no-multi-spaces': require('./rules/no-multi-spaces'),
+    'no-multiple-template-root': require('./rules/no-multiple-template-root'),
     'no-parsing-error': require('./rules/no-parsing-error'),
+    'no-ref-as-operand': require('./rules/no-ref-as-operand'),
     'no-reserved-component-names': require('./rules/no-reserved-component-names'),
     'no-reserved-keys': require('./rules/no-reserved-keys'),
     'no-restricted-syntax': require('./rules/no-restricted-syntax'),
@@ -63,6 +68,7 @@ module.exports = {
     'no-unused-vars': require('./rules/no-unused-vars'),
     'no-use-v-if-with-v-for': require('./rules/no-use-v-if-with-v-for'),
     'no-v-html': require('./rules/no-v-html'),
+    'no-v-model-argument': require('./rules/no-v-model-argument'),
     'object-curly-spacing': require('./rules/object-curly-spacing'),
     'order-in-components': require('./rules/order-in-components'),
     'padding-line-between-blocks': require('./rules/padding-line-between-blocks'),
diff --git a/lib/rules/component-definition-name-casing.js b/lib/rules/component-definition-name-casing.js
index 9713cca2..efb3c359 100644
--- a/lib/rules/component-definition-name-casing.js
+++ b/lib/rules/component-definition-name-casing.js
@@ -17,9 +17,7 @@ module.exports = {
     type: 'suggestion',
     docs: {
       description: 'enforce specific casing for component definition name',
-      category: undefined,
-      // TODO Change with major version.
-      // category: 'strongly-recommended',
+      category: 'strongly-recommended',
       url: 'https://eslint.vuejs.org/rules/component-definition-name-casing.html'
     },
     fixable: 'code', // or "code" or "whitespace"
diff --git a/lib/rules/component-tags-order.js b/lib/rules/component-tags-order.js
index 7129aa36..3cfb7484 100644
--- a/lib/rules/component-tags-order.js
+++ b/lib/rules/component-tags-order.js
@@ -21,9 +21,7 @@ module.exports = {
     type: 'suggestion',
     docs: {
       description: 'enforce order of component top-level elements',
-      category: undefined,
-      // TODO Change with major version.
-      // category: 'recommended',
+      category: 'recommended',
       url: 'https://eslint.vuejs.org/rules/component-tags-order.html'
     },
     fixable: null,
diff --git a/lib/rules/name-property-casing.js b/lib/rules/name-property-casing.js
index 98403b31..97987233 100644
--- a/lib/rules/name-property-casing.js
+++ b/lib/rules/name-property-casing.js
@@ -18,10 +18,10 @@ module.exports = {
     docs: {
       description: 'enforce specific casing for the name property in Vue components',
       category: 'strongly-recommended',
-      url: 'https://eslint.vuejs.org/rules/name-property-casing.html'
+      url: 'https://eslint.vuejs.org/rules/name-property-casing.html',
+      replacedBy: ['component-definition-name-casing']
     },
-    // deprecated: true, // TODO Change with major version.
-    // replacedBy: ['component-definition-name-casing'], // TODO Change with major version.
+    deprecated: true,
     fixable: 'code', // or "code" or "whitespace"
     schema: [
       {
diff --git a/lib/rules/no-custom-modifiers-on-v-model.js b/lib/rules/no-custom-modifiers-on-v-model.js
new file mode 100644
index 00000000..0d4305af
--- /dev/null
+++ b/lib/rules/no-custom-modifiers-on-v-model.js
@@ -0,0 +1,57 @@
+/**
+ * @author Przemyslaw Falowski (@przemkow)
+ * @fileoverview This rule checks whether v-model used on the component do not have custom modifiers
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Helpers
+// ------------------------------------------------------------------------------
+
+const VALID_MODIFIERS = new Set(['lazy', 'number', 'trim'])
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow custom modifiers on v-model used on the component',
+      category: 'essential',
+      url: 'https://eslint.vuejs.org/rules/no-custom-modifiers-on-v-model.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      notSupportedModifier: "'v-model' directives don't support the modifier '{{name}}'."
+    }
+  },
+  create (context) {
+    return utils.defineTemplateBodyVisitor(context, {
+      "VAttribute[directive=true][key.name.name='model']" (node) {
+        const element = node.parent.parent
+
+        if (utils.isCustomComponent(element)) {
+          for (const modifier of node.key.modifiers) {
+            if (!VALID_MODIFIERS.has(modifier.name)) {
+              context.report({
+                node,
+                loc: node.loc,
+                messageId: 'notSupportedModifier',
+                data: { name: modifier.name }
+              })
+            }
+          }
+        }
+      }
+    })
+  }
+}
diff --git a/lib/rules/no-deprecated-filter.js b/lib/rules/no-deprecated-filter.js
new file mode 100644
index 00000000..2ec4256e
--- /dev/null
+++ b/lib/rules/no-deprecated-filter.js
@@ -0,0 +1,43 @@
+/**
+ * @author Przemyslaw Falowski (@przemkow)
+ * @fileoverview disallow using deprecated filters syntax
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow using deprecated filters syntax',
+      category: undefined,
+      url: 'https://eslint.vuejs.org/rules/no-deprecated-filter.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      noDeprecatedFilter: 'Filters are deprecated.'
+    }
+  },
+
+  create: function (context) {
+    return utils.defineTemplateBodyVisitor(context, {
+      'VFilterSequenceExpression' (node) {
+        context.report({
+          node,
+          loc: node.loc,
+          messageId: 'noDeprecatedFilter'
+        })
+      }
+    })
+  }
+}
diff --git a/lib/rules/no-deprecated-v-bind-sync.js b/lib/rules/no-deprecated-v-bind-sync.js
new file mode 100644
index 00000000..240e3b0b
--- /dev/null
+++ b/lib/rules/no-deprecated-v-bind-sync.js
@@ -0,0 +1,54 @@
+/**
+ * @author Przemyslaw Falowski (@przemkow)
+ * @fileoverview Disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)',
+      category: undefined,
+      url: 'https://eslint.vuejs.org/rules/no-deprecated-v-bind-sync.html'
+    },
+    fixable: 'code',
+    schema: [],
+    messages: {
+      syncModifierIsDeprecated: "'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."
+    }
+  },
+  create (context) {
+    return utils.defineTemplateBodyVisitor(context, {
+      "VAttribute[directive=true][key.name.name='bind']" (node) {
+        if (node.key.modifiers.map(mod => mod.name).includes('sync')) {
+          context.report({
+            node,
+            loc: node.loc,
+            messageId: 'syncModifierIsDeprecated',
+            fix: (fixer) => {
+              const isUsingSpreadSyntax = node.key.argument == null
+              const hasMultipleModifiers = node.key.modifiers.length > 1
+              if (isUsingSpreadSyntax || hasMultipleModifiers) {
+                return
+              }
+
+              const bindArgument = context.getSourceCode().getText(node.key.argument)
+              return fixer.replaceText(node.key, `v-model:${bindArgument}`)
+            }
+          })
+        }
+      }
+    })
+  }
+}
diff --git a/lib/rules/no-multiple-template-root.js b/lib/rules/no-multiple-template-root.js
new file mode 100644
index 00000000..d4de9461
--- /dev/null
+++ b/lib/rules/no-multiple-template-root.js
@@ -0,0 +1,98 @@
+/**
+ * @fileoverview disallow adding multiple root nodes to the template
+ * @author Przemyslaw Falowski (@przemkow)
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow adding multiple root nodes to the template',
+      category: 'essential',
+      url: 'https://eslint.vuejs.org/rules/no-multiple-template-root.html'
+    },
+    fixable: null,
+    schema: []
+  },
+
+  create: function (context) {
+    const sourceCode = context.getSourceCode()
+
+    return {
+      Program (program) {
+        const element = program.templateBody
+        if (element == null) {
+          return
+        }
+
+        const rootElements = []
+        let extraText = null
+        let extraElement = null
+        let vIf = false
+        for (const child of element.children) {
+          if (child.type === 'VElement') {
+            if (rootElements.length === 0) {
+              rootElements.push(child)
+              vIf = utils.hasDirective(child, 'if')
+            } else if (vIf && utils.hasDirective(child, 'else-if')) {
+              rootElements.push(child)
+            } else if (vIf && utils.hasDirective(child, 'else')) {
+              rootElements.push(child)
+              vIf = false
+            } else {
+              extraElement = child
+            }
+          } else if (sourceCode.getText(child).trim() !== '') {
+            extraText = child
+          }
+        }
+
+        if (extraText != null) {
+          context.report({
+            node: extraText,
+            loc: extraText.loc,
+            message: 'The template root requires an element rather than texts.'
+          })
+        } else if (extraElement != null) {
+          context.report({
+            node: extraElement,
+            loc: extraElement.loc,
+            message: 'The template root requires exactly one element.'
+          })
+        } else {
+          for (const element of rootElements) {
+            const tag = element.startTag
+            const name = element.name
+
+            if (name === 'template' || name === 'slot') {
+              context.report({
+                node: tag,
+                loc: tag.loc,
+                message: "The template root disallows '<{{name}}>' elements.",
+                data: { name }
+              })
+            }
+            if (utils.hasDirective(element, 'for')) {
+              context.report({
+                node: tag,
+                loc: tag.loc,
+                message: "The template root disallows 'v-for' directives."
+              })
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/lib/rules/no-ref-as-operand.js b/lib/rules/no-ref-as-operand.js
new file mode 100644
index 00000000..ea57d9da
--- /dev/null
+++ b/lib/rules/no-ref-as-operand.js
@@ -0,0 +1,124 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+const { ReferenceTracker, findVariable } = require('eslint-utils')
+
+module.exports = {
+  meta: {
+    type: 'suggestion',
+    docs: {
+      description: 'disallow use of value wrapped by `ref()` (Composition API) as an operand',
+      category: undefined,
+      url: 'https://eslint.vuejs.org/rules/no-ref-as-operand.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      requireDotValue: 'Must use `.value` to read or write the value wrapped by `ref()`.'
+    }
+  },
+  create (context) {
+    const refReferenceIds = new Map()
+
+    function reportIfRefWrapped (node) {
+      if (!refReferenceIds.has(node)) {
+        return
+      }
+      context.report({
+        node,
+        messageId: 'requireDotValue'
+      })
+    }
+    return {
+      'Program' () {
+        const tracker = new ReferenceTracker(context.getScope())
+        const traceMap = {
+          vue: {
+            [ReferenceTracker.ESM]: true,
+            ref: {
+              [ReferenceTracker.CALL]: true
+            }
+          }
+        }
+
+        for (const { node } of tracker.iterateEsmReferences(traceMap)) {
+          const variableDeclarator = node.parent
+          if (
+            !variableDeclarator ||
+            variableDeclarator.type !== 'VariableDeclarator' ||
+            variableDeclarator.id.type !== 'Identifier'
+          ) {
+            continue
+          }
+          const variable = findVariable(context.getScope(), variableDeclarator.id)
+          if (!variable) {
+            continue
+          }
+          const variableDeclaration = (
+            variableDeclarator.parent &&
+            variableDeclarator.parent.type === 'VariableDeclaration' &&
+            variableDeclarator.parent
+          ) || null
+          for (const reference of variable.references) {
+            if (!reference.isRead()) {
+              continue
+            }
+
+            refReferenceIds.set(reference.identifier, {
+              variableDeclarator,
+              variableDeclaration
+            })
+          }
+        }
+      },
+      // if (refValue)
+      'IfStatement>Identifier' (node) {
+        reportIfRefWrapped(node)
+      },
+      // switch (refValue)
+      'SwitchStatement>Identifier' (node) {
+        reportIfRefWrapped(node)
+      },
+      // -refValue, +refValue, !refValue, ~refValue, typeof refValue
+      'UnaryExpression>Identifier' (node) {
+        reportIfRefWrapped(node)
+      },
+      // refValue++, refValue--
+      'UpdateExpression>Identifier' (node) {
+        reportIfRefWrapped(node)
+      },
+      // refValue+1, refValue-1
+      'BinaryExpression>Identifier' (node) {
+        reportIfRefWrapped(node)
+      },
+      // refValue+=1, refValue-=1, foo+=refValue, foo-=refValue
+      'AssignmentExpression>Identifier' (node) {
+        reportIfRefWrapped(node)
+      },
+      // refValue || other, refValue && other. ignore: other || refValue
+      'LogicalExpression>Identifier' (node) {
+        if (node.parent.left !== node) {
+          return
+        }
+        // Report only constants.
+        const info = refReferenceIds.get(node)
+        if (!info) {
+          return
+        }
+        if (!info.variableDeclaration || info.variableDeclaration.kind !== 'const') {
+          return
+        }
+        reportIfRefWrapped(node)
+      },
+      // refValue ? x : y
+      'ConditionalExpression>Identifier' (node) {
+        if (node.parent.test !== node) {
+          return
+        }
+        reportIfRefWrapped(node)
+      }
+    }
+  }
+}
diff --git a/lib/rules/no-use-v-if-with-v-for.js b/lib/rules/no-use-v-if-with-v-for.js
index 754086f7..70d72075 100644
--- a/lib/rules/no-use-v-if-with-v-for.js
+++ b/lib/rules/no-use-v-if-with-v-for.js
@@ -37,7 +37,7 @@ function getVForUsingIterationVar (vIf) {
       variable.kind === 'v-for'
     )
     if (targetVFor) {
-      return targetVFor.id.parent
+      return targetVFor
     }
   }
   return undefined
@@ -76,13 +76,20 @@ module.exports = {
         if (utils.hasDirective(element, 'for')) {
           if (isUsingIterationVar(node)) {
             if (!allowUsingIterationVar) {
-              const vFor = getVForUsingIterationVar(node)
+              const vForVar = getVForUsingIterationVar(node)
+
+              let targetVForExpr = vForVar.id.parent
+              while (targetVForExpr.type !== 'VForExpression') {
+                targetVForExpr = targetVForExpr.parent
+              }
+              const iteratorNode = targetVForExpr.right
               context.report({
                 node,
                 loc: node.loc,
-                message: "The '{{iteratorName}}' variable inside 'v-for' directive should be replaced with a computed property that returns filtered array instead. You should not mix 'v-for' with 'v-if'.",
+                message: "The '{{iteratorName}}' {{kind}} inside 'v-for' directive should be replaced with a computed property that returns filtered array instead. You should not mix 'v-for' with 'v-if'.",
                 data: {
-                  iteratorName: vFor.right.name
+                  iteratorName: iteratorNode.type === 'Identifier' ? iteratorNode.name : context.getSourceCode().getText(iteratorNode),
+                  kind: iteratorNode.type === 'Identifier' ? 'variable' : 'expression'
                 }
               })
             }
diff --git a/lib/rules/no-v-model-argument.js b/lib/rules/no-v-model-argument.js
new file mode 100644
index 00000000..85b149ea
--- /dev/null
+++ b/lib/rules/no-v-model-argument.js
@@ -0,0 +1,47 @@
+/**
+ * @author Przemyslaw Falowski (@przemkow)
+ * @fileoverview This rule checks whether v-model used on custom component do not have an argument
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow adding an argument to `v-model` used in custom component',
+      category: 'essential',
+      url: 'https://eslint.vuejs.org/rules/no-v-model-argument.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      vModelRequireNoArgument: "'v-model' directives require no argument."
+    }
+  },
+
+  create (context) {
+    return utils.defineTemplateBodyVisitor(context, {
+      "VAttribute[directive=true][key.name.name='model']" (node) {
+        const element = node.parent.parent
+
+        if (node.key.argument && utils.isCustomComponent(element)) {
+          context.report({
+            node,
+            loc: node.loc,
+            messageId: 'vModelRequireNoArgument'
+          })
+        }
+      }
+    })
+  }
+}
diff --git a/lib/rules/v-slot-style.js b/lib/rules/v-slot-style.js
index 782dc435..1fc51ec0 100644
--- a/lib/rules/v-slot-style.js
+++ b/lib/rules/v-slot-style.js
@@ -77,9 +77,7 @@ module.exports = {
     type: 'suggestion',
     docs: {
       description: 'enforce `v-slot` directive style',
-      category: undefined, // strongly-recommended
-      // TODO Change with major version.
-      // category: 'strongly-recommended',
+      category: 'strongly-recommended',
       url: 'https://eslint.vuejs.org/rules/v-slot-style.html'
     },
     fixable: 'code',
diff --git a/lib/rules/valid-template-root.js b/lib/rules/valid-template-root.js
index 2932435d..860b550d 100644
--- a/lib/rules/valid-template-root.js
+++ b/lib/rules/valid-template-root.js
@@ -39,72 +39,27 @@ module.exports = {
 
         const hasSrc = utils.hasAttribute(element, 'src')
         const rootElements = []
-        let extraText = null
-        let extraElement = null
-        let vIf = false
+
         for (const child of element.children) {
-          if (child.type === 'VElement') {
-            if (rootElements.length === 0 && !hasSrc) {
-              rootElements.push(child)
-              vIf = utils.hasDirective(child, 'if')
-            } else if (vIf && utils.hasDirective(child, 'else-if')) {
-              rootElements.push(child)
-            } else if (vIf && utils.hasDirective(child, 'else')) {
-              rootElements.push(child)
-              vIf = false
-            } else {
-              extraElement = child
-            }
-          } else if (sourceCode.getText(child).trim() !== '') {
-            extraText = child
+          if (sourceCode.getText(child).trim() !== '') {
+            rootElements.push(child)
           }
         }
 
-        if (hasSrc && (extraText != null || extraElement != null)) {
-          context.report({
-            node: extraText || extraElement,
-            loc: (extraText || extraElement).loc,
-            message: "The template root with 'src' attribute is required to be empty."
-          })
-        } else if (extraText != null) {
-          context.report({
-            node: extraText,
-            loc: extraText.loc,
-            message: 'The template root requires an element rather than texts.'
-          })
-        } else if (extraElement != null) {
-          context.report({
-            node: extraElement,
-            loc: extraElement.loc,
-            message: 'The template root requires exactly one element.'
-          })
+        if (hasSrc && rootElements.length) {
+          for (const element of rootElements) {
+            context.report({
+              node: element,
+              loc: element.loc,
+              message: "The template root with 'src' attribute is required to be empty."
+            })
+          }
         } else if (rootElements.length === 0 && !hasSrc) {
           context.report({
             node: element,
             loc: element.loc,
-            message: 'The template root requires exactly one element.'
+            message: 'The template requires child element.'
           })
-        } else {
-          for (const element of rootElements) {
-            const tag = element.startTag
-            const name = element.name
-
-            if (name === 'template' || name === 'slot') {
-              context.report({
-                node: tag,
-                loc: tag.loc,
-                message: "The template root disallows '<{{name}}>' elements.",
-                data: { name }
-              })
-            }
-            if (utils.hasDirective(element, 'for')) {
-              context.report({
-                node: tag,
-                loc: tag.loc,
-                message: "The template root disallows 'v-for' directives."
-              })
-            }
-          }
         }
       }
     }
diff --git a/lib/rules/valid-v-bind-sync.js b/lib/rules/valid-v-bind-sync.js
index e8a03fbf..a0029ea6 100644
--- a/lib/rules/valid-v-bind-sync.js
+++ b/lib/rules/valid-v-bind-sync.js
@@ -52,9 +52,7 @@ module.exports = {
     type: 'problem',
     docs: {
       description: 'enforce valid `.sync` modifier on `v-bind` directives',
-      category: undefined,
-      // TODO Change with major version.
-      // category: 'essential',
+      category: 'essential',
       url: 'https://eslint.vuejs.org/rules/valid-v-bind-sync.html'
     },
     fixable: null,
diff --git a/lib/rules/valid-v-model.js b/lib/rules/valid-v-model.js
index d969f2c0..69b35890 100644
--- a/lib/rules/valid-v-model.js
+++ b/lib/rules/valid-v-model.js
@@ -112,24 +112,27 @@ module.exports = {
           })
         }
 
-        if (node.key.argument) {
-          context.report({
-            node,
-            loc: node.loc,
-            message: "'v-model' directives require no argument."
-          })
-        }
-
-        for (const modifier of node.key.modifiers) {
-          if (!VALID_MODIFIERS.has(modifier.name)) {
+        if (!utils.isCustomComponent(element)) {
+          if (node.key.argument) {
             context.report({
               node,
               loc: node.loc,
-              message: "'v-model' directives don't support the modifier '{{name}}'.",
-              data: { name: modifier.name }
+              message: "'v-model' directives require no argument."
             })
           }
+
+          for (const modifier of node.key.modifiers) {
+            if (!VALID_MODIFIERS.has(modifier.name)) {
+              context.report({
+                node,
+                loc: node.loc,
+                message: "'v-model' directives don't support the modifier '{{name}}'.",
+                data: { name: modifier.name }
+              })
+            }
+          }
         }
+
         if (!utils.hasAttributeValue(node)) {
           context.report({
             node,
diff --git a/lib/rules/valid-v-slot.js b/lib/rules/valid-v-slot.js
index 3b2b57b7..2704ce81 100644
--- a/lib/rules/valid-v-slot.js
+++ b/lib/rules/valid-v-slot.js
@@ -142,9 +142,7 @@ module.exports = {
     type: 'problem',
     docs: {
       description: 'enforce valid `v-slot` directives',
-      category: undefined, // essential
-      // TODO Change with major version.
-      // category: 'essential',
+      category: 'essential',
       url: 'https://eslint.vuejs.org/rules/valid-v-slot.html'
     },
     fixable: null,
diff --git a/package.json b/package.json
index 1744009a..619e27af 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint-plugin-vue",
-  "version": "6.2.1",
+  "version": "6.2.2",
   "description": "Official ESLint plugin for Vue.js",
   "main": "lib/index.js",
   "scripts": {
@@ -47,9 +47,10 @@
     "eslint": "^5.0.0 || ^6.0.0"
   },
   "dependencies": {
+    "eslint-utils": "^2.0.0",
     "natural-compare": "^1.4.0",
-    "vue-eslint-parser": "^7.0.0",
-    "semver": "^5.6.0"
+    "semver": "^5.6.0",
+    "vue-eslint-parser": "^7.0.0"
   },
   "devDependencies": {
     "@types/node": "^4.2.16",


Test patch:
diff --git a/tests/lib/rules/no-custom-modifiers-on-v-model.js b/tests/lib/rules/no-custom-modifiers-on-v-model.js
new file mode 100644
index 00000000..ad80cdb7
--- /dev/null
+++ b/tests/lib/rules/no-custom-modifiers-on-v-model.js
@@ -0,0 +1,72 @@
+/**
+ * @author Przemyslaw Falowski (@przemkow)
+ * @fileoverview This rule checks whether v-model used on the component do not have custom modifiers
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-custom-modifiers-on-v-model')
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2015 }
+})
+
+ruleTester.run('no-custom-modifiers-on-v-model', rule, {
+
+  valid: [
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:propName="foo"></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model="foo"></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:propName.trim="foo"></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model.trim="foo"></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:propName.lazy="foo"></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model.lazy="foo"></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:propName.number="foo"></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model.number="foo"></template>'
+    }
+  ],
+
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:propName.aaa="foo"></template>',
+      errors: ["'v-model' directives don't support the modifier 'aaa'."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model.aaa="foo"></template>',
+      errors: ["'v-model' directives don't support the modifier 'aaa'."]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-deprecated-filter.js b/tests/lib/rules/no-deprecated-filter.js
new file mode 100644
index 00000000..20957e46
--- /dev/null
+++ b/tests/lib/rules/no-deprecated-filter.js
@@ -0,0 +1,72 @@
+/**
+ * @author Przemyslaw Falowski (@przemkow)
+ * @fileoverview disallow using deprecated filters syntax
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-deprecated-filter')
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2015 }
+})
+
+ruleTester.run('no-deprecated-filter', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: '<template>{{ msg }}</template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>{{ method(msg) }}</template>'
+    }
+  ],
+
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: '<template>{{ msg | filter }}</template>',
+      errors: ['Filters are deprecated.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>{{ msg | filter(x) }}</template>',
+      errors: ['Filters are deprecated.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>{{ msg | filterA | filterB }}</template>',
+      errors: ['Filters are deprecated.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div v-for="msg in messages">{{ msg | filter }}</div></template>',
+      errors: ['Filters are deprecated.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div v-bind:id="msg | filter"></div></template>',
+      errors: ['Filters are deprecated.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div v-bind:id="msg | filter(aaa)"></div></template>',
+      errors: ['Filters are deprecated.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div v-bind:id="msg | filterA | filterB"></div></template>',
+      errors: ['Filters are deprecated.']
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-deprecated-v-bind-sync.js b/tests/lib/rules/no-deprecated-v-bind-sync.js
new file mode 100644
index 00000000..6cf90026
--- /dev/null
+++ b/tests/lib/rules/no-deprecated-v-bind-sync.js
@@ -0,0 +1,154 @@
+/**
+ * @author Przemyslaw Falowski (@przemkow)
+ * @fileoverview Disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-deprecated-v-bind-sync')
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2015 }
+})
+
+ruleTester.run('no-deprecated-v-bind-sync', rule, {
+
+  valid: [
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent v-bind:foo='bar'/></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent :foo='bar'/></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent v-bind:[dynamicArg]='bar'/></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent :[dynamicArg]='bar'/></template>"
+    }
+  ],
+
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent v-bind:foo.sync='bar'/></template>",
+      output: "<template><MyComponent v-model:foo='bar'/></template>",
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent :foo.sync='bar'/></template>",
+      output: "<template><MyComponent v-model:foo='bar'/></template>",
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent v-bind:[dynamicArg].sync='bar'/></template>",
+      output: "<template><MyComponent v-model:[dynamicArg]='bar'/></template>",
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent :[dynamicArg].sync='bar'/></template>",
+      output: "<template><MyComponent v-model:[dynamicArg]='bar'/></template>",
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent v-bind.sync='bar'/></template>",
+      output: "<template><MyComponent v-bind.sync='bar'/></template>",
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent :foo.sync.unknown="foo" /></template>',
+      output: '<template><MyComponent :foo.sync.unknown="foo" /></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent :[dynamicArg].sync.unknown="foo" /></template>',
+      output: '<template><MyComponent :[dynamicArg].sync.unknown="foo" /></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="x.foo" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="x.foo" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[x]" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[x]" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[x - 1]" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[x - 1]" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[`${x}`]" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[`${x}`]" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[`prefix_${x}`]" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[`prefix_${x}`]" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[x ? x : \'_\']" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[x ? x : \'_\']" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[x || \'_\']" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[x || \'_\']" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[x()]" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[x()]" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[/r/.match(x) ? 0 : 1]" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[/r/.match(x) ? 0 : 1]" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[typeof x]" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[typeof x]" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[tag`${x}`]" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[tag`${x}`]" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-multiple-template-root.js b/tests/lib/rules/no-multiple-template-root.js
new file mode 100644
index 00000000..3c8a99bc
--- /dev/null
+++ b/tests/lib/rules/no-multiple-template-root.js
@@ -0,0 +1,100 @@
+/**
+ * @fileoverview disallow adding multiple root nodes to the template
+ * @author Przemyslaw Falowski (@przemkow)
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+var rule = require('../../../lib/rules/no-multiple-template-root')
+
+var RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2015 }
+})
+ruleTester.run('no-multiple-template-root', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: '<template><div>abc</div></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>\n    <div>abc</div>\n</template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>\n    <!-- comment -->\n    <div>abc</div>\n</template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>\n    <!-- comment -->\n    <div v-if="foo">abc</div>\n    <div v-else>abc</div>\n</template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>\n    <!-- comment -->\n    <div v-if="foo">abc</div>\n    <div v-else-if="bar">abc</div>\n    <div v-else>abc</div>\n</template>'
+    },
+    {
+      filename: 'test.vue',
+      code: `<template>\n    <c1 v-if="1" />\n    <c2 v-else-if="1" />\n    <c3 v-else />\n</template>`
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div v-if="foo"></div></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div v-if="foo"></div><div v-else-if="bar"></div></template>'
+    }
+  ],
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: '<template><div></div><div></div></template>',
+      errors: ['The template root requires exactly one element.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>\n    <div></div>\n    <div></div>\n</template>',
+      errors: ['The template root requires exactly one element.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>{{a b c}}</template>',
+      errors: ['The template root requires an element rather than texts.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div></div>aaaaaa</template>',
+      errors: ['The template root requires an element rather than texts.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>aaaaaa<div></div></template>',
+      errors: ['The template root requires an element rather than texts.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div v-for="x in list"></div></template>',
+      errors: ["The template root disallows 'v-for' directives."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><slot></slot></template>',
+      errors: ["The template root disallows '<slot>' elements."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><template></template></template>',
+      errors: ["The template root disallows '<template>' elements."]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-ref-as-operand.js b/tests/lib/rules/no-ref-as-operand.js
new file mode 100644
index 00000000..b8632c88
--- /dev/null
+++ b/tests/lib/rules/no-ref-as-operand.js
@@ -0,0 +1,334 @@
+/**
+ * @author Yosuke Ota
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/no-ref-as-operand')
+
+const tester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2019, sourceType: 'module' }
+})
+
+tester.run('no-ref-as-operand', rule, {
+  valid: [
+    `
+    import { ref } from 'vue'
+    const count = ref(0)
+    console.log(count.value) // 0
+
+    count.value++
+    console.log(count.value) // 1
+    `,
+    `
+    <script>
+      import { ref } from 'vue'
+      export default {
+        setup() {
+          const count = ref(0)
+          console.log(count.value) // 0
+
+          count.value++
+          console.log(count.value) // 1
+          return {
+            count
+          }
+        }
+      }
+    </script>
+    `,
+    `
+    import { ref } from 'vue'
+    const count = ref(0)
+    if (count.value) {}
+    switch (count.value) {}
+    var foo = -count.value
+    var foo = +count.value
+    count.value++
+    count.value--
+    count.value + 1
+    1 - count.value
+    count.value || other
+    count.value && other
+    var foo = count.value ? x : y
+    `,
+    `
+    import { ref } from 'vue'
+    const foo = ref(true)
+    if (bar) foo
+    `,
+    `
+    import { ref } from 'vue'
+    const foo = ref(true)
+    var a = other || foo // ignore
+    var b = other && foo // ignore
+
+    let bar = ref(true)
+    var a = bar || other
+    var b = bar || other
+    `,
+    `
+    import { ref } from 'vue'
+    let count = not_ref(0)
+
+    count++
+    `,
+    `
+    import { ref } from 'vue'
+    const foo = ref(0)
+    const bar = ref(0)
+    var baz = x ? foo : bar
+    `,
+    `
+    import { ref } from 'vue'
+    // Probably wrong, but not checked by this rule.
+    const {value} = ref(0)
+    value++
+    `,
+    `
+    import { ref } from 'vue'
+    const count = ref(0)
+    function foo() {
+      let count = 0
+      count++
+    }
+    `,
+    `
+    import { ref } from 'unknown'
+    const count = ref(0)
+    count++
+    `,
+    `
+    import { ref } from 'vue'
+    const count = ref
+    count++
+    `
+  ],
+  invalid: [
+    {
+      code: `
+      import { ref } from 'vue'
+      let count = ref(0)
+
+      count++ // error
+      console.log(count + 1) // error
+      console.log(1 + count) // error
+      `,
+      errors: [
+        {
+          message: 'Must use `.value` to read or write the value wrapped by `ref()`.',
+          line: 5,
+          column: 7,
+          endLine: 5,
+          endColumn: 12
+        },
+        {
+          message: 'Must use `.value` to read or write the value wrapped by `ref()`.',
+          line: 6,
+          column: 19,
+          endLine: 6,
+          endColumn: 24
+        },
+        {
+          message: 'Must use `.value` to read or write the value wrapped by `ref()`.',
+          line: 7,
+          column: 23,
+          endLine: 7,
+          endColumn: 28
+        }
+      ]
+    },
+    {
+      code: `
+      <script>
+        import { ref } from 'vue'
+        export default {
+          setup() {
+            let count = ref(0)
+
+            count++ // error
+            console.log(count + 1) // error
+            console.log(1 + count) // error
+            return {
+              count
+            }
+          }
+        }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'requireDotValue',
+          line: 8,
+          column: 13,
+          endLine: 8,
+          endColumn: 18
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 9,
+          column: 25,
+          endLine: 9,
+          endColumn: 30
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 10,
+          column: 29,
+          endLine: 10,
+          endColumn: 34
+        }
+      ]
+    },
+    {
+      code: `
+      import { ref } from 'vue'
+      const foo = ref(true)
+      if (foo) {
+        //
+      }
+      `,
+      errors: [
+        {
+          messageId: 'requireDotValue',
+          line: 4
+        }
+      ]
+    },
+    {
+      code: `
+      import { ref } from 'vue'
+      const foo = ref(true)
+      switch (foo) {
+        //
+      }
+      `,
+      errors: [
+        {
+          messageId: 'requireDotValue',
+          line: 4
+        }
+      ]
+    },
+    {
+      code: `
+      import { ref } from 'vue'
+      const foo = ref(0)
+      var a = -foo
+      var b = +foo
+      var c = !foo
+      var d = ~foo
+      `,
+      errors: [
+        {
+          messageId: 'requireDotValue',
+          line: 4
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 5
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 6
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 7
+        }
+      ]
+    },
+    {
+      code: `
+      import { ref } from 'vue'
+      let foo = ref(0)
+      foo += 1
+      foo -= 1
+      baz += foo
+      baz -= foo
+      `,
+      errors: [
+        {
+          messageId: 'requireDotValue',
+          line: 4
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 5
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 6
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 7
+        }
+      ]
+    },
+    {
+      code: `
+      import { ref } from 'vue'
+      const foo = ref(true)
+      var a = foo || other
+      var b = foo && other
+      `,
+      errors: [
+        {
+          messageId: 'requireDotValue',
+          line: 4
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 5
+        }
+      ]
+    },
+    {
+      code: `
+      import { ref } from 'vue'
+      let foo = ref(true)
+      var a = foo ? x : y
+      `,
+      errors: [
+        {
+          messageId: 'requireDotValue',
+          line: 4
+        }
+      ]
+    },
+    {
+      code: `
+      <script>
+        import { ref } from 'vue'
+        let count = ref(0)
+        export default {
+          setup() {
+            count++ // error
+            console.log(count + 1) // error
+            console.log(1 + count) // error
+            return {
+              count
+            }
+          }
+        }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'requireDotValue',
+          line: 7
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 8
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 9
+        }
+      ]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-use-v-if-with-v-for.js b/tests/lib/rules/no-use-v-if-with-v-for.js
index a47e858a..c8bcda78 100644
--- a/tests/lib/rules/no-use-v-if-with-v-for.js
+++ b/tests/lib/rules/no-use-v-if-with-v-for.js
@@ -73,6 +73,55 @@ tester.run('no-use-v-if-with-v-for', rule, {
           </ul>
         </template>
       `
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="{x} in list" v-if="x"></div></div></template>',
+      options: [{ allowUsingIterationVar: true }]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="{x,y,z} in list" v-if="y.foo"></div></div></template>',
+      options: [{ allowUsingIterationVar: true }]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="({x,y,z},i) in list" v-if="i%2==0"></div></div></template>',
+      options: [{ allowUsingIterationVar: true }]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div v-if="shown"><div v-for="({x,y,z},i) in list"></div></div></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <template>
+          <ul>
+            <li
+              v-for="{user} in activeUsers"
+              :key="user.id"
+            >
+              {{ user.name }}
+            <li>
+          </ul>
+        </template>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <template>
+          <ul v-if="shouldShowUsers">
+            <li
+              v-for="{user} in users"
+              :key="user.id"
+            >
+              {{ user.name }}
+            <li>
+          </ul>
+        </template>
+      `
     }
   ],
   invalid: [
@@ -139,6 +188,70 @@ tester.run('no-use-v-if-with-v-for', rule, {
         message: "This 'v-if' should be moved to the wrapper element.",
         line: 6
       }]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="{x,y,z} in list" v-if="z.isActive"></div></div></template>',
+      errors: [{
+        message: "The 'list' variable inside 'v-for' directive should be replaced with a computed property that returns filtered array instead. You should not mix 'v-for' with 'v-if'.",
+        line: 1
+      }]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <template>
+          <ul>
+            <li
+              v-for="{foo, bar, user} in users"
+              v-if="user.isActive"
+              :key="user.id"
+            >
+              {{ user.name }}
+            <li>
+          </ul>
+        </template>
+      `,
+      errors: [{
+        message: "The 'users' variable inside 'v-for' directive should be replaced with a computed property that returns filtered array instead. You should not mix 'v-for' with 'v-if'.",
+        line: 6
+      }]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <template>
+          <ul>
+            <li
+              v-for="{foo, bar, user} in users"
+              v-if="shouldShowUsers"
+              :key="user.id"
+            >
+              {{ user.name }}
+            <li>
+          </ul>
+        </template>
+      `,
+      errors: [{
+        message: "This 'v-if' should be moved to the wrapper element.",
+        line: 6
+      }]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="{x} in list()" v-if="x.isActive"></div></div></template>',
+      errors: [{
+        message: "The 'list()' expression inside 'v-for' directive should be replaced with a computed property that returns filtered array instead. You should not mix 'v-for' with 'v-if'.",
+        line: 1
+      }]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="i in 5" v-if="i"></div></div></template>',
+      errors: [{
+        message: "The '5' expression inside 'v-for' directive should be replaced with a computed property that returns filtered array instead. You should not mix 'v-for' with 'v-if'.",
+        line: 1
+      }]
     }
   ]
 })
diff --git a/tests/lib/rules/no-v-model-argument.js b/tests/lib/rules/no-v-model-argument.js
new file mode 100644
index 00000000..5cf5c595
--- /dev/null
+++ b/tests/lib/rules/no-v-model-argument.js
@@ -0,0 +1,44 @@
+/**
+ * @author Przemyslaw Falowski (@przemkow)
+ * @fileoverview This rule checks whether v-model used on custom component do not have an argument
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-v-model-argument')
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2015 }
+})
+
+ruleTester.run('no-v-model-argument', rule, {
+
+  valid: [
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model="bar"></MyComponent></template>'
+    }
+  ],
+
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:foo="bar"></MyComponent></template>',
+      errors: ["'v-model' directives require no argument."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:foo.trim="bar"></MyComponent></template>',
+      errors: ["'v-model' directives require no argument."]
+    }
+  ]
+})
diff --git a/tests/lib/rules/valid-template-root.js b/tests/lib/rules/valid-template-root.js
index 2fd9dbc0..033188cc 100644
--- a/tests/lib/rules/valid-template-root.js
+++ b/tests/lib/rules/valid-template-root.js
@@ -74,53 +74,45 @@ tester.run('valid-template-root', rule, {
     {
       filename: 'test.vue',
       code: '<template lang="pug">test</template>'
-    }
-  ],
-  invalid: [
+    },
     {
       filename: 'test.vue',
-      code: '<template>\n</template>',
-      errors: ['The template root requires exactly one element.']
+      code: '<template><div></div><div></div></template>'
     },
     {
       filename: 'test.vue',
-      code: '<template><div></div><div></div></template>',
-      errors: ['The template root requires exactly one element.']
+      code: '<template>\n    <div></div>\n    <div></div>\n</template>'
     },
     {
       filename: 'test.vue',
-      code: '<template>\n    <div></div>\n    <div></div>\n</template>',
-      errors: ['The template root requires exactly one element.']
+      code: '<template>{{a b c}}</template>'
     },
     {
       filename: 'test.vue',
-      code: '<template>{{a b c}}</template>',
-      errors: ['The template root requires an element rather than texts.']
+      code: '<template><div></div>aaaaaa</template>'
     },
     {
       filename: 'test.vue',
-      code: '<template><div></div>aaaaaa</template>',
-      errors: ['The template root requires an element rather than texts.']
+      code: '<template>aaaaaa<div></div></template>'
     },
     {
       filename: 'test.vue',
-      code: '<template>aaaaaa<div></div></template>',
-      errors: ['The template root requires an element rather than texts.']
+      code: '<template><div v-for="x in list"></div></template>'
     },
     {
       filename: 'test.vue',
-      code: '<template><div v-for="x in list"></div></template>',
-      errors: ["The template root disallows 'v-for' directives."]
+      code: '<template><slot></slot></template>'
     },
     {
       filename: 'test.vue',
-      code: '<template><slot></slot></template>',
-      errors: ["The template root disallows '<slot>' elements."]
-    },
+      code: '<template><template></template></template>'
+    }
+  ],
+  invalid: [
     {
       filename: 'test.vue',
-      code: '<template><template></template></template>',
-      errors: ["The template root disallows '<template>' elements."]
+      code: '<template>\n</template>',
+      errors: ['The template requires child element.']
     },
     {
       filename: 'test.vue',
diff --git a/tests/lib/rules/valid-v-model.js b/tests/lib/rules/valid-v-model.js
index f267ad45..9b2d19cc 100644
--- a/tests/lib/rules/valid-v-model.js
+++ b/tests/lib/rules/valid-v-model.js
@@ -114,6 +114,26 @@ tester.run('valid-v-model', rule, {
     {
       filename: 'test.vue',
       code: '<template><input v-bind:type="a" v-model="b"></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:aaa="a"></MyComponent></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:aaa.modifier="a"></MyComponent></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model.modifier="a"></MyComponent></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:aaa.modifier.modifierTwo="a"></MyComponent></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model.modifier.modifierTwo="a"></MyComponent></template>'
     }
   ],
   invalid: [