Files
codeql/cpp/ql/lib/semmle/code/cpp/Namespace.qll
2022-03-09 18:28:07 +01:00

238 lines
7.3 KiB
Plaintext

/**
* Provides classes for modeling namespaces, `using` directives and `using` declarations.
*/
import semmle.code.cpp.Element
import semmle.code.cpp.Type
import semmle.code.cpp.metrics.MetricNamespace
/**
* A C++ namespace. For example the (single) namespace `A` in the following
* code:
* ```
* namespace A
* {
* // ...
* }
*
* // ...
*
* namespace A
* {
* // ...
* }
* ```
* Note that namespaces are somewhat nebulous entities, as they do not in
* general have a single well-defined location in the source code. The
* related notion of a `NamespaceDeclarationEntry` is rather more concrete,
* and should be used when a location is required. For example, the `std::`
* namespace is particularly nebulous, as parts of it are defined across a
* wide range of headers. As a more extreme example, the global namespace
* is never explicitly declared, but might correspond to a large proportion
* of the source code.
*/
class Namespace extends NameQualifyingElement, @namespace {
/**
* Gets the location of the namespace. Most namespaces do not have a
* single well-defined source location, so a dummy location is returned,
* unless the namespace has exactly one declaration entry.
*/
override Location getLocation() {
if strictcount(this.getADeclarationEntry()) = 1
then result = this.getADeclarationEntry().getLocation()
else result instanceof UnknownDefaultLocation
}
/** Gets the simple name of this namespace. */
override string getName() { namespaces(underlyingElement(this), result) }
/** Holds if this element is named `name`. */
predicate hasName(string name) { name = this.getName() }
/** Holds if this namespace is anonymous. */
predicate isAnonymous() { this.hasName("(unnamed namespace)") }
/** Gets the name of the parent namespace, if it exists. */
private string getParentName() {
result = this.getParentNamespace().getName() and
result != ""
}
/** Gets the qualified name of this namespace. For example: `a::b`. */
string getQualifiedName() {
if exists(this.getParentName())
then result = this.getParentNamespace().getQualifiedName() + "::" + this.getName()
else result = this.getName()
}
/** Gets the parent namespace, if any. */
Namespace getParentNamespace() {
namespacembrs(unresolveElement(result), underlyingElement(this))
or
not namespacembrs(_, underlyingElement(this)) and result instanceof GlobalNamespace
}
/** Gets a child declaration of this namespace. */
Declaration getADeclaration() { namespacembrs(underlyingElement(this), unresolveElement(result)) }
/** Gets a child namespace of this namespace. */
Namespace getAChildNamespace() {
namespacembrs(underlyingElement(this), unresolveElement(result))
}
/** Holds if the namespace is inline. */
predicate isInline() { namespace_inline(underlyingElement(this)) }
/** Holds if this namespace may be from source. */
override predicate fromSource() { this.getADeclaration().fromSource() }
/** Gets the metric namespace. */
MetricNamespace getMetrics() { result = this }
/** Gets a version of the `QualifiedName` that is more suitable for display purposes. */
string getFriendlyName() { result = this.getQualifiedName() }
final override string toString() { result = this.getFriendlyName() }
/** Gets a declaration of (part of) this namespace. */
NamespaceDeclarationEntry getADeclarationEntry() { result.getNamespace() = this }
/** Gets a file which declares (part of) this namespace. */
File getAFile() { result = this.getADeclarationEntry().getLocation().getFile() }
}
/**
* A declaration of (part of) a C++ namespace. This corresponds to a single
* `namespace N { ... }` occurrence in the source code. For example the two
* mentions of `A` in the following code:
* ```
* namespace A
* {
* // ...
* }
*
* // ...
*
* namespace A
* {
* // ...
* }
* ```
*/
class NamespaceDeclarationEntry extends Locatable, @namespace_decl {
/**
* Get the namespace that this declaration entry corresponds to. There
* is a one-to-many relationship between `Namespace` and
* `NamespaceDeclarationEntry`.
*/
Namespace getNamespace() {
namespace_decls(underlyingElement(this), unresolveElement(result), _, _)
}
override string toString() { result = this.getNamespace().getFriendlyName() }
/**
* Gets the location of the token preceding the namespace declaration
* entry's body.
*
* For named declarations, such as "namespace MyStuff { ... }", this will
* give the "MyStuff" token.
*
* For anonymous declarations, such as "namespace { ... }", this will
* give the "namespace" token.
*/
override Location getLocation() { namespace_decls(underlyingElement(this), _, result, _) }
/**
* Gets the location of the namespace declaration entry's body. For
* example: the "{ ... }" in "namespace N { ... }".
*/
Location getBodyLocation() { namespace_decls(underlyingElement(this), _, _, result) }
override string getAPrimaryQlClass() { result = "NamespaceDeclarationEntry" }
}
/**
* A C++ `using` directive or `using` declaration.
*/
class UsingEntry extends Locatable, @using {
override Location getLocation() { usings(underlyingElement(this), _, result) }
}
/**
* A C++ `using` declaration. For example:
* ```
* using std::string;
* ```
*/
class UsingDeclarationEntry extends UsingEntry {
UsingDeclarationEntry() {
not exists(Namespace n | usings(underlyingElement(this), unresolveElement(n), _))
}
/**
* Gets the declaration that is referenced by this using declaration. For
* example, `std::string` in `using std::string`.
*/
Declaration getDeclaration() { usings(underlyingElement(this), unresolveElement(result), _) }
override string toString() { result = "using " + this.getDeclaration().getDescription() }
}
/**
* A C++ `using` directive. For example:
* ```
* using namespace std;
* ```
*/
class UsingDirectiveEntry extends UsingEntry {
UsingDirectiveEntry() {
exists(Namespace n | usings(underlyingElement(this), unresolveElement(n), _))
}
/**
* Gets the namespace that is referenced by this using directive. For
* example, `std` in `using namespace std`.
*/
Namespace getNamespace() { usings(underlyingElement(this), unresolveElement(result), _) }
override string toString() { result = "using namespace " + this.getNamespace().getFriendlyName() }
}
/**
* Holds if `g` is an instance of `GlobalNamespace`. This predicate
* is used suppress a warning in `GlobalNamespace.getADeclaration()`
* by providing a fake use of `this`.
*/
private predicate suppressWarningForUnused(GlobalNamespace g) { any() }
/**
* The C/C++ global namespace.
*/
class GlobalNamespace extends Namespace {
GlobalNamespace() { this.hasName("") }
override Declaration getADeclaration() {
suppressWarningForUnused(this) and
result.isTopLevel() and
not namespacembrs(_, unresolveElement(result))
}
/** Gets a child namespace of the global namespace. */
override Namespace getAChildNamespace() {
suppressWarningForUnused(this) and
not namespacembrs(unresolveElement(result), _)
}
override Namespace getParentNamespace() { none() }
override string getFriendlyName() { result = "(global namespace)" }
}
/**
* The C++ `std::` namespace.
*/
class StdNamespace extends Namespace {
StdNamespace() { this.hasName("std") and this.getParentNamespace() instanceof GlobalNamespace }
}