-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathHashedButNoHash.ql
More file actions
75 lines (67 loc) · 2.14 KB
/
HashedButNoHash.ql
File metadata and controls
75 lines (67 loc) · 2.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
* @name Hashed value without GetHashCode definition
* @description Finds uses of hashing on types that define 'Equals(...)' but not 'GetHashCode()'.
* @kind problem
* @problem.severity warning
* @precision high
* @id cs/gethashcode-is-not-defined
* @tags quality
* reliability
* correctness
*/
import csharp
import semmle.code.csharp.frameworks.System
import semmle.code.csharp.frameworks.system.Collections
import semmle.code.csharp.frameworks.system.collections.Generic
/**
* Holds if `t` is a dictionary type.
*/
predicate dictionary(ValueOrRefType t) {
exists(Type base | base = t.getABaseType*().getUnboundDeclaration() |
base instanceof SystemCollectionsGenericIDictionaryInterface or
base instanceof SystemCollectionsGenericIReadOnlyDictionaryInterface or
base instanceof SystemCollectionsIDictionaryInterface
)
}
/**
* Holds if `c` is a hashset type.
*/
predicate hashSet(ValueOrRefType t) {
t.getABaseType*().getUnboundDeclaration() instanceof SystemCollectionsGenericHashSetClass
}
predicate hashStructure(Type t) { dictionary(t) or hashSet(t) }
/**
* Holds if the expression `e` relies on `GetHashCode()` implementation.
* That is, if the call assumes that `e1.Equals(e2)` implies `e1.GetHashCode() == e2.GetHashCode()`.
*/
predicate usesHashing(Expr e) {
exists(MethodCall mc, string name |
name = ["Add", "Contains", "ContainsKey", "Remove", "TryAdd", "TryGetValue"] and
mc.getArgument(0) = e and
mc.getTarget().hasName(name) and
hashStructure(mc.getTarget().getDeclaringType())
)
or
exists(IndexerCall ic |
ic.getArgument(0) = e and
dictionary(ic.getTarget().getDeclaringType())
)
}
predicate eqWithoutHash(RefType t) {
t.getAMethod() instanceof EqualsMethod and
not t.getAMethod() instanceof GetHashCodeMethod
}
predicate hashCall(Expr e) {
exists(MethodCall mc |
mc.getQualifier() = e and
mc.getTarget() instanceof GetHashCodeMethod
)
}
from Expr e, Type t
where
(usesHashing(e) or hashCall(e)) and
e.getType() = t and
eqWithoutHash(t)
select e,
"This expression is hashed, but type '" + t.getName() +
"' only defines Equals(...) not GetHashCode()."