Skip to content

Commit 0a2e288

Browse files
committed
C++: Rework how qualified names are computed
1 parent b51ce87 commit 0a2e288

File tree

9 files changed

+261
-178
lines changed

9 files changed

+261
-178
lines changed

cpp/ql/src/semmle/code/cpp/Declaration.qll

Lines changed: 56 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import semmle.code.cpp.Element
22
import semmle.code.cpp.Specifier
33
import semmle.code.cpp.Namespace
4+
private import semmle.code.cpp.internal.QualifiedName as Q
45

56
/**
67
* A C/C++ declaration: for example, a variable declaration, a type
@@ -30,16 +31,7 @@ abstract class Declaration extends Locatable, @declaration {
3031
* namespace of the structure.
3132
*/
3233
Namespace getNamespace() {
33-
// Top level declaration in a namespace ...
34-
result.getADeclaration() = this
35-
36-
// ... or nested in another structure.
37-
or
38-
exists (Declaration m
39-
| m = this and result = m.getDeclaringType().getNamespace())
40-
or
41-
exists (EnumConstant c
42-
| c = this and result = c.getDeclaringEnum().getNamespace())
34+
result = this.(Q::Declaration).getNamespace()
4335
or
4436
exists (Parameter p
4537
| p = this and result = p.getFunction().getNamespace())
@@ -50,40 +42,56 @@ abstract class Declaration extends Locatable, @declaration {
5042

5143
/**
5244
* Gets the name of the declaration, fully qualified with its
53-
* namespace. For example: "A::B::C::myfcn".
45+
* namespace and declaring type.
46+
*
47+
* For performance, prefer the multi-argument `hasQualifiedName` or
48+
* `hasGlobalName` predicates since they don't construct so many intermediate
49+
* strings. For debugging, the `semmle.code.cpp.Print` module produces more
50+
* detailed output but are also more expensive to compute.
51+
*
52+
* Example: `getQualifiedName() =
53+
* "namespace1::namespace2::TemplateClass1<int>::Class2::memberName"`.
5454
*/
5555
string getQualifiedName() {
56-
// MemberFunction, MemberVariable, MemberType
57-
exists (Declaration m
58-
| m = this and
59-
not m instanceof EnumConstant and
60-
result = m.getDeclaringType().getQualifiedName() + "::" + m.getName())
61-
or
62-
exists (EnumConstant c
63-
| c = this and
64-
result = c.getDeclaringEnum().getQualifiedName() + "::" + c.getName())
65-
or
66-
exists (GlobalOrNamespaceVariable v, string s1, string s2
67-
| v = this and
68-
s2 = v.getNamespace().getQualifiedName() and
69-
s1 = v.getName()
70-
| (s2 != "" and result = s2 + "::" + s1) or (s2 = "" and result = s1))
71-
or
72-
exists (Function f, string s1, string s2
73-
| f = this and f.isTopLevel() and
74-
s2 = f.getNamespace().getQualifiedName() and
75-
s1 = f.getName()
76-
| (s2 != "" and result = s2 + "::" + s1) or (s2 = "" and result = s1))
77-
or
78-
exists (UserType t, string s1, string s2
79-
| t = this and t.isTopLevel() and
80-
s2 = t.getNamespace().getQualifiedName() and
81-
s1 = t.getName()
82-
| (s2 != "" and result = s2 + "::" + s1) or (s2 = "" and result = s1))
56+
result = this.(Q::Declaration).getQualifiedName()
8357
}
8458

85-
predicate hasQualifiedName(string name) {
86-
this.getQualifiedName() = name
59+
/**
60+
* Holds if this declaration has the fully-qualified name `qualifiedName`.
61+
* See `getQualifiedName`.
62+
*/
63+
predicate hasQualifiedName(string qualifiedName) {
64+
this.getQualifiedName() = qualifiedName
65+
}
66+
67+
/**
68+
* Holds if this declaration has a fully-qualified name with a name-space
69+
* component of `namespaceQualifier`, a declaring type of `typeQualifier`,
70+
* and a base name of `baseName`. Template parameters and arguments are
71+
* stripped from all components. Missing components are `""`.
72+
*
73+
* Example: `hasQualifiedName("namespace1::namespace2",
74+
* "TemplateClass1::Class2", "memberName")`.
75+
*
76+
* Example (the class `std::vector`): `hasQualifiedName("std", "", "vector")`
77+
* or `hasQualifiedName("std", "vector")`.
78+
*
79+
* Example (the `size` member function of class `std::vector`):
80+
* `hasQualifiedName("std", "vector", "size")`.
81+
*/
82+
predicate hasQualifiedName(string namespaceQualifier, string typeQualifier, string baseName) {
83+
this.(Q::Declaration).hasQualifiedName(namespaceQualifier, typeQualifier, baseName)
84+
}
85+
86+
/**
87+
* Holds if this declaration has a fully-qualified name with a name-space
88+
* component of `namespaceQualifier`, no declaring type, and a base name of
89+
* `baseName`.
90+
*
91+
* See the 3-argument `hasQualifiedName` for more examples.
92+
*/
93+
predicate hasQualifiedName(string namespaceQualifier, string baseName) {
94+
this.hasQualifiedName(namespaceQualifier, "", baseName)
8795
}
8896

8997
override string toString() { result = this.getName() }
@@ -93,22 +101,24 @@ abstract class Declaration extends Locatable, @declaration {
93101
*
94102
* This name doesn't include a namespace or any argument types, so
95103
* for example both functions `::open()` and `::std::ifstream::open(...)`
96-
* have the same name.
104+
* have the same name. The name of a template _class_ includes a string
105+
* representation of its parameters, and the names of its instantiations
106+
* include string representations of their arguments. Template _functions_
107+
* and their instantiations do not include template parameters or arguments.
97108
*
98-
* To get the name including the namespace, use `getQualifiedName` or
99-
* `hasQualifiedName`.
109+
* To get the name including the namespace, use `hasQualifiedName`.
100110
*
101111
* To test whether this declaration has a particular name in the global
102112
* namespace, use `hasGlobalName`.
103113
*/
104-
abstract string getName();
114+
string getName() { result = this.(Q::Declaration).getName() }
115+
105116
/** Holds if this declaration has the given name. */
106117
predicate hasName(string name) { name = this.getName() }
107118

108119
/** Holds if this declaration has the given name in the global namespace. */
109120
predicate hasGlobalName(string name) {
110-
hasName(name)
111-
and getNamespace() instanceof GlobalNamespace
121+
this.hasQualifiedName("", "", name)
112122
}
113123

114124
/** Gets a specifier of this declaration. */

cpp/ql/src/semmle/code/cpp/Enum.qll

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,6 @@ class EnumConstant extends Declaration, @enumconstant {
100100
result = this.getDeclaringEnum().getDeclaringType()
101101
}
102102

103-
/**
104-
* Gets the name of this enumerator.
105-
*/
106-
override string getName() { enumconstants(underlyingElement(this),_,_,_,result,_) }
107-
108103
/**
109104
* Gets the value that this enumerator is initialized to, as a
110105
* string. This can be a value explicitly given to the enumerator, or an

cpp/ql/src/semmle/code/cpp/FriendDecl.qll

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,6 @@ class FriendDecl extends Declaration, @frienddecl {
3535
/** Gets the location of this friend declaration. */
3636
override Location getLocation() { frienddecls(underlyingElement(this),_,_,result) }
3737

38-
/** Gets a descriptive string for this friend declaration. */
39-
override string getName() {
40-
result = this.getDeclaringClass().getName() + "'s friend"
41-
}
42-
4338
/**
4439
* Friend declarations do not have specifiers. It makes no difference
4540
* whether they are declared in a public, protected or private section of

cpp/ql/src/semmle/code/cpp/Function.qll

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ private import semmle.code.cpp.internal.ResolveClass
1717
* in more detail in `Declaration.qll`.
1818
*/
1919
class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
20-
override string getName() { functions(underlyingElement(this),result,_) }
21-
2220
/**
2321
* DEPRECATED: Use `getIdentityString(Declaration)` from `semmle.code.cpp.Print` instead.
2422
* Gets the full signature of this function, including return type, parameter

cpp/ql/src/semmle/code/cpp/ObjectiveC.qll

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,6 @@ deprecated class FinallyBlock extends Block {
148148
deprecated class Property extends Declaration {
149149
Property() { none() }
150150

151-
/** Gets the name of this property. */
152-
override string getName() { none() }
153-
154151
/**
155152
* Gets nothing (provided for compatibility with Declaration).
156153
*

cpp/ql/src/semmle/code/cpp/Parameter.qll

Lines changed: 9 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import semmle.code.cpp.Location
22
import semmle.code.cpp.Declaration
33
private import semmle.code.cpp.internal.ResolveClass
4+
private import semmle.code.cpp.internal.QualifiedName as Q
45

56
/**
67
* A C/C++ function parameter or catch block parameter.
@@ -13,26 +14,6 @@ private import semmle.code.cpp.internal.ResolveClass
1314
* have multiple declarations.
1415
*/
1516
class Parameter extends LocalScopeVariable, @parameter {
16-
17-
/**
18-
* Gets the canonical name, or names, of this parameter.
19-
*
20-
* The canonical names are the first non-empty category from the
21-
* following list:
22-
* 1. The name given to the parameter at the function's definition or
23-
* (for catch block parameters) at the catch block.
24-
* 2. A name given to the parameter at a function declaration.
25-
* 3. The name "p#i" where i is the index of the parameter.
26-
*/
27-
override string getName() {
28-
exists (VariableDeclarationEntry vde
29-
| vde = getANamedDeclarationEntry() and result = vde.getName()
30-
| vde.isDefinition() or not getANamedDeclarationEntry().isDefinition())
31-
or
32-
(not exists(getANamedDeclarationEntry()) and
33-
result = "p#" + this.getIndex().toString())
34-
}
35-
3617
/**
3718
* Gets the name of this parameter, including it's type.
3819
*
@@ -53,27 +34,6 @@ class Parameter extends LocalScopeVariable, @parameter {
5334
else result = typeString + nameString))
5435
}
5536

56-
private VariableDeclarationEntry getANamedDeclarationEntry() {
57-
result = getAnEffectiveDeclarationEntry() and result.getName() != ""
58-
}
59-
60-
/**
61-
* Gets a declaration entry corresponding to this declaration.
62-
*
63-
* This predicate is the same as getADeclarationEntry(), except that for
64-
* parameters of instantiated function templates, gives the declaration
65-
* entry of the prototype instantiation of the parameter (as
66-
* non-prototype instantiations don't have declaration entries of their
67-
* own).
68-
*/
69-
private VariableDeclarationEntry getAnEffectiveDeclarationEntry() {
70-
if getFunction().isConstructedFrom(_)
71-
then exists (Function prototypeInstantiation
72-
| prototypeInstantiation.getParameter(getIndex()) = result.getVariable() and
73-
getFunction().isConstructedFrom(prototypeInstantiation))
74-
else result = getADeclarationEntry()
75-
}
76-
7737
/**
7838
* Gets the name of this parameter in the given block (which should be
7939
* the body of a function with which the parameter is associated).
@@ -95,7 +55,7 @@ class Parameter extends LocalScopeVariable, @parameter {
9555
* In other words, this predicate holds precisely when the result of
9656
* `getName()` is not "p#i" (where `i` is the index of the parameter).
9757
*/
98-
predicate isNamed() { exists(getANamedDeclarationEntry()) }
58+
predicate isNamed() { exists(this.(Q::Parameter).getANamedDeclarationEntry()) }
9959

10060
/**
10161
* Gets the function to which this parameter belongs, if it is a function
@@ -135,8 +95,13 @@ class Parameter extends LocalScopeVariable, @parameter {
13595
* of the declaration locations.
13696
*/
13797
override Location getLocation() {
138-
exists(VariableDeclarationEntry vde | vde = getAnEffectiveDeclarationEntry() and result = vde.getLocation() |
139-
vde.isDefinition() or not getAnEffectiveDeclarationEntry().isDefinition()
98+
exists(VariableDeclarationEntry vde |
99+
vde = this.(Q::Parameter).getAnEffectiveDeclarationEntry() and
100+
result = vde.getLocation()
101+
|
102+
vde.isDefinition()
103+
or
104+
not this.(Q::Parameter).getAnEffectiveDeclarationEntry().isDefinition()
140105
)
141106
}
142107
}

cpp/ql/src/semmle/code/cpp/UserType.qll

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ private import semmle.code.cpp.internal.ResolveClass
99
* `Enum`, and `TypedefType`.
1010
*/
1111
class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @usertype {
12-
/**
13-
* Gets the name of this type.
14-
*/
15-
override string getName() { usertypes(underlyingElement(this),result,_) }
12+
override string getName() {
13+
result = Declaration.super.getName()
14+
}
1615

1716
/**
1817
* Gets the simple name of this type, without any template parameters. For example

cpp/ql/src/semmle/code/cpp/Variable.qll

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,6 @@ class Variable extends Declaration, @variable {
4141
/** Holds if this variable is `volatile`. */
4242
predicate isVolatile() { this.getType().isVolatile() }
4343

44-
/** Gets the name of this variable. */
45-
override string getName() { none() }
46-
4744
/** Gets the type of this variable. */
4845
Type getType() { none() }
4946

@@ -291,8 +288,6 @@ deprecated class StackVariable extends Variable {
291288
* A local variable can be declared by a `DeclStmt` or a `ConditionDeclExpr`.
292289
*/
293290
class LocalVariable extends LocalScopeVariable, @localvariable {
294-
override string getName() { localvariables(underlyingElement(this),_,result) }
295-
296291
override Type getType() { localvariables(underlyingElement(this),unresolveElement(result),_) }
297292

298293
override Function getFunction() {
@@ -305,8 +300,6 @@ class LocalVariable extends LocalScopeVariable, @localvariable {
305300
* A C/C++ variable which has global scope or namespace scope.
306301
*/
307302
class GlobalOrNamespaceVariable extends Variable, @globalvariable {
308-
override string getName() { globalvariables(underlyingElement(this),_,result) }
309-
310303
override Type getType() { globalvariables(underlyingElement(this),unresolveElement(result),_) }
311304

312305
override Element getEnclosingElement() { none() }
@@ -354,8 +347,6 @@ class MemberVariable extends Variable, @membervariable {
354347
/** Holds if this member is public. */
355348
predicate isPublic() { this.hasSpecifier("public") }
356349

357-
override string getName() { membervariables(underlyingElement(this),_,result) }
358-
359350
override Type getType() {
360351
if (strictcount(this.getAType()) = 1) then (
361352
result = this.getAType()

0 commit comments

Comments
 (0)