Files
codeql/javascript/ql/src/Declarations/UnusedProperty.ql
Max Schaefer a803120414 Lower precision for a number of queries.
These queries are currently run by default, but don't have their results displayed.

Looking through results on LGTM.com, they are either false positives (e.g., `BitwiseSignCheck` which flags many perfectly harmless operations and `CompareIdenticalValues` which mostly flags NaN checks) or harmless results that developers are unlikely to care about (e.g., `EmptyArrayInit` or `MisspelledIdentifier`).

With this PR, the only queries that are still run but not displayed are security queries, where different considerations may apply.
2020-05-19 13:43:17 +01:00

80 lines
2.6 KiB
Plaintext

/**
* @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 low
*/
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 + "."