Repository: vuejs/eslint-plugin-vue
Pull request #1064 (2020-03-05 11:08:50)

Fix iterateProperties to support arrow functions
current iterateProperties doesn't support arrow function properties. However it supports functions and iterate their returned objects. 

So we can't treat properties from arrow functions same way:

default way works:
```
data: function () {
  return {
   property: 'value'
  }
},
```

But following examples won't work:
```
data: () => {
  return {
   property: 'value'
  }
},
```

or like this:

```
data: () => ({
  property: 'value'
}),
```

Pull request adds support for `ArrowFunctionExpression` in `iterateProperties`.

frenchrabbit: @ota-meshi , added same cases but with 2 types of arrow functions(`() => {return...}`, `()=>({})`), where was tests with `function () {}`

Patch:
diff --git a/docs/rules/README.md b/docs/rules/README.md
index ab1f5b22..5b21a084 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,13 +160,18 @@ 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-lifecycle-after-await](./no-lifecycle-after-await.md) | disallow asynchronously registered lifecycle hooks |  |
+| [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-setup-props-destructure](./no-setup-props-destructure.md) | disallow destructuring of `props` passed to `setup` |  |
 | [vue/no-static-inline-styles](./no-static-inline-styles.md) | disallow static inline `style` attributes |  |
 | [vue/no-unsupported-features](./no-unsupported-features.md) | disallow unsupported Vue.js syntax on the specified version | :wrench: |
 | [vue/object-curly-spacing](./object-curly-spacing.md) | enforce consistent spacing inside braces | :wrench: |
