Repository: jsx-eslint/eslint-plugin-react (JavaScript)

Issue:
prop-types doesn't check nextProps of componentWillReceiveProps
* **ESLint Version: 4.15.0**
* **Node Version: 8.7.0**
* **npm Version: 5.5.1**

**parser babel-eslint**

<details>
<summary>Configuration</summary>

<!-- Paste your configuration below: -->

```

/* global module, __dirname */
/* eslint-disable import/no-commonjs, import/no-nodejs-modules */

const path = require('path');

module.exports = {
  globals: {
    require: false,
    process: false,
    Promise: false,
    fbq: false,
    dataLayer: false,
    ga: false,
    mixpanel: false,
    yaCounter: false,
    Raven: false,

    // browser env
    localStorage: false,
    document: false,
    console: false,
    window: false,
    setTimeout: false,
    clearTimeout: false,
    setInterval: false,
    clearInterval: false,
    navigator: false,
    FormData: false,
  },
  parser: 'babel-eslint',
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    sourceType: 'module',
  },
  plugins: [
    'react',
    'import',
    'class-property',
    'brackets',
  ],
  settings: {
    'import/resolver': {
      webpack: {
        config: path.resolve(__dirname, 'webpack-config/dev.js'),
      },
    },
    propWrapperFunctions: ['forbidExtraProps'],
  },
  rules: {
    'quote-props': ['error', 'as-needed'],
    'no-use-before-define': ['error', {functions: false, classes: false}],
    'space-before-blocks': ['error'],
    'no-redeclare': ['error'],
    'no-case-declarations': ['error'],
    'no-const-assign': ['error'],
    'key-spacing': ['error'],
    'keyword-spacing': ['error'],
    'no-dupe-class-members': ['error'],
    'comma-spacing': ['error'],
    'no-var': ['error'],
    'prefer-const': ['error'],
    'space-infix-ops': ['error'],
    'eol-last': ['error', 'always'],
    indent: ['error', 2, {SwitchCase: 1}],
    'no-mixed-spaces-and-tabs': ['error'],
    'linebreak-style': ['error', 'unix'],
    'default-case': ['error'],
    'no-console': ['error'],
    'no-alert': ['error'],
    'no-debugger': ['error'],
    'no-global-assign': ['error'],
    'no-dupe-args': ['error'],
    'no-dupe-keys': ['error'],
    'no-duplicate-case': ['error'],
    'no-empty': ['error', {allowEmptyCatch: true}],
    'no-extra-boolean-cast': ['error'],
    'no-extra-parens': ['error', 'functions'],
    'no-extra-semi': ['error'],
    'no-irregular-whitespace': ['error'],
    'no-multiple-empty-lines': ['error', {max: 2, maxEOF: 1}],
    'no-sparse-arrays': ['error'],
    'no-undef': ['error'],
    'no-unexpected-multiline': ['error'],
    'no-unused-expressions': ['error', {allowTaggedTemplates: true, allowTernary: true}],
    'no-unreachable': ['error'],
    semi: ['error', 'always', {omitLastInOneLineBlock: true}],
    'object-curly-spacing': ['error', 'never'],
    'arrow-parens': ['error', 'as-needed'],
    quotes: ['error', 'single'],
    'jsx-quotes': ['error'],
    curly: ['error'],
    'brace-style': ['error', 'stroustrup'],
    'no-multi-spaces': ['error'],
    'no-duplicate-imports': ['error'],
    'no-restricted-syntax': [
      'error',
      {
        selector: 'AssignmentExpression[left.object.name=\'window\']',
        message: 'Don\'t write to window!',
      },
    ],
    'comma-dangle': ['error', {
      arrays: 'always-multiline',
      objects: 'always-multiline',
      imports: 'always-multiline',
      exports: 'always-multiline',
      functions: 'always-multiline',
    }],

    'react/no-unused-state': 'error',
    'react/no-unused-prop-types': 'error',
    'react/prop-types': 'error',

    'react/jsx-uses-react': 'error',
    'react/jsx-uses-vars': 'error',
    'react/jsx-tag-spacing': ['error', {beforeSelfClosing: 'never'}],
    'react/jsx-no-target-blank': 'error',
    'react/jsx-no-undef': 'error',
    'react/jsx-no-duplicate-props': 'error',
    'react/jsx-key': 'error',
    'react/jsx-indent': ['error', 2],
    'react/jsx-indent-props': ['error', 2],

    'import/no-unresolved': 'error',
    'import/named': 'error',
    'import/default': 'error',
    'import/no-commonjs': 'error',
    'import/no-nodejs-modules': 'error',

    'class-property/class-property-semicolon': ['error', 'always'],

    'brackets/array-bracket-newline': 'error',
    'brackets/call-parens-newline': 'error',
    'brackets/conditional-parens-newline': 'error',
    'brackets/func-parens-newline': 'error',
    'brackets/object-curly-newline': 'error',

  },
};


```

