mirror of
https://github.com/github/codeql.git
synced 2026-04-28 18:25:24 +02:00
Merge pull request #607 from esben-semmle/js/more-react-methods
JS: model additional React component methods
This commit is contained in:
@@ -34,6 +34,11 @@ abstract class ReactComponent extends ASTNode {
|
||||
*/
|
||||
abstract Function getInstanceMethod(string name);
|
||||
|
||||
/**
|
||||
* Gets a static method of this component with the given name.
|
||||
*/
|
||||
abstract Function getStaticMethod(string name);
|
||||
|
||||
/**
|
||||
* Gets the abstract value that represents this component.
|
||||
*/
|
||||
@@ -175,6 +180,11 @@ abstract class ReactComponent extends ASTNode {
|
||||
// setState with object: `this.setState({foo: 42})`
|
||||
result = arg0
|
||||
)
|
||||
or
|
||||
result.flowsToExpr(getStaticMethod("getDerivedStateFromProps").getAReturnedExpr())
|
||||
or
|
||||
// shouldComponentUpdate: (nextProps, nextState)
|
||||
result = DataFlow::parameterNode(getInstanceMethod("shouldComponentUpdate").getParameter(1))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,9 +200,14 @@ abstract class ReactComponent extends ASTNode {
|
||||
callback = getAMethodCall("setState").getCallback(0) and
|
||||
stateParameterIndex = 0
|
||||
or
|
||||
// componentDidUpdate: (prevProps, prevState)
|
||||
callback = getInstanceMethod("componentDidUpdate").flow() and
|
||||
stateParameterIndex = 1
|
||||
stateParameterIndex = 1 and (
|
||||
// componentDidUpdate: (prevProps, prevState)
|
||||
callback = getInstanceMethod("componentDidUpdate").flow() or
|
||||
// getDerivedStateFromProps: (props, state)
|
||||
callback = getStaticMethod("getDerivedStateFromProps").flow() or
|
||||
// getSnapshotBeforeUpdate: (prevProps, prevState)
|
||||
callback = getInstanceMethod("getSnapshotBeforeUpdate").flow()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -205,6 +220,9 @@ abstract class ReactComponent extends ASTNode {
|
||||
result.flowsTo(getComponentCreatorSource().getAnInvocation().getArgument(0))
|
||||
or
|
||||
result = getADefaultPropsSource()
|
||||
or
|
||||
// shouldComponentUpdate: (nextProps, nextState)
|
||||
result = DataFlow::parameterNode(getInstanceMethod("shouldComponentUpdate").getParameter(0))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -234,9 +252,14 @@ abstract class ReactComponent extends ASTNode {
|
||||
callback = getAMethodCall("setState").getCallback(0) and
|
||||
propsParameterIndex = 1
|
||||
or
|
||||
// componentDidUpdate: (prevProps, prevState)
|
||||
callback = getInstanceMethod("componentDidUpdate").flow() and
|
||||
propsParameterIndex = 0
|
||||
propsParameterIndex = 0 and (
|
||||
// componentDidUpdate: (prevProps, prevState)
|
||||
callback = getInstanceMethod("componentDidUpdate").flow() or
|
||||
// getDerivedStateFromProps: (props, state)
|
||||
callback = getStaticMethod("getDerivedStateFromProps").flow() or
|
||||
// getSnapshotBeforeUpdate: (prevProps, prevState)
|
||||
callback = getInstanceMethod("getSnapshotBeforeUpdate").flow()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -272,6 +295,10 @@ class FunctionalComponent extends ReactComponent, Function {
|
||||
name = "render" and result = this
|
||||
}
|
||||
|
||||
override Function getStaticMethod(string name) {
|
||||
none()
|
||||
}
|
||||
|
||||
override DataFlow::SourceNode getADirectPropsAccess() {
|
||||
result = DataFlow::parameterNode(getParameter(0))
|
||||
}
|
||||
@@ -302,6 +329,14 @@ private abstract class SharedReactPreactClassComponent extends ReactComponent, C
|
||||
result = ClassDefinition.super.getInstanceMethod(name)
|
||||
}
|
||||
|
||||
override Function getStaticMethod(string name) {
|
||||
exists(MethodDeclaration decl |
|
||||
decl = getMethod(name) and
|
||||
decl.isStatic() and
|
||||
result = decl.getBody()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::SourceNode getADirectPropsAccess() {
|
||||
result.(DataFlow::PropRef).accesses(ref(), "props") or
|
||||
result = DataFlow::parameterNode(getConstructor().getBody().getParameter(0))
|
||||
@@ -428,6 +463,10 @@ class ES5Component extends ReactComponent, ObjectExpr {
|
||||
result = getPropertyByName(name).getInit()
|
||||
}
|
||||
|
||||
override Function getStaticMethod(string name) {
|
||||
none()
|
||||
}
|
||||
|
||||
override DataFlow::SourceNode getADirectPropsAccess() {
|
||||
result.(DataFlow::PropRef).accesses(ref(), "props")
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
| props.js:2:5:3:5 | class C ... {\\n } |
|
||||
| props.js:13:31:17:5 | {\\n ... }\\n } |
|
||||
| props.js:26:5:28:5 | functio ... ;\\n } |
|
||||
| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} |
|
||||
| statePropertyReads.js:1:1:13:1 | class R ... }\\n} |
|
||||
| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} |
|
||||
| statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} |
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:18:22:18:31 | { baz: 42} |
|
||||
| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:3:16:3:17 | {} |
|
||||
| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:5:38:5:46 | nextState |
|
||||
| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:7:45:7:56 | prevState.p3 |
|
||||
| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:8:18:8:19 | {} |
|
||||
| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:12:18:12:19 | {} |
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
| probably-a-component.js:1:1:6:1 | class H ... }\\n} | probably-a-component.js:3:9:3:18 | this.props |
|
||||
| props.js:2:5:3:5 | class C ... {\\n } | props.js:2:37:2:36 | args |
|
||||
| props.js:26:5:28:5 | functio ... ;\\n } | props.js:26:16:26:20 | props |
|
||||
| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:1:33:1:32 | args |
|
||||
| statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | statePropertyWrites.js:38:24:38:33 | this.props |
|
||||
| thisAccesses.js:31:2:36:1 | functio ... iv/>;\\n} | thisAccesses.js:31:12:31:16 | props |
|
||||
| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:48:18:48:18 | y |
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:2:44:2:48 | state |
|
||||
| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:8:40:8:48 | prevState |
|
||||
| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:7:24:7:32 | prevState |
|
||||
| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:10:35:10:43 | prevState |
|
||||
@@ -0,0 +1,4 @@
|
||||
import semmle.javascript.frameworks.React
|
||||
|
||||
from ReactComponent c
|
||||
select c, c.getAPreviousStateSource()
|
||||
@@ -10,6 +10,8 @@
|
||||
| probably-a-component.js:1:1:6:1 | class H ... }\\n} | render | probably-a-component.js:2:11:5:5 | () {\\n ... ;\\n } |
|
||||
| props.js:13:31:17:5 | {\\n ... }\\n } | getDefaultProps | props.js:14:24:16:9 | () {\\n ... } |
|
||||
| props.js:26:5:28:5 | functio ... ;\\n } | render | props.js:26:5:28:5 | functio ... ;\\n } |
|
||||
| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | getSnapshotBeforeUpdate | rare-lifecycle-methods.js:8:28:10:5 | (prevPr ... ;\\n } |
|
||||
| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | shouldComponentUpdate | rare-lifecycle-methods.js:5:26:7:5 | (nextPr ... ;\\n } |
|
||||
| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | componentDidUpdate | statePropertyReads.js:10:23:12:5 | (prevPr ... ;\\n } |
|
||||
| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | getInitialState | statePropertyWrites.js:25:20:29:5 | () { // ... ;\\n } |
|
||||
| statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | getInitialState | statePropertyWrites.js:40:20:44:3 | functio ... };\\n } |
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
| props.js:13:31:17:5 | {\\n ... }\\n } | props.js:14:24:14:23 | this |
|
||||
| props.js:26:5:28:5 | functio ... ;\\n } | props.js:26:5:26:4 | this |
|
||||
| props.js:26:5:28:5 | functio ... ;\\n } | props.js:34:5:34:55 | new C({ ... ctor"}) |
|
||||
| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:1:33:1:32 | this |
|
||||
| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:2:36:2:35 | this |
|
||||
| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:5:26:5:25 | this |
|
||||
| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:8:28:8:27 | this |
|
||||
| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:2:16:2:15 | this |
|
||||
| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:3:9:3:12 | this |
|
||||
| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:5:9:5:12 | this |
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
class C extends React.Component {
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
return {};
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return true;
|
||||
}
|
||||
getSnapshotBeforeUpdate(prevProps, prevState) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
| plainfn.js:22:16:22:20 | React |
|
||||
| props.js:2:21:2:25 | React |
|
||||
| props.js:13:13:13:17 | React |
|
||||
| rare-lifecycle-methods.js:1:17:1:21 | React |
|
||||
| requiredReactRefs.js:1:13:1:28 | require("react") |
|
||||
| requiredReactRefs.js:3:1:3:5 | React |
|
||||
| requiredReactRefs.js:6:5:6:9 | React |
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
| issue7506.js:12:1:12:34 | class C ... e\\n }\\n} | Component state property 'p1' is $@, but it is never read. | issue7506.js:15:5:15:10 | p1: '' | written |
|
||||
| undefined.js:1:1:1:34 | class C ... }\\n} | Component state property 'notWritten' is $@, but it is never written. | undefined.js:8:9:8:29 | this.st ... Written | read |
|
||||
| undefined.js:1:1:1:34 | class C ... }\\n} | Component state property 'notWrittenButReadInChain' is $@, but it is never written. | undefined.js:9:9:9:43 | this.st ... InChain | read |
|
||||
| undefined.js:32:1:32:34 | class C ... }\\n} | Component state property 'notWrittenThrougExternalPropertyAccess' is $@, but it is never written. | undefined.js:35:9:35:57 | this.st ... yAccess | read |
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
class C1 extends React.Component {
|
||||
|
||||
state = {
|
||||
p1: ''
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
const { p1: p2 } = state
|
||||
}
|
||||
}
|
||||
|
||||
class C2 extends React.Component {
|
||||
|
||||
state = {
|
||||
p1: ''
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps_unmodeled(props, state) {
|
||||
const { p1: p2 } = state
|
||||
}
|
||||
}
|
||||
|
||||
// semmle-extractor-options: --experimental
|
||||
@@ -156,4 +156,15 @@ React.createClass({
|
||||
mixins: [{ f: () => this.state.writtenThroughMixin = 42 }]
|
||||
});
|
||||
|
||||
class C11 extends React.Component {
|
||||
|
||||
static getDerivedStateFromProps(p, s) {
|
||||
return { writeIn_getDerivedStateFromProps};
|
||||
}
|
||||
|
||||
otherMethod() {
|
||||
this.state.writeIn_getDerivedStateFromProps; // OK
|
||||
}
|
||||
}
|
||||
|
||||
//semmle-extractor-options: --experimental
|
||||
|
||||
@@ -61,4 +61,16 @@ React.createClass({
|
||||
mixins: [{ f: () => this.state.readThroughMixin }]
|
||||
});
|
||||
|
||||
class C6 extends React.Component {
|
||||
|
||||
static getDerivedStateFromProps(p, s) {
|
||||
s.readIn_getDerivedStateFromProps;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.state.readIn_getDerivedStateFromProps = 42; // OK
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//semmle-extractor-options: --experimental
|
||||
|
||||
Reference in New Issue
Block a user