@@ -174,9 +184,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 +192,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-lifecycle-after-await.md b/docs/rules/no-lifecycle-after-await.md
new file mode 100644
index 00000000..aaa521f7
--- /dev/null
+++ b/docs/rules/no-lifecycle-after-await.md
@@ -0,0 +1,47 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-lifecycle-after-await
+description: disallow asynchronously registered lifecycle hooks
+---
+# vue/no-lifecycle-after-await
+> disallow asynchronously registered lifecycle hooks
+
+## :book: Rule Details
+
+This rule reports the lifecycle hooks after `await` expression.  
+In `setup()` function, `onXXX` lifecycle hooks should be registered synchronously.
+
+<eslint-code-block :rules="{'vue/no-lifecycle-after-await': ['error']}">
+
+```vue
+<script>
+import { onMounted } from 'vue'
+export default {
+  async setup() {
+    /* ✓ GOOD */
+    onMounted(() => { /* ... */ })
+
+    await doSomething()
+
+    /* ✗ BAD */
+    onMounted(() => { /* ... */ })
+  }
+}
+</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-lifecycle-after-await.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-lifecycle-after-await.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-setup-props-destructure.md b/docs/rules/no-setup-props-destructure.md
new file mode 100644
index 00000000..2c56d9aa
--- /dev/null
+++ b/docs/rules/no-setup-props-destructure.md
@@ -0,0 +1,98 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-setup-props-destructure
+description: disallow destructuring of `props` passed to `setup`
+---
+# vue/no-setup-props-destructure
+> disallow destructuring of `props` passed to `setup`
+
+## :book: Rule Details
+
+This rule reports the destructuring of `props` passed to `setup` causing the value to lose reactivity.
+
+<eslint-code-block :rules="{'vue/no-setup-props-destructure': ['error']}">
+
+```vue
+<script>
+export default {
+  /* ✓ GOOD */
+  setup(props) {
+    watch(() => {
+      console.log(props.count)
+    })
+
+    return () => {
+      return h('div', props.count)
+    }
+  }
+}
+</script>
+```
+
+</eslint-code-block>
+
+Destructuring the `props` passed to `setup` will cause the value to lose reactivity.
+
+<eslint-code-block :rules="{'vue/no-setup-props-destructure': ['error']}">
+
+```vue
+<script>
+export default {
+  /* ✗ BAD */
+  setup({ count }) {
+    watch(() => {
+      console.log(count) // not going to detect changes
+    })
+
+    return () => {
+      return h('div', count) // not going to update
+    }
+  }
+}
+</script>
+```
+
+</eslint-code-block>
+
+Also, destructuring in root scope of `setup()` should error, but ok inside nested callbacks or returned render functions:
+
+<eslint-code-block :rules="{'vue/no-setup-props-destructure': ['error']}">
+
+```vue
+<script>
+export default {
+  setup(props) {
+    /* ✗ BAD */
+    const { count } = props
+
+    watch(() => {
+      /* ✓ GOOD */
+      const { count } = props
+      console.log(count)
+    })
+
+    return () => {
+      /* ✓ GOOD */
+      const { count } = props
+      return h('div', 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-setup-props-destructure.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-setup-props-destructure.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/docs/user-guide/README.md b/docs/user-guide/README.md
index 01710c07..1c4d0e38 100644
--- a/docs/user-guide/README.md
+++ b/docs/user-guide/README.md
@@ -85,6 +85,9 @@ All component-related rules are applied to code that passes any of the following
 * `Vue.component()` expression
 * `Vue.extend()` expression
 * `Vue.mixin()` expression
+* `app.component()` expression
+* `app.mixin()` expression
+* `createApp()` expression
 * `export default {}` in `.vue` or `.jsx` file
 
 However, if you want to take advantage of the rules in any of your custom objects that are Vue components, you might need to use the special comment `// @vue/component` that marks an object in the next line as a Vue component in any file, e.g.:
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..55d75305 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -39,18 +39,25 @@ 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-lifecycle-after-await': require('./rules/no-lifecycle-after-await'),
     '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'),
+    'no-setup-props-destructure': require('./rules/no-setup-props-destructure'),
     'no-shared-component-data': require('./rules/no-shared-component-data'),
     'no-side-effects-in-computed-properties': require('./rules/no-side-effects-in-computed-properties'),
     'no-spaces-around-equal-signs-in-attribute': require('./rules/no-spaces-around-equal-signs-in-attribute'),
@@ -63,6 +70,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-lifecycle-after-await.js b/lib/rules/no-lifecycle-after-await.js
new file mode 100644
index 00000000..1f78165e
--- /dev/null
+++ b/lib/rules/no-lifecycle-after-await.js
@@ -0,0 +1,111 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+const { ReferenceTracker } = require('eslint-utils')
+const utils = require('../utils')
+
+const LIFECYCLE_HOOKS = ['onBeforeMount', 'onBeforeUnmount', 'onBeforeUpdate', 'onErrorCaptured', 'onMounted', 'onRenderTracked', 'onRenderTriggered', 'onUnmounted', 'onUpdated', 'onActivated', 'onDeactivated']
+
+module.exports = {
+  meta: {
+    type: 'suggestion',
+    docs: {
+      description: 'disallow asynchronously registered lifecycle hooks',
+      category: undefined,
+      url: 'https://eslint.vuejs.org/rules/no-lifecycle-after-await.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      forbidden: 'The lifecycle hooks after `await` expression are forbidden.'
+    }
+  },
+  create (context) {
+    const lifecycleHookCallNodes = new Set()
+    const setupFunctions = new Map()
+    const forbiddenNodes = new Map()
+
+    function addForbiddenNode (property, node) {
+      let list = forbiddenNodes.get(property)
+      if (!list) {
+        list = []
+        forbiddenNodes.set(property, list)
+      }
+      list.push(node)
+    }
+
+    let scopeStack = { upper: null, functionNode: null }
+
+    return Object.assign(
+      {
+        'Program' () {
+          const tracker = new ReferenceTracker(context.getScope())
+          const traceMap = {
+            vue: {
+              [ReferenceTracker.ESM]: true
+            }
+          }
+          for (const lifecycleHook of LIFECYCLE_HOOKS) {
+            traceMap.vue[lifecycleHook] = {
+              [ReferenceTracker.CALL]: true
+            }
+          }
+
+          for (const { node } of tracker.iterateEsmReferences(traceMap)) {
+            lifecycleHookCallNodes.add(node)
+          }
+        },
+        'Property[value.type=/^(Arrow)?FunctionExpression$/]' (node) {
+          if (utils.getStaticPropertyName(node) !== 'setup') {
+            return
+          }
+
+          setupFunctions.set(node.value, {
+            setupProperty: node,
+            afterAwait: false
+          })
+        },
+        ':function' (node) {
+          scopeStack = { upper: scopeStack, functionNode: node }
+        },
+        'AwaitExpression' () {
+          const setupFunctionData = setupFunctions.get(scopeStack.functionNode)
+          if (!setupFunctionData) {
+            return
+          }
+          setupFunctionData.afterAwait = true
+        },
+        'CallExpression' (node) {
+          const setupFunctionData = setupFunctions.get(scopeStack.functionNode)
+          if (!setupFunctionData || !setupFunctionData.afterAwait) {
+            return
+          }
+
+          if (lifecycleHookCallNodes.has(node)) {
+            addForbiddenNode(setupFunctionData.setupProperty, node)
+          }
+        },
+        ':function:exit' (node) {
+          scopeStack = scopeStack.upper
+
+          setupFunctions.delete(node)
+        }
+      },
+      utils.executeOnVue(context, obj => {
+        const reportsList = obj.properties
+          .map(item => forbiddenNodes.get(item))
+          .filter(reports => !!reports)
+        for (const reports of reportsList) {
+          for (const node of reports) {
+            context.report({
+              node,
+              messageId: 'forbidden'
+            })
+          }
+        }
+      })
+    )
+  }
+}
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-setup-props-destructure.js b/lib/rules/no-setup-props-destructure.js
new file mode 100644
index 00000000..af7e3754
--- /dev/null
+++ b/lib/rules/no-setup-props-destructure.js
@@ -0,0 +1,136 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+const { findVariable } = require('eslint-utils')
+const utils = require('../utils')
+
+module.exports = {
+  meta: {
+    type: 'suggestion',
+    docs: {
+      description: 'disallow destructuring of `props` passed to `setup`',
+      category: undefined,
+      url: 'https://eslint.vuejs.org/rules/no-setup-props-destructure.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      destructuring: 'Destructuring the `props` will cause the value to lose reactivity.',
+      getProperty: 'Getting a value from the `props` in root scope of `setup()` will cause the value to lose reactivity.'
+    }
+  },
+  create (context) {
+    const setupFunctions = new Map()
+    const forbiddenNodes = new Map()
+
+    function addForbiddenNode (property, node, messageId) {
+      let list = forbiddenNodes.get(property)
+      if (!list) {
+        list = []
+        forbiddenNodes.set(property, list)
+      }
+      list.push({
+        node,
+        messageId
+      })
+    }
+
+    function verify (left, right, { propsReferenceIds, setupProperty }) {
+      if (!right) {
+        return
+      }
+
+      if (left.type === 'ArrayPattern' || left.type === 'ObjectPattern') {
+        if (propsReferenceIds.has(right)) {
+          addForbiddenNode(setupProperty, left, 'getProperty')
+        }
+      } else if (left.type === 'Identifier' && right.type === 'MemberExpression') {
+        if (propsReferenceIds.has(right.object)) {
+          addForbiddenNode(setupProperty, right, 'getProperty')
+        }
+      }
+    }
+
+    let scopeStack = { upper: null, functionNode: null }
+
+    return Object.assign(
+      {
+        'Property[value.type=/^(Arrow)?FunctionExpression$/]' (node) {
+          if (utils.getStaticPropertyName(node) !== 'setup') {
+            return
+          }
+          const param = node.value.params[0]
+          if (!param) {
+            // no arguments
+            return
+          }
+          if (param.type === 'RestElement') {
+            // cannot check
+            return
+          }
+          if (param.type === 'ArrayPattern' || param.type === 'ObjectPattern') {
+            addForbiddenNode(node, param, 'destructuring')
+            return
+          }
+          setupFunctions.set(node.value, {
+            setupProperty: node,
+            propsParam: param,
+            propsReferenceIds: new Set()
+          })
+        },
+        ':function' (node) {
+          scopeStack = { upper: scopeStack, functionNode: node }
+        },
+        ':function>*' (node) {
+          const setupFunctionData = setupFunctions.get(node.parent)
+          if (!setupFunctionData || setupFunctionData.propsParam !== node) {
+            return
+          }
+          const variable = findVariable(context.getScope(), node)
+          if (!variable) {
+            return
+          }
+          const { propsReferenceIds } = setupFunctionData
+          for (const reference of variable.references) {
+            if (!reference.isRead()) {
+              continue
+            }
+
+            propsReferenceIds.add(reference.identifier)
+          }
+        },
+        'VariableDeclarator' (node) {
+          const setupFunctionData = setupFunctions.get(scopeStack.functionNode)
+          if (!setupFunctionData) {
+            return
+          }
+          verify(node.id, node.init, setupFunctionData)
+        },
+        'AssignmentExpression' (node) {
+          const setupFunctionData = setupFunctions.get(scopeStack.functionNode)
+          if (!setupFunctionData) {
+            return
+          }
+          verify(node.left, node.right, setupFunctionData)
+        },
+        ':function:exit' (node) {
+          scopeStack = scopeStack.upper
+
+          setupFunctions.delete(node)
+        }
+      },
+      utils.executeOnVue(context, obj => {
+        const reportsList = obj.properties
+          .map(item => forbiddenNodes.get(item))
+          .filter(reports => !!reports)
+        for (const reports of reportsList) {
+          for (const report of reports) {
+            context.report(report)
+          }
+        }
+      })
+    )
+  }
+}
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/lib/utils/index.js b/lib/utils/index.js
index 011ec657..e784f2ab 100644
--- a/lib/utils/index.js
+++ b/lib/utils/index.js
@@ -264,7 +264,7 @@ module.exports = {
     return componentsNode.value.properties
       .filter(p => p.type === 'Property')
       .map(node => {
-        const name = this.getStaticPropertyName(node)
+        const name = getStaticPropertyName(node)
         return name ? { node, name } : null
       })
       .filter(comp => comp != null)
@@ -402,42 +402,7 @@ module.exports = {
    * @param {Property|MethodDefinition|MemberExpression|Literal|TemplateLiteral|Identifier} node - The node to get.
    * @return {string|null} The property name if static. Otherwise, null.
    */
-  getStaticPropertyName (node) {
-    let prop
-    switch (node && node.type) {
-      case 'Property':
-      case 'MethodDefinition':
-        prop = node.key
-        break
-      case 'MemberExpression':
-        prop = node.property
-        break
-      case 'Literal':
-      case 'TemplateLiteral':
-      case 'Identifier':
-        prop = node
-        break
-      // no default
-    }
-
-    switch (prop && prop.type) {
-      case 'Literal':
-        return String(prop.value)
-      case 'TemplateLiteral':
-        if (prop.expressions.length === 0 && prop.quasis.length === 1) {
-          return prop.quasis[0].value.cooked
-        }
-        break
-      case 'Identifier':
-        if (!node.computed) {
-          return prop.name
-        }
-        break
-      // no default
-    }
-
-    return null
-  },
+  getStaticPropertyName,
 
   /**
    * Get all props by looking at all component's properties
@@ -464,8 +429,8 @@ module.exports = {
         .filter(prop => prop.type === 'Property')
         .map(prop => {
           return {
-            key: prop.key, value: this.unwrapTypes(prop.value), node: prop,
-            propName: this.getStaticPropertyName(prop)
+            key: prop.key, value: unwrapTypes(prop.value), node: prop,
+            propName: getStaticPropertyName(prop)
           }
         })
     } else {
@@ -548,28 +513,52 @@ module.exports = {
       const callee = node.callee
 
       if (callee.type === 'MemberExpression') {
-        const calleeObject = this.unwrapTypes(callee.object)
+        const calleeObject = unwrapTypes(callee.object)
+
+        if (calleeObject.type === 'Identifier') {
+          const propName = getStaticPropertyName(callee.property)
+          if (calleeObject.name === 'Vue') {
+            // for Vue.js 2.x
+            // Vue.component('xxx', {}) || Vue.mixin({}) || Vue.extend('xxx', {})
+            const isFullVueComponentForVue2 =
+              ['component', 'mixin', 'extend'].includes(propName) &&
+              isObjectArgument(node)
+
+            return isFullVueComponentForVue2
+          }
 
-        const isFullVueComponent = calleeObject.type === 'Identifier' &&
-          calleeObject.name === 'Vue' &&
-          callee.property.type === 'Identifier' &&
-          ['component', 'mixin', 'extend'].indexOf(callee.property.name) > -1 &&
-          node.arguments.length >= 1 &&
-          node.arguments.slice(-1)[0].type === 'ObjectExpression'
+          // for Vue.js 3.x
+          // app.component('xxx', {}) || app.mixin({})
+          const isFullVueComponent =
+            ['component', 'mixin'].includes(propName) &&
+            isObjectArgument(node)
 
-        return isFullVueComponent
+          return isFullVueComponent
+        }
       }
 
       if (callee.type === 'Identifier') {
-        const isDestructedVueComponent = callee.name === 'component' &&
-          node.arguments.length >= 1 &&
-          node.arguments.slice(-1)[0].type === 'ObjectExpression'
-
-        return isDestructedVueComponent
+        if (callee.name === 'component') {
+          // for Vue.js 2.x
+          // component('xxx', {})
+          const isDestructedVueComponent = isObjectArgument(node)
+          return isDestructedVueComponent
+        }
+        if (callee.name === 'createApp') {
+          // for Vue.js 3.x
+          // createApp({})
+          const isAppVueComponent = isObjectArgument(node)
+          return isAppVueComponent
+        }
       }
     }
 
     return false
+
+    function isObjectArgument (node) {
+      return node.arguments.length > 0 &&
+        unwrapTypes(node.arguments.slice(-1)[0]).type === 'ObjectExpression'
+    }
   },
 
   /**
@@ -584,7 +573,7 @@ module.exports = {
       callee.type === 'Identifier' &&
       callee.name === 'Vue' &&
       node.arguments.length &&
-      node.arguments[0].type === 'ObjectExpression'
+      unwrapTypes(node.arguments[0]).type === 'ObjectExpression'
   },
 
   /**
@@ -647,7 +636,7 @@ module.exports = {
       'CallExpression:exit' (node) {
         // Vue.component('xxx', {}) || component('xxx', {})
         if (!_this.isVueComponent(node) || isDuplicateNode(node.arguments.slice(-1)[0])) return
-        cb(node.arguments.slice(-1)[0])
+        cb(unwrapTypes(node.arguments.slice(-1)[0]))
       }
     }
   },
@@ -664,10 +653,10 @@ module.exports = {
         const callee = callExpr.callee
 
         if (callee.type === 'MemberExpression') {
-          const calleeObject = this.unwrapTypes(callee.object)
+          const calleeObject = unwrapTypes(callee.object)
 
           if (calleeObject.type === 'Identifier' &&
-            calleeObject.name === 'Vue' &&
+            // calleeObject.name === 'Vue' && // Any names can be used in Vue.js 3.x. e.g. app.component()
             callee.property === node &&
             callExpr.arguments.length >= 1) {
             cb(callExpr)
@@ -682,9 +671,9 @@ module.exports = {
    * @param {Set} groups Name of parent group
    */
   * iterateProperties (node, groups) {
-    const nodes = node.properties.filter(p => p.type === 'Property' && groups.has(this.getStaticPropertyName(p.key)))
+    const nodes = node.properties.filter(p => p.type === 'Property' && groups.has(getStaticPropertyName(p.key)))
     for (const item of nodes) {
-      const name = this.getStaticPropertyName(item.key)
+      const name = getStaticPropertyName(item.key)
       if (!name) continue
 
       if (item.value.type === 'ArrayExpression') {
@@ -693,6 +682,8 @@ module.exports = {
         yield * this.iterateObjectExpression(item.value, name)
       } else if (item.value.type === 'FunctionExpression') {
         yield * this.iterateFunctionExpression(item.value, name)
+      } else if (item.value.type === 'ArrowFunctionExpression') {
+        yield * this.iterateArrowFunctionExpression(item.value, name)
       }
     }
   },
@@ -705,7 +696,7 @@ module.exports = {
   * iterateArrayExpression (node, groupName) {
     assert(node.type === 'ArrayExpression')
     for (const item of node.elements) {
-      const name = this.getStaticPropertyName(item)
+      const name = getStaticPropertyName(item)
       if (name) {
         const obj = { name, groupName, node: item }
         yield obj
@@ -721,7 +712,7 @@ module.exports = {
   * iterateObjectExpression (node, groupName) {
     assert(node.type === 'ObjectExpression')
     for (const item of node.properties) {
-      const name = this.getStaticPropertyName(item)
+      const name = getStaticPropertyName(item)
       if (name) {
         const obj = { name, groupName, node: item.key }
         yield obj
@@ -745,6 +736,24 @@ module.exports = {
     }
   },
 
+  /**
+   * Return generator with all elements inside ArrowFunctionExpression
+   * @param {ASTNode} node Node to check
+   * @param {string} groupName Name of parent group
+   */
+  * iterateArrowFunctionExpression (node, groupName) {
+    assert(node.type === 'ArrowFunctionExpression')
+    if (node.body.type === 'BlockStatement') {
+      for (const item of node.body.body) {
+        if (item.type === 'ReturnStatement' && item.argument && item.argument.type === 'ObjectExpression') {
+          yield * this.iterateObjectExpression(item.argument, groupName)
+        }
+      }
+    } else if (node.body.type === 'ObjectExpression') {
+      yield * this.iterateObjectExpression(node.body, groupName)
+    }
+  },
+
   /**
    * Find all functions which do not always return values
    * @param {boolean} treatUndefinedAsUnspecified
@@ -865,7 +874,56 @@ module.exports = {
    * @param {T} node
    * @return {T}
    */
-  unwrapTypes (node) {
-    return node.type === 'TSAsExpression' ? node.expression : node
+  unwrapTypes
+}
+/**
+* Unwrap typescript types like "X as F"
+* @template T
+* @param {T} node
+* @return {T}
+*/
+function unwrapTypes (node) {
+  return node.type === 'TSAsExpression' ? node.expression : node
+}
+
+/**
+ * Gets the property name of a given node.
+ * @param {Property|MethodDefinition|MemberExpression|Literal|TemplateLiteral|Identifier} node - The node to get.
+ * @return {string|null} The property name if static. Otherwise, null.
+ */
+function getStaticPropertyName (node) {
+  let prop
+  switch (node && node.type) {
+    case 'Property':
+    case 'MethodDefinition':
+      prop = node.key
+      break
+    case 'MemberExpression':
+      prop = node.property
+      break
+    case 'Literal':
+    case 'TemplateLiteral':
+    case 'Identifier':
+      prop = node
+      break
+      // no default
+  }
+
+  switch (prop && prop.type) {
+    case 'Literal':
+      return String(prop.value)
+    case 'TemplateLiteral':
+      if (prop.expressions.length === 0 && prop.quasis.length === 1) {
+        return prop.quasis[0].value.cooked
+      }
+      break
+    case 'Identifier':
+      if (!node.computed) {
+        return prop.name
+      }
+      break
+      // no default
   }
+
+  return null
 }
diff --git a/package.json b/package.json
index 1744009a..57a44ce2 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": {
@@ -8,6 +8,7 @@
     "test:base": "mocha \"tests/lib/**/*.js\" --reporter dot",
     "test": "nyc npm run test:base -- \"tests/integrations/*.js\" --timeout 60000",
     "debug": "mocha --inspect-brk \"tests/lib/**/*.js\" --reporter dot --timeout 60000",
+    "cover:report": "nyc report --reporter=html",
     "lint": "eslint . --rulesdir eslint-internal-rules",
     "pretest": "npm run lint",
     "preversion": "npm test && npm run update && git add .",
@@ -47,9 +48,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/component-definition-name-casing.js b/tests/lib/rules/component-definition-name-casing.js
index 98cb4367..d14eedb7 100644
--- a/tests/lib/rules/component-definition-name-casing.js
+++ b/tests/lib/rules/component-definition-name-casing.js
@@ -116,6 +116,12 @@ ruleTester.run('component-definition-name-casing', rule, {
       options: ['kebab-case'],
       parserOptions
     },
+    {
+      filename: 'test.vue',
+      code: `app.component('FooBar', component)`,
+      options: ['PascalCase'],
+      parserOptions
+    },
     {
       filename: 'test.vue',
       code: `Vue.mixin({})`,
@@ -137,6 +143,12 @@ ruleTester.run('component-definition-name-casing', rule, {
       options: ['kebab-case'],
       parserOptions
     },
+    {
+      filename: 'test.vue',
+      code: `app.component(\`fooBar\${foo}\`, component)`,
+      options: ['kebab-case'],
+      parserOptions
+    },
     // https://github.com/vuejs/eslint-plugin-vue/issues/1018
     {
       filename: 'test.js',
@@ -292,6 +304,17 @@ ruleTester.run('component-definition-name-casing', rule, {
         line: 1
       }]
     },
+    {
+      filename: 'test.vue',
+      code: `app.component('foo-bar', component)`,
+      output: `app.component('FooBar', component)`,
+      parserOptions,
+      errors: [{
+        message: 'Property name "foo-bar" is not PascalCase.',
+        type: 'Literal',
+        line: 1
+      }]
+    },
     {
       filename: 'test.vue',
       code: `(Vue as VueConstructor<Vue>).component('foo-bar', component)`,
@@ -315,6 +338,17 @@ ruleTester.run('component-definition-name-casing', rule, {
         line: 1
       }]
     },
+    {
+      filename: 'test.vue',
+      code: `app.component('foo-bar', {})`,
+      output: `app.component('FooBar', {})`,
+      parserOptions,
+      errors: [{
+        message: 'Property name "foo-bar" is not PascalCase.',
+        type: 'Literal',
+        line: 1
+      }]
+    },
     {
       filename: 'test.js',
       code: `Vue.component('foo_bar', {})`,
diff --git a/tests/lib/rules/match-component-file-name.js b/tests/lib/rules/match-component-file-name.js
index d8a1b141..a97c5941 100644
--- a/tests/lib/rules/match-component-file-name.js
+++ b/tests/lib/rules/match-component-file-name.js
@@ -429,6 +429,16 @@ ruleTester.run('match-component-file-name', rule, {
       options: [{ extensions: ['js'] }],
       parserOptions
     },
+    {
+      filename: 'MyComponent.js',
+      code: `
+        app.component('MyComponent', {
+          template: '<div />'
+        })
+      `,
+      options: [{ extensions: ['js'] }],
+      parserOptions
+    },
     {
       filename: 'MyComponent.js',
       code: `
@@ -701,6 +711,19 @@ ruleTester.run('match-component-file-name', rule, {
         message: 'Component name `MComponent` should match file name `MyComponent`.'
       }]
     },
+    {
+      filename: 'MyComponent.js',
+      code: `
+        app.component(\`MComponent\`, {
+          template: '<div />'
+        })
+      `,
+      options: [{ extensions: ['js'] }],
+      parserOptions,
+      errors: [{
+        message: 'Component name `MComponent` should match file name `MyComponent`.'
+      }]
+    },
 
     // casing
     {
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-dupe-keys.js b/tests/lib/rules/no-dupe-keys.js
index 1abb6764..f1a35568 100644
--- a/tests/lib/rules/no-dupe-keys.js
+++ b/tests/lib/rules/no-dupe-keys.js
@@ -45,6 +45,58 @@ ruleTester.run('no-dupe-keys', rule, {
       parserOptions: { ecmaVersion: 6, sourceType: 'module' }
     },
 
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          props: ['foo'],
+          computed: {
+            bar () {
+            }
+          },
+          data: () => {
+            return {
+              dat: null
+            }
+          },
+          data: () => {
+            return
+          },
+          methods: {
+            _foo () {},
+            test () {
+            }
+          }
+        }
+      `,
+      parserOptions: { ecmaVersion: 6, sourceType: 'module' }
+    },
+
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          props: ['foo'],
+          computed: {
+            bar () {
+            }
+          },
+          data: () => ({
+            dat: null
+          }),
+          data: () => {
+            return
+          },
+          methods: {
+            _foo () {},
+            test () {
+            }
+          }
+        }
+      `,
+      parserOptions: { ecmaVersion: 6, sourceType: 'module' }
+    },
+
     {
       filename: 'test.vue',
       code: `
@@ -82,6 +134,78 @@ ruleTester.run('no-dupe-keys', rule, {
       parserOptions: { ecmaVersion: 2018, sourceType: 'module' }
     },
 
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          ...foo(),
+          props: {
+            ...foo(),
+            foo: String
+          },
+          computed: {
+            ...mapGetters({
+              test: 'getTest'
+            }),
+            bar: {
+              get () {
+              }
+            }
+          },
+          data: {
+            ...foo(),
+            dat: null
+          },
+          methods: {
+            ...foo(),
+            test () {
+            }
+          },
+          data: () => {
+            return {
+              ...dat
+            }
+          },
+        }
+      `,
+      parserOptions: { ecmaVersion: 2018, sourceType: 'module' }
+    },
+
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          ...foo(),
+          props: {
+            ...foo(),
+            foo: String
+          },
+          computed: {
+            ...mapGetters({
+              test: 'getTest'
+            }),
+            bar: {
+              get () {
+              }
+            }
+          },
+          data: {
+            ...foo(),
+            dat: null
+          },
+          methods: {
+            ...foo(),
+            test () {
+            }
+          },
+          data: () => ({
+            ...dat
+          }),
+        }
+      `,
+      parserOptions: { ecmaVersion: 2018, sourceType: 'module' }
+    },
+
     {
       filename: 'test.js',
       code: `
@@ -136,6 +260,68 @@ ruleTester.run('no-dupe-keys', rule, {
         line: 14
       }]
     },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          props: ['foo'],
+          computed: {
+            foo () {
+            }
+          },
+          data: () => {
+            return {
+              foo: null
+            }
+          },
+          methods: {
+            foo () {
+            }
+          }
+        }
+      `,
+      parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+      errors: [{
+        message: 'Duplicated key \'foo\'.',
+        line: 5
+      }, {
+        message: 'Duplicated key \'foo\'.',
+        line: 10
+      }, {
+        message: 'Duplicated key \'foo\'.',
+        line: 14
+      }]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          props: ['foo'],
+          computed: {
+            foo () {
+            }
+          },
+          data: () => ({
+            foo: null
+          }),
+          methods: {
+            foo () {
+            }
+          }
+        }
+      `,
+      parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+      errors: [{
+        message: 'Duplicated key \'foo\'.',
+        line: 5
+      }, {
+        message: 'Duplicated key \'foo\'.',
+        line: 9
+      }, {
+        message: 'Duplicated key \'foo\'.',
+        line: 12
+      }]
+    },
     {
       filename: 'test.vue',
       code: `
diff --git a/tests/lib/rules/no-lifecycle-after-await.js b/tests/lib/rules/no-lifecycle-after-await.js
new file mode 100644
index 00000000..eaa99f84
--- /dev/null
+++ b/tests/lib/rules/no-lifecycle-after-await.js
@@ -0,0 +1,180 @@
+/**
+ * @author Yosuke Ota
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/no-lifecycle-after-await')
+
+const tester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2019, sourceType: 'module' }
+})
+
+tester.run('no-lifecycle-after-await', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      import {onMounted} from 'vue'
+      export default {
+        async setup() {
+          onMounted(() => { /* ... */ }) // ok
+
+          await doSomething()
+        }
+      }
+      </script>
+      `
+    }, {
+      filename: 'test.vue',
+      code: `
+      <script>
+      import {onMounted} from 'vue'
+      export default {
+        async setup() {
+          onMounted(() => { /* ... */ })
+        }
+      }
+      </script>
+      `
+    }, {
+      filename: 'test.vue',
+      code: `
+      <script>
+      import {onBeforeMount, onBeforeUnmount, onBeforeUpdate, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onUnmounted, onUpdated, onActivated, onDeactivated} from 'vue'
+      export default {
+        async setup() {
+          onBeforeMount(() => { /* ... */ })
+          onBeforeUnmount(() => { /* ... */ })
+          onBeforeUpdate(() => { /* ... */ })
+          onErrorCaptured(() => { /* ... */ })
+          onMounted(() => { /* ... */ })
+          onRenderTracked(() => { /* ... */ })
+          onRenderTriggered(() => { /* ... */ })
+          onUnmounted(() => { /* ... */ })
+          onUpdated(() => { /* ... */ })
+          onActivated(() => { /* ... */ })
+          onDeactivated(() => { /* ... */ })
+
+          await doSomething()
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      import {onMounted} from 'vue'
+      export default {
+        async _setup() {
+          await doSomething()
+
+          onMounted(() => { /* ... */ }) // error
+        }
+      }
+      </script>
+      `
+    }
+  ],
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      import {onMounted} from 'vue'
+      export default {
+        async setup() {
+          await doSomething()
+
+          onMounted(() => { /* ... */ }) // error
+        }
+      }
+      </script>
+      `,
+      errors: [
+        {
+          message: 'The lifecycle hooks after `await` expression are forbidden.',
+          line: 8,
+          column: 11,
+          endLine: 8,
+          endColumn: 41
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      import {onBeforeMount, onBeforeUnmount, onBeforeUpdate, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onUnmounted, onUpdated, onActivated, onDeactivated} from 'vue'
+      export default {
+        async setup() {
+          await doSomething()
+
+          onBeforeMount(() => { /* ... */ })
+          onBeforeUnmount(() => { /* ... */ })
+          onBeforeUpdate(() => { /* ... */ })
+          onErrorCaptured(() => { /* ... */ })
+          onMounted(() => { /* ... */ })
+          onRenderTracked(() => { /* ... */ })
+          onRenderTriggered(() => { /* ... */ })
+          onUnmounted(() => { /* ... */ })
+          onUpdated(() => { /* ... */ })
+          onActivated(() => { /* ... */ })
+          onDeactivated(() => { /* ... */ })
+        }
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'forbidden',
+          line: 8
+        },
+        {
+          messageId: 'forbidden',
+          line: 9
+        },
+        {
+          messageId: 'forbidden',
+          line: 10
+        },
+        {
+          messageId: 'forbidden',
+          line: 11
+        },
+        {
+          messageId: 'forbidden',
+          line: 12
+        },
+        {
+          messageId: 'forbidden',
+          line: 13
+        },
+        {
+          messageId: 'forbidden',
+          line: 14
+        },
+        {
+          messageId: 'forbidden',
+          line: 15
+        },
+        {
+          messageId: 'forbidden',
+          line: 16
+        },
+        {
+          messageId: 'forbidden',
+          line: 17
+        },
+        {
+          messageId: 'forbidden',
+          line: 18
+        }
+      ]
+    }
+  ]
+})
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-reserved-component-names.js b/tests/lib/rules/no-reserved-component-names.js
index 43a34f15..0585b5c9 100644
--- a/tests/lib/rules/no-reserved-component-names.js
+++ b/tests/lib/rules/no-reserved-component-names.js
@@ -274,6 +274,11 @@ ruleTester.run('no-reserved-component-names', rule, {
       code: `Vue.component('FooBar', {})`,
       parserOptions
     },
+    {
+      filename: 'test.vue',
+      code: `app.component('FooBar', {})`,
+      parserOptions
+    },
     {
       filename: 'test.js',
       code: `
@@ -349,6 +354,18 @@ ruleTester.run('no-reserved-component-names', rule, {
         }]
       }
     }),
+    ...invalidElements.map(name => {
+      return {
+        filename: 'test.vue',
+        code: `app.component('${name}', component)`,
+        parserOptions,
+        errors: [{
+          message: `Name "${name}" is reserved.`,
+          type: 'Literal',
+          line: 1
+        }]
+      }
+    }),
     ...invalidElements.map(name => {
       return {
         filename: 'test.vue',
@@ -361,6 +378,18 @@ ruleTester.run('no-reserved-component-names', rule, {
         }]
       }
     }),
+    ...invalidElements.map(name => {
+      return {
+        filename: 'test.vue',
+        code: `app.component(\`${name}\`, {})`,
+        parserOptions,
+        errors: [{
+          message: `Name "${name}" is reserved.`,
+          type: 'TemplateLiteral',
+          line: 1
+        }]
+      }
+    }),
     ...invalidElements.map(name => {
       return {
         filename: 'test.vue',
diff --git a/tests/lib/rules/no-reserved-keys.js b/tests/lib/rules/no-reserved-keys.js
index 08c7f244..05836bc4 100644
--- a/tests/lib/rules/no-reserved-keys.js
+++ b/tests/lib/rules/no-reserved-keys.js
@@ -45,6 +45,50 @@ ruleTester.run('no-reserved-keys', rule, {
         }
       `,
       parserOptions
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          props: ['foo'],
+          computed: {
+            bar () {
+            }
+          },
+          data: () => {
+            return {
+              dat: null
+            }
+          },
+          methods: {
+            _foo () {},
+            test () {
+            }
+          }
+        }
+      `,
+      parserOptions
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          props: ['foo'],
+          computed: {
+            bar () {
+            }
+          },
+          data: () => ({
+            dat: null
+          }),
+          methods: {
+            _foo () {},
+            test () {
+            }
+          }
+        }
+      `,
+      parserOptions
     }
   ],
 
@@ -79,6 +123,38 @@ ruleTester.run('no-reserved-keys', rule, {
         line: 4
       }]
     },
+    {
+      filename: 'test.js',
+      code: `
+        new Vue({
+          data: () => {
+            return {
+              _foo: String
+            }
+          }
+        })
+      `,
+      parserOptions: { ecmaVersion: 6 },
+      errors: [{
+        message: "Keys starting with with '_' are reserved in '_foo' group.",
+        line: 5
+      }]
+    },
+    {
+      filename: 'test.js',
+      code: `
+        new Vue({
+          data: () => ({
+            _foo: String
+          })
+        })
+      `,
+      parserOptions: { ecmaVersion: 6 },
+      errors: [{
+        message: "Keys starting with with '_' are reserved in '_foo' group.",
+        line: 4
+      }]
+    },
     {
       filename: 'test.js',
       code: `
diff --git a/tests/lib/rules/no-setup-props-destructure.js b/tests/lib/rules/no-setup-props-destructure.js
new file mode 100644
index 00000000..2f3bccc5
--- /dev/null
+++ b/tests/lib/rules/no-setup-props-destructure.js
@@ -0,0 +1,335 @@
+/**
+ * @author Yosuke Ota
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/no-setup-props-destructure')
+
+const tester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2019, sourceType: 'module' }
+})
+
+tester.run('no-setup-props-destructure', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(props) {
+          watch(() => {
+            console.log(props.count) // ok
+          })
+
+          return () => {
+            return h('div', props.count) // ok
+          }
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(props) {
+          watch(() => {
+            const { count } = props // ok
+            console.log(count)
+          })
+
+          return () => {
+            const { count } = props // ok
+            return h('div', count)
+          }
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        _setup({count}) {
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup() {
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(...args) {
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(props) {
+          watch(() => {
+            ({ count } = props)
+          })
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      var noVue = {
+        setup(props) {
+          const { count } = props
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(props) {
+          const {x} = noProps
+          ({y} = noProps)
+          const z = noProps.z
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(props) {
+          ({props} = x)
+        }
+      }
+      </script>
+      `
+    }
+  ],
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup({ count }) { // error
+          watch(() => {
+            console.log(count) // not going to detect changes
+          })
+
+          return () => {
+            return h('div', count) // not going to update
+          }
+        }
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'destructuring',
+          line: 4,
+          column: 15,
+          endLine: 4,
+          endColumn: 24
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(props) {
+          const { count } = props // error
+        }
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'getProperty',
+          line: 5,
+          column: 17,
+          endLine: 5,
+          endColumn: 26
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup: (props) => {
+          const { count } = props
+        }
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'getProperty',
+          line: 5
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(props) {
+          ({ count } = props)
+        }
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'getProperty',
+          line: 5
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(p) {
+          const { count } = p
+        }
+      }
+
+      Vue.component('component', {
+        setup(p) {
+          const { count } = p
+        }
+      })
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'getProperty',
+          line: 5
+        },
+        {
+          messageId: 'getProperty',
+          line: 11
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(p) {
+          const { count } = p
+        },
+        _setup(p) {
+          const { count } = p
+        },
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'getProperty',
+          line: 5
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(p) {
+          const { x } = p
+          const { y } = p
+        }
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'getProperty',
+          line: 5
+        },
+        {
+          messageId: 'getProperty',
+          line: 6
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(p) {
+          const foo = p.bar
+        }
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'getProperty',
+          line: 5
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(p) {
+          let foo
+          foo = p.bar
+        }
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'getProperty',
+          line: 6
+        }
+      ]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-shared-component-data.js b/tests/lib/rules/no-shared-component-data.js
index ce0e0062..ade9e9ef 100644
--- a/tests/lib/rules/no-shared-component-data.js
+++ b/tests/lib/rules/no-shared-component-data.js
@@ -135,6 +135,30 @@ ruleTester.run('no-shared-component-data', rule, {
 return {
             foo: 'bar'
           };
+}
+        })
+      `,
+      parserOptions,
+      errors: [{
+        message: '`data` property in component must be a function.',
+        line: 3
+      }]
+    },
+    {
+      filename: 'test.js',
+      code: `
+        app.component('some-comp', {
+          data: {
+            foo: 'bar'
+          }
+        })
+      `,
+      output: `
+        app.component('some-comp', {
+          data: function() {
+return {
+            foo: 'bar'
+          };
 }
         })
       `,
diff --git a/tests/lib/rules/no-side-effects-in-computed-properties.js b/tests/lib/rules/no-side-effects-in-computed-properties.js
index df186b3a..fa14ef24 100644
--- a/tests/lib/rules/no-side-effects-in-computed-properties.js
+++ b/tests/lib/rules/no-side-effects-in-computed-properties.js
@@ -296,6 +296,25 @@ ruleTester.run('no-side-effects-in-computed-properties', rule, {
         message: 'Unexpected side effect in "test1" computed property.'
       }],
       parser: require.resolve('@typescript-eslint/parser')
+    },
+
+    {
+      code: `app.component('test', {
+        computed: {
+          test1() {
+            this.firstName = 'lorem'
+            asd.qwe.zxc = 'lorem'
+            return this.firstName + ' ' + this.lastName
+          },
+        }
+      })`,
+      parserOptions,
+      errors: [
+        {
+          line: 4,
+          message: 'Unexpected side effect in "test1" computed property.'
+        }
+      ]
     }
   ]
 })
diff --git a/tests/lib/rules/no-template-shadow.js b/tests/lib/rules/no-template-shadow.js
index 68fefce0..08e0e03b 100644
--- a/tests/lib/rules/no-template-shadow.js
+++ b/tests/lib/rules/no-template-shadow.js
@@ -70,6 +70,52 @@ ruleTester.run('no-template-shadow', rule, {
           }
         }
       </script>`
+    },
+    {
+      filename: 'test.vue',
+      code: `<template>
+        <div v-for="i in b" />
+        <div v-for="b in c" />
+        <div v-for="d in f" />
+      </template>
+      <script>
+        export default {
+          ...a,
+          data: () => {
+            return {
+              ...b,
+              c: [1, 2, 3]
+            }
+          },
+          computed: {
+            ...d,
+            e,
+            ['f']: [1, 2],
+          }
+        }
+      </script>`
+    },
+    {
+      filename: 'test.vue',
+      code: `<template>
+        <div v-for="i in b" />
+        <div v-for="b in c" />
+        <div v-for="d in f" />
+      </template>
+      <script>
+        export default {
+          ...a,
+          data: () => ({
+            ...b,
+            c: [1, 2, 3]
+          }),
+          computed: {
+            ...d,
+            e,
+            ['f']: [1, 2],
+          }
+        }
+      </script>`
     }
   ],
 
@@ -208,6 +254,76 @@ ruleTester.run('no-template-shadow', rule, {
         type: 'Identifier',
         line: 7
       }]
+    },
+    {
+      filename: 'test.vue',
+      code: `<template>
+        <div v-for="i in c" />
+        <div v-for="a in c" />
+        <div v-for="b in c" />
+        <div v-for="d in c" />
+        <div v-for="e in f" />
+        <div v-for="f in c" />
+      </template>
+      <script>
+        export default {
+          ...a,
+          data: () => {
+            return {
+              ...b,
+              c: [1, 2, 3]
+            }
+          },
+          computed: {
+            ...d,
+            e,
+            ['f']: [1, 2],
+          }
+        }
+      </script>`,
+      errors: [{
+        message: "Variable 'e' is already declared in the upper scope.",
+        type: 'Identifier',
+        line: 6
+      }, {
+        message: "Variable 'f' is already declared in the upper scope.",
+        type: 'Identifier',
+        line: 7
+      }]
+    },
+    {
+      filename: 'test.vue',
+      code: `<template>
+        <div v-for="i in c" />
+        <div v-for="a in c" />
+        <div v-for="b in c" />
+        <div v-for="d in c" />
+        <div v-for="e in f" />
+        <div v-for="f in c" />
+      </template>
+      <script>
+        export default {
+          ...a,
+          data: () => ({
+              ...b,
+              c: [1, 2, 3]
+          }),
+          computed: {
+            ...d,
+            e,
+            ['f']: [1, 2],
+          }
+        }
+      </script>`,
+      errors: [{
+        message: "Variable 'e' is already declared in the upper scope.",
+        type: 'Identifier',
+        line: 6
+      }, {
+        message: "Variable 'f' is already declared in the upper scope.",
+        type: 'Identifier',
+        line: 7
+      }]
     }
   ]
 })
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/order-in-components.js b/tests/lib/rules/order-in-components.js
index b022508f..f94b4ec6 100644
--- a/tests/lib/rules/order-in-components.js
+++ b/tests/lib/rules/order-in-components.js
@@ -243,6 +243,38 @@ ruleTester.run('order-in-components', rule, {
         line: 9
       }]
     },
+    {
+      filename: 'test.js',
+      code: `
+        app.component('smart-list', {
+          name: 'app',
+          data () {
+            return {
+              msg: 'Welcome to Your Vue.js App'
+            }
+          },
+          components: {},
+          template: '<div></div>'
+        })
+      `,
+      parserOptions: { ecmaVersion: 6 },
+      output: `
+        app.component('smart-list', {
+          name: 'app',
+          components: {},
+          data () {
+            return {
+              msg: 'Welcome to Your Vue.js App'
+            }
+          },
+          template: '<div></div>'
+        })
+      `,
+      errors: [{
+        message: 'The "components" property should be above the "data" property on line 4.',
+        line: 9
+      }]
+    },
     {
       filename: 'test.js',
       code: `
diff --git a/tests/lib/rules/require-render-return.js b/tests/lib/rules/require-render-return.js
index e7fde4e6..580a6b89 100644
--- a/tests/lib/rules/require-render-return.js
+++ b/tests/lib/rules/require-render-return.js
@@ -86,7 +86,7 @@ ruleTester.run('require-render-return', rule, {
         render() {
           if (a) {
             if (b) {
-              
+
             }
             if (c) {
               return true
@@ -180,6 +180,21 @@ ruleTester.run('require-render-return', rule, {
         line: 2
       }]
     },
+    {
+      code: `app.component('test', {
+        render: function () {
+          if (a) {
+            return
+          }
+        }
+      })`,
+      parserOptions,
+      errors: [{
+        message: 'Expected to return a value in render function.',
+        type: 'Identifier',
+        line: 2
+      }]
+    },
     {
       code: `Vue.component('test2', {
         render: function () {
@@ -199,7 +214,7 @@ ruleTester.run('require-render-return', rule, {
       code: `Vue.component('test2', {
         render: function () {
           if (a) {
-            
+
           } else {
             return h('div', 'hello')
           }
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: [
diff --git a/tests/lib/utils/vue-component.js b/tests/lib/utils/vue-component.js
index db5d9733..69d663e9 100644
--- a/tests/lib/utils/vue-component.js
+++ b/tests/lib/utils/vue-component.js
@@ -108,6 +108,12 @@ function validTests (ext) {
       code: `export default Foo.extend({})`,
       parser: require.resolve('@typescript-eslint/parser'),
       parserOptions
+    },
+    {
+      filename: `test.${ext}`,
+      code: `export default Foo.extend({} as ComponentOptions)`,
+      parser: require.resolve('@typescript-eslint/parser'),
+      parserOptions
     }
   ]
 }
@@ -144,6 +150,18 @@ function invalidTests (ext) {
       parserOptions,
       errors: [makeError(1)]
     },
+    {
+      filename: `test.${ext}`,
+      code: `app.component('name', {})`,
+      parserOptions,
+      errors: [makeError(1)]
+    },
+    {
+      filename: `test.${ext}`,
+      code: `app.mixin({})`,
+      parserOptions,
+      errors: [makeError(1)]
+    },
     {
       filename: `test.${ext}`,
       code: `export default (Vue as VueConstructor<Vue>).extend({})`,
@@ -158,6 +176,19 @@ function invalidTests (ext) {
       parserOptions,
       errors: [makeError(1)]
     },
+    {
+      filename: `test.${ext}`,
+      code: `export default Vue.extend({} as ComponentOptions)`,
+      parser: require.resolve('@typescript-eslint/parser'),
+      parserOptions,
+      errors: [makeError(1)]
+    },
+    {
+      filename: `test.${ext}`,
+      code: `createApp({})`,
+      parserOptions,
+      errors: [makeError(1)]
+    },
     {
       filename: `test.${ext}`,
       code: `