mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
C#: Move qualified name computation into QualifiedName.qll
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
import CIL
|
||||
private import dotnet
|
||||
private import semmle.code.csharp.Member as CS
|
||||
private import semmle.code.csharp.commons.QualifiedName
|
||||
|
||||
/**
|
||||
* A declaration. Either a member (`Member`) or a variable (`Variable`).
|
||||
@@ -23,6 +24,14 @@ class Declaration extends DotNet::Declaration, Element, @cil_declaration {
|
||||
}
|
||||
|
||||
override Declaration getUnboundDeclaration() { result = this }
|
||||
|
||||
override predicate hasQualifiedName(string qualifier, string name) {
|
||||
exists(string dqualifier, string dname |
|
||||
this.getDeclaringType().hasQualifiedName(dqualifier, dname) and
|
||||
qualifier = getQualifiedName(dqualifier, dname)
|
||||
) and
|
||||
name = this.getName()
|
||||
}
|
||||
}
|
||||
|
||||
private CS::Declaration toCSharpNonTypeParameter(Declaration d) { result.matchesHandle(d) }
|
||||
|
||||
@@ -472,16 +472,6 @@ class Operator extends Callable, Member, Attributable, Overridable, @operator {
|
||||
override string toString() { result = Callable.super.toString() }
|
||||
|
||||
override Parameter getRawParameter(int i) { result = this.getParameter(i) }
|
||||
|
||||
override predicate hasQualifiedName(string qualifier, string name) {
|
||||
super.hasQualifiedName(qualifier, _) and
|
||||
name = this.getFunctionName()
|
||||
}
|
||||
|
||||
override predicate hasQualifiedName(string namespace, string type, string name) {
|
||||
super.hasQualifiedName(namespace, type, _) and
|
||||
name = this.getFunctionName()
|
||||
}
|
||||
}
|
||||
|
||||
/** A clone method on a record. */
|
||||
@@ -1113,14 +1103,6 @@ class LocalFunction extends Callable, Modifiable, Attributable, @local_function
|
||||
|
||||
override Callable getEnclosingCallable() { result = this.getStatement().getEnclosingCallable() }
|
||||
|
||||
override predicate hasQualifiedName(string qualifier, string name) {
|
||||
exists(string cqualifier, string type |
|
||||
this.getEnclosingCallable().hasQualifiedName(cqualifier, type) and
|
||||
qualifier = getQualifiedName(cqualifier, type)
|
||||
) and
|
||||
name = this.getName()
|
||||
}
|
||||
|
||||
override Location getALocation() { result = this.getStatement().getALocation() }
|
||||
|
||||
override Parameter getRawParameter(int i) { result = this.getParameter(i) }
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
*/
|
||||
|
||||
import Location
|
||||
import Member
|
||||
private import semmle.code.csharp.ExprOrStmtParent
|
||||
private import dotnet
|
||||
private import commons.QualifiedName
|
||||
|
||||
/**
|
||||
* A program element. Either a control flow element (`ControlFlowElement`), an
|
||||
|
||||
@@ -101,20 +101,6 @@ private string getTypeArgumentsNames(ConstructedGeneric cg) {
|
||||
result = strictconcat(Type t, int i | t = cg.getTypeArgument(i) | t.getName(), "," order by i)
|
||||
}
|
||||
|
||||
bindingset[t]
|
||||
private string getFullName(Type t) {
|
||||
exists(string qualifier, string name |
|
||||
t.hasQualifiedName(qualifier, name) and
|
||||
result = getQualifiedName(qualifier, name)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the concatenation of the `getFullName` of type arguments. */
|
||||
language[monotonicAggregates]
|
||||
private string getTypeArgumentsQualifiedNames(ConstructedGeneric cg) {
|
||||
result = strictconcat(Type t, int i | t = cg.getTypeArgument(i) | getFullName(t), "," order by i)
|
||||
}
|
||||
|
||||
/**
|
||||
* An unbound generic type. This is a generic type with type parameters
|
||||
* (for example `List<T>`) or elided type parameters (for example `List<>`).
|
||||
@@ -161,19 +147,6 @@ class UnboundGenericType extends ValueOrRefType, UnboundGeneric {
|
||||
final override string getName() {
|
||||
result = this.getUndecoratedName() + "<" + getTypeParameterCommas(this) + ">"
|
||||
}
|
||||
|
||||
final override predicate hasQualifiedName(string qualifier, string name) {
|
||||
exists(string name0 | name = name0 + "<" + getTypeParameterCommas(this) + ">" |
|
||||
exists(string enclosing |
|
||||
this.getDeclaringType().hasQualifiedName(qualifier, enclosing) and
|
||||
name0 = enclosing + "+" + this.getUndecoratedName()
|
||||
)
|
||||
or
|
||||
not exists(this.getDeclaringType()) and
|
||||
qualifier = this.getNamespace().getFullName() and
|
||||
name0 = this.getUndecoratedName()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,11 +213,6 @@ class TypeParameter extends DotNet::TypeParameter, Type, @type_parameter {
|
||||
/** Gets the generic that defines this type parameter. */
|
||||
UnboundGeneric getGeneric() { type_parameters(this, _, result, _) }
|
||||
|
||||
final override predicate hasQualifiedName(string qualifier, string name) {
|
||||
qualifier = "" and
|
||||
name = this.getName()
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TypeParameter" }
|
||||
}
|
||||
|
||||
@@ -440,19 +408,6 @@ class ConstructedType extends ValueOrRefType, ConstructedGeneric {
|
||||
final override string getName() {
|
||||
result = this.getUndecoratedName() + "<" + getTypeArgumentsNames(this) + ">"
|
||||
}
|
||||
|
||||
override predicate hasQualifiedName(string qualifier, string name) {
|
||||
exists(string name0 | name = name0 + "<" + getTypeArgumentsQualifiedNames(this) + ">" |
|
||||
exists(string enclosing |
|
||||
this.getDeclaringType().hasQualifiedName(qualifier, enclosing) and
|
||||
name0 = enclosing + "+" + this.getUndecoratedName()
|
||||
)
|
||||
or
|
||||
not exists(this.getDeclaringType()) and
|
||||
qualifier = this.getNamespace().getFullName() and
|
||||
name0 = this.getUndecoratedName()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -624,11 +579,6 @@ class ConstructedMethod extends Method, ConstructedGeneric {
|
||||
result = this.getUndecoratedName() + "<" + getTypeArgumentsNames(this) + ">"
|
||||
}
|
||||
|
||||
override predicate hasQualifiedName(string namespace, string type, string name) {
|
||||
this.getDeclaringType().hasQualifiedName(namespace, type) and
|
||||
name = this.getUndecoratedName() + "<" + getTypeArgumentsQualifiedNames(this) + ">"
|
||||
}
|
||||
|
||||
final override string getUndecoratedName() { methods(this, result, _, _, _) }
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@ import Variable
|
||||
private import dotnet
|
||||
private import Implements
|
||||
private import TypeRef
|
||||
private import commons.QualifiedName
|
||||
|
||||
private module QualifiedNameInput implements QualifiedNameInputSig { }
|
||||
|
||||
/**
|
||||
* A declaration.
|
||||
@@ -21,6 +24,10 @@ class Declaration extends DotNet::Declaration, Element, @declaration {
|
||||
|
||||
override string toString() { result = this.getName() }
|
||||
|
||||
override predicate hasQualifiedName(string qualifier, string name) {
|
||||
QualifiedName<QualifiedNameInput>::hasQualifiedName(this, qualifier, name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the fully qualified name of this declaration, including types, for example
|
||||
* the fully qualified name with types of `M` on line 3 is `N.C.M(int, string)` in
|
||||
@@ -199,6 +206,10 @@ class Member extends DotNet::Member, Modifiable, @member {
|
||||
override predicate isRequired() { Modifiable.super.isRequired() }
|
||||
|
||||
override predicate isFile() { Modifiable.super.isFile() }
|
||||
|
||||
final override predicate hasQualifiedName(string namespace, string type, string name) {
|
||||
QualifiedName<QualifiedNameInput>::hasQualifiedName(this, namespace, type, name)
|
||||
}
|
||||
}
|
||||
|
||||
private class TOverridable = @virtualizable or @callable_accessor;
|
||||
|
||||
@@ -7,7 +7,7 @@ private import dotnet
|
||||
/**
|
||||
* A type container. Either a namespace (`Namespace`) or a type (`Type`).
|
||||
*/
|
||||
class TypeContainer extends DotNet::NamedElement, Element, @type_container { }
|
||||
class TypeContainer extends Declaration, @type_container { }
|
||||
|
||||
/**
|
||||
* A namespace, for example
|
||||
@@ -30,6 +30,10 @@ class Namespace extends DotNet::Namespace, TypeContainer, Declaration, @namespac
|
||||
parent_namespace(result, this)
|
||||
}
|
||||
|
||||
override predicate hasQualifiedName(string qualifier, string name) {
|
||||
DotNet::Namespace.super.hasQualifiedName(qualifier, name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a type directly declared in this namespace, if any.
|
||||
* For example, the class `File` in
|
||||
|
||||
@@ -56,23 +56,6 @@ private predicate isObjectClass(Class c) { c instanceof ObjectType }
|
||||
* Either a value type (`ValueType`) or a reference type (`RefType`).
|
||||
*/
|
||||
class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_or_ref_type {
|
||||
/**
|
||||
* Holds if this type has the qualified name `qualifier`.`name`.
|
||||
*
|
||||
* For example the class `System.IO.IOException` has
|
||||
* `qualifier`=`System.IO` and `name`=`IOException`.
|
||||
*/
|
||||
override predicate hasQualifiedName(string qualifier, string name) {
|
||||
exists(string enclosing |
|
||||
this.getDeclaringType().hasQualifiedName(qualifier, enclosing) and
|
||||
name = enclosing + "+" + this.getUndecoratedName()
|
||||
)
|
||||
or
|
||||
not exists(this.getDeclaringType()) and
|
||||
qualifier = this.getNamespace().getFullName() and
|
||||
name = this.getUndecoratedName()
|
||||
}
|
||||
|
||||
/** Gets the namespace containing this type. */
|
||||
Namespace getNamespace() {
|
||||
if exists(this.getDeclaringType())
|
||||
@@ -409,11 +392,6 @@ class NonNestedType extends ValueOrRefType {
|
||||
* The `void` type.
|
||||
*/
|
||||
class VoidType extends ValueOrRefType, @void_type {
|
||||
override predicate hasQualifiedName(string qualifier, string name) {
|
||||
qualifier = "System" and
|
||||
name = "Void"
|
||||
}
|
||||
|
||||
final override string getName() { result = "Void" }
|
||||
|
||||
final override string getUndecoratedName() { result = "Void" }
|
||||
@@ -1028,11 +1006,6 @@ class NullableType extends ValueType, ConstructedType, @nullable_type {
|
||||
override Type getTypeArgument(int p) { p = 0 and result = this.getUnderlyingType() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "NullableType" }
|
||||
|
||||
final override predicate hasQualifiedName(string qualifier, string name) {
|
||||
qualifier = "System" and
|
||||
name = "Nullable<" + this.getUnderlyingType().getQualifiedName() + ">"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1102,13 +1075,6 @@ class ArrayType extends DotNet::ArrayType, RefType, @array_type {
|
||||
not type_location(this, _) and
|
||||
result = this.getElementType().getALocation()
|
||||
}
|
||||
|
||||
final override predicate hasQualifiedName(string qualifier, string name) {
|
||||
exists(Type elementType, string name0 |
|
||||
elementType.hasQualifiedName(qualifier, name0) and
|
||||
name = name0 + this.getDimensionString(elementType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1137,13 +1103,6 @@ class PointerType extends DotNet::PointerType, Type, @pointer_type {
|
||||
override string toString() { result = DotNet::PointerType.super.toString() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PointerType" }
|
||||
|
||||
final override predicate hasQualifiedName(string qualifier, string name) {
|
||||
exists(string name0 |
|
||||
this.getReferentType().hasQualifiedName(qualifier, name0) and
|
||||
name = name0 + "*"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1230,10 +1189,6 @@ class TupleType extends ValueType, @tuple_type {
|
||||
|
||||
override Type getChild(int i) { result = this.getUnderlyingType().getChild(i) }
|
||||
|
||||
final override predicate hasQualifiedName(string qualifier, string name) {
|
||||
this.getUnderlyingType().hasQualifiedName(qualifier, name)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TupleType" }
|
||||
}
|
||||
|
||||
|
||||
@@ -75,8 +75,6 @@ class LocalScopeVariable extends Variable, @local_scope_variable {
|
||||
* Holds if this local variable or parameter is `scoped`.
|
||||
*/
|
||||
predicate isScoped() { scoped_annotation(this, _) }
|
||||
|
||||
override predicate hasQualifiedName(string qualifier, string name) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,201 @@
|
||||
/** Provides predicates for working with fully qualified names. */
|
||||
|
||||
private import csharp
|
||||
private import dotnet
|
||||
|
||||
/**
|
||||
* Provides predicates related to C# qualified names.
|
||||
* Holds if namespace `n` has the qualified name `qualifier`.`name`.
|
||||
*
|
||||
* For example if the qualified name is `System.Collections.Generic`, then
|
||||
* `qualifier`=`System.Collections` and `name`=`Generic`.
|
||||
*/
|
||||
predicate namespaceHasQualifiedName(DotNet::Namespace n, string qualifier, string name) {
|
||||
if n instanceof DotNet::GlobalNamespace
|
||||
then qualifier = "" and name = ""
|
||||
else (
|
||||
exists(string pqualifier, string pname |
|
||||
namespaceHasQualifiedName(n.getParentNamespace(), pqualifier, pname) and
|
||||
qualifier = getQualifiedName(pqualifier, pname)
|
||||
) and
|
||||
name = n.getName()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a string of `N` commas where `N + 1` is the number of type parameters of this unbound generic. */
|
||||
private string getTypeParameterCommas(UnboundGeneric ug) {
|
||||
result = strictconcat(int i | exists(ug.getTypeParameter(i)) | "", ",")
|
||||
}
|
||||
|
||||
/** Provides the input to `QualifiedName`. */
|
||||
signature module QualifiedNameInputSig {
|
||||
/** Gets the suffix to print after unbound generic `ug`. */
|
||||
default string getUnboundGenericSuffix(UnboundGeneric ug) {
|
||||
result = "<" + getTypeParameterCommas(ug) + ">"
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides predicates for computing fully qualified names. */
|
||||
module QualifiedName<QualifiedNameInputSig Input> {
|
||||
private string getDimensionString(ArrayType at, Type elementType) {
|
||||
exists(Type et, string res |
|
||||
et = at.getElementType() and
|
||||
res = at.getArraySuffix() and
|
||||
if et instanceof ArrayType
|
||||
then result = res + getDimensionString(et, elementType)
|
||||
else (
|
||||
result = res and elementType = et
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[t]
|
||||
private string getFullName(Type t) {
|
||||
exists(string qualifier, string name |
|
||||
hasQualifiedName(t, qualifier, name) and
|
||||
result = getQualifiedName(qualifier, name)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the concatenation of the `getFullName` of type arguments. */
|
||||
language[monotonicAggregates]
|
||||
private string getTypeArgumentsQualifiedNames(ConstructedGeneric cg) {
|
||||
result =
|
||||
strictconcat(Type t, int i | t = cg.getTypeArgument(i) | getFullName(t), "," order by i)
|
||||
}
|
||||
|
||||
/** Holds if declaration `d` has the qualified name `qualifier`.`name`. */
|
||||
predicate hasQualifiedName(Declaration d, string qualifier, string name) {
|
||||
d =
|
||||
any(ValueOrRefType vort |
|
||||
vort instanceof VoidType and
|
||||
qualifier = "System" and
|
||||
name = "Void"
|
||||
or
|
||||
vort =
|
||||
any(ArrayType at |
|
||||
exists(Type elementType, string name0 |
|
||||
hasQualifiedName(elementType, qualifier, name0) and
|
||||
name = name0 + getDimensionString(at, elementType)
|
||||
)
|
||||
)
|
||||
or
|
||||
hasQualifiedName(vort.(TupleType).getUnderlyingType(), qualifier, name)
|
||||
or
|
||||
qualifier = "System" and
|
||||
name = "Nullable<" + getFullName(vort.(NullableType).getUnderlyingType()) + ">"
|
||||
or
|
||||
vort =
|
||||
any(UnboundGenericType ugt |
|
||||
exists(string name0 | name = name0 + Input::getUnboundGenericSuffix(ugt) |
|
||||
exists(string enclosing |
|
||||
hasQualifiedName(ugt.getDeclaringType(), qualifier, enclosing) and
|
||||
name0 = enclosing + "+" + ugt.getUndecoratedName()
|
||||
)
|
||||
or
|
||||
not exists(ugt.getDeclaringType()) and
|
||||
qualifier = ugt.getNamespace().getFullName() and
|
||||
name0 = ugt.getUndecoratedName()
|
||||
)
|
||||
)
|
||||
or
|
||||
vort =
|
||||
any(ConstructedType ct |
|
||||
exists(string name0 | name = name0 + "<" + getTypeArgumentsQualifiedNames(ct) + ">" |
|
||||
exists(string enclosing |
|
||||
hasQualifiedName(ct.getDeclaringType(), qualifier, enclosing) and
|
||||
name0 = enclosing + "+" + ct.getUndecoratedName()
|
||||
)
|
||||
or
|
||||
not exists(ct.getDeclaringType()) and
|
||||
qualifier = ct.getNamespace().getFullName() and
|
||||
name0 = ct.getUndecoratedName()
|
||||
)
|
||||
)
|
||||
or
|
||||
not vort instanceof VoidType and
|
||||
not vort instanceof ArrayType and
|
||||
not vort instanceof TupleType and
|
||||
not vort instanceof NullableType and
|
||||
not vort instanceof UnboundGenericType and
|
||||
not vort instanceof ConstructedType and
|
||||
(
|
||||
exists(string enclosing |
|
||||
hasQualifiedName(vort.getDeclaringType(), qualifier, enclosing) and
|
||||
name = enclosing + "+" + vort.getUndecoratedName()
|
||||
)
|
||||
or
|
||||
not exists(vort.getDeclaringType()) and
|
||||
qualifier = vort.getNamespace().getFullName() and
|
||||
name = vort.getUndecoratedName()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(string name0 |
|
||||
hasQualifiedName(d.(PointerType).getReferentType(), qualifier, name0) and
|
||||
name = name0 + "*"
|
||||
)
|
||||
or
|
||||
qualifier = "" and
|
||||
name = d.(TypeParameter).getName()
|
||||
or
|
||||
d =
|
||||
any(LocalFunction lf |
|
||||
exists(string cqualifier, string type |
|
||||
hasQualifiedName(lf.getEnclosingCallable(), cqualifier, type) and
|
||||
qualifier = getQualifiedName(cqualifier, type)
|
||||
) and
|
||||
name = lf.getName()
|
||||
)
|
||||
or
|
||||
// no case for `LocalScopeVariable`
|
||||
namespaceHasQualifiedName(d, qualifier, name)
|
||||
or
|
||||
not d instanceof ValueOrRefType and
|
||||
not d instanceof PointerType and
|
||||
not d instanceof TypeParameter and
|
||||
not d instanceof LocalFunction and
|
||||
not d instanceof LocalScopeVariable and
|
||||
not d instanceof Namespace and
|
||||
exists(string dqualifier, string dname |
|
||||
hasQualifiedName(d.getDeclaringType(), dqualifier, dname) and
|
||||
qualifier = getQualifiedName(dqualifier, dname)
|
||||
) and
|
||||
(
|
||||
name = d.(Operator).getFunctionName()
|
||||
or
|
||||
not d instanceof Operator and
|
||||
name = d.getName()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if member `m` has name `name` and is defined in type `type`
|
||||
* with namespace `namespace`.
|
||||
*/
|
||||
predicate hasQualifiedName(Member m, string namespace, string type, string name) {
|
||||
m =
|
||||
any(ConstructedMethod cm |
|
||||
hasQualifiedName(cm.getDeclaringType(), namespace, type) and
|
||||
name = cm.getUndecoratedName() + "<" + getTypeArgumentsQualifiedNames(cm) + ">"
|
||||
)
|
||||
or
|
||||
m =
|
||||
any(UnboundGenericMethod ugm |
|
||||
hasQualifiedName(ugm.getDeclaringType(), namespace, type) and
|
||||
name = ugm.getUndecoratedName() + Input::getUnboundGenericSuffix(ugm)
|
||||
)
|
||||
or
|
||||
not m instanceof ConstructedMethod and
|
||||
not m instanceof UnboundGenericMethod and
|
||||
hasQualifiedName(m.getDeclaringType(), namespace, type) and
|
||||
(
|
||||
name = m.(Operator).getFunctionName()
|
||||
or
|
||||
not m instanceof Operator and
|
||||
name = m.getName()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the concatenation of `qualifier` and `name`, separated by a dot.
|
||||
|
||||
@@ -8,14 +8,6 @@ private import semmle.code.csharp.commons.QualifiedName
|
||||
|
||||
/** A declaration. */
|
||||
class Declaration extends NamedElement, @dotnet_declaration {
|
||||
override predicate hasQualifiedName(string qualifier, string name) {
|
||||
exists(string dqualifier, string dname |
|
||||
this.getDeclaringType().hasQualifiedName(dqualifier, dname) and
|
||||
qualifier = getQualifiedName(dqualifier, dname)
|
||||
) and
|
||||
name = this.getName()
|
||||
}
|
||||
|
||||
/** Gets the name of this declaration, without additional decoration such as `<...>`. */
|
||||
string getUndecoratedName() { none() }
|
||||
|
||||
|
||||
@@ -26,11 +26,7 @@ class Namespace extends Declaration, @namespace {
|
||||
* `qualifier`=`System.Collections` and `name`=`Generic`.
|
||||
*/
|
||||
override predicate hasQualifiedName(string qualifier, string name) {
|
||||
exists(string pqualifier, string pname |
|
||||
this.getParentNamespace().hasQualifiedName(pqualifier, pname) and
|
||||
qualifier = getQualifiedName(pqualifier, pname)
|
||||
) and
|
||||
name = this.getName()
|
||||
namespaceHasQualifiedName(this, qualifier, name)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this namespace. */
|
||||
@@ -51,7 +47,7 @@ class Namespace extends Declaration, @namespace {
|
||||
*/
|
||||
string getFullName() {
|
||||
exists(string namespace, string name |
|
||||
this.hasQualifiedName(namespace, name) and
|
||||
namespaceHasQualifiedName(this, namespace, name) and
|
||||
result = getQualifiedName(namespace, name)
|
||||
)
|
||||
}
|
||||
@@ -60,8 +56,4 @@ class Namespace extends Declaration, @namespace {
|
||||
/** The global namespace. */
|
||||
class GlobalNamespace extends Namespace {
|
||||
GlobalNamespace() { this.getName() = "" }
|
||||
|
||||
override predicate hasQualifiedName(string qualifier, string name) {
|
||||
qualifier = "" and name = ""
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user