</details>

**What did you do? Please include the actual source code causing the issue, as well as the command that you used to run ESLint.**

<!-- Paste the source code below: -->
```
import {Component} from 'react';

export default class MyComponent extends Component {
  componentWillReceiveProps({x}) { // no error about x (but should be)
    const {y} = this.props; // 'y' is missing in props validation (react/prop-types)
  }
}

```

![image](https://user-images.githubusercontent.com/9456433/34712592-216de864-f545-11e7-93d8-fa2e81633592.png)


Some duplication of https://github.com/yannickcr/eslint-plugin-react/issues/814


  
  


Discussion:
Just wanted to clarify something. Should this also apply to the `shouldComponentUpdate` function?
Theoretically yes :-) please file a new issue if it’s not
 #1832 a related issue I'm guessing 
I used like:   componentWillReceiveProps(nextProps: Props)
I am getting syntax error at  :
what is the syntax now?
That’s not JavaScript, that’s typescript or flow.



Patch:
diff --git a/lib/rules/prop-types.js b/lib/rules/prop-types.js
index e6226d80dc..a17d7cd0e9 100644
--- a/lib/rules/prop-types.js
+++ b/lib/rules/prop-types.js
@@ -606,6 +606,15 @@ module.exports = {
           type = 'destructuring';
           properties = node.params[0].properties;
           break;
+        case 'MethodDefinition':
+          const destructuring = node.value && node.value.params && node.value.params[0] && node.value.params[0].type === 'ObjectPattern';
+          if (destructuring) {
+            type = 'destructuring';
+            properties = node.value.params[0].properties;
+            break;
+          } else {
+            return;
+          }
         case 'VariableDeclarator':
           for (let i = 0, j = node.id.properties.length; i < j; i++) {
             // let {props: {firstname}} = this
@@ -1018,6 +1027,11 @@ module.exports = {
       },
 
       MethodDefinition: function(node) {
+        const destructuring = node.value && node.value.params && node.value.params[0] && node.value.params[0].type === 'ObjectPattern';
+        if (node.key.name === 'componentWillReceiveProps' && destructuring) {
+          markPropTypesAsUsed(node);
+        }
+
         if (!node.static || node.kind !== 'get' || !propsUtil.isPropTypesDeclaration(node)) {
           return;
         }


Test patch:
diff --git a/tests/lib/rules/prop-types.js b/tests/lib/rules/prop-types.js
index 08153a711a..3ee4072ac3 100644
--- a/tests/lib/rules/prop-types.js
+++ b/tests/lib/rules/prop-types.js
@@ -3095,6 +3095,45 @@ ruleTester.run('prop-types', rule, {
       errors: [
         {message: '\'foo\' is missing in props validation'}
       ]
+    }, {
+      code: [
+        'class Hello extends Component {',
+        '  static propTypes = {',
+        '    bar: PropTypes.func',
+        '  }',
+        '  componentWillReceiveProps({foo}) {',
+        '    if (foo) {',
+        '      return;',
+        '    }',
+        '  }',
+        '  render() {',
+        '    return <div bar={this.props.bar} />;',
+        '  }',
+        '}'
+      ].join('\n'),
+      parser: 'babel-eslint',
+      errors: [
+        {message: '\'foo\' is missing in props validation'}
+      ]
+    }, {
+      code: [
+        'class Hello extends Component {',
+        '  componentWillReceiveProps({foo}) {',
+        '    if (foo) {',
+        '      return;',
+        '    }',
+        '  }',
+        '  render() {',
+        '    return <div bar={this.props.bar} />;',
+        '  }',
+        '}',
+        'Hello.propTypes = {',
+        '    bar: PropTypes.func',
+        '  }'
+      ].join('\n'),
+      errors: [
+        {message: '\'foo\' is missing in props validation'}
+      ]
     }, {
       code: [
         'class Hello extends React.Component {',