/** * 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() { usings(underlyingElement(this), _, _, 1) } /** * 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() { usings(underlyingElement(this), _, _, 2) } /** * 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() } } /** * A C++ `using enum` declaration. For example: * ``` * enum class Foo { a, b }; * using enum Foo; * ``` */ class UsingEnumDeclarationEntry extends UsingEntry { UsingEnumDeclarationEntry() { usings(underlyingElement(this), _, _, 3) } /** * Gets the enumeration that is referenced by this using directive. For * example, `Foo` in `using enum Foo`. */ Enum getEnum() { usings(underlyingElement(this), unresolveElement(result), _, _) } override string toString() { result = "using enum " + this.getEnum().getQualifiedName() } } /** * 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 and its inline namespaces. */ class StdNamespace extends Namespace { StdNamespace() { this.hasName("std") and this.getParentNamespace() instanceof GlobalNamespace or this.isInline() and this.getParentNamespace() instanceof StdNamespace } }