C++: Rework how qualified names are computed

This commit is contained in:
Jonas Jensen
2019-05-01 10:04:12 +02:00
parent b51ce87ae8
commit 0a2e28858a
9 changed files with 261 additions and 178 deletions

View File

@@ -1,6 +1,7 @@
import semmle.code.cpp.Element import semmle.code.cpp.Element
import semmle.code.cpp.Specifier import semmle.code.cpp.Specifier
import semmle.code.cpp.Namespace import semmle.code.cpp.Namespace
private import semmle.code.cpp.internal.QualifiedName as Q
/** /**
* A C/C++ declaration: for example, a variable declaration, a type * A C/C++ declaration: for example, a variable declaration, a type
@@ -30,16 +31,7 @@ abstract class Declaration extends Locatable, @declaration {
* namespace of the structure. * namespace of the structure.
*/ */
Namespace getNamespace() { Namespace getNamespace() {
// Top level declaration in a namespace ... result = this.(Q::Declaration).getNamespace()
result.getADeclaration() = this
// ... or nested in another structure.
or
exists (Declaration m
| m = this and result = m.getDeclaringType().getNamespace())
or
exists (EnumConstant c
| c = this and result = c.getDeclaringEnum().getNamespace())
or or
exists (Parameter p exists (Parameter p
| p = this and result = p.getFunction().getNamespace()) | p = this and result = p.getFunction().getNamespace())
@@ -50,40 +42,56 @@ abstract class Declaration extends Locatable, @declaration {
/** /**
* Gets the name of the declaration, fully qualified with its * Gets the name of the declaration, fully qualified with its
* namespace. For example: "A::B::C::myfcn". * namespace and declaring type.
*
* For performance, prefer the multi-argument `hasQualifiedName` or
* `hasGlobalName` predicates since they don't construct so many intermediate
* strings. For debugging, the `semmle.code.cpp.Print` module produces more
* detailed output but are also more expensive to compute.
*
* Example: `getQualifiedName() =
* "namespace1::namespace2::TemplateClass1<int>::Class2::memberName"`.
*/ */
string getQualifiedName() { string getQualifiedName() {
// MemberFunction, MemberVariable, MemberType result = this.(Q::Declaration).getQualifiedName()
exists (Declaration m
| m = this and
not m instanceof EnumConstant and
result = m.getDeclaringType().getQualifiedName() + "::" + m.getName())
or
exists (EnumConstant c
| c = this and
result = c.getDeclaringEnum().getQualifiedName() + "::" + c.getName())
or
exists (GlobalOrNamespaceVariable v, string s1, string s2
| v = this and
s2 = v.getNamespace().getQualifiedName() and
s1 = v.getName()
| (s2 != "" and result = s2 + "::" + s1) or (s2 = "" and result = s1))
or
exists (Function f, string s1, string s2
| f = this and f.isTopLevel() and
s2 = f.getNamespace().getQualifiedName() and
s1 = f.getName()
| (s2 != "" and result = s2 + "::" + s1) or (s2 = "" and result = s1))
or
exists (UserType t, string s1, string s2
| t = this and t.isTopLevel() and
s2 = t.getNamespace().getQualifiedName() and
s1 = t.getName()
| (s2 != "" and result = s2 + "::" + s1) or (s2 = "" and result = s1))
} }
predicate hasQualifiedName(string name) { /**
this.getQualifiedName() = name * Holds if this declaration has the fully-qualified name `qualifiedName`.
* See `getQualifiedName`.
*/
predicate hasQualifiedName(string qualifiedName) {
this.getQualifiedName() = qualifiedName
}
/**
* Holds if this declaration has a fully-qualified name with a name-space
* component of `namespaceQualifier`, a declaring type of `typeQualifier`,
* and a base name of `baseName`. Template parameters and arguments are
* stripped from all components. Missing components are `""`.
*
* Example: `hasQualifiedName("namespace1::namespace2",
* "TemplateClass1::Class2", "memberName")`.
*
* Example (the class `std::vector`): `hasQualifiedName("std", "", "vector")`
* or `hasQualifiedName("std", "vector")`.
*
* Example (the `size` member function of class `std::vector`):
* `hasQualifiedName("std", "vector", "size")`.
*/
predicate hasQualifiedName(string namespaceQualifier, string typeQualifier, string baseName) {
this.(Q::Declaration).hasQualifiedName(namespaceQualifier, typeQualifier, baseName)
}
/**
* Holds if this declaration has a fully-qualified name with a name-space
* component of `namespaceQualifier`, no declaring type, and a base name of
* `baseName`.
*
* See the 3-argument `hasQualifiedName` for more examples.
*/
predicate hasQualifiedName(string namespaceQualifier, string baseName) {
this.hasQualifiedName(namespaceQualifier, "", baseName)
} }
override string toString() { result = this.getName() } override string toString() { result = this.getName() }
@@ -93,22 +101,24 @@ abstract class Declaration extends Locatable, @declaration {
* *
* This name doesn't include a namespace or any argument types, so * This name doesn't include a namespace or any argument types, so
* for example both functions `::open()` and `::std::ifstream::open(...)` * for example both functions `::open()` and `::std::ifstream::open(...)`
* have the same name. * have the same name. The name of a template _class_ includes a string
* representation of its parameters, and the names of its instantiations
* include string representations of their arguments. Template _functions_
* and their instantiations do not include template parameters or arguments.
* *
* To get the name including the namespace, use `getQualifiedName` or * To get the name including the namespace, use `hasQualifiedName`.
* `hasQualifiedName`.
* *
* To test whether this declaration has a particular name in the global * To test whether this declaration has a particular name in the global
* namespace, use `hasGlobalName`. * namespace, use `hasGlobalName`.
*/ */
abstract string getName(); string getName() { result = this.(Q::Declaration).getName() }
/** Holds if this declaration has the given name. */ /** Holds if this declaration has the given name. */
predicate hasName(string name) { name = this.getName() } predicate hasName(string name) { name = this.getName() }
/** Holds if this declaration has the given name in the global namespace. */ /** Holds if this declaration has the given name in the global namespace. */
predicate hasGlobalName(string name) { predicate hasGlobalName(string name) {
hasName(name) this.hasQualifiedName("", "", name)
and getNamespace() instanceof GlobalNamespace
} }
/** Gets a specifier of this declaration. */ /** Gets a specifier of this declaration. */

View File

@@ -100,11 +100,6 @@ class EnumConstant extends Declaration, @enumconstant {
result = this.getDeclaringEnum().getDeclaringType() result = this.getDeclaringEnum().getDeclaringType()
} }
/**
* Gets the name of this enumerator.
*/
override string getName() { enumconstants(underlyingElement(this),_,_,_,result,_) }
/** /**
* Gets the value that this enumerator is initialized to, as a * Gets the value that this enumerator is initialized to, as a
* string. This can be a value explicitly given to the enumerator, or an * string. This can be a value explicitly given to the enumerator, or an

View File

@@ -35,11 +35,6 @@ class FriendDecl extends Declaration, @frienddecl {
/** Gets the location of this friend declaration. */ /** Gets the location of this friend declaration. */
override Location getLocation() { frienddecls(underlyingElement(this),_,_,result) } override Location getLocation() { frienddecls(underlyingElement(this),_,_,result) }
/** Gets a descriptive string for this friend declaration. */
override string getName() {
result = this.getDeclaringClass().getName() + "'s friend"
}
/** /**
* Friend declarations do not have specifiers. It makes no difference * Friend declarations do not have specifiers. It makes no difference
* whether they are declared in a public, protected or private section of * whether they are declared in a public, protected or private section of

View File

@@ -17,8 +17,6 @@ private import semmle.code.cpp.internal.ResolveClass
* in more detail in `Declaration.qll`. * in more detail in `Declaration.qll`.
*/ */
class Function extends Declaration, ControlFlowNode, AccessHolder, @function { class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
override string getName() { functions(underlyingElement(this),result,_) }
/** /**
* DEPRECATED: Use `getIdentityString(Declaration)` from `semmle.code.cpp.Print` instead. * DEPRECATED: Use `getIdentityString(Declaration)` from `semmle.code.cpp.Print` instead.
* Gets the full signature of this function, including return type, parameter * Gets the full signature of this function, including return type, parameter

View File

@@ -148,9 +148,6 @@ deprecated class FinallyBlock extends Block {
deprecated class Property extends Declaration { deprecated class Property extends Declaration {
Property() { none() } Property() { none() }
/** Gets the name of this property. */
override string getName() { none() }
/** /**
* Gets nothing (provided for compatibility with Declaration). * Gets nothing (provided for compatibility with Declaration).
* *

View File

@@ -1,6 +1,7 @@
import semmle.code.cpp.Location import semmle.code.cpp.Location
import semmle.code.cpp.Declaration import semmle.code.cpp.Declaration
private import semmle.code.cpp.internal.ResolveClass private import semmle.code.cpp.internal.ResolveClass
private import semmle.code.cpp.internal.QualifiedName as Q
/** /**
* A C/C++ function parameter or catch block parameter. * A C/C++ function parameter or catch block parameter.
@@ -13,26 +14,6 @@ private import semmle.code.cpp.internal.ResolveClass
* have multiple declarations. * have multiple declarations.
*/ */
class Parameter extends LocalScopeVariable, @parameter { class Parameter extends LocalScopeVariable, @parameter {
/**
* Gets the canonical name, or names, of this parameter.
*
* The canonical names are the first non-empty category from the
* following list:
* 1. The name given to the parameter at the function's definition or
* (for catch block parameters) at the catch block.
* 2. A name given to the parameter at a function declaration.
* 3. The name "p#i" where i is the index of the parameter.
*/
override string getName() {
exists (VariableDeclarationEntry vde
| vde = getANamedDeclarationEntry() and result = vde.getName()
| vde.isDefinition() or not getANamedDeclarationEntry().isDefinition())
or
(not exists(getANamedDeclarationEntry()) and
result = "p#" + this.getIndex().toString())
}
/** /**
* Gets the name of this parameter, including it's type. * Gets the name of this parameter, including it's type.
* *
@@ -53,27 +34,6 @@ class Parameter extends LocalScopeVariable, @parameter {
else result = typeString + nameString)) else result = typeString + nameString))
} }
private VariableDeclarationEntry getANamedDeclarationEntry() {
result = getAnEffectiveDeclarationEntry() and result.getName() != ""
}
/**
* Gets a declaration entry corresponding to this declaration.
*
* This predicate is the same as getADeclarationEntry(), except that for
* parameters of instantiated function templates, gives the declaration
* entry of the prototype instantiation of the parameter (as
* non-prototype instantiations don't have declaration entries of their
* own).
*/
private VariableDeclarationEntry getAnEffectiveDeclarationEntry() {
if getFunction().isConstructedFrom(_)
then exists (Function prototypeInstantiation
| prototypeInstantiation.getParameter(getIndex()) = result.getVariable() and
getFunction().isConstructedFrom(prototypeInstantiation))
else result = getADeclarationEntry()
}
/** /**
* Gets the name of this parameter in the given block (which should be * Gets the name of this parameter in the given block (which should be
* the body of a function with which the parameter is associated). * the body of a function with which the parameter is associated).
@@ -95,7 +55,7 @@ class Parameter extends LocalScopeVariable, @parameter {
* In other words, this predicate holds precisely when the result of * In other words, this predicate holds precisely when the result of
* `getName()` is not "p#i" (where `i` is the index of the parameter). * `getName()` is not "p#i" (where `i` is the index of the parameter).
*/ */
predicate isNamed() { exists(getANamedDeclarationEntry()) } predicate isNamed() { exists(this.(Q::Parameter).getANamedDeclarationEntry()) }
/** /**
* Gets the function to which this parameter belongs, if it is a function * Gets the function to which this parameter belongs, if it is a function
@@ -135,8 +95,13 @@ class Parameter extends LocalScopeVariable, @parameter {
* of the declaration locations. * of the declaration locations.
*/ */
override Location getLocation() { override Location getLocation() {
exists(VariableDeclarationEntry vde | vde = getAnEffectiveDeclarationEntry() and result = vde.getLocation() | exists(VariableDeclarationEntry vde |
vde.isDefinition() or not getAnEffectiveDeclarationEntry().isDefinition() vde = this.(Q::Parameter).getAnEffectiveDeclarationEntry() and
result = vde.getLocation()
|
vde.isDefinition()
or
not this.(Q::Parameter).getAnEffectiveDeclarationEntry().isDefinition()
) )
} }
} }

View File

@@ -9,10 +9,9 @@ private import semmle.code.cpp.internal.ResolveClass
* `Enum`, and `TypedefType`. * `Enum`, and `TypedefType`.
*/ */
class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @usertype { class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @usertype {
/** override string getName() {
* Gets the name of this type. result = Declaration.super.getName()
*/ }
override string getName() { usertypes(underlyingElement(this),result,_) }
/** /**
* Gets the simple name of this type, without any template parameters. For example * Gets the simple name of this type, without any template parameters. For example

View File

@@ -41,9 +41,6 @@ class Variable extends Declaration, @variable {
/** Holds if this variable is `volatile`. */ /** Holds if this variable is `volatile`. */
predicate isVolatile() { this.getType().isVolatile() } predicate isVolatile() { this.getType().isVolatile() }
/** Gets the name of this variable. */
override string getName() { none() }
/** Gets the type of this variable. */ /** Gets the type of this variable. */
Type getType() { none() } Type getType() { none() }
@@ -291,8 +288,6 @@ deprecated class StackVariable extends Variable {
* A local variable can be declared by a `DeclStmt` or a `ConditionDeclExpr`. * A local variable can be declared by a `DeclStmt` or a `ConditionDeclExpr`.
*/ */
class LocalVariable extends LocalScopeVariable, @localvariable { class LocalVariable extends LocalScopeVariable, @localvariable {
override string getName() { localvariables(underlyingElement(this),_,result) }
override Type getType() { localvariables(underlyingElement(this),unresolveElement(result),_) } override Type getType() { localvariables(underlyingElement(this),unresolveElement(result),_) }
override Function getFunction() { override Function getFunction() {
@@ -305,8 +300,6 @@ class LocalVariable extends LocalScopeVariable, @localvariable {
* A C/C++ variable which has global scope or namespace scope. * A C/C++ variable which has global scope or namespace scope.
*/ */
class GlobalOrNamespaceVariable extends Variable, @globalvariable { class GlobalOrNamespaceVariable extends Variable, @globalvariable {
override string getName() { globalvariables(underlyingElement(this),_,result) }
override Type getType() { globalvariables(underlyingElement(this),unresolveElement(result),_) } override Type getType() { globalvariables(underlyingElement(this),unresolveElement(result),_) }
override Element getEnclosingElement() { none() } override Element getEnclosingElement() { none() }
@@ -354,8 +347,6 @@ class MemberVariable extends Variable, @membervariable {
/** Holds if this member is public. */ /** Holds if this member is public. */
predicate isPublic() { this.hasSpecifier("public") } predicate isPublic() { this.hasSpecifier("public") }
override string getName() { membervariables(underlyingElement(this),_,result) }
override Type getType() { override Type getType() {
if (strictcount(this.getAType()) = 1) then ( if (strictcount(this.getAType()) = 1) then (
result = this.getAType() result = this.getAType()

View File

@@ -1,5 +1,3 @@
private import semmle.code.cpp.internal.ResolveClass as ResolveClass
class Namespace extends @namespace { class Namespace extends @namespace {
string toString() { result = "QualifiedName Namespace" } string toString() { result = "QualifiedName Namespace" }
@@ -26,61 +24,72 @@ abstract class Declaration extends @declaration {
string toString() { result = "QualifiedName Declaration" } string toString() { result = "QualifiedName Declaration" }
/** Gets the name of this declaration. */ /** Gets the name of this declaration. */
cached
abstract string getName(); abstract string getName();
// Note: This is not as full featured as the getNamespace in the AST, string getTypeQualifierWithoutArgs() {
// but it covers the cases we need here. exists(UserType declaringType |
declaringType = this.(EnumConstant).getDeclaringEnum()
or
declaringType = this.getDeclaringType()
|
result = getTypeQualifierForMembersWithoutArgs(declaringType)
)
}
string getTypeQualifierWithArgs() {
exists(UserType declaringType |
declaringType = this.(EnumConstant).getDeclaringEnum()
or
declaringType = this.getDeclaringType()
|
result = getTypeQualifierForMembersWithArgs(declaringType)
)
}
Namespace getNamespace() { Namespace getNamespace() {
// Top level declaration in a namespace ... // Top level declaration in a namespace ...
result.getADeclaration() = this result.getADeclaration() = this
or or
// ... or nested in another structure. // ... or nested in another structure.
exists(Declaration m | m = this and result = m.getDeclaringType().getNamespace()) exists(Declaration m | m = this and result = m.getDeclaringType().getNamespace())
or
exists(EnumConstant c | c = this and result = c.getDeclaringEnum().getNamespace())
}
predicate hasQualifiedName(string namespaceQualifier, string typeQualifier, string baseName) {
declarationHasQualifiedName(baseName, typeQualifier, namespaceQualifier, this)
} }
string getQualifiedName() { string getQualifiedName() {
// MemberFunction, MemberVariable, MemberType exists(string ns, string name |
exists(Declaration m | ns = this.getNamespace().getQualifiedName() and
m = this and name = this.getName() and
result = m.getDeclaringType().getQualifiedName() + "::" + m.getName() this.canHaveQualifiedName()
)
or
exists(EnumConstant c |
c = this and
result = c.getDeclaringEnum().getQualifiedName() + "::" + c.getName()
)
or
exists(GlobalOrNamespaceVariable v, string s1, string s2 |
v = this and
s2 = v.getNamespace().getQualifiedName() and
s1 = v.getName()
| |
s2 != "" and result = s2 + "::" + s1 exists(string t | t = this.getTypeQualifierWithArgs() |
if ns != ""
then result = ns + "::" + t + "::" + name
else result = t + "::" + name
)
or or
s2 = "" and result = s1 not hasTypeQualifier(this) and
if ns != ""
then result = ns + "::" + name
else result = name
) )
}
predicate canHaveQualifiedName() {
this.hasDeclaringType()
or or
exists(Function f, string s1, string s2 | this instanceof EnumConstant
f = this and
f.isTopLevel() and
s2 = f.getNamespace().getQualifiedName() and
s1 = f.getName()
|
s2 != "" and result = s2 + "::" + s1
or
s2 = "" and result = s1
)
or or
exists(UserType t, string s1, string s2 | this instanceof Function
t = this and or
t.isTopLevel() and this instanceof UserType
s2 = t.getNamespace().getQualifiedName() and or
s1 = t.getName() this instanceof GlobalOrNamespaceVariable
|
s2 != "" and result = s2 + "::" + s1
or
s2 = "" and result = s1
)
} }
predicate isTopLevel() { predicate isTopLevel() {
@@ -107,11 +116,13 @@ abstract class Declaration extends @declaration {
* For templates, both the template itself and all instantiations of * For templates, both the template itself and all instantiations of
* the template are considered to have the same declaring class. * the template are considered to have the same declaring class.
*/ */
Class getDeclaringType() { this = result.getAMember() } UserType getDeclaringType() { this = result.getAMember() }
} }
class Variable extends Declaration, @variable { class Variable extends Declaration, @variable {
override string getName() { none() } override string getName() { none() }
VariableDeclarationEntry getADeclarationEntry() { result.getDeclaration() = this }
} }
class TemplateVariable extends Variable { class TemplateVariable extends Variable {
@@ -126,9 +137,73 @@ class LocalVariable extends LocalScopeVariable, @localvariable {
override string getName() { localvariables(this, _, result) } override string getName() { localvariables(this, _, result) }
} }
/**
* A particular declaration or definition of a C/C++ variable.
*/
class VariableDeclarationEntry extends @var_decl {
string toString() { result = "QualifiedName DeclarationEntry" }
Variable getDeclaration() { result = getVariable() }
/**
* Gets the variable which is being declared or defined.
*/
Variable getVariable() { var_decls(this, result, _, _, _) }
predicate isDefinition() { var_def(this) }
string getName() { var_decls(this, _, _, result, _) and result != "" }
}
class Parameter extends LocalScopeVariable, @parameter { class Parameter extends LocalScopeVariable, @parameter {
@functionorblock function;
int index;
Parameter() { params(this, function, index, _) }
/**
* Gets the canonical name, or names, of this parameter.
*
* The canonical names are the first non-empty category from the
* following list:
* 1. The name given to the parameter at the function's definition or
* (for catch block parameters) at the catch block.
* 2. A name given to the parameter at a function declaration.
* 3. The name "p#i" where i is the index of the parameter.
*/
override string getName() { override string getName() {
exists(int i | params(this, _, i, _) and result = "p#" + i.toString()) exists(VariableDeclarationEntry vde |
vde = getANamedDeclarationEntry() and result = vde.getName()
|
vde.isDefinition() or not getANamedDeclarationEntry().isDefinition()
)
or
not exists(getANamedDeclarationEntry()) and
result = "p#" + index.toString()
}
VariableDeclarationEntry getANamedDeclarationEntry() {
result = getAnEffectiveDeclarationEntry() and exists(result.getName())
}
/**
* Gets a declaration entry corresponding to this declaration.
*
* This predicate is the same as getADeclarationEntry(), except that for
* parameters of instantiated function templates, gives the declaration
* entry of the prototype instantiation of the parameter (as
* non-prototype instantiations don't have declaration entries of their
* own).
*/
VariableDeclarationEntry getAnEffectiveDeclarationEntry() {
if function.(Function).isConstructedFrom(_)
then
exists(Function prototypeInstantiation |
prototypeInstantiation.getParameter(index) = result.getVariable() and
function.(Function).isConstructedFrom(prototypeInstantiation)
)
else result = getADeclarationEntry()
} }
} }
@@ -146,10 +221,15 @@ class EnumConstant extends Declaration, @enumconstant {
override string getName() { enumconstants(this, _, _, _, result, _) } override string getName() { enumconstants(this, _, _, _, result, _) }
UserType getDeclaringEnum() { enumconstants(this, result, _, _, _, _) } UserType getDeclaringEnum() { enumconstants(this, result, _, _, _, _) }
// Unlike the usual `EnumConstant`, this one doesn't have a `getDeclaringType()`.
} }
class Function extends Declaration, @function { class Function extends Declaration, @function {
override string getName() { functions(this, result, _) } override string getName() { functions(this, result, _) }
predicate isConstructedFrom(Function f) { function_instantiation(this, f) }
Parameter getParameter(int n) { params(result, this, n, _) }
} }
class TemplateFunction extends Function { class TemplateFunction extends Function {
@@ -162,22 +242,11 @@ class TemplateFunction extends Function {
} }
class UserType extends Declaration, @usertype { class UserType extends Declaration, @usertype {
override string getName() { usertypes(this, result, _) } override string getName() { result = getUserTypeNameWithArgs(this) }
predicate isLocal() { enclosingfunction(this, _) } predicate isLocal() { enclosingfunction(this, _) }
}
class ProxyClass extends UserType {
ProxyClass() { usertypes(this, _, 9) }
}
class TemplateParameter extends UserType {
TemplateParameter() { usertypes(this, _, 7) or usertypes(this, _, 8) }
}
class Class extends UserType {
Class() { ResolveClass::isClass(this) }
// Gets a member of this class, if it's a class.
Declaration getAMember() { Declaration getAMember() {
exists(Declaration d | member(this, _, d) | exists(Declaration d | member(this, _, d) |
result = d or result = d or
@@ -188,10 +257,18 @@ class Class extends UserType {
} }
} }
class TemplateClass extends Class { class ProxyClass extends UserType {
ProxyClass() { usertypes(this, _, 9) }
}
class TemplateParameter extends UserType {
TemplateParameter() { usertypes(this, _, 7) or usertypes(this, _, 8) }
}
class TemplateClass extends UserType {
TemplateClass() { usertypes(this, _, 6) } TemplateClass() { usertypes(this, _, 6) }
Class getAnInstantiation() { UserType getAnInstantiation() {
class_instantiation(result, this) and class_instantiation(result, this) and
class_template_argument(result, _, _) class_template_argument(result, _, _)
} }
@@ -204,7 +281,63 @@ deprecated class Property extends Declaration {
} }
class FriendDecl extends Declaration, @frienddecl { class FriendDecl extends Declaration, @frienddecl {
override string getName() { result = this.getDeclaringClass().getName() + "'s friend" } override string getName() {
result = getUserTypeNameWithArgs(this.getDeclaringClass()) + "'s friend"
}
Class getDeclaringClass() { frienddecls(this, result, _, _) } UserType getDeclaringClass() { frienddecls(this, result, _, _) }
}
private string getUserTypeNameWithArgs(UserType t) { usertypes(t, result, _) }
private string getUserTypeNameWithoutArgs(UserType t) {
result = getUserTypeNameWithArgs(t).splitAt("<", 0)
}
private predicate hasTypeQualifier(Declaration d) {
d instanceof EnumConstant
or
d.hasDeclaringType()
}
private string getTypeQualifierForMembersWithArgs(UserType t) {
result = t.getTypeQualifierWithArgs() + "::" + getUserTypeNameWithArgs(t)
or
not hasTypeQualifier(t) and
result = getUserTypeNameWithArgs(t)
}
private string getTypeQualifierForMembersWithoutArgs(UserType t) {
result = t.getTypeQualifierWithoutArgs() + "::" + getUserTypeNameWithoutArgs(t)
or
not hasTypeQualifier(t) and
result = getUserTypeNameWithoutArgs(t)
}
// The order of parameters on this predicate is chosen to match the most common
// use case: finding a declaration that has a specific name. The declaration
// comes last because it's the output.
cached
private predicate declarationHasQualifiedName(
string baseName, string typeQualifier, string namespaceQualifier, Declaration d
) {
namespaceQualifier = d.getNamespace().getQualifiedName() and
(
if hasTypeQualifier(d)
then typeQualifier = d.getTypeQualifierWithoutArgs()
else typeQualifier = ""
) and
(
baseName = getUserTypeNameWithoutArgs(d)
or
// If a declaration isn't a `UserType`, there are two ways it can still
// contain `<`:
// 1. If it's `operator<` or `operator<<`.
// 2. If it's a conversion operator like `operator TemplateClass<Arg>`.
// Perhaps these names ought to be fixed up, but we don't do that
// currently.
not d instanceof UserType and
baseName = d.getName()
) and
d.canHaveQualifiedName()
} }