Skip to content

Commit 35d320b

Browse files
First draft of Rule 0-1-1, unnecessary assignments.
1 parent 0774cfc commit 35d320b

File tree

22 files changed

+2883
-94
lines changed

22 files changed

+2883
-94
lines changed

c/common/src/codingstandards/c/Objects.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import cpp
2-
import codingstandards.c.StorageDuration
2+
import codingstandards.cpp.lifetimes.StorageDuration
33
import codingstandards.c.IdentifierLinkage
44
import semmle.code.cpp.valuenumbering.HashCons
55
import codingstandards.cpp.Clvalues

cpp/common/src/codingstandards/cpp/Iterators.qll

Lines changed: 0 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -252,78 +252,6 @@ class AdditiveOperatorFunctionCall extends FunctionCall {
252252
}
253253
}
254254

255-
/**
256-
* Models a collection of STL container classes.
257-
*/
258-
class STLContainer extends Class {
259-
STLContainer() {
260-
getNamespace() instanceof StdNS and
261-
getSimpleName() in [
262-
"vector", "list", "deque", "set", "multiset", "map", "multimap", "stack", "queue",
263-
"priority_queue", "string", "forward_list", "unordered_set", "unordered_multiset",
264-
"unordered_map", "unordered_multimap", "valarray", "string", "basic_string"
265-
]
266-
or
267-
getSimpleName() = "string"
268-
or
269-
getSimpleName() = "basic_string"
270-
}
271-
272-
/**
273-
* Get a call to a named function of this class.
274-
*/
275-
FunctionCall getACallTo(string name) {
276-
exists(Function f |
277-
f = getAMemberFunction() and
278-
f.hasName(name) and
279-
result = f.getACallToThisFunction()
280-
)
281-
}
282-
283-
/**
284-
* Gets all calls to all functions of this class.
285-
*/
286-
FunctionCall getACallToAFunction() {
287-
exists(Function f |
288-
f = getAMemberFunction() and
289-
result = f.getACallToThisFunction()
290-
)
291-
}
292-
293-
FunctionCall getACallToSize() { result = getACallTo("size") }
294-
295-
FunctionCall getANonConstIteratorBeginFunctionCall() { result = getACallTo("begin") }
296-
297-
IteratorSource getAConstIteratorBeginFunctionCall() { result = getACallTo("cbegin") }
298-
299-
IteratorSource getANonConstIteratorEndFunctionCall() { result = getACallTo("end") }
300-
301-
IteratorSource getAConstIteratorEndFunctionCall() { result = getACallTo("cend") }
302-
303-
IteratorSource getANonConstIteratorFunctionCall() {
304-
result = getACallToAFunction() and
305-
result.getTarget().getType() instanceof NonConstIteratorType
306-
}
307-
308-
IteratorSource getAConstIteratorFunctionCall() {
309-
result = getACallToAFunction() and
310-
result.getTarget().getType() instanceof ConstIteratorType
311-
}
312-
313-
IteratorSource getAnIteratorFunctionCall() {
314-
result = getANonConstIteratorFunctionCall() or result = getAConstIteratorFunctionCall()
315-
}
316-
317-
IteratorSource getAnIteratorBeginFunctionCall() {
318-
result = getANonConstIteratorBeginFunctionCall() or
319-
result = getAConstIteratorBeginFunctionCall()
320-
}
321-
322-
IteratorSource getAnIteratorEndFunctionCall() {
323-
result = getANonConstIteratorEndFunctionCall() or result = getAConstIteratorEndFunctionCall()
324-
}
325-
}
326-
327255
/**
328256
* Models the set of iterator sources. Useful for encapsulating dataflow coming
329257
* from a function call producing an iterator.
@@ -338,13 +266,6 @@ class IteratorSource extends FunctionCall {
338266
Variable getOwner() { result = getQualifier().(VariableAccess).getTarget() }
339267
}
340268

341-
/**
342-
* Models a variable that is a `STLContainer`
343-
*/
344-
class STLContainerVariable extends Variable {
345-
STLContainerVariable() { getType() instanceof STLContainer }
346-
}
347-
348269
/**
349270
* Usually an iterator range consists of two sequential iterator arguments, for
350271
* example, the start and end. However, there are exceptions to this rule so
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import cpp
2+
3+
/**
4+
* The configuration signature for the SuccessorUnless module.
5+
*
6+
* Typically, the `Node` type should be a `ControlFlowNode`, but it can be overridden to enable
7+
* other kinds of graphs.
8+
*/
9+
signature module SuccessorUnlessConfigSig {
10+
/** The state type used to carry context through the CFG exploration. */
11+
class State;
12+
13+
/** The node type used to represent CFG nodes. Overridable. */
14+
class Node {
15+
Node getASuccessor();
16+
}
17+
18+
predicate isStart(State state, Node node);
19+
20+
predicate isUnless(State state, Node node);
21+
22+
predicate isEnd(State state, Node node);
23+
}
24+
25+
/**
26+
* A module that finds successor of a node -- unless there is an intermediate node that satisfies
27+
* a given condition.
28+
*
29+
* Also accepts a state parameter to allow a context to flow through the CFG.
30+
*
31+
* ## Usage
32+
*
33+
* Implement the module `ConfigSig`, with some context type:
34+
*
35+
* ```ql
36+
* module MyConfig implements SuccessorUnless<SomeContext>::ConfigSig {
37+
* predicate isStart(SomeContext state, ControlFlowNode node) {
38+
* node = state.someStartCondition()
39+
* }
40+
*
41+
* predicate isUnless(SomeContext state, ControlFlowNode node) {
42+
* node = state.someUnlessCondition()
43+
* }
44+
*
45+
* predicate isEnd(SomeContext state, ControlFlowNode node) {
46+
* node = state.someEndCondition()
47+
* }
48+
* }
49+
*
50+
* import SuccessorUnless<SomeContext>::Make<MyConfig> as Successor
51+
* ```
52+
*
53+
* ## Rationale
54+
*
55+
* Why does this module exist? While it may be tempting to write:
56+
*
57+
* ```ql
58+
* exists(ControlFlowNode start, ControlFlowNode end) {
59+
* isStart(start) and
60+
* isEnd(end) and
61+
* end = start.getASuccessor*() and
62+
* not exists(ControlFlowNode mid |
63+
* mid = start.getASuccessor+() and
64+
* end = mid.getASuccessor*() and
65+
* isUnless(mid)
66+
* )
67+
* }
68+
* ```
69+
*
70+
* This has an unintuitive trap case in looping CFGs:
71+
*
72+
* ```c
73+
* while (cond) {
74+
* start();
75+
* end();
76+
* unless();
77+
* }
78+
* ```
79+
*
80+
* In the above code, `unless()` is a successor of `start()`, and `end()` is also a successor of
81+
* `unless()` (via the loop back edge). However, there is no path from `start()` to `end()` that
82+
* does not pass through `unless()`.
83+
*
84+
* This module will correctly handle this case. Forward exploration through the graph will stop
85+
* at the `unless()` nodes, such that only paths from `start()` to `end()` that do not pass through
86+
* `unless()` nodes will be found.
87+
*/
88+
module SuccessorUnless<SuccessorUnlessConfigSig Config> {
89+
predicate isSuccessor(Config::State state, Config::Node start, Config::Node end) {
90+
isMid(state, start, end) and
91+
Config::isEnd(state, end)
92+
}
93+
94+
private predicate isMid(Config::State state, Config::Node start, Config::Node mid) {
95+
// TODO: Explore if forward-reverse pruning would be beneficial for performance here.
96+
Config::isStart(state, start) and
97+
(
98+
mid = start
99+
or
100+
exists(Config::Node prevMid |
101+
isMid(state, start, prevMid) and
102+
mid = prevMid.getASuccessor() and
103+
not Config::isUnless(state, mid) and
104+
not Config::isEnd(state, prevMid)
105+
)
106+
)
107+
}
108+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
2+
import cpp
3+
import RuleMetadata
4+
import codingstandards.cpp.exclusions.RuleMetadata
5+
6+
newtype DeadCode5Query = TUnnecessaryWriteToLocalObjectQuery()
7+
8+
predicate isDeadCode5QueryMetadata(Query query, string queryId, string ruleId, string category) {
9+
query =
10+
// `Query` instance for the `unnecessaryWriteToLocalObject` query
11+
DeadCode5Package::unnecessaryWriteToLocalObjectQuery() and
12+
queryId =
13+
// `@id` for the `unnecessaryWriteToLocalObject` query
14+
"cpp/misra/unnecessary-write-to-local-object" and
15+
ruleId = "RULE-0-1-1" and
16+
category = "advisory"
17+
}
18+
19+
module DeadCode5Package {
20+
Query unnecessaryWriteToLocalObjectQuery() {
21+
//autogenerate `Query` type
22+
result =
23+
// `Query` type for `unnecessaryWriteToLocalObject` query
24+
TQueryCPP(TDeadCode5PackageQuery(TUnnecessaryWriteToLocalObjectQuery()))
25+
}
26+
}

cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import Const
1616
import Conversions
1717
import Conversions2
1818
import DeadCode
19+
import DeadCode5
1920
import Declarations
2021
import ExceptionSafety
2122
import Exceptions1
@@ -78,6 +79,7 @@ newtype TCPPQuery =
7879
TConversionsPackageQuery(ConversionsQuery q) or
7980
TConversions2PackageQuery(Conversions2Query q) or
8081
TDeadCodePackageQuery(DeadCodeQuery q) or
82+
TDeadCode5PackageQuery(DeadCode5Query q) or
8183
TDeclarationsPackageQuery(DeclarationsQuery q) or
8284
TExceptionSafetyPackageQuery(ExceptionSafetyQuery q) or
8385
TExceptions1PackageQuery(Exceptions1Query q) or
@@ -140,6 +142,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
140142
isConversionsQueryMetadata(query, queryId, ruleId, category) or
141143
isConversions2QueryMetadata(query, queryId, ruleId, category) or
142144
isDeadCodeQueryMetadata(query, queryId, ruleId, category) or
145+
isDeadCode5QueryMetadata(query, queryId, ruleId, category) or
143146
isDeclarationsQueryMetadata(query, queryId, ruleId, category) or
144147
isExceptionSafetyQueryMetadata(query, queryId, ruleId, category) or
145148
isExceptions1QueryMetadata(query, queryId, ruleId, category) or

0 commit comments

Comments
 (0)