-
Notifications
You must be signed in to change notification settings - Fork 1.9k
JS: Captured Nodes, type inference + a query #946
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
bfbf686
JS: fixup changenote for js/unbound-event-handler-receiver
0cf2eae
JS: introduce CapturedSource
91dccc3
JS: add query js/unused-property
8af501d
JS: avoid double reporting dead code with js/unused-variable
c84d898
JS: change notes for js/unused-property and js/unused-variable
bdd8691
JS: add type inference for the return value of captured method calls
6766716
JS: add PropWrite tests for parameter field initializers
6c1b29e
JS: add missing flowstep for unused parameter field initializers
047b69a
JS: address review comments
0d94fe3
JS: analyze assignments in `with` correctly
1150f4c
JS: add documentation to test case
65fb142
JS: format test case (update expected output)
6636798
JS: rename CapturedSource -> LocalObject
4dc147d
JS: rename CapturedSource -> LocalObject (files)
ab1b1c1
JS: update docstring
9511bdf
JS: address review comment
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| <!DOCTYPE qhelp PUBLIC | ||
| "-//Semmle//qhelp//EN" | ||
| "qhelp.dtd"> | ||
| <qhelp> | ||
| <overview> | ||
| <p> | ||
| Unused object properties make code harder to maintain and use. Clients that are unaware that a | ||
| property is unused may perform nontrivial computations to compute a value that is ultimately | ||
| unused. | ||
| </p> | ||
|
|
||
| </overview> | ||
| <recommendation> | ||
| <p>Remove the unused property.</p> | ||
|
|
||
| </recommendation> | ||
| <example> | ||
|
|
||
| <p> | ||
| In this code, the function <code>f</code> initializes a property <code>prop_a</code> with a | ||
| call to the function <code>expensiveComputation</code>, but later on this property is never read. | ||
| Removing <code>prop</code> would improve code quality and performance. | ||
| </p> | ||
|
|
||
| <sample src="examples/UnusedProperty.js" /> | ||
|
|
||
| </example> | ||
| <references> | ||
|
|
||
| <li>Coding Horror: <a href="http://blog.codinghorror.com/code-smells/">Code Smells</a>.</li> | ||
|
|
||
|
|
||
| </references> | ||
| </qhelp> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| /** | ||
| * @name Unused property | ||
| * @description Unused properties may be a symptom of a bug and should be examined carefully. | ||
| * @kind problem | ||
| * @problem.severity recommendation | ||
| * @id js/unused-property | ||
| * @tags maintainability | ||
| * @precision high | ||
| */ | ||
|
|
||
| import javascript | ||
| import semmle.javascript.dataflow.LocalObjects | ||
| import UnusedVariable | ||
| import UnusedParameter | ||
| import Expressions.ExprHasNoEffect | ||
|
|
||
| predicate hasUnknownPropertyRead(LocalObject obj) { | ||
| // dynamic reads | ||
| exists(DataFlow::PropRead r | obj.getAPropertyRead() = r | not exists(r.getPropertyName())) | ||
| or | ||
| // reflective reads | ||
| obj.flowsToExpr(any(EnhancedForLoop l).getIterationDomain()) | ||
| or | ||
| obj.flowsToExpr(any(InExpr l).getRightOperand()) | ||
| or | ||
| obj.flowsToExpr(any(SpreadElement e).getOperand()) | ||
| or | ||
| exists(obj.getAPropertyRead("hasOwnProperty")) | ||
| or | ||
| exists(obj.getAPropertyRead("propertyIsEnumerable")) | ||
| } | ||
|
|
||
| /** | ||
| * Holds if `obj` flows to an expression that must have a specific type. | ||
| */ | ||
| predicate flowsToTypeRestrictedExpression(LocalObject obj) { | ||
| exists (Expr restricted, TypeExpr type | | ||
| obj.flowsToExpr(restricted) and | ||
| not type.isAny() | | ||
| exists (TypeAssertion assertion | | ||
| type = assertion.getTypeAnnotation() and | ||
| restricted = assertion.getExpression() | ||
| ) | ||
| or | ||
| exists (BindingPattern v | | ||
| type = v.getTypeAnnotation() and | ||
| restricted = v.getAVariable().getAnAssignedExpr() | ||
| ) | ||
| // no need to reason about writes to typed fields, captured nodes do not reach them | ||
| ) | ||
| } | ||
|
|
||
| from DataFlow::PropWrite write, LocalObject obj, string name | ||
| where | ||
| write = obj.getAPropertyWrite(name) and | ||
| not exists(obj.getAPropertyRead(name)) and | ||
| // `obj` is the only base object for the write: it is not spurious | ||
| not write.getBase().analyze().getAValue() != obj.analyze().getAValue() and | ||
| not hasUnknownPropertyRead(obj) and | ||
| // avoid reporting if the definition is unreachable | ||
| write.getAstNode().getFirstControlFlowNode().getBasicBlock() instanceof ReachableBasicBlock and | ||
| // avoid implicitly read properties | ||
| not ( | ||
| name = "toString" or | ||
| name = "valueOf" or | ||
| name.matches("@@%") // @@iterator, for example | ||
| ) and | ||
| // avoid flagging properties that a type system requires | ||
| not flowsToTypeRestrictedExpression(obj) and | ||
| // flagged by js/unused-local-variable | ||
| not exists(UnusedLocal l | l.getAnAssignedExpr().getUnderlyingValue().flow() = obj) and | ||
| // flagged by js/unused-parameter | ||
| not exists(Parameter p | isAnAccidentallyUnusedParameter(p) | | ||
| p.getDefault().getUnderlyingValue().flow() = obj | ||
| ) and | ||
| // flagged by js/useless-expression | ||
| not inVoidContext(obj.asExpr()) | ||
| select write, "Unused property " + name + "." |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| /** | ||
| * Provides classes and predicates for the 'js/unused-local-variable' query. | ||
| */ | ||
|
|
||
| import javascript | ||
|
|
||
| /** | ||
| * A local variable that is neither used nor exported, and is not a parameter | ||
| * or a function name. | ||
| */ | ||
| class UnusedLocal extends LocalVariable { | ||
| UnusedLocal() { | ||
| not exists(getAnAccess()) and | ||
| not exists(Parameter p | this = p.getAVariable()) and | ||
| not exists(FunctionExpr fe | this = fe.getVariable()) and | ||
| not exists(ClassExpr ce | this = ce.getVariable()) and | ||
| not exists(ExportDeclaration ed | ed.exportsAs(this, _)) and | ||
| not exists(LocalVarTypeAccess type | type.getVariable() = this) and | ||
| // common convention: variables with leading underscore are intentionally unused | ||
| getName().charAt(0) != "_" | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| function f() { | ||
| var o = { | ||
| prop_a: expensiveComputation(), | ||
| prop_b: anotherComputation() | ||
| }; | ||
|
|
||
| return o.prop_b; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| /** | ||
| * Provides classes and predicates for the 'js/useless-expression' query. | ||
| */ | ||
|
|
||
| import javascript | ||
|
|
||
| /** | ||
| * Holds if `e` appears in a syntactic context where its value is discarded. | ||
| */ | ||
| predicate inVoidContext(Expr e) { | ||
| exists(ExprStmt parent | | ||
| // e is a toplevel expression in an expression statement | ||
| parent = e.getParent() and | ||
| // but it isn't an HTML attribute or a configuration object | ||
| not exists(TopLevel tl | tl = parent.getParent() | | ||
| tl instanceof CodeInAttribute | ||
| or | ||
| // if the toplevel in its entirety is of the form `({ ... })`, | ||
| // it is probably a configuration object (e.g., a require.js build configuration) | ||
| tl.getNumChildStmt() = 1 and e.stripParens() instanceof ObjectExpr | ||
| ) | ||
| ) | ||
| or | ||
| exists(SeqExpr seq, int i, int n | | ||
| e = seq.getOperand(i) and | ||
| n = seq.getNumOperands() | ||
| | | ||
| i < n - 1 or inVoidContext(seq) | ||
| ) | ||
| or | ||
| exists(ForStmt stmt | e = stmt.getUpdate()) | ||
| or | ||
| exists(ForStmt stmt | e = stmt.getInit() | | ||
| // Allow the pattern `for(i; i < 10; i++)` | ||
| not e instanceof VarAccess | ||
| ) | ||
| or | ||
| exists(LogicalBinaryExpr logical | e = logical.getRightOperand() and inVoidContext(logical)) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggesting adding a comma between
but later onandthis property, to aid comprehension