mirror of
https://github.com/github/codeql.git
synced 2026-02-19 16:33:40 +01:00
92 lines
2.8 KiB
Plaintext
92 lines
2.8 KiB
Plaintext
/**
|
|
* @name Use of default ToString()
|
|
* @description Calling the default implementation of 'ToString' returns a value
|
|
* that is unlikely to be what you expect.
|
|
* @kind problem
|
|
* @problem.severity warning
|
|
* @precision very-high
|
|
* @id cs/call-to-object-tostring
|
|
* @tags reliability
|
|
* maintainability
|
|
*/
|
|
|
|
import csharp
|
|
import semmle.code.csharp.commons.Strings
|
|
import semmle.code.csharp.frameworks.System
|
|
|
|
/**
|
|
* Holds if expression `e`, of type `t`, invokes `ToString()` either explicitly
|
|
* or implicitly.
|
|
*/
|
|
predicate invokesToString(Expr e, ValueOrRefType t) {
|
|
// Explicit invocation
|
|
exists(MethodCall mc | mc.getQualifier() = e |
|
|
mc.getTarget() instanceof ToStringMethod and
|
|
t = mc.getQualifier().getType()
|
|
)
|
|
or
|
|
// Explicit or implicit invocation
|
|
e instanceof ImplicitToStringExpr and
|
|
t = e.stripCasts().getType()
|
|
or
|
|
// Implicit invocation via forwarder method
|
|
t = e.stripCasts().getType() and
|
|
not t instanceof StringType and
|
|
exists(AssignableDefinitions::ImplicitParameterDefinition def, Parameter p, ParameterRead pr |
|
|
e = p.getAnAssignedArgument()
|
|
|
|
|
def.getParameter() = p and
|
|
pr = def.getAReachableRead() and
|
|
pr.getAControlFlowNode().postDominates(p.getCallable().getEntryPoint()) and
|
|
invokesToString(pr, _)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if `t`, or any sub type of `t`, inherits the default `ToString()`
|
|
* method from `System.Object` or `System.ValueType`.
|
|
*/
|
|
predicate alwaysDefaultToString(ValueOrRefType t) {
|
|
exists(ToStringMethod m | t.hasMethod(m) |
|
|
m.getDeclaringType() instanceof SystemObjectClass or
|
|
m.getDeclaringType() instanceof SystemValueTypeClass
|
|
) and
|
|
not exists(RefType overriding |
|
|
overriding.getAMethod() instanceof ToStringMethod and
|
|
overriding.getABaseType+() = t
|
|
) and
|
|
not t.isAbstract() and
|
|
not t instanceof Interface
|
|
}
|
|
|
|
newtype TDefaultToStringType = TDefaultToStringType0(ValueOrRefType t) { alwaysDefaultToString(t) }
|
|
|
|
class DefaultToStringType extends TDefaultToStringType {
|
|
ValueOrRefType t;
|
|
|
|
DefaultToStringType() { this = TDefaultToStringType0(t) }
|
|
|
|
ValueOrRefType getType() { result = t }
|
|
|
|
string toString() { result = t.toString() }
|
|
|
|
// A workaround for generating empty URLs for non-source locations, because qltest
|
|
// does not support non-source locations
|
|
string getURL() {
|
|
exists(Location l | l = t.getLocation() |
|
|
if l instanceof SourceLocation
|
|
then
|
|
exists(string path, int a, int b, int c, int d | l.hasLocationInfo(path, a, b, c, d) |
|
|
toUrl(path, a, b, c, d, result)
|
|
)
|
|
else result = ""
|
|
)
|
|
}
|
|
}
|
|
|
|
from Expr e, DefaultToStringType t
|
|
where invokesToString(e, t.getType())
|
|
select e,
|
|
"Default 'ToString()': $@ inherits 'ToString()' from 'Object', and so is not suitable for printing.",
|
|
t, t.getType().getName()
|