Skip to content

Commit 69d18f2

Browse files
authored
Merge pull request #2 from rvermeulen/rvermeulen/lambda-scope
Handle lambda scope using `Scope` class
2 parents 908b6e9 + 20ef634 commit 69d18f2

File tree

3 files changed

+90
-16
lines changed

3 files changed

+90
-16
lines changed

cpp/common/src/codingstandards/cpp/rules/identifierhidden/IdentifierHidden.qll

+25-16
Original file line numberDiff line numberDiff line change
@@ -23,43 +23,52 @@ class NonVolatileConstIntegralOrEnumType extends IntegralOrEnumType {
2323
}
2424

2525
/**
26-
* Holds if declaration `innerDecl`, declared in a lambda, hides a declaration `outerDecl` captured by the lambda.
26+
* Holds if declaration `innerDecl`, declared in a lambda, hides a declaration `outerDecl` by the lambda.
2727
*/
2828
predicate hiddenInLambda(UserVariable outerDecl, UserVariable innerDecl) {
29-
exists(Scope s, Closure le |
30-
//innerDecl declared inside of the lambda
31-
s.getADeclaration() = innerDecl and
32-
s.getAnAncestor() = le and
33-
//a variable can be accessed (therefore hide) another when:
34-
//it is explicitly captured
29+
exists(
30+
Scope innerScope, LambdaExpression lambdaExpr, Scope lambdaExprScope, Scope outerScope,
31+
Closure lambdaClosure
32+
|
33+
// The variable `innerDecl` is declared inside of the lambda.
34+
innerScope.getADeclaration() = innerDecl and
35+
// Because a lambda is compiled down to a closure, we need to use the closure to determine if the declaration
36+
// is part of the lambda.
37+
innerScope.getAnAncestor() = lambdaClosure and
38+
// Next we determine the scope of the lambda expression to determine if `outerDecl` is visible in the scope of the lambda.
39+
lambdaClosure.getLambdaExpression() = lambdaExpr and
40+
lambdaExprScope.getAnExpr() = lambdaExpr and
41+
outerScope.getADeclaration() = outerDecl and
42+
lambdaExprScope.getStrictParent*() = outerScope and
3543
(
44+
// A definition can be hidden if it is in scope and it is captured by the lambda,
3645
exists(LambdaCapture cap |
37-
outerDecl.getAnAccess() = cap.getInitializer().(VariableAccess) and
38-
le.getLambdaExpression().getACapture() = cap and
39-
//captured variable (outerDecl) is in the same (function) scope as the lambda itself
40-
outerDecl.getParentScope() = le.getEnclosingFunction().getBasicBlock().(Scope)
46+
lambdaExpr.getACapture() = cap and
47+
// The outer declaration is captured by the lambda
48+
outerDecl.getAnAccess() = cap.getInitializer()
4149
)
4250
or
43-
//is non-local
51+
// it is is non-local,
4452
outerDecl instanceof GlobalVariable
4553
or
46-
//has static or thread local storage duration
54+
// it has static or thread local storage duration,
4755
(outerDecl.isThreadLocal() or outerDecl.isStatic())
4856
or
49-
//is a reference that has been initialized with a constant expression.
57+
//it is a reference that has been initialized with a constant expression.
5058
outerDecl.getType().stripTopLevelSpecifiers() instanceof ReferenceType and
5159
exists(outerDecl.getInitializer().getExpr().getValue())
5260
or
53-
//const non-volatile integral or enumeration type and has been initialized with a constant expression
61+
//it const non-volatile integral or enumeration type and has been initialized with a constant expression
5462
outerDecl.getType() instanceof NonVolatileConstIntegralOrEnumType and
5563
exists(outerDecl.getInitializer().getExpr().getValue())
5664
or
57-
//is constexpr and has no mutable members
65+
//it is constexpr and has no mutable members
5866
outerDecl.isConstexpr() and
5967
not exists(Class c |
6068
c = outerDecl.getType() and not c.getAMember() instanceof MutableVariable
6169
)
6270
) and
71+
// Finally, the variables must have the same names.
6372
innerDecl.getName() = outerDecl.getName()
6473
)
6574
}

cpp/common/test/rules/identifierhidden/IdentifierHidden.expected

+4
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@
1111
| test.cpp:81:5:81:5 | a | Declaration is hiding declaration $@. | test.cpp:79:5:79:5 | a | a |
1212
| test.cpp:102:9:102:9 | b | Declaration is hiding declaration $@. | test.cpp:96:11:96:11 | b | b |
1313
| test.cpp:114:9:114:17 | globalvar | Declaration is hiding declaration $@. | test.cpp:110:5:110:13 | globalvar | globalvar |
14+
| test.cpp:133:11:133:11 | b | Declaration is hiding declaration $@. | test.cpp:127:13:127:13 | b | b |
15+
| test.cpp:142:9:142:10 | a1 | Declaration is hiding declaration $@. | test.cpp:140:14:140:15 | a1 | a1 |
16+
| test.cpp:147:9:147:10 | a2 | Declaration is hiding declaration $@. | test.cpp:145:20:145:21 | a2 | a2 |
17+
| test.cpp:152:9:152:10 | a3 | Declaration is hiding declaration $@. | test.cpp:150:17:150:18 | a3 | a3 |

cpp/common/test/rules/identifierhidden/test.cpp

+61
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,65 @@ int f6() {
119119
auto lambda_without_shadowing = []() { return globalvar + globalvar; };
120120

121121
return lambda_with_shadowing();
122+
}
123+
124+
void f7(int p) {
125+
// Introduce a nested scope to test scope comparison.
126+
if (p != 0) {
127+
int a1, b;
128+
auto lambda1 = [a1]() {
129+
int b = 10; // COMPLIANT - exception - non captured variable b
130+
};
131+
132+
auto lambda2 = [b]() {
133+
int b = 10; // NON_COMPLIANT - not an exception - captured
134+
// variable b
135+
};
136+
}
137+
}
138+
139+
void f8() {
140+
static int a1;
141+
auto lambda1 = []() {
142+
int a1 = 10; // NON_COMPLIANT - Lambda can access static variable.
143+
};
144+
145+
thread_local int a2;
146+
auto lambda2 = []() {
147+
int a2 = 10; // NON_COMPLIANT - Lambda can access thread local variable.
148+
};
149+
150+
constexpr int a3 = 10;
151+
auto lambda3 = []() {
152+
int a3 = a3 + 1; // NON_COMPLIANT - Lambda can access const
153+
// expression without mutable members.
154+
};
155+
156+
const int &a4 = a3;
157+
auto lambda4 = []() {
158+
int a4 = a4 + 1; // NON_COMPLIANT[FALSE_NEGATIVE] - Lambda can access
159+
// reference initialized with constant expression.
160+
};
161+
162+
const int a5 = 10;
163+
auto lambda5 = []() {
164+
int a5 = a5 + 1; // NON_COMPLIANT[FALSE_NEGATIVE] - Lambda can access const
165+
// non-volatile integral or enumeration type initialized
166+
// with constant expression.
167+
};
168+
169+
volatile const int a6 = 10;
170+
auto lambda6 = []() {
171+
int a6 =
172+
a6 + 1; // COMPLIANT - Lambda cannot access const volatile integral or
173+
// enumeration type initialized with constant expression.
174+
};
175+
}
176+
177+
void f9() {
178+
auto lambda1 = []() {
179+
int a1 = 10; // COMPLIANT
180+
};
181+
182+
int a1 = 10;
122183
}

0 commit comments

Comments
 (0)