mirror of
https://github.com/github/codeql.git
synced 2025-12-18 18:10:39 +01:00
778 lines
23 KiB
Plaintext
778 lines
23 KiB
Plaintext
/**
|
|
* Provides checks for the consistency of the data model and database.
|
|
*/
|
|
|
|
private import CIL
|
|
private import csharp as CS
|
|
|
|
private newtype ConsistencyCheck =
|
|
MissingEntityCheck() or
|
|
TypeCheck(Type t) or
|
|
CfgCheck(ControlFlowNode n) or
|
|
DeclarationCheck(Declaration d) or
|
|
MissingCSharpCheck(CS::Declaration d)
|
|
|
|
/**
|
|
* A consistency violation in the database or data model.
|
|
*/
|
|
abstract class ConsistencyViolation extends ConsistencyCheck {
|
|
abstract string toString();
|
|
|
|
abstract string getMessage();
|
|
}
|
|
|
|
/**
|
|
* A check that is deliberately disabled.
|
|
*/
|
|
abstract class DisabledCheck extends ConsistencyViolation {
|
|
DisabledCheck() { none() }
|
|
}
|
|
|
|
/**
|
|
* A consistency violation on a control flow node.
|
|
*/
|
|
abstract class CfgViolation extends ConsistencyViolation, CfgCheck {
|
|
ControlFlowNode node;
|
|
|
|
CfgViolation() { this = CfgCheck(node) }
|
|
|
|
override string toString() { result = node.toString() }
|
|
}
|
|
|
|
/**
|
|
* A consistency violation in a specific instruction.
|
|
*/
|
|
abstract class InstructionViolation extends CfgViolation, CfgCheck {
|
|
Instruction instruction;
|
|
|
|
InstructionViolation() { this = CfgCheck(instruction) }
|
|
|
|
private string getInstructionsUpTo() {
|
|
result =
|
|
concat(Instruction i |
|
|
i.getIndex() <= instruction.getIndex() and
|
|
i.getImplementation() = instruction.getImplementation()
|
|
|
|
|
i.toString() + " [push: " + i.getPushCount() + ", pop: " + i.getPopCount() + "]", "; "
|
|
order by
|
|
i.getIndex()
|
|
)
|
|
}
|
|
|
|
override string toString() {
|
|
result =
|
|
instruction.getImplementation().getMethod().toStringWithTypes() + ": " +
|
|
instruction.toString() + ", " + this.getInstructionsUpTo()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A literal that does not have exactly one `getValue()`.
|
|
*/
|
|
class MissingValue extends InstructionViolation {
|
|
MissingValue() { exists(Literal l | l = instruction | count(l.getValue()) != 1) }
|
|
|
|
override string getMessage() { result = "Literal has invalid getValue()" }
|
|
}
|
|
|
|
/**
|
|
* A call that does not have exactly one `getTarget()`.
|
|
*/
|
|
class MissingCallTarget extends InstructionViolation {
|
|
MissingCallTarget() {
|
|
exists(Call c | c = instruction |
|
|
count(c.getTarget()) != 1 and not c instanceof Opcodes::Calli
|
|
or
|
|
count(c.(Opcodes::Calli).getTargetType()) != 1 and c instanceof Opcodes::Calli
|
|
)
|
|
}
|
|
|
|
override string getMessage() { result = "Call has invalid target" }
|
|
}
|
|
|
|
/**
|
|
* An instruction that has not been assigned a specific QL class.
|
|
*/
|
|
class MissingOpCode extends InstructionViolation {
|
|
MissingOpCode() { not exists(instruction.getOpcodeName()) }
|
|
|
|
override string getMessage() {
|
|
result = "Opcode " + instruction.getOpcode() + " is missing a QL class"
|
|
}
|
|
|
|
override string toString() {
|
|
result = "Unknown instruction in " + instruction.getImplementation().getMethod().toString()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An instruction that is missing an operand. It means that there is no instruction which pushes
|
|
* a value onto the stack for this instruction to pop.
|
|
*
|
|
* If this fails, it means that the `getPopCount`/`getPushCount`/control flow graph has failed.
|
|
* It could also mean that the target of a call has failed and has not determined the
|
|
* correct number of arguments.
|
|
*/
|
|
class MissingOperand extends InstructionViolation {
|
|
MissingOperand() {
|
|
exists(int op | op in [0 .. instruction.getPopCount() - 1] |
|
|
not exists(instruction.getOperand(op)) and not instruction instanceof DeadInstruction
|
|
)
|
|
}
|
|
|
|
int getMissingOperand() {
|
|
result in [0 .. instruction.getPopCount() - 1] and
|
|
not exists(instruction.getOperand(result))
|
|
}
|
|
|
|
override string getMessage() {
|
|
result = "This instruction is missing operand " + this.getMissingOperand()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A dead instruction, not reachable from any entry point.
|
|
* These should not exist, however it turns out that the Mono compiler sometimes
|
|
* emits them.
|
|
*/
|
|
class DeadInstruction extends Instruction {
|
|
DeadInstruction() { not exists(EntryPoint e | e.getASuccessor+() = this) }
|
|
}
|
|
|
|
/**
|
|
* An instruction that is not reachable from any entry point.
|
|
*
|
|
* If this fails, it means that the calculation of the call graph is incorrect.
|
|
* Disabled, because Mono compiler sometimes emits dead instructions.
|
|
*/
|
|
class DeadInstructionViolation extends InstructionViolation, DisabledCheck {
|
|
DeadInstructionViolation() { instruction instanceof DeadInstruction }
|
|
|
|
override string getMessage() { result = "This instruction is not reachable" }
|
|
}
|
|
|
|
class YesNoBranch extends ConditionalBranch {
|
|
YesNoBranch() { not this instanceof Opcodes::Switch }
|
|
}
|
|
|
|
/**
|
|
* A branch instruction that does not have exactly 2 successors.
|
|
*/
|
|
class InvalidBranchSuccessors extends InstructionViolation {
|
|
InvalidBranchSuccessors() {
|
|
// Mono compiler sometimes generates branches to the next instruction, which is just wrong.
|
|
// However it is valid CIL.
|
|
exists(YesNoBranch i | i = instruction | not count(i.getASuccessor()) in [1 .. 2])
|
|
}
|
|
|
|
override string getMessage() {
|
|
result = "Conditional branch has " + count(instruction.getASuccessor()) + " successors"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An instruction that has a true/false successor but is not a branch.
|
|
*/
|
|
class OnlyYesNoBranchHasTrueFalseSuccessors extends InstructionViolation {
|
|
OnlyYesNoBranchHasTrueFalseSuccessors() {
|
|
(exists(instruction.getTrueSuccessor()) or exists(instruction.getFalseSuccessor())) and
|
|
not instruction instanceof YesNoBranch
|
|
}
|
|
|
|
override string getMessage() { result = "This instruction has getTrue/FalseSuccessor()" }
|
|
}
|
|
|
|
/**
|
|
* An unconditional branch instruction that has more than one successor.
|
|
*/
|
|
class UnconditionalBranchSuccessors extends InstructionViolation {
|
|
UnconditionalBranchSuccessors() {
|
|
exists(UnconditionalBranch i | i = instruction | count(i.getASuccessor()) != 1)
|
|
}
|
|
|
|
override string getMessage() {
|
|
result = "Unconditional branch has " + count(instruction.getASuccessor()) + " successors"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A branch instruction that does not have a true successor.
|
|
*/
|
|
class NoTrueSuccessor extends InstructionViolation {
|
|
NoTrueSuccessor() { exists(YesNoBranch i | i = instruction | not exists(i.getTrueSuccessor())) }
|
|
|
|
override string getMessage() { result = "Missing a true successor" }
|
|
}
|
|
|
|
/**
|
|
* A branch instruction that does not have a false successor.
|
|
*/
|
|
class NoFalseSuccessor extends InstructionViolation {
|
|
NoFalseSuccessor() { exists(YesNoBranch i | i = instruction | not exists(i.getFalseSuccessor())) }
|
|
|
|
override string getMessage() { result = "Missing a false successor" }
|
|
}
|
|
|
|
/**
|
|
* An instruction whose true successor is not a successor.
|
|
*/
|
|
class TrueSuccessorIsSuccessor extends InstructionViolation {
|
|
TrueSuccessorIsSuccessor() {
|
|
exists(instruction.getTrueSuccessor()) and
|
|
not instruction.getTrueSuccessor() = instruction.getASuccessor()
|
|
}
|
|
|
|
override string getMessage() { result = "True successor isn't a successor" }
|
|
}
|
|
|
|
/**
|
|
* An instruction whose false successor is not a successor.
|
|
*/
|
|
class FalseSuccessorIsSuccessor extends InstructionViolation {
|
|
FalseSuccessorIsSuccessor() {
|
|
exists(instruction.getFalseSuccessor()) and
|
|
not instruction.getFalseSuccessor() = instruction.getASuccessor()
|
|
}
|
|
|
|
override string getMessage() { result = "True successor isn't a successor" }
|
|
}
|
|
|
|
/**
|
|
* An access that does not have exactly one target.
|
|
*/
|
|
class AccessMissingTarget extends InstructionViolation {
|
|
AccessMissingTarget() { exists(Access i | i = instruction | count(i.getTarget()) != 1) }
|
|
|
|
override string getMessage() { result = "Access has invalid getTarget()" }
|
|
}
|
|
|
|
/**
|
|
* A catch handler that doesn't have a caught exception type.
|
|
*/
|
|
class CatchHandlerMissingType extends CfgViolation {
|
|
CatchHandlerMissingType() { exists(CatchHandler h | h = node | not exists(h.getCaughtType())) }
|
|
|
|
override string getMessage() { result = "Catch handler missing caught type" }
|
|
}
|
|
|
|
/**
|
|
* A CFG node that does not have a stack size.
|
|
*/
|
|
class MissingStackSize extends CfgViolation {
|
|
MissingStackSize() {
|
|
(
|
|
not exists(node.getStackSizeAfter()) or
|
|
not exists(node.getStackSizeBefore())
|
|
) and
|
|
not node instanceof DeadInstruction
|
|
}
|
|
|
|
override string getMessage() { result = "Inconsistent stack size" }
|
|
}
|
|
|
|
/**
|
|
* A CFG node that does not have exactly one stack size.
|
|
* Disabled because inconsistent stack sizes have been observed.
|
|
*/
|
|
class InvalidStackSize extends CfgViolation, DisabledCheck {
|
|
InvalidStackSize() {
|
|
(
|
|
count(node.getStackSizeAfter()) != 1 or
|
|
count(node.getStackSizeBefore()) != 1
|
|
) and
|
|
not node instanceof DeadInstruction
|
|
}
|
|
|
|
override string getMessage() {
|
|
result =
|
|
"Inconsistent stack sizes " + count(node.getStackSizeBefore()) + " before and " +
|
|
count(node.getStackSizeAfter()) + " after"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A CFG node that does not have exactly 1 `getPopCount()`.
|
|
*/
|
|
class InconsistentPopCount extends CfgViolation {
|
|
InconsistentPopCount() { count(node.getPopCount()) != 1 }
|
|
|
|
override string getMessage() {
|
|
result = "Cfg node has " + count(node.getPopCount()) + " pop counts"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A CFG node that does not have exactly one `getPushCount()`.
|
|
*/
|
|
class InconsistentPushCount extends CfgViolation {
|
|
InconsistentPushCount() { count(node.getPushCount()) != 1 }
|
|
|
|
override string getMessage() {
|
|
result = "Cfg node has " + count(node.getPushCount()) + " push counts"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A return instruction that does not have a stack size of 0 after it.
|
|
*/
|
|
class InvalidReturn extends InstructionViolation {
|
|
InvalidReturn() { instruction instanceof Return and instruction.getStackSizeAfter() != 0 }
|
|
|
|
override string getMessage() { result = "Return has invalid stack size" }
|
|
}
|
|
|
|
/**
|
|
* A throw instruction that does not have a stack size of 0 after it.
|
|
*/
|
|
class InvalidThrow extends InstructionViolation, DisabledCheck {
|
|
InvalidThrow() { instruction instanceof Throw and instruction.getStackSizeAfter() != 0 }
|
|
|
|
override string getMessage() {
|
|
result = "Throw has invalid stack size: " + instruction.getStackSizeAfter()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A field access where the field is "static" but the instruction is "instance".
|
|
*/
|
|
class StaticFieldTarget extends InstructionViolation {
|
|
StaticFieldTarget() {
|
|
exists(FieldAccess i | i = instruction |
|
|
(i instanceof Opcodes::Stfld or i instanceof Opcodes::Stfld) and
|
|
i.getTarget().isStatic()
|
|
)
|
|
}
|
|
|
|
override string getMessage() { result = "Inconsistent static field" }
|
|
}
|
|
|
|
/**
|
|
* A branch without a target.
|
|
*/
|
|
class BranchWithoutTarget extends InstructionViolation {
|
|
BranchWithoutTarget() {
|
|
instruction = any(Branch b | not exists(b.getTarget()) and not b instanceof Opcodes::Switch)
|
|
}
|
|
|
|
override string getMessage() { result = "Branch without target" }
|
|
}
|
|
|
|
/**
|
|
* A consistency violation in a type.
|
|
*/
|
|
class TypeViolation extends ConsistencyViolation, TypeCheck {
|
|
/** Gets the type containing the violation. */
|
|
Type getType() { this = TypeCheck(result) }
|
|
|
|
override string toString() { result = this.getType().toString() }
|
|
|
|
abstract override string getMessage();
|
|
}
|
|
|
|
/**
|
|
* A type that has both type arguments and type parameters.
|
|
*/
|
|
class TypeIsBothConstructedAndUnbound extends TypeViolation {
|
|
TypeIsBothConstructedAndUnbound() {
|
|
this.getType() instanceof ConstructedGeneric and this.getType() instanceof UnboundGeneric
|
|
}
|
|
|
|
override string getMessage() { result = "Type is both constructed and unbound" }
|
|
}
|
|
|
|
/**
|
|
* The location of a constructed generic type should be the same
|
|
* as the location of its unbound generic type.
|
|
*/
|
|
class InconsistentTypeLocation extends TypeViolation {
|
|
InconsistentTypeLocation() {
|
|
this.getType().getLocation() != this.getType().getUnboundDeclaration().getLocation()
|
|
}
|
|
|
|
override string getMessage() { result = "Inconsistent constructed type location" }
|
|
}
|
|
|
|
/**
|
|
* A constructed type that does not match its unbound generic type.
|
|
*/
|
|
class TypeParameterMismatch extends TypeViolation {
|
|
TypeParameterMismatch() {
|
|
this.getType().(ConstructedGeneric).getNumberOfTypeArguments() !=
|
|
this.getType().getUnboundType().(UnboundGeneric).getNumberOfTypeParameters()
|
|
}
|
|
|
|
override string getMessage() {
|
|
result =
|
|
"Constructed type (" + this.getType().toStringWithTypes() + ") has " +
|
|
this.getType().(ConstructedGeneric).getNumberOfTypeArguments() +
|
|
" type arguments and unbound type (" + this.getType().getUnboundType().toStringWithTypes() +
|
|
") has " + this.getType().getUnboundType().(UnboundGeneric).getNumberOfTypeParameters() +
|
|
" type parameters"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A consistency violation in a method.
|
|
*/
|
|
class MethodViolation extends ConsistencyViolation, DeclarationCheck {
|
|
/** Gets the method containing the violation. */
|
|
Method getMethod() { this = DeclarationCheck(result) }
|
|
|
|
override string toString() { result = this.getMethod().toString() }
|
|
|
|
override string getMessage() { none() }
|
|
}
|
|
|
|
/**
|
|
* The location of a constructed method should be equal to the
|
|
* location of its unbound generic.
|
|
*/
|
|
class InconsistentMethodLocation extends MethodViolation {
|
|
InconsistentMethodLocation() {
|
|
this.getMethod().getLocation() != this.getMethod().getUnboundDeclaration().getLocation()
|
|
}
|
|
|
|
override string getMessage() { result = "Inconsistent constructed method location" }
|
|
}
|
|
|
|
/**
|
|
* A constructed method that does not match its unbound method.
|
|
*/
|
|
class ConstructedMethodTypeParams extends MethodViolation {
|
|
ConstructedMethodTypeParams() {
|
|
this.getMethod().(ConstructedGeneric).getNumberOfTypeArguments() !=
|
|
this.getMethod().getUnboundDeclaration().(UnboundGeneric).getNumberOfTypeParameters()
|
|
}
|
|
|
|
override string getMessage() {
|
|
result =
|
|
"The constructed method " + this.getMethod().toStringWithTypes() +
|
|
" does not match unbound method " +
|
|
this.getMethod().getUnboundDeclaration().toStringWithTypes()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A violation marking an entity that should be present but is not.
|
|
*/
|
|
abstract class MissingEntityViolation extends ConsistencyViolation, MissingEntityCheck {
|
|
override string toString() { result = "Missing entity" }
|
|
}
|
|
|
|
/**
|
|
* The type `object` is missing from the database.
|
|
*/
|
|
class MissingObjectViolation extends MissingEntityViolation {
|
|
MissingObjectViolation() {
|
|
exists(this) and
|
|
not exists(ObjectType o)
|
|
}
|
|
|
|
override string getMessage() { result = "Object missing" }
|
|
}
|
|
|
|
/**
|
|
* An override that is invalid because the overridden method is not in a base class.
|
|
*/
|
|
class InvalidOverride extends MethodViolation {
|
|
private Method base;
|
|
|
|
InvalidOverride() {
|
|
base = this.getMethod().getOverriddenMethod() and
|
|
not this.getMethod().getDeclaringType().getABaseType+() = base.getDeclaringType() and
|
|
base.getDeclaringType().isUnboundDeclaration() // Bases classes of constructed types aren't extracted properly.
|
|
}
|
|
|
|
override string getMessage() {
|
|
result =
|
|
"Overridden method from " + base.getDeclaringType().getQualifiedName() +
|
|
" is not in a base type"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A pointer type that does not have a pointee type.
|
|
*/
|
|
class InvalidPointerType extends TypeViolation {
|
|
InvalidPointerType() {
|
|
exists(PointerType p | p = this.getType() | count(p.getReferentType()) != 1)
|
|
}
|
|
|
|
override string getMessage() { result = "Invalid Pointertype.getPointeeType()" }
|
|
}
|
|
|
|
/**
|
|
* An array with an invalid `getElementType`.
|
|
*/
|
|
class ArrayTypeMissingElement extends TypeViolation {
|
|
ArrayTypeMissingElement() {
|
|
exists(ArrayType t | t = this.getType() | count(t.getElementType()) != 1)
|
|
}
|
|
|
|
override string getMessage() { result = "Invalid ArrayType.getElementType()" }
|
|
}
|
|
|
|
/**
|
|
* An array with an invalid `getRank`.
|
|
*/
|
|
class ArrayTypeInvalidRank extends TypeViolation {
|
|
ArrayTypeInvalidRank() { exists(ArrayType t | t = this.getType() | not t.getRank() > 0) }
|
|
|
|
override string getMessage() { result = "Invalid ArrayType.getRank()" }
|
|
}
|
|
|
|
/**
|
|
* A type should have at most one kind, except for missing referenced types
|
|
* where the interface/class is unknown.
|
|
*/
|
|
class KindViolation extends TypeViolation {
|
|
KindViolation() {
|
|
count(typeKind(this.getType())) != 1 and
|
|
exists(this.getType().getLocation())
|
|
}
|
|
|
|
override string getMessage() {
|
|
result = "Invalid kinds on type: " + concat(typeKind(this.getType()), " ")
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The type of a kind must be consistent between a constructed generic and its
|
|
* unbound generic.
|
|
*/
|
|
class InconsistentKind extends TypeViolation {
|
|
InconsistentKind() {
|
|
typeKind(this.getType()) != typeKind(this.getType().getUnboundDeclaration())
|
|
}
|
|
|
|
override string getMessage() { result = "Inconsistent type kind of source declaration" }
|
|
}
|
|
|
|
private string typeKind(Type t) {
|
|
t instanceof Interface and result = "interface"
|
|
or
|
|
t instanceof Class and result = "class"
|
|
or
|
|
t instanceof TypeParameter and result = "type parameter"
|
|
or
|
|
t instanceof ArrayType and result = "array"
|
|
or
|
|
t instanceof PointerType and result = "pointer"
|
|
}
|
|
|
|
/**
|
|
* A violation in a `Member`.
|
|
*/
|
|
abstract class DeclarationViolation extends ConsistencyViolation, DeclarationCheck {
|
|
abstract override string getMessage();
|
|
|
|
/** Gets the member containing the potential violation. */
|
|
Declaration getDeclaration() { this = DeclarationCheck(result) }
|
|
|
|
override string toString() { result = this.getDeclaration().toString() }
|
|
}
|
|
|
|
/**
|
|
* Properties that have no accessors.
|
|
*/
|
|
class PropertyWithNoAccessors extends DeclarationViolation {
|
|
PropertyWithNoAccessors() {
|
|
exists(Property p | p = this.getDeclaration() | not exists(p.getAnAccessor()))
|
|
}
|
|
|
|
override string getMessage() { result = "Property has no accessors" }
|
|
}
|
|
|
|
/**
|
|
* An expression that have an unexpected push count.
|
|
*/
|
|
class ExprPushCount extends InstructionViolation {
|
|
ExprPushCount() {
|
|
instruction instanceof Expr and
|
|
not instruction instanceof Opcodes::Dup and
|
|
if instruction instanceof Call
|
|
then not instruction.getPushCount() in [0 .. 1]
|
|
else instruction.(Expr).getPushCount() != 1
|
|
}
|
|
|
|
override string getMessage() {
|
|
result = "Instruction has unexpected push count " + instruction.getPushCount()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An expression that does not have exactly one type.
|
|
* Note that calls with no return have type `System.Void`.
|
|
*/
|
|
class ExprMissingType extends InstructionViolation {
|
|
ExprMissingType() {
|
|
// Don't have types for the following op codes:
|
|
not instruction instanceof Opcodes::Ldftn and
|
|
not instruction instanceof Opcodes::Localloc and
|
|
not instruction instanceof Opcodes::Ldvirtftn and
|
|
not instruction instanceof Opcodes::Arglist and
|
|
not instruction instanceof Opcodes::Refanytype and
|
|
instruction.getPushCount() >= 1 and
|
|
count(instruction.getType()) != 1
|
|
}
|
|
|
|
override string getMessage() { result = "Expression is missing getType()" }
|
|
}
|
|
|
|
/**
|
|
* An instruction that has a push count of 0, yet is still used as an operand
|
|
*/
|
|
class InvalidExpressionViolation extends InstructionViolation {
|
|
InvalidExpressionViolation() {
|
|
instruction.getPushCount() = 0 and
|
|
exists(Instruction expr | instruction = expr.getAnOperand())
|
|
}
|
|
|
|
override string getMessage() {
|
|
result = "This instruction is used as an operand but pushes no values"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A type that has multiple entities with the same qualified name in `System`.
|
|
* .NET Core does sometimes duplicate types, so this check is disabled.
|
|
*/
|
|
class TypeMultiplyDefined extends TypeViolation, DisabledCheck {
|
|
TypeMultiplyDefined() {
|
|
this.getType().getParent().getName() = "System" and
|
|
not this.getType() instanceof ConstructedGeneric and
|
|
not this.getType() instanceof ArrayType and
|
|
this.getType().isPublic() and
|
|
count(Type t |
|
|
not t instanceof ConstructedGeneric and
|
|
t.toStringWithTypes() = this.getType().toStringWithTypes()
|
|
) != 1
|
|
}
|
|
|
|
override string getMessage() {
|
|
result =
|
|
"This type (" + this.getType().toStringWithTypes() + ") has " +
|
|
count(Type t |
|
|
not t instanceof ConstructedGeneric and
|
|
t.toStringWithTypes() = this.getType().toStringWithTypes()
|
|
) + " entities"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A C# declaration which is expected to have a corresponding CIL declaration, but for some reason does not.
|
|
*/
|
|
class MissingCilDeclaration extends ConsistencyViolation, MissingCSharpCheck {
|
|
MissingCilDeclaration() {
|
|
exists(CS::Declaration decl | this = MissingCSharpCheck(decl) |
|
|
expectedCilDeclaration(decl) and
|
|
not exists(Declaration d | decl = d.getCSharpDeclaration())
|
|
)
|
|
}
|
|
|
|
CS::Declaration getDeclaration() { this = MissingCSharpCheck(result) }
|
|
|
|
override string getMessage() {
|
|
result =
|
|
"Cannot locate CIL for " + this.getDeclaration().toStringWithTypes() + " of class " +
|
|
this.getDeclaration().getPrimaryQlClasses()
|
|
}
|
|
|
|
override string toString() { result = this.getDeclaration().toStringWithTypes() }
|
|
}
|
|
|
|
/**
|
|
* Holds if the C# declaration is expected to have a CIl declaration.
|
|
*/
|
|
private predicate expectedCilDeclaration(CS::Declaration decl) {
|
|
decl = decl.getUnboundDeclaration() and
|
|
not decl instanceof CS::ArrayType and
|
|
decl.getALocation() instanceof CS::Assembly and
|
|
not decl.(CS::Modifiable).isInternal() and
|
|
not decl.(CS::Constructor).getNumberOfParameters() = 0 and // These are sometimes implicit
|
|
not decl.(CS::Method).getReturnType() instanceof CS::UnknownType and
|
|
not exists(CS::Parameter p | p = decl.(CS::Parameterizable).getAParameter() |
|
|
not expectedCilDeclaration(p)
|
|
) and
|
|
not decl instanceof CS::AnonymousClass and
|
|
(decl instanceof CS::Parameter implies expectedCilDeclaration(decl.(CS::Parameter).getType())) and
|
|
(decl instanceof CS::Parameter implies expectedCilDeclaration(decl.getParent())) and
|
|
(decl instanceof CS::Member implies expectedCilDeclaration(decl.getParent())) and
|
|
(
|
|
decl instanceof CS::Field
|
|
or
|
|
decl instanceof CS::Property
|
|
or
|
|
decl instanceof CS::ValueOrRefType
|
|
or
|
|
decl instanceof CS::Event
|
|
or
|
|
decl instanceof CS::Constructor
|
|
or
|
|
decl instanceof CS::Destructor
|
|
or
|
|
decl instanceof CS::Operator
|
|
or
|
|
decl instanceof CS::Method
|
|
or
|
|
decl instanceof CS::Parameter
|
|
)
|
|
}
|
|
|
|
/** A member with an invalid name. */
|
|
class MemberWithInvalidName extends DeclarationViolation {
|
|
MemberWithInvalidName() {
|
|
exists(string name | name = this.getDeclaration().(Member).getName() |
|
|
exists(name.indexOf(".")) and
|
|
not name = ".ctor" and
|
|
not name = ".cctor"
|
|
)
|
|
}
|
|
|
|
override string getMessage() {
|
|
result = "Invalid name " + this.getDeclaration().(Member).getName()
|
|
}
|
|
}
|
|
|
|
class ConstructedSourceDeclarationMethod extends MethodViolation {
|
|
Method method;
|
|
|
|
ConstructedSourceDeclarationMethod() {
|
|
method = this.getMethod() and
|
|
method = method.getUnboundDeclaration() and
|
|
(
|
|
method instanceof ConstructedGeneric or
|
|
method.getDeclaringType() instanceof ConstructedGeneric
|
|
)
|
|
}
|
|
|
|
override string getMessage() {
|
|
result = "Source declaration " + method.toStringWithTypes() + " is constructed"
|
|
}
|
|
}
|
|
|
|
/** A declaration with multiple labels. */
|
|
class DeclarationWithMultipleLabels extends DeclarationViolation {
|
|
DeclarationWithMultipleLabels() {
|
|
exists(Declaration d | this = DeclarationCheck(d) | strictcount(d.getLabel()) > 1)
|
|
}
|
|
|
|
override string getMessage() {
|
|
result = "Multiple labels " + concat(this.getDeclaration().getLabel(), ", ")
|
|
}
|
|
}
|
|
|
|
/** A declaration without a label. */
|
|
class DeclarationWithoutLabel extends DeclarationViolation {
|
|
DeclarationWithoutLabel() {
|
|
exists(Declaration d | this = DeclarationCheck(d) |
|
|
d.isUnboundDeclaration() and
|
|
not d instanceof TypeParameter and
|
|
not exists(d.getLabel()) and
|
|
(d instanceof Callable or d instanceof Type)
|
|
)
|
|
}
|
|
|
|
override string getMessage() { result = "No label" }
|
|
}
|