mirror of
https://github.com/github/codeql.git
synced 2026-02-05 17:51:06 +01:00
304 lines
9.4 KiB
Plaintext
304 lines
9.4 KiB
Plaintext
/**
|
|
* INTERNAL: Do not use. Provides classes and predicates for getting names of
|
|
* declarations, especially qualified names. Import this library `private` and
|
|
* qualified.
|
|
*
|
|
* This file contains classes that mirror the standard AST classes for C++, but
|
|
* these classes are only concerned with naming.
|
|
*/
|
|
|
|
private import semmle.code.cpp.Declaration as D
|
|
|
|
class Namespace extends @namespace {
|
|
string toString() { result = "QualifiedName Namespace" }
|
|
|
|
string getName() { namespaces(this, result) }
|
|
|
|
string getQualifiedName() {
|
|
if namespacembrs(_, this)
|
|
then
|
|
exists(Namespace ns |
|
|
namespacembrs(ns, this) and
|
|
result = ns.getQualifiedName() + "::" + this.getName()
|
|
)
|
|
else result = this.getName()
|
|
}
|
|
|
|
/**
|
|
* Gets a namespace qualifier, like `"namespace1::namespace2"`, through which
|
|
* the members of this namespace can be named. When `inline namespace` is
|
|
* used, this predicate may have multiple results.
|
|
*
|
|
* This predicate does not take namespace aliases into account. Unlike inline
|
|
* namespaces, specialization of templates cannot happen through an alias.
|
|
* Aliases are also local to the compilation unit, while inline namespaces
|
|
* affect the whole program.
|
|
*/
|
|
string getAQualifierForMembers() {
|
|
if namespacembrs(_, this)
|
|
then
|
|
exists(Namespace ns | namespacembrs(ns, this) |
|
|
result = ns.getAQualifierForMembers() + "::" + this.getName()
|
|
or
|
|
// If this is an inline namespace, its members are also visible in any
|
|
// namespace where the members of the parent are visible.
|
|
namespace_inline(this) and
|
|
result = ns.getAQualifierForMembers()
|
|
)
|
|
else result = this.getName()
|
|
}
|
|
|
|
Declaration getADeclaration() {
|
|
if this.getName() = ""
|
|
then result.isTopLevel() and not namespacembrs(_, result)
|
|
else namespacembrs(this, result)
|
|
}
|
|
}
|
|
|
|
class Declaration extends @declaration {
|
|
string toString() { result = "QualifiedName Declaration" }
|
|
|
|
/** Gets the name of this declaration. */
|
|
final string getName() { result = this.(D::Declaration).getName() }
|
|
|
|
string getTypeQualifierWithoutArgs() {
|
|
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() {
|
|
// Top level declaration in a namespace ...
|
|
result.getADeclaration() = this
|
|
or
|
|
// ... or nested in another structure.
|
|
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() {
|
|
exists(string ns, string name |
|
|
ns = this.getNamespace().getQualifiedName() and
|
|
name = this.getName() and
|
|
this.canHaveQualifiedName()
|
|
|
|
|
exists(string t | t = this.getTypeQualifierWithArgs() |
|
|
if ns != "" then result = ns + "::" + t + "::" + name else result = t + "::" + name
|
|
)
|
|
or
|
|
not hasTypeQualifier(this) and
|
|
if ns != "" then result = ns + "::" + name else result = name
|
|
)
|
|
}
|
|
|
|
predicate canHaveQualifiedName() {
|
|
this.hasDeclaringType()
|
|
or
|
|
this instanceof EnumConstant
|
|
or
|
|
this instanceof Function
|
|
or
|
|
this instanceof UserType
|
|
or
|
|
this instanceof GlobalOrNamespaceVariable
|
|
}
|
|
|
|
predicate isTopLevel() {
|
|
not (
|
|
this.isMember() or
|
|
this instanceof FriendDecl or
|
|
this instanceof EnumConstant or
|
|
this instanceof Parameter or
|
|
this instanceof ProxyClass or
|
|
this instanceof LocalVariable or
|
|
this instanceof TemplateParameter or
|
|
this.(UserType).isLocal()
|
|
)
|
|
}
|
|
|
|
/** Holds if this declaration is a member of a class/struct/union. */
|
|
predicate isMember() { this.hasDeclaringType() }
|
|
|
|
/** Holds if this declaration is a member of a class/struct/union. */
|
|
predicate hasDeclaringType() { exists(this.getDeclaringType()) }
|
|
|
|
/**
|
|
* Gets the class where this member is declared, if it is a member.
|
|
* For templates, both the template itself and all instantiations of
|
|
* the template are considered to have the same declaring class.
|
|
*/
|
|
UserType getDeclaringType() { this = result.getAMember() }
|
|
}
|
|
|
|
class Variable extends Declaration, @variable {
|
|
VariableDeclarationEntry getADeclarationEntry() { result.getDeclaration() = this }
|
|
}
|
|
|
|
class TemplateVariable extends Variable {
|
|
TemplateVariable() { is_variable_template(this) }
|
|
|
|
Variable getAnInstantiation() { variable_instantiation(result, this) }
|
|
}
|
|
|
|
class LocalScopeVariable extends Variable, @localscopevariable { }
|
|
|
|
class LocalVariable extends LocalScopeVariable, @localvariable { }
|
|
|
|
/**
|
|
* A particular declaration or definition of a C/C++ variable.
|
|
*/
|
|
class VariableDeclarationEntry extends @var_decl {
|
|
string toString() { result = "QualifiedName DeclarationEntry" }
|
|
|
|
Variable getDeclaration() { result = this.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 {
|
|
@functionorblock function;
|
|
int index;
|
|
|
|
Parameter() { params(this, function, index, _) }
|
|
}
|
|
|
|
class GlobalOrNamespaceVariable extends Variable, @globalvariable { }
|
|
|
|
// Unlike the usual `EnumConstant`, this one doesn't have a
|
|
// `getDeclaringType()`. This simplifies the recursive computation of type
|
|
// qualifier names since it can assume that any declaration with a
|
|
// `getDeclaringType()` should use that type in its type qualifier name.
|
|
class EnumConstant extends Declaration, @enumconstant {
|
|
UserType getDeclaringEnum() { enumconstants(this, result, _, _, _, _) }
|
|
}
|
|
|
|
class Function extends Declaration, @function {
|
|
predicate isConstructedFrom(Function f) { function_instantiation(this, f) }
|
|
|
|
Parameter getParameter(int n) { params(result, this, n, _) }
|
|
}
|
|
|
|
class TemplateFunction extends Function {
|
|
TemplateFunction() { is_function_template(this) and function_template_argument(this, _, _) }
|
|
|
|
Function getAnInstantiation() {
|
|
function_instantiation(result, this) and
|
|
not exists(@fun_decl fd | fun_decls(fd, this, _, _, _) and fun_specialized(fd))
|
|
}
|
|
}
|
|
|
|
class UserType extends Declaration, @usertype {
|
|
predicate isLocal() { enclosingfunction(this, _) }
|
|
|
|
// Gets a member of this class, if it's a class.
|
|
Declaration getAMember() {
|
|
exists(Declaration d | member(this, _, d) |
|
|
result = d or
|
|
result = d.(TemplateClass).getAnInstantiation() or
|
|
result = d.(TemplateFunction).getAnInstantiation() or
|
|
result = d.(TemplateVariable).getAnInstantiation()
|
|
)
|
|
}
|
|
}
|
|
|
|
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) }
|
|
|
|
UserType getAnInstantiation() {
|
|
class_instantiation(result, this) and
|
|
class_template_argument(result, _, _)
|
|
}
|
|
}
|
|
|
|
class FriendDecl extends Declaration, @frienddecl {
|
|
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().getAQualifierForMembers() 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()
|
|
}
|