Skip to content

Commit 69e9477

Browse files
Merge pull request #1051 from github/michaelrfairhurst/side-effects-6-rule-28-3-1-predicate-side-effects
Implement package SideEffects6 with rule 28.3.1, predicate should not have side effects
2 parents 3c3f5a3 + bd18210 commit 69e9477

File tree

21 files changed

+528
-8
lines changed

21 files changed

+528
-8
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import cpp
2+
3+
/**
4+
* A predicate to simplify getting a namespace for a template parameter, since
5+
* `TemplateParameterType`'s `getNamespace()` has no result, and `enclosingElement()` has no result,
6+
* and there are multiple cases to navigate to work around this.
7+
*/
8+
Namespace getTemplateParameterNamespace(TypeTemplateParameter param) {
9+
exists(Declaration decl |
10+
param = decl.(TemplateClass).getATemplateArgument() or
11+
param = decl.(TemplateVariable).getATemplateArgument() or
12+
param = decl.(TemplateFunction).getATemplateArgument()
13+
|
14+
result = decl.getNamespace()
15+
)
16+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ import SideEffects1
7474
import SideEffects2
7575
import SideEffects4
7676
import SideEffects5
77+
import SideEffects6
7778
import SmartPointers1
7879
import SmartPointers2
7980
import Statements
@@ -161,6 +162,7 @@ newtype TCPPQuery =
161162
TSideEffects2PackageQuery(SideEffects2Query q) or
162163
TSideEffects4PackageQuery(SideEffects4Query q) or
163164
TSideEffects5PackageQuery(SideEffects5Query q) or
165+
TSideEffects6PackageQuery(SideEffects6Query q) or
164166
TSmartPointers1PackageQuery(SmartPointers1Query q) or
165167
TSmartPointers2PackageQuery(SmartPointers2Query q) or
166168
TStatementsPackageQuery(StatementsQuery q) or
@@ -248,6 +250,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
248250
isSideEffects2QueryMetadata(query, queryId, ruleId, category) or
249251
isSideEffects4QueryMetadata(query, queryId, ruleId, category) or
250252
isSideEffects5QueryMetadata(query, queryId, ruleId, category) or
253+
isSideEffects6QueryMetadata(query, queryId, ruleId, category) or
251254
isSmartPointers1QueryMetadata(query, queryId, ruleId, category) or
252255
isSmartPointers2QueryMetadata(query, queryId, ruleId, category) or
253256
isStatementsQueryMetadata(query, queryId, ruleId, category) or
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
2+
import cpp
3+
import RuleMetadata
4+
import codingstandards.cpp.exclusions.RuleMetadata
5+
6+
newtype SideEffects6Query =
7+
TPredicateWithPersistentSideEffectsQuery() or
8+
TNonConstPredicateFunctionObjectQuery()
9+
10+
predicate isSideEffects6QueryMetadata(Query query, string queryId, string ruleId, string category) {
11+
query =
12+
// `Query` instance for the `predicateWithPersistentSideEffects` query
13+
SideEffects6Package::predicateWithPersistentSideEffectsQuery() and
14+
queryId =
15+
// `@id` for the `predicateWithPersistentSideEffects` query
16+
"cpp/misra/predicate-with-persistent-side-effects" and
17+
ruleId = "RULE-28-3-1" and
18+
category = "required"
19+
or
20+
query =
21+
// `Query` instance for the `nonConstPredicateFunctionObject` query
22+
SideEffects6Package::nonConstPredicateFunctionObjectQuery() and
23+
queryId =
24+
// `@id` for the `nonConstPredicateFunctionObject` query
25+
"cpp/misra/non-const-predicate-function-object" and
26+
ruleId = "RULE-28-3-1" and
27+
category = "required"
28+
}
29+
30+
module SideEffects6Package {
31+
Query predicateWithPersistentSideEffectsQuery() {
32+
//autogenerate `Query` type
33+
result =
34+
// `Query` type for `predicateWithPersistentSideEffects` query
35+
TQueryCPP(TSideEffects6PackageQuery(TPredicateWithPersistentSideEffectsQuery()))
36+
}
37+
38+
Query nonConstPredicateFunctionObjectQuery() {
39+
//autogenerate `Query` type
40+
result =
41+
// `Query` type for `nonConstPredicateFunctionObject` query
42+
TQueryCPP(TSideEffects6PackageQuery(TNonConstPredicateFunctionObjectQuery()))
43+
}
44+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/**
2+
* A library for handling "predicate" types, which are function parameters in the standard library.
3+
*
4+
* For example, `std::sort` takes a predicate as its third argument, and `std::set` takes a
5+
* predicate as its second template parameter. Predicates are always template parameters, and we
6+
* can identify them by their name -- this is what MISRA expects us to do.
7+
*/
8+
9+
import cpp
10+
private import codingstandards.cpp.StdNamespace
11+
private import codingstandards.cpp.ast.Templates
12+
private import codingstandards.cpp.types.Templates
13+
private import semmle.code.cpp.dataflow.new.DataFlow
14+
15+
/**
16+
* A "predicate" type parameter as defined by MISRA, which is a standard library function named
17+
* "Compare" or "Predicate" or "BinaryPredicate" in the standard library.
18+
*
19+
* To be more widely useful, we match more flexibly on the name, as `_Compare` is also common, etc.
20+
*
21+
* To get a particular `Type` that is _used_ as a predicate type, rather than the type parameter
22+
* itself, see `getASubstitutedType()`.
23+
*/
24+
class PredicateType extends TypeTemplateParameter {
25+
PredicateType() {
26+
this.getName().matches(["%Compare%", "%Predicate%"]) and
27+
getTemplateParameterNamespace(this) instanceof StdNS
28+
}
29+
30+
/**
31+
* Get a type that is used (anywhere, via some substitution) as this predicate type parameter.
32+
*
33+
* For example, `std::sort(..., ..., [](int a, int b) { return a < b; })` creates a `Closure`
34+
* type, and substitutes that closure type for the predicate type parameter of `std::sort`.
35+
*/
36+
Type getASubstitutedType(Substitution sub) { result = sub.getSubstitutedTypeForParameter(this) }
37+
}
38+
39+
/**
40+
* A class type that has been substituted for a predicate type parameter, and has an `operator()`
41+
* member function.
42+
*
43+
* For example, the closure type in `std::sort(..., ..., [](int a, int b) { return a < b; })` is a
44+
* `PredicateFunctionObject`, and so is any `std::less<int>` that is used as a predicate type
45+
* parameter, etc.
46+
*
47+
* This does not cover function pointer types, as these are not class types.
48+
*/
49+
class PredicateFunctionObject extends Class {
50+
PredicateType pred;
51+
Function operator;
52+
Substitution sub;
53+
54+
PredicateFunctionObject() {
55+
this = pred.getASubstitutedType(sub) and
56+
operator.getDeclaringType() = this and
57+
operator.getName() = "operator()"
58+
}
59+
60+
/**
61+
* Get the predicate type parameter that this function object is being substituted for.
62+
*/
63+
PredicateType getPredicateType() { result = pred }
64+
65+
/**
66+
* Get the `operator()` function that this function object defines. This is the function that will
67+
* be invoked and essentially defines the predicate behavior.
68+
*/
69+
Function getCallOperator() { result = operator }
70+
71+
/**
72+
* Get the `Substitution` object that makes this type a `PredicateFunctionObject`.
73+
*
74+
* This is a particular instantiation of some template that contains a predicate type parameter,
75+
* which is substituted by this type in that instantiation. The `Substitution` object may refer
76+
* to a `TemplateClass`, `TemplateVariable`, or `TemplateFunction`.
77+
*/
78+
Substitution getSubstitution() { result = sub }
79+
}
80+
81+
/**
82+
* Gets a function access where the purpose of that access is to pass the accessed function as a
83+
* predicate argument to a standard library template.
84+
*
85+
* For example, in `std::sort(..., ..., myCompare)`, where `myCompare` is a function, then
86+
* `myCompare` will be converted into a function pointer and that function pointer will be used as
87+
* a predicate in that `std::sort` call.
88+
*
89+
* This is more complex to identify than `PredicateFunctionObject` because the addressee of the
90+
* function pointer is not necessarily statically known.
91+
*/
92+
class PredicateFunctionPointerUse extends FunctionAccess {
93+
Expr functionPointerArgument;
94+
FunctionCall templateFunctionCall;
95+
FunctionTemplateInstantiation instantiation;
96+
Substitution sub;
97+
PredicateType pred;
98+
Parameter parameter;
99+
int index;
100+
101+
PredicateFunctionPointerUse() {
102+
functionPointerArgument = templateFunctionCall.getArgument(index) and
103+
templateFunctionCall.getTarget() = instantiation and
104+
parameter = instantiation.getParameter(index) and
105+
sub.asFunctionSubstitution() = instantiation and
106+
parameter.getType() = sub.getSubstitutedTypeForParameter(pred) and
107+
exists(DataFlow::Node func, DataFlow::Node arg |
108+
func.asExpr() = this and
109+
arg.asExpr() = functionPointerArgument and
110+
DataFlow::localFlow(func, arg)
111+
)
112+
}
113+
114+
/**
115+
* Get the predicate type parameter that this function pointer is being substituted for.
116+
*/
117+
PredicateType getPredicateType() { result = pred }
118+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import cpp
2+
3+
private newtype TSubstitution =
4+
TClassSubstitution(ClassTemplateInstantiation cti) or
5+
TFunctionSubstitution(FunctionTemplateInstantiation fti) or
6+
TVariableSubstitution(VariableTemplateInstantiation vti)
7+
8+
/**
9+
* A class to facilitate working with template substitutions, especially since templates may be a
10+
* `TemplateClass`, `TemplateFunction`, or `TemplateVariable`, which complicates their usage.
11+
*
12+
* A `Substitution` in particular refers to an instantiation of that template of some kind, and
13+
* allows analysis of which parameters were substituted with which types in that instantiation.
14+
*/
15+
class Substitution extends TSubstitution {
16+
ClassTemplateInstantiation asClassSubstitution() { this = TClassSubstitution(result) }
17+
18+
FunctionTemplateInstantiation asFunctionSubstitution() { this = TFunctionSubstitution(result) }
19+
20+
VariableTemplateInstantiation asVariableSubstitution() { this = TVariableSubstitution(result) }
21+
22+
/**
23+
* Get the nth template parameter type of the template that is being substituted.
24+
*
25+
* For example, in `std::vector<int>`, the 0th template parameter is `typename T`.
26+
*/
27+
TypeTemplateParameter getTemplateParameter(int index) {
28+
result = this.asClassSubstitution().getTemplate().getTemplateArgument(index) or
29+
result = this.asFunctionSubstitution().getTemplate().getTemplateArgument(index) or
30+
result = this.asVariableSubstitution().getTemplate().getTemplateArgument(index)
31+
}
32+
33+
/**
34+
* Get the type that is substituting the nth template parameter in this substitution.
35+
*
36+
* For example, in `std::vector<int>`, the 0th substituted type is `int`.
37+
*/
38+
Type getSubstitutedType(int index) {
39+
result = this.asClassSubstitution().getTemplateArgument(index) or
40+
result = this.asFunctionSubstitution().getTemplateArgument(index) or
41+
result = this.asVariableSubstitution().getTemplateArgument(index)
42+
}
43+
44+
/**
45+
* Get the type that is substituting the given template parameter in this substitution.
46+
*
47+
* For example, in `std::vector<int>`, this predicate matches the given type `int` with the type
48+
* parameter `typename T`.
49+
*/
50+
Type getSubstitutedTypeForParameter(TypeTemplateParameter param) {
51+
exists(int idx |
52+
this.getTemplateParameter(idx) = param and
53+
result = this.getSubstitutedType(idx)
54+
)
55+
}
56+
57+
string toString() {
58+
result = this.asClassSubstitution().toString() or
59+
result = this.asFunctionSubstitution().toString() or
60+
result = this.asVariableSubstitution().toString()
61+
}
62+
63+
/**
64+
* Get a `Locatable` that represents where this substitution was declared in the source code.
65+
*
66+
* The result may be a `TypeMention`, `Call`, etc. depending on the kind of template and how it is
67+
* being used, but it handles the various template cases for you.
68+
*
69+
* Note that this instantiation may have been declared in multiple places.
70+
*/
71+
Locatable getASubstitutionSite() {
72+
result.(TypeMention).getMentionedType() = this.asClassSubstitution()
73+
or
74+
result.(Call).getTarget() = this.asFunctionSubstitution()
75+
or
76+
result.(FunctionAccess).getTarget() = this.asFunctionSubstitution()
77+
or
78+
result.(VariableAccess).getTarget() = this.asVariableSubstitution()
79+
}
80+
}

cpp/common/test/includes/standard-library/deque.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define _GHLIBCPP_DEQUE
33
#include <iterator>
44
#include <string>
5+
#include "memory.h"
56
#include <initializer_list>
67
#include <empty.h>
78

cpp/common/test/includes/standard-library/functional.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,36 @@ template <class R, class... Args> class function<R(Args...)> {
121121
template <class F> function &operator=(F &&);
122122
};
123123

124+
template <typename T>
125+
struct less {
126+
bool operator()(const T &x, const T &y) const;
127+
typedef T first_argument_type;
128+
typedef T second_argument_type;
129+
typedef bool result_type;
130+
};
131+
template <typename T>
132+
struct less_equal {
133+
bool operator()(const T &x, const T &y) const;
134+
typedef T first_argument_type;
135+
typedef T second_argument_type;
136+
typedef bool result_type;
137+
};
138+
139+
template <typename T>
140+
struct greater {
141+
bool operator()(const T &x, const T &y) const;
142+
typedef T first_argument_type;
143+
typedef T second_argument_type;
144+
typedef bool result_type;
145+
};
146+
147+
template <typename T>
148+
struct greater_equal {
149+
bool operator()(const T &x, const T &y) const;
150+
typedef T first_argument_type;
151+
typedef T second_argument_type;
152+
typedef bool result_type;
153+
};
124154
template <class T = void> struct plus {
125155
using result_type = T; // deprecated in C++17
126156
using first_argument_type = T; // deprecated in C++17

cpp/common/test/includes/standard-library/iosfwd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#ifndef _GHLIBCPP_IOSFWD
22
#define _GHLIBCPP_IOSFWD
3+
#include "memory.h"
34
namespace std {
45
template <class charT> class char_traits;
56
template <> class char_traits<char>;

cpp/common/test/includes/standard-library/map

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#include "iterator.h"
2-
#include <memory>
3-
#include <utility>
4-
#include <bits/stl_function.h>
5-
#include <empty.h>
2+
#include "utility.h"
3+
#include "functional.h"
4+
#include "memory.h"
5+
#include "empty.h"
66

77
namespace std {
88

cpp/common/test/includes/standard-library/set

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "iterator.h"
2-
#include <bits/stl_function.h>
3-
#include <empty.h>
2+
#include "memory.h"
3+
#include "functional.h"
4+
#include "empty.h"
45

56
namespace std {
67
template <typename _Key, typename _Compare = std::less<_Key>,

0 commit comments

Comments
 (0)