C#: Move qualified name computation into QualifiedName.qll

This commit is contained in:
Tom Hvitved
2023-11-01 14:39:37 +01:00
parent e75562e508
commit c717e346fb
11 changed files with 224 additions and 136 deletions

View File

@@ -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) }

View File

@@ -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) }

View File

@@ -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

View File

@@ -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, _, _, _) }
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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" }
}

View File

@@ -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() }
}
/**

View File

@@ -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.

View File

@@ -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() }

View File

@@ -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 = ""
}
}