mirror of
https://github.com/github/codeql.git
synced 2026-05-05 13:45:19 +02:00
524 lines
18 KiB
Plaintext
524 lines
18 KiB
Plaintext
/** Provides classes relating to declarations and type members. */
|
|
|
|
import Callable
|
|
import Element
|
|
import Modifier
|
|
import Variable
|
|
private import Implements
|
|
private import TypeRef
|
|
private import commons.QualifiedName
|
|
|
|
private module QualifiedNameInput implements QualifiedNameInputSig {
|
|
string getUnboundGenericSuffix(UnboundGeneric ug) {
|
|
result = "<" + strictconcat(int i | exists(ug.getTypeParameter(i)) | "", ",") + ">"
|
|
}
|
|
}
|
|
|
|
private module FullyQualifiedNameInput implements QualifiedNameInputSig {
|
|
string getUnboundGenericSuffix(UnboundGeneric ug) {
|
|
result = "`" + ug.getNumberOfTypeParameters()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A declaration.
|
|
*
|
|
* Either a modifiable (`Modifiable`) or an assignable (`Assignable`).
|
|
*/
|
|
class Declaration extends NamedElement, @declaration {
|
|
/** Gets the name of this declaration, without additional decoration such as `<...>`. */
|
|
string getUndecoratedName() { none() }
|
|
|
|
/** Holds if this element has undecorated name 'name'. */
|
|
final predicate hasUndecoratedName(string name) { name = this.getUndecoratedName() }
|
|
|
|
/**
|
|
* Gets the unbound version of this declaration, that is, the declaration where
|
|
* all type arguments have been removed. For example, in
|
|
*
|
|
* ```csharp
|
|
* class C<T>
|
|
* {
|
|
* class Nested
|
|
* {
|
|
* }
|
|
*
|
|
* void Method<S>() { }
|
|
* }
|
|
* ```
|
|
*
|
|
* we have the following
|
|
*
|
|
* | Declaration | Unbound declaration |
|
|
* |-------------------------|---------------------|
|
|
* | `C<int>` | ``C`1`` |
|
|
* | ``C`1.Nested`` | ``C`1.Nested`` |
|
|
* | `C<int>.Nested` | ``C`1.Nested`` |
|
|
* | ``C`1.Method`1`` | ``C`1.Method`1`` |
|
|
* | ``C<int>.Method`1`` | ``C`1.Method`1`` |
|
|
* | `C<int>.Method<string>` | ``C`1.Method`1`` |
|
|
*/
|
|
Declaration getUnboundDeclaration() { result = this }
|
|
|
|
/** Holds if this declaration is unbound. */
|
|
final predicate isUnboundDeclaration() { this.getUnboundDeclaration() = this }
|
|
|
|
/** Gets the type containing this declaration, if any. */
|
|
ValueOrRefType getDeclaringType() { none() }
|
|
|
|
/** Holds if this declaration is unconstructed and in source code. */
|
|
final predicate isSourceDeclaration() { this.fromSource() and this.isUnboundDeclaration() }
|
|
|
|
override string toString() { result = this.getName() }
|
|
|
|
override predicate hasFullyQualifiedName(string qualifier, string name) {
|
|
QualifiedName<FullyQualifiedNameInput>::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
|
|
*
|
|
* ```csharp
|
|
* namespace N {
|
|
* class C {
|
|
* void M(int i, string s) { }
|
|
* }
|
|
* }
|
|
* ```
|
|
*/
|
|
deprecated string getFullyQualifiedNameWithTypes() {
|
|
exists(string fullqual, string qual, string name |
|
|
this.getDeclaringType().hasFullyQualifiedName(qual, name) and
|
|
fullqual = getQualifiedName(qual, name) and
|
|
if this instanceof NestedType
|
|
then result = fullqual + "+" + this.toStringWithTypes()
|
|
else result = fullqual + "." + this.toStringWithTypes()
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if this declaration has been generated by the compiler, for example
|
|
* implicit constructors or accessors.
|
|
*/
|
|
predicate isCompilerGenerated() { compiler_generated(this) }
|
|
}
|
|
|
|
/** A declaration that can have a modifier. */
|
|
class Modifiable extends Declaration, @modifiable {
|
|
/** Gets a modifier of this declaration. */
|
|
Modifier getAModifier() { has_modifiers(this, result) }
|
|
|
|
/** Holds if this declaration has `name` as a modifier. */
|
|
predicate hasModifier(string name) { this.getAModifier().hasName(name) }
|
|
|
|
/** Holds if this declaration is `static`. */
|
|
predicate isStatic() { this.hasModifier("static") }
|
|
|
|
/** Holds if this declaration is `public`. */
|
|
predicate isPublic() { this.hasModifier("public") }
|
|
|
|
/** Holds if this declaration is `protected`. */
|
|
predicate isProtected() { this.hasModifier("protected") }
|
|
|
|
/** Holds if this declaration is `internal`. */
|
|
predicate isInternal() { this.hasModifier("internal") }
|
|
|
|
/** Holds if this declaration is `private`. */
|
|
predicate isPrivate() { this.hasModifier("private") }
|
|
|
|
/** Holds if this declaration has the modifier `new`. */
|
|
predicate isNew() { this.hasModifier("new") }
|
|
|
|
/** Holds if this declaration is `sealed`. */
|
|
predicate isSealed() { this.hasModifier("sealed") }
|
|
|
|
/** Holds if this declaration is `abstract`. */
|
|
predicate isAbstract() { this.hasModifier("abstract") }
|
|
|
|
/** Holds if this declaration is `extern`. */
|
|
predicate isExtern() { this.hasModifier("extern") }
|
|
|
|
/** Holds if this declaration is `partial`. */
|
|
predicate isPartial() { this.hasModifier("partial") }
|
|
|
|
/** Holds if this declaration is `const`. */
|
|
predicate isConst() { this.hasModifier("const") }
|
|
|
|
/** Holds if this declaration has the modifier `required`. */
|
|
predicate isRequired() { this.hasModifier("required") }
|
|
|
|
/** Holds if this declaration is `file` local. */
|
|
predicate isFile() { this.hasModifier("file") }
|
|
|
|
/** Holds if this declaration is `unsafe`. */
|
|
predicate isUnsafe() {
|
|
this.hasModifier("unsafe")
|
|
or
|
|
exists(Type t, Type child |
|
|
t = this.(Parameterizable).getAParameter().getType() or
|
|
t = this.(Property).getType() or
|
|
t = this.(Callable).getReturnType() or
|
|
t = this.(DelegateType).getReturnType()
|
|
|
|
|
child = t.getAChild*() and
|
|
(
|
|
child instanceof PointerType
|
|
or
|
|
child instanceof FunctionPointerType
|
|
)
|
|
)
|
|
}
|
|
|
|
/** Holds if this declaration is `async`. */
|
|
predicate isAsync() { this.hasModifier("async") }
|
|
|
|
private predicate isReallyPrivate() {
|
|
this.isPrivate() and
|
|
not this.isProtected() and
|
|
// Rare case when a member is defined with the same name in multiple assemblies with different visibility
|
|
not this.isPublic()
|
|
}
|
|
|
|
/**
|
|
* Holds if this declaration is effectively `private`. A declaration is considered
|
|
* effectively `private` if it can only be referenced from
|
|
* - the declaring and its nested types, similarly to `private` declarations, and
|
|
* - the enclosing types.
|
|
*
|
|
* Note that explicit interface implementations are also considered effectively
|
|
* `private` if the implemented interface is itself effectively `private`. Finally,
|
|
* `private protected` members are not considered effectively `private`, because
|
|
* they can be overridden within the declaring assembly.
|
|
*/
|
|
predicate isEffectivelyPrivate() {
|
|
this.isReallyPrivate() or
|
|
this.getDeclaringType+().(Modifiable).isReallyPrivate() or
|
|
this.(Virtualizable).getExplicitlyImplementedInterface().isEffectivelyPrivate()
|
|
}
|
|
|
|
private predicate isReallyInternal() {
|
|
(
|
|
this.isInternal() and not this.isProtected()
|
|
or
|
|
this.isPrivate() and this.isProtected()
|
|
) and
|
|
// Rare case when a member is defined with the same name in multiple assemblies with different visibility
|
|
not this.isPublic()
|
|
}
|
|
|
|
/**
|
|
* Holds if this declaration is effectively `internal`. A declaration is considered
|
|
* effectively `internal` if it can only be referenced from the declaring assembly.
|
|
*
|
|
* Note that friend assemblies declared in `InternalsVisibleToAttribute` are not
|
|
* considered. Explicit interface implementations are also considered effectively
|
|
* `internal` if the implemented interface is itself effectively `internal`. Finally,
|
|
* `internal protected` members are not considered effectively `internal`, because
|
|
* they can be overridden outside the declaring assembly.
|
|
*/
|
|
predicate isEffectivelyInternal() {
|
|
this.isReallyInternal() or
|
|
this.getDeclaringType+().(Modifiable).isReallyInternal() or
|
|
this.(Virtualizable).getExplicitlyImplementedInterface().isEffectivelyInternal()
|
|
}
|
|
|
|
/**
|
|
* Holds if this declaration is effectively `public`, meaning that it can be
|
|
* referenced outside the declaring assembly.
|
|
*/
|
|
predicate isEffectivelyPublic() {
|
|
not this.isEffectivelyPrivate() and not this.isEffectivelyInternal()
|
|
}
|
|
}
|
|
|
|
/** A declaration that is a member of a type. */
|
|
class Member extends Modifiable, @member {
|
|
/** Gets an access to this member. */
|
|
MemberAccess getAnAccess() { result.getTarget() = this }
|
|
|
|
/**
|
|
* Holds if this member has name `name` and is defined in type `type`
|
|
* with namespace `namespace`.
|
|
*/
|
|
cached
|
|
final predicate hasFullyQualifiedName(string namespace, string type, string name) {
|
|
QualifiedName<FullyQualifiedNameInput>::hasQualifiedName(this, namespace, type, name)
|
|
}
|
|
}
|
|
|
|
private class TOverridable = @virtualizable or @callable_accessor;
|
|
|
|
/**
|
|
* A declaration that can be overridden or implemented. That is, a method,
|
|
* a property, an indexer, an event, an accessor, or an operator.
|
|
*
|
|
* Unlike `Virtualizable`, this class includes accessors.
|
|
*/
|
|
class Overridable extends Declaration, TOverridable {
|
|
/**
|
|
* Gets any interface this member explicitly implements; this only applies
|
|
* to members that can be declared on an interface, i.e. methods, properties,
|
|
* indexers and events.
|
|
*/
|
|
Interface getExplicitlyImplementedInterface() {
|
|
explicitly_implements(this, result)
|
|
or
|
|
not explicitly_implements(this, any(Interface i)) and
|
|
explicitly_implements(this, getTypeRef(result))
|
|
}
|
|
|
|
/**
|
|
* Holds if this member implements an interface member explicitly.
|
|
*/
|
|
predicate implementsExplicitInterface() { exists(this.getExplicitlyImplementedInterface()) }
|
|
|
|
/** Holds if this member can be overridden or implemented. */
|
|
predicate isOverridableOrImplementable() { none() }
|
|
|
|
/** Gets the member that is immediately overridden by this member, if any. */
|
|
Overridable getOverridee() {
|
|
overrides(this, result)
|
|
or
|
|
// For accessors (which are `Callable`s), the extractor generates entries
|
|
// in the `overrides` relation. However, we want the relation to be on
|
|
// the declarations containing the accessors instead
|
|
exists(Accessor accessorOverrider, Accessor accessorOverridee |
|
|
accessorOverrider = this.(DeclarationWithAccessors).getAnAccessor() and
|
|
accessorOverridee = result.(DeclarationWithAccessors).getAnAccessor() and
|
|
overrides(accessorOverrider, accessorOverridee)
|
|
)
|
|
}
|
|
|
|
/** Gets a member that immediately overrides this member, if any. */
|
|
Overridable getAnOverrider() { this = result.getOverridee() }
|
|
|
|
/** Holds if this member is overridden by some other member. */
|
|
predicate isOverridden() { exists(this.getAnOverrider()) }
|
|
|
|
/** Holds if this member overrides another member. */
|
|
predicate overrides() { exists(this.getOverridee()) }
|
|
|
|
/**
|
|
* Gets the interface member that is immediately implemented by this member, if any.
|
|
*
|
|
* The type `t` is a type that implements the interface type in which
|
|
* the result is declared, in such a way that this member is the
|
|
* implementation of the result.
|
|
*
|
|
* Example:
|
|
*
|
|
* ```csharp
|
|
* interface I { void M(); }
|
|
*
|
|
* class A { public void M() { } }
|
|
*
|
|
* class B : A, I { }
|
|
*
|
|
* class C : A, I { new public void M() }
|
|
* ```
|
|
*
|
|
* In the example above, the following (and nothing else) holds:
|
|
* `A.M.getImplementee(B) = I.M` and
|
|
* `C.M.getImplementee(C) = I.M`.
|
|
*/
|
|
Overridable getImplementee(ValueOrRefType t) { implements(this, result, t) }
|
|
|
|
/** Gets the interface member that is immediately implemented by this member, if any. */
|
|
Overridable getImplementee() { result = this.getImplementee(_) }
|
|
|
|
/**
|
|
* Gets a member that immediately implements this interface member, if any.
|
|
*
|
|
* The type `t` is a type that implements the interface type in which
|
|
* this member is declared, in such a way that the result is the
|
|
* implementation of this member.
|
|
*
|
|
* Example:
|
|
*
|
|
* ```csharp
|
|
* interface I { void M(); }
|
|
*
|
|
* class A { public void M() { } }
|
|
*
|
|
* class B : A, I { }
|
|
*
|
|
* class C : A, I { new public void M() }
|
|
* ```
|
|
*
|
|
* In the example above, the following (and nothing else) holds:
|
|
* `I.M.getAnImplementor(B) = A.M` and
|
|
* `I.M.getAnImplementor(C) = C.M`.
|
|
*/
|
|
Overridable getAnImplementor(ValueOrRefType t) { this = result.getImplementee(t) }
|
|
|
|
/** Gets a member that immediately implements this interface member, if any. */
|
|
Overridable getAnImplementor() { this = result.getImplementee() }
|
|
|
|
/**
|
|
* Gets an interface member that is (transitively) implemented by this
|
|
* member, if any. That is, either this member immediately implements
|
|
* the interface member, or this member overrides (transitively) another
|
|
* member that immediately implements the interface member.
|
|
*
|
|
* Note that this is generally *not* equivalent with
|
|
* `getOverridee*().getImplementee()`, as the example below illustrates:
|
|
*
|
|
* ```csharp
|
|
* interface I { void M(); }
|
|
*
|
|
* class A { public virtual void M() { } }
|
|
*
|
|
* class B : A, I { }
|
|
*
|
|
* class C : A { public override void M() }
|
|
*
|
|
* class D : B { public override void M() }
|
|
* ```
|
|
*
|
|
* - If this member is `A.M` then `I.M = getAnUltimateImplementee()`.
|
|
* - If this member is `C.M` then it is *not* the case that
|
|
* `I.M = getAnUltimateImplementee()`, because `C` is not a sub type of `I`.
|
|
* (An example where `getOverridee*().getImplementee()` would be incorrect.)
|
|
* - If this member is `D.M` then `I.M = getAnUltimateImplementee()`.
|
|
*/
|
|
pragma[nomagic]
|
|
Overridable getAnUltimateImplementee() {
|
|
exists(Overridable implementation, ValueOrRefType implementationType |
|
|
implements(implementation, result, implementationType)
|
|
|
|
|
this = implementation
|
|
or
|
|
this.getOverridee+() = implementation and
|
|
this.getDeclaringType().getABaseType+() = implementationType
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets a member that (transitively) implements this interface member,
|
|
* if any. That is, either this interface member is immediately implemented
|
|
* by the result, or the result overrides (transitively) another member that
|
|
* immediately implements this interface member.
|
|
*
|
|
* Note that this is generally *not* equivalent with
|
|
* `getImplementor().getAnOverrider*()` (see `getImplementee`).
|
|
*/
|
|
Overridable getAnUltimateImplementor() { this = result.getAnUltimateImplementee() }
|
|
|
|
/** Holds if this interface member is implemented by some other member. */
|
|
predicate isImplemented() { exists(this.getAnImplementor()) }
|
|
|
|
/** Holds if this member implements (transitively) an interface member. */
|
|
predicate implements() { exists(this.getAnUltimateImplementee()) }
|
|
|
|
/**
|
|
* Holds if this member overrides or implements (transitively)
|
|
* `that` member.
|
|
*/
|
|
predicate overridesOrImplements(Overridable that) {
|
|
this.getOverridee+() = that or
|
|
this.getAnUltimateImplementee() = that
|
|
}
|
|
|
|
/**
|
|
* Holds if this member overrides or implements (reflexively, transitively)
|
|
* `that` member.
|
|
*/
|
|
predicate overridesOrImplementsOrEquals(Overridable that) {
|
|
this = that or
|
|
this.overridesOrImplements(that)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A member where the `virtual` modifier is valid. That is, a method,
|
|
* a property, an indexer, an event, or an operator.
|
|
*
|
|
* Equivalently, these are the members that can be defined in an interface.
|
|
*
|
|
* Unlike `Overridable`, this class excludes accessors.
|
|
*/
|
|
class Virtualizable extends Overridable, Member, @virtualizable {
|
|
/** Holds if this member has the modifier `override`. */
|
|
predicate isOverride() { this.hasModifier("override") }
|
|
|
|
/** Holds if this member is `virtual`. */
|
|
predicate isVirtual() { this.hasModifier("virtual") }
|
|
|
|
override predicate isPublic() {
|
|
Member.super.isPublic() or
|
|
this.implementsExplicitInterface()
|
|
}
|
|
|
|
override predicate isPrivate() {
|
|
super.isPrivate() and
|
|
not this.implementsExplicitInterface()
|
|
}
|
|
|
|
override predicate isOverridableOrImplementable() {
|
|
not this.isSealed() and
|
|
not this.getDeclaringType().isSealed() and
|
|
(
|
|
this.isVirtual() or
|
|
this.isOverride() or
|
|
this.isAbstract() or
|
|
this.getDeclaringType() instanceof Interface
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A parameterizable declaration. Either a callable (`Callable`), a delegate
|
|
* type (`DelegateType`), or an indexer (`Indexer`).
|
|
*/
|
|
class Parameterizable extends Declaration, @parameterizable {
|
|
/** Gets raw parameter `i`, including the `this` parameter at index 0. */
|
|
Parameter getRawParameter(int i) { params(result, _, _, i, _, this, _) }
|
|
|
|
/** Gets the `i`th parameter, excluding the `this` parameter. */
|
|
Parameter getParameter(int i) { params(result, _, _, i, _, this, _) }
|
|
|
|
/** Gets the number of parameters of this callable. */
|
|
int getNumberOfParameters() { result = count(this.getAParameter()) }
|
|
|
|
/** Holds if this declaration has no parameters. */
|
|
predicate hasNoParameters() { not exists(this.getAParameter()) }
|
|
|
|
/** Gets a parameter, if any. */
|
|
Parameter getAParameter() { result = this.getParameter(_) }
|
|
|
|
/** Gets a raw parameter (including the qualifier), if any. */
|
|
final Parameter getARawParameter() { result = this.getRawParameter(_) }
|
|
|
|
/**
|
|
* Gets the type of the parameter, possibly prefixed
|
|
* with `out`, `ref`, or `params`, where appropriate.
|
|
*/
|
|
private string parameterTypeToString(int i) {
|
|
exists(Parameter p, string prefix |
|
|
p = this.getParameter(i) and
|
|
result = prefix + p.getType().toStringWithTypes()
|
|
|
|
|
if p.isOut()
|
|
then prefix = "out "
|
|
else
|
|
if p.isRef()
|
|
then prefix = "ref "
|
|
else
|
|
if p.isParams()
|
|
then prefix = "params "
|
|
else prefix = ""
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets the types of the parameters of this declaration as a
|
|
* comma-separated string.
|
|
*/
|
|
language[monotonicAggregates]
|
|
string parameterTypesToString() {
|
|
result =
|
|
concat(int i | exists(this.getParameter(i)) | this.parameterTypeToString(i), ", " order by i)
|
|
}
|
|
}
|