-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathAV Rule 82.ql
More file actions
114 lines (104 loc) · 3.31 KB
/
AV Rule 82.ql
File metadata and controls
114 lines (104 loc) · 3.31 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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
* @name Overloaded assignment does not return 'this'
* @description An assignment operator should return a reference to *this. Both the standard library types and the built-in types behave in this manner.
* @kind problem
* @problem.severity warning
* @precision high
* @id cpp/assignment-does-not-return-this
* @tags reliability
* readability
* language-features
* external/jsf
*/
import cpp
/*
* Applies to all assignment operators, not just the copy assignment operator.
*/
predicate callOnThis(FunctionCall fc) {
// `this->f(...)`
fc.getQualifier() instanceof ThisExpr
or
// `(*this).f(...)`
fc.getQualifier().(PointerDereferenceExpr).getChild(0) instanceof ThisExpr
}
predicate pointerThis(Expr e) {
e instanceof ThisExpr
or
// `f(...)`
// (includes `this = ...`, where `=` is overloaded so a `FunctionCall`)
exists(FunctionCall fc | fc = e and callOnThis(fc) | returnsPointerThis(fc.getTarget()))
or
// `this = ...` (where `=` is not overloaded, so an `AssignExpr`)
pointerThis(e.(AssignExpr).getLValue())
}
predicate dereferenceThis(Expr e) {
pointerThis(e.(PointerDereferenceExpr).getChild(0))
or
// `f(...)`
// (includes `*this = ...`, where `=` is overloaded so a `FunctionCall`)
exists(FunctionCall fc | fc = e and callOnThis(fc) | returnsDereferenceThis(fc.getTarget()))
or
// `*this = ...` (where `=` is not overloaded, so an `AssignExpr`)
dereferenceThis(e.(AssignExpr).getLValue())
or
// `e ? ... : ... `
exists(ConditionalExpr cond |
cond = e and
dereferenceThis(cond.getThen()) and
dereferenceThis(cond.getElse())
)
or
// `..., ... `
dereferenceThis(e.(CommaExpr).getRightOperand())
}
/**
* Holds if all `return` statements in `f` return `this`, possibly indirectly.
* This includes functions whose body is not in the database.
*/
predicate returnsPointerThis(Function f) {
forall(ReturnStmt s | s.getEnclosingFunction() = f and reachable(s) |
// `return this`
pointerThis(s.getExpr())
)
}
/**
* Holds if all `return` statements in `f` return a reference to `*this`,
* possibly indirectly. This includes functions whose body is not in the
* database.
*/
predicate returnsDereferenceThis(Function f) {
forall(ReturnStmt s | s.getEnclosingFunction() = f and reachable(s) |
// `return *this`
dereferenceThis(s.getExpr())
)
}
predicate assignOperatorWithWrongType(Operator op, string msg) {
op.hasName("operator=") and
exists(op.getBlock()) and
exists(Class c |
c = op.getDeclaringType() and
op.getUnspecifiedType() = c and
msg =
"Assignment operator in class " + c.getName() + " should have return type " + c.getName() +
"&. Otherwise a copy is created at each call."
)
}
predicate assignOperatorWithWrongResult(Operator op, string msg) {
op.hasName("operator=") and
not returnsDereferenceThis(op) and
exists(op.getBlock()) and
not op.getType() instanceof VoidType and
not assignOperatorWithWrongType(op, _) and
msg =
"Assignment operator in class " + op.getDeclaringType().getName() +
" does not return a reference to *this."
}
from Operator op, string msg
where
(
assignOperatorWithWrongType(op, msg) or
assignOperatorWithWrongResult(op, msg)
) and
// exclude code in templates which may be incomplete
not op.isFromUninstantiatedTemplate(_)
select op, msg