CodeQL library for C#ΒΆ
When youβre analyzing a C# program, you can make use of the large collection of classes in the CodeQL library for C#.
About the CodeQL libraries for C#ΒΆ
There is an extensive core library for analyzing CodeQL databases extracted from C# projects. The classes in this library present the data from a database in an object-oriented form and provide abstractions and predicates to help you with common analysis tasks. The library is implemented as a set of QL modules, that is, files with the extension .qll. The module csharp.qll imports all the core C# library modules, so you can include the complete library by beginning your query with:
import csharp
Since this is required for all C# queries, itβs omitted from code snippets below.
The core library contains all the program elements, including files, types, methods, variables, statements, and expressions. This is sufficient for most queries, however additional libraries can be imported for bespoke functionality such as control flow and data flow. For information about these additional libraries, see βCodeQL for C#.β
Class hierarchiesΒΆ
Each section contains a class hierarchy, showing the inheritance structure between CodeQL classes. For example:
ExprOperationArithmeticOperationUnaryArithmeticOperationUnaryMinusExpr,UnaryPlusExprMutatorOperationIncrementOperationPreIncrExpr,PostIncrExpr
DecrementOperationPreDecrExpr,PostDecrExpr
BinaryArithmeticOperationAddExpr,SubExpr,MulExpr,DivExpr,RemExpr
This means that the class AddExpr extends class BinaryArithmeticOperation, which in turn extends class ArithmeticOperation and so on. If you want to query any arithmetic operation, use the class ArithmeticOperation, but if you specifically want to limit the query to addition operations, use the class AddExpr.
Classes can also be considered to be sets, and the extends relation between classes defines a subset. Every member of class AddExpr is also in the class BinaryArithmeticOperation. In general, classes overlap and an entity can be a member of several classes.
This overview omits some of the less important or intermediate classes from the class hierarchy.
Each class has predicates, which are logical propositions about that class. They also define navigable relationships between classes. Predicates are inherited, so for example the AddExpr class inherits the predicates getLeftOperand() and getRightOperand() from BinaryArithmeticOperation, and getType() from class Expr. This is similar to how methods are inherited in object-oriented programming languages.
In this overview, we present the most common and useful predicates. For the complete list of predicates available on each class, you can look in the CodeQL source code, use autocomplete in the editor, or see the C# reference.
ExercisesΒΆ
Each section in this topic contains exercises to check your understanding.
Exercise 1: Simplify this query:
from BinaryArithmeticOperation op
where op instanceof AddExpr
select op
(Answer)
FilesΒΆ
Files are represented by the class File, and directories by the class Folder. The database contains all of the source files and assemblies used during the compilation.
Class hierarchyΒΆ
File- any file in the database (including source files, XML and assemblies)SourceFile- a file containing source code
Folder- a directory
PredicatesΒΆ
getName()- gets the full path of the file (for example,C:\Temp\test.cs).getNumberOfLines()- gets the number of lines (for source files only).getShortName()- gets the name of the file without the extension (for example,test).getBaseName()- gets the name and extension of the file (for example,test.cs).getParent()- gets the parent directory.
ExamplesΒΆ
Count the number of source files:
select count(SourceFile f)
Count the number of lines of code, excluding the directory external:
select sum(SourceFile f |
not exists(Folder ext | ext.getShortName() = "external" |
ext.getAFolder*().getAFile() = f) |
f.getNumberOfLines())
ExercisesΒΆ
Exercise 2: Write a query to find the source file with the largest number of lines. Hint: Find the source file with the same number of lines as the max number of lines in any file. (Answer)
ElementsΒΆ
The class Element is the base class for all parts of a C# program, and itβs the root of the element class hierarchy. All program elements (such as types, methods, statements, and expressions) ultimately derive from this common base class.
Element forms a hierarchical structure of the program, which can be navigated using the getParent() and getChild() predicates. This is much like an abstract syntax tree, and also applies to elements in assemblies.
PredicatesΒΆ
The Element class provides common functionality for all program elements, including:
getLocation()- gets the text span in the source code.getFile()- gets theFilecontaining theElement.getParent()- gets the parentElement, if any.getAChild()- gets a childElementof this element, if any.
ExamplesΒΆ
To list all elements in Main.cs, their QL class and location:
from Element e
where e.getFile().getShortName() = "Main"
select e, e.getAQlClass(), e.getLocation()
Note that getAQlClass() is available on all entities and is a useful way to figure out the QL class of something. Often the same element will have several classes which are all returned by getAQlClass().
LocationsΒΆ
Location represents a section of text in the source code, or an assembly. All elements have a Location obtained by their getLocation() predicate. A SourceLocation represents a span of text in source code, whereas an Assembly location represents a referenced assembly.
Sometimes elements have several locations, for example if they occur in both source code and an assembly. In this case, only the SourceLocation is returned.
Class hierarchyΒΆ
LocationSourceLocationAssembly
PredicatesΒΆ
Some predicates of Location include:
getFile()- gets theFile.getStartLine()- gets the first line of the text.getEndLine()- gets the last line of the text.getStartColumn()- gets the column of the start of the text.getEndColumn()- gets the column of the end of the text.
ExamplesΒΆ
Find all elements that are one character wide:
from Element e, Location l
where l = e.getLocation()
and l.getStartLine() = l.getEndLine()
and l.getStartColumn() = l.getEndColumn()
select e, "This element is a single character."
DeclarationsΒΆ
Declaration is the common class of all entities defined in the program, such as types, methods, variables etc. The database contains all declarations from the source code and all referenced assemblies.
Class hierarchyΒΆ
ElementDeclarationCallableUnboundGenericConstructedGenericModifiable- a declaration which can have a modifier (for examplepublic)Member- a declaration that is member of a type
Assignable- an element that can be assigned toVariablePropertyIndexerEvent
PredicatesΒΆ
Useful member predicates on Declaration include:
getDeclaringType()- gets the type containing the declaration, if any.getName()/hasName(string)- gets the name of the declared entity.isSourceDeclaration()- whether the declaration is source code and is not a constructed type/method.getSourceDeclaration()- gets the original (unconstructed) declaration.
ExamplesΒΆ
Find declarations containing a username:
from Declaration decl
where decl.getName().regexpMatch("[uU]ser([Nn]ame)?")
select decl, "A username."
VariablesΒΆ
The class Variable represents C# variables, such as fields, parameters and local variables. The database contains all variables from the source code, as well as all fields and parameters from assemblies referenced by the program.
Class hierarchyΒΆ
ElementDeclarationVariable- any type of variableField- a field in aclass/structMemberConstant- aconstfieldEnumConstant- a field in anenum
LocalScopeVariable- a variable whose scope is limited to a singleCallableLocalVariable- a local variable in aCallableLocalConstant- a locally defined constant in aCallable
Parameter- a parameter to aCallable
PredicatesΒΆ
Some common predicates on Variable are:
getType()- gets theTypeof this variable.getAnAccess()- gets an expression that accesses (reads or writes) this variable, if any.getAnAssignedValue()- gets an expression that is assigned to this variable, if any.getInitializer()- gets the expression used to initialize the variable, if any.
ExamplesΒΆ
Find all unused local variables:
from LocalVariable v
where not exists(v.getAnAccess())
select v, "This local variable is unused."
TypesΒΆ
Types are represented by the CodeQL class Type and consist of builtin types, interfaces, classes, structs, enums, and type parameters. The database contains types from the program and all referenced assemblies including mscorlib and the .NET framework.
The builtin types (object, int, double etc.) have corresponding types (System.Object, System.Int32 etc.) in mscorlib.
Class ValueOrRefType represents defined types, such as a class, struct, interface or enum.
Class hierarchyΒΆ
ElementDeclarationModifiable- a declaration which can have a modifier (for examplepublic)Member- a declaration that is member of a typeType- all typesValueOrRefType- a defined typeValueType- a value type (see below for further hierarchy)RefType- a reference type (see below for further hierarchy)NestedType- a type defined in another type
VoidType-voidPointerType- a pointer type
The ValueType class extends further:
ValueType- a value typeSimpleType- a simple built-in typeBoolType-boolCharType-charIntegralTypeUnsignedIntegralTypeByteType-byteUShortType-unsigned short/System.UInt16UIntType-unsigned int/System.UInt32ULongType-unsigned long/System.UInt64
SignedIntegralTypeSByteType-signed byteShortType-short/System.Int16IntType-int/System.Int32LongType-long/System.Int64
FloatingPointTypeFloatType-float/System.SingleDoubleType-double/System.Double
DecimalType-decimal/System.Decimal
Enum- anenumStruct- astructNullableTypeArrayType
The RefType class extends further:
RefTypeClass- aclassAnonymousClassObjectType-object/System.ObjectStringType-string/System.String
Interface- aninterfaceDelegateTypeNullType- the type ofnullDynamicType-dynamic
NestedType- a type defined in another type
These class hierarchies omit generic types for simplicity.
PredicatesΒΆ
Useful members of ValueOrRefType include:
getQualifiedName()/hasQualifiedName(string)- gets the qualified name of the type (for example,"System.String").getABaseInterface()- gets an immediate interface of this type, if any.getABaseType()- gets an immediate base class or interface of this type, if any.getBaseClass()- gets the immediate base class of this type, if any.getASubType()- gets an immediate subtype, a type which directly inherits from this type, if any.getAMember()- gets any member (field/method/property etc), if any.getAMethod()- gets a method, if any.getAProperty()- gets a property, if any.getAnIndexer()- gets an indexer, if any.getAnEvent()- gets an event, if any.getAnOperator()- gets an operator, if any.getANestedType()- gets a nested type.getNamespace()- gets the enclosing namespace.
ExamplesΒΆ
Find all members of System.Object:
from ObjectType object
select object.getAMember()
Find all types which directly implement System.Collections.IEnumerable:
from Interface ienumerable
where ienumerable.hasQualifiedName("System.Collections.IEnumerable")
select ienumerable.getASubType()
List all simple types in the System namespace:
select any(SimpleType t | t.getNamespace().hasName("System"))
Find all variables of type PointerType:
from Variable v
where v.fromSource()
and v.getType() instanceof PointerType
select v
List all classes in source files:
from Class c
where c.fromSource()
select c
ExercisesΒΆ
Exercise 3: Write a query to list the methods in string. (Answer)
Exercise 4: Adapt the example to find all types which indirectly implement IEnumerable. (Answer)
Exercise 5: Write a query to find all classes starting with the letter A. (Answer)
CallablesΒΆ
Callables are represented by the class Callable and are anything that can be called independently, such as methods, constructors, destructors, operators, anonymous functions, indexers, and property accessors.
The database contains all of the callables in your program and in all referenced assemblies.
Class hierarchyΒΆ
ElementDeclarationCallableMethodExtensionMethod
ConstructorStaticConstructorInstanceConstructor
DestructorOperatorUnaryOperatorPlusOperator,MinusOperator,NotOperator,ComplementOperator,IncrementOperator,DecrementOperator,FalseOperator,TrueOperator
BinaryOperatorAddOperator,SubOperator,MulOperator,DivOperator,RemOperator,AndOperator,OrOperator,XorOperator,LShiftOperator,RShiftOperator,EQOperator,NEOperator,LTOperator,GTOperator,LEOperator,GEOperator
ConversionOperatorImplicitConversionOperatorExplicitConversionOperator
AnonymousFunctionExprLambdaExprAnonymousMethodExpr
AccessorGetterSetterEventAccessorAddEventAccessor,RemoveEventAccessor
PredicatesΒΆ
Here are a few useful predicates on the Callable class:
getParameter(int)/getAParameter()- gets a parameter.calls(Callable)- whether thereβs a direct call from one callable to another.getReturnType()- gets the return type.getBody()/getExpressionBody()- gets the body of the callable.
Since Callable extends Declaration, it also has predicates from Declaration, such as:
getName()/hasName(string)getSourceDeclaration()getName()getDeclaringType()
Methods have additional predicates, including:
getAnOverridee()- gets a method that is immediately overridden by this method.getAnOverrider()- gets a method that immediately overrides this method.getAnImplementee()- gets an interface method that is immediately implemented by this method.getAnImplementor()- gets a method that immediately implements this interface method.
ExamplesΒΆ
List all types which override ToString:
from Method m
where m.hasName("ToString")
select m
Find methods that look like ToString methods but donβt override Object.ToString:
from Method toString, Method falseToString
where toString.hasQualifiedName("System.Object.ToString")
and falseToString.getName().toLowerCase() = "tostring"
and not falseToString.overrides*(toString)
and falseToString.getNumberOfParameters() = 0
select falseToString, "This method looks like it overrides Object.ToString but it doesn't."
Find all methods which take a pointer type:
from Method m
where m.getAParameter().getType() instanceof PointerType
select m, "This method uses pointers."
Find all classes which have a destructor but arenβt disposable:
from Class c
where c.getAMember() instanceof Destructor
and not c.getABaseType*().hasQualifiedName("System.IDisposable")
select c, "This class has a destructor but is not IDisposable."
Find Main methods which are not private:
from Method m
where m.hasName("Main")
and not m.isPrivate()
select m, "Main method should be private."
StatementsΒΆ
Statements are represented by the class Stmt and make up the body of methods (and other callables). The database contains all statements in the source code, but does not contain any statements from referenced assemblies where the source code is not available.
Class hierarchyΒΆ
ElementControlFlowElementStmtBlockStmt-{ ... }ExprStmtSelectionStmtIfStmt-ifSwitchStmt-switch
LabeledStmtConstCaseDefaultCase-defaultLabelStmt
LoopStmtWhileStmt-while(...) { ... }DoStmt-do { ... } while(...)ForStmt-forForEachStmt-foreach
JumpStmtBreakStmt-breakContinueStmt-continueGotoStmt-gotoGotoLabelStmtGotoCaseStmtGotoDefaultStmt
ThrowStmt-throwReturnStmt-returnYieldStmtYieldBreakStmt-yield breakYieldReturnStmt-yield return
TryStmt-tryCatchClause-catchSpecificCatchClauseGeneralCatchClause
CheckedStmt-checkedUncheckedStmt-uncheckedLockStmt-lockUsingStmt-usingLocalVariableDeclStmtLocalConstantDeclStmt
EmptyStmt-;UnsafeStmt-unsafeFixedStmt-fixed
ExamplesΒΆ
Find long methods:
from Method m
where m.getBody().(BlockStmt).getNumberOfStmts() >= 100
select m, "This is a long method!"
Find for(;;):
from ForStmt for
where not exists(for.getAnInitializer())
and not exists(for.getUpdate(_))
and not exists(for.getCondition())
select for, "Infinite loop."
Find catch(NullDefererenceException):
from SpecificCatchClause catch
where catch.getCaughtExceptionType().hasQualifiedName("System.NullReferenceException")
select catch, "Catch NullReferenceException."
Find an if statement with a constant condition:
from IfStmt ifStmt
where ifStmt.getCondition().hasValue()
select ifStmt, "This 'if' statement is constant."
Find an if statement with an empty βthenβ block:
from IfStmt ifStmt
where ifStmt.getThen().(BlockStmt).isEmpty()
select ifStmt, "If statement with empty 'then' block."
The (BlockStmt) is an inline cast, which restricts the query to cases where the result of getThen() has the QL class BlockStmt, and allows predicates on BlockStmt to be used, such as isEmpty().
ExercisesΒΆ
Exercise 6: Write a query to list all empty methods. (Answer)
Exercise 7: Modify the last example to also detect empty statements (;) in the βthenβ block. (Answer)
Exercise 8: Modify the last example to exclude chains of if statements, where the else part is another if statement. (Answer)
ExpressionsΒΆ
The Expr class represents all C# expressions in the program. An expression is something producing a value such as a+b or new List<int>(). The database contains all expressions from the source code, but no expressions from referenced assemblies where the source code is not available.
The Access class represents any use or cross-reference of another Declaration such a variable, property, method or field. The getTarget() predicate gets the declaration being accessed.
The Call class represents a call to a Callable, for example to a Method or an Accessor, and the getTarget() method gets the Callable being called. The Operation class consists of arithmetic, bitwise operations and logical operations.
Some expressions use a qualifier, which is the object on which the expression operates. A typical example is a MethodCall. In this case, the getQualifier() predicate is used to get the expression on the left of the ., and getArgument(int) is used to get the arguments of the call.
Class hierarchyΒΆ
ElementControlFlowElementExprLocalVariableDeclExprLocalConstantDeclExpr
OperationUnaryOperationSizeofExpr,PointerIndirectionExpr,AddressOfExpr
BinaryOperationComparisonOperationEqualityOperationEQExpr,NEExprRelationalOperationGTExpr,LTExpr,GEExpr,LEExpr
AssignmentAssignOperationAddOrRemoveEventExprAddEventExprRemoveEventExpr
AssignArithmeticOperationAssignAddExpr,AssignSubExpr,AssignMulExpr,AssignDivExpr,AssignRemExpr
AssignBitwiseOperationAssignAndExpr,AssignOrExpr,AssignXorExpr,AssignLShiftExpr,AssignRShiftExpr
AssignExprMemberInitializer
ArithmeticOperationUnaryArithmeticOperationUnaryMinusExpr,UnaryPlusExprMutatorOperationIncrementOperationPreIncrExpr,PostIncrExpr
DecrementOperationPreDecrExpr,PostDecrExpr
BinaryArithmeticOperationAddExpr,SubExpr,MulExpr,DivExpr,RemExpr
BitwiseOperationUnaryBitwiseOperationComplementOperation
BinaryBitwiseOperationLShiftExpr,RShiftExpr,BitwiseAndExpr,BitwiseOrExpr,BitwiseXorExpr
LogicalOperationUnaryLogicalOperationLogicalNotOperation
BinaryLogicalOperationLogicalAndExpr,LogicalOrExpr,NullCoalescingExpr
ConditionalExpr
ParenthesisedExpr,CheckedExpr,UncheckedExpr,IsExpr,AsExpr,CastExpr,TypeofExpr,DefaultValueExpr,AwaitExpr,NameofExpr,InterpolatedStringExprAccessThisAccessBaseAccessMemberAccessMethodAccessVirtualMethodAccess
FieldAccess,PropertyAccess,IndexerAccess,EventAccess,MethodAccess
AssignableAccessVariableAccessParameterAccessLocalVariableAccessLocalScopeVariableAccessFieldAccessMemberConstantAccess
PropertyAccessTrivialPropertyAccessVirtualPropertyAccess
IndexerAccessVirtualIndexerAccess
EventAccessVirtualEventAccess
TypeAccessArrayAccess
CallPropertyCallIndexerCallEventCallMethodCallVirtualMethodCallElementInitializer
ConstructorInitializerOperatorCallMutatorOperatorCall
DelegateCallObjectCreationDefaultValueTypeObjectCreationTypeParameterObjectCreationAnonymousObjectCreation
ObjectOrCollectionInitializerObjectInitializerCollectionInitializer
DelegateCreationExplicitDelegateCreation,ImplicitDelegateCreation
ArrayInitializerArrayCreationAnonymousFunctionExprLambdaExprAnonymousMethodExpr
LiteralBoolLiteral,CharLiteral,IntegerLiteral,IntLiteral,LongLiteral,UIntLiteral,ULongLiteral,RealLiteral,FloatLiteral,DoubleLiteral,DecimalLiteral,StringLiteral,NullLiteral
PredicatesΒΆ
Useful predicates on Expr include:
getType()- gets theTypeof the expression.getValue()- gets the compile-time constant, if any.hasValue()- whether the expression has a compile-time constant.getEnclosingStmt()- gets the statement containing the expression, if any.getEnclosingCallable()- gets the callable containing the expression, if any.stripCasts()- remove all explicit or implicit casts.isImplicit()- whether the expression was implicit, such as an implicitthisqualifier (ThisAccess).
ExamplesΒΆ
Find calls to String.Format with just one argument:
from MethodCall c
where c.getTarget().hasQualifiedName("System.String.Format")
and c.getNumberOfArguments() = 1
select c, "Missing arguments to 'String.Format'."
Find all comparisons of floating point values:
from ComparisonOperation cmp
where (cmp instanceof EQExpr or cmp instanceof NEExpr)
and cmp.getAnOperand().getType() instanceof FloatingPointType
select cmp, "Comparison of floating point values."
Find hard-coded passwords:
from Variable v, string value
where v.getName().regexpMatch("[pP]ass(word|wd|)")
and value = v.getAnAssignedValue().getValue()
select v, "Hard-coded password '" + value + "'."
ExercisesΒΆ
Exercise 9: Limit the previous query to string types. Exclude empty passwords or null passwords. (Answer)
AttributesΒΆ
C# attributes are represented by the class Attribute. They can be present on many C# elements, such as classes, methods, fields, and parameters. The database contains attributes from the source code and all assembly references.
The attribute of any Element can be obtained via getAnAttribute(), whereas if you have an attribute, you can find its element via getTarget(). These two query fragments are identical:
attribute = element.getAnAttribute()
element = attribute.getTarget()
Class hierarchyΒΆ
ElementAttribute
PredicatesΒΆ
getTarget()- gets theElementto which this attribute applies.getArgument(int)- gets the given argument of the attribute.getType()- gets the type of this attribute. Note that the class name must end in"Attribute".
ExamplesΒΆ
Find all obsolete elements:
from Element e, Attribute attribute
where e = attribute.getTarget()
and attribute.getType().hasName("ObsoleteAttribute")
select e, "This is obsolete because " + attribute.getArgument(0).getValue()
Model NUnit test fixtures:
class TestFixture extends Class
{
TestFixture() {
this.getAnAttribute().getType().hasName("TestFixtureAttribute")
}
TestMethod getATest() {
result = this.getAMethod()
}
}
class TestMethod extends Method
{
TestMethod() {
this.getAnAttribute().getType().hasName("TestAttribute")
}
}
from TestFixture f
select f, f.getATest()
ExercisesΒΆ
Exercise 10: Write a query to find just obsolete methods. (Answer)
Exercise 11: Write a query to find all places where the Obsolete attribute is used without a reason string (that is, [Obsolete]). (Answer)
Exercise 12: In the first example, what happens if the Obsolete attribute doesnβt have a reason string? How could the query be fixed to accommodate this? (Answer)
AnswersΒΆ
Exercise 1ΒΆ
from AddExpr op
select op
or
select any(AddExpr op)
Exercise 2ΒΆ
from File f
where f.getNumberOfLines() = max(any(File g).getNumberOfLines())
select f
Exercise 3ΒΆ
from StringType s
select s.getAMethod()
Exercise 4ΒΆ
from Interface ienumerable
where ienumerable.hasQualifiedName("System.Collections.IEnumerable")
select ienumerable.getASubType*()
Exercise 5ΒΆ
from Class a
where a.getName().toLowerCase().matches("a%")
select a
Exercise 6ΒΆ
select any(Method m | m.getBody().(BlockStmt).isEmpty())
Exercise 7ΒΆ
from IfStmt ifStmt
where ifStmt.getThen().(BlockStmt).isEmpty() or ifStmt.getThen() instanceof EmptyStmt
select ifStmt, "If statement with empty 'then' block."
Exercise 8ΒΆ
from IfStmt ifStmt
where (ifStmt.getThen().(BlockStmt).isEmpty() or ifStmt.getThen() instanceof EmptyStmt)
and not ifStmt.getElse() instanceof IfStmt
select ifStmt, "If statement with empty 'then' block."
Exercise 9ΒΆ
from Variable v, StringLiteral value
where v.getName().regexpMatch("[pP]ass(word|wd|)")
and value = v.getAnAssignedValue()
and value.getValue() != ""
select v, "Hard-coded password '" + value.getValue() + "'."
Exercise 10ΒΆ
from Method method, Attribute attribute
where method = attribute.getTarget()
and attribute.getType().hasName("ObsoleteAttribute")
select method, "This is obsolete because " + attribute.getArgument(0).getValue()
Exercise 11ΒΆ
from Attribute attribute
where attribute.getType().hasName("ObsoleteAttribute")
and not exists(attribute.getArgument(0))
select attribute, "Missing reason in 'Obsolete' attribute."
Exercise 12ΒΆ
The query does not return results where the argument is missing.
Here is the fixed version:
from Element e, Attribute attribute, string reason
where e = attribute.getTarget()
and attribute.getType().hasName("ObsoleteAttribute")
and if exists(attribute.getArgument(0))
then reason = attribute.getArgument(0).getValue()
else reason = "(not given)"
select e, "This is obsolete because " + reason
Further readingΒΆ
βQL language referenceβ
βCodeQL toolsβ