diff --git a/csharp/ql/consistency-queries/PrimaryQlClass.ql b/csharp/ql/consistency-queries/PrimaryQlClass.ql new file mode 100644 index 000000000000..eca49551c65f --- /dev/null +++ b/csharp/ql/consistency-queries/PrimaryQlClass.ql @@ -0,0 +1,6 @@ +import csharp + +query predicate missingPrimaryQlClass(Element e) { + not exists(e.getAPrimaryQlClass()) and + e.fromSource() +} diff --git a/csharp/ql/lib/semmle/code/csharp/Generics.qll b/csharp/ql/lib/semmle/code/csharp/Generics.qll index 9190523e3c08..ef3aced9cb9d 100644 --- a/csharp/ql/lib/semmle/code/csharp/Generics.qll +++ b/csharp/ql/lib/semmle/code/csharp/Generics.qll @@ -277,6 +277,8 @@ class TypeParameterConstraints extends Element, @type_parameter_constraints { /** Gets a textual representation of these constraints. */ override string toString() { result = "where " + this.getTypeParameter().getName() + ": ..." } + + override string getAPrimaryQlClass() { result = "TypeParameterConstraints" } } /** diff --git a/csharp/ql/lib/semmle/code/csharp/Modifier.qll b/csharp/ql/lib/semmle/code/csharp/Modifier.qll index 39652070af38..faae0c2c2e59 100644 --- a/csharp/ql/lib/semmle/code/csharp/Modifier.qll +++ b/csharp/ql/lib/semmle/code/csharp/Modifier.qll @@ -13,6 +13,8 @@ class Modifier extends Element, @modifier { predicate hasName(string name) { name = this.getName() } override string toString() { result = this.getName() } + + override string getAPrimaryQlClass() { result = "Modifier" } } /** @@ -20,4 +22,6 @@ class Modifier extends Element, @modifier { */ class AccessModifier extends Modifier { AccessModifier() { this.hasName(["public", "private", "internal", "protected"]) } + + override string getAPrimaryQlClass() { result = "AccessModifier" } } diff --git a/csharp/ql/lib/semmle/code/csharp/Type.qll b/csharp/ql/lib/semmle/code/csharp/Type.qll index 109c1df00c76..5aeaeb904d50 100644 --- a/csharp/ql/lib/semmle/code/csharp/Type.qll +++ b/csharp/ql/lib/semmle/code/csharp/Type.qll @@ -413,6 +413,8 @@ class VoidType extends DotNet::ValueOrRefType, Type, @void_type { final override string getUndecoratedName() { result = "Void" } override SystemNamespace getDeclaringNamespace() { any() } + + override string getAPrimaryQlClass() { result = "VoidType" } } /** @@ -454,6 +456,8 @@ class BoolType extends SimpleType, @bool_type { override string toStringWithTypes() { result = "bool" } override int getSize() { result = 1 } + + override string getAPrimaryQlClass() { result = "BoolType" } } /** @@ -467,6 +471,8 @@ class CharType extends SimpleType, @char_type { override int minValue() { result = 0 } override int maxValue() { result = 65535 } + + override string getAPrimaryQlClass() { result = "CharType" } } /** @@ -506,6 +512,8 @@ class SByteType extends SignedIntegralType, @sbyte_type { override int minValue() { result = -128 } override int maxValue() { result = 127 } + + override string getAPrimaryQlClass() { result = "SByteType" } } /** @@ -519,6 +527,8 @@ class ShortType extends SignedIntegralType, @short_type { override int minValue() { result = -32768 } override int maxValue() { result = 32767 } + + override string getAPrimaryQlClass() { result = "ShortType" } } /** @@ -532,6 +542,8 @@ class IntType extends SignedIntegralType, @int_type { override int minValue() { result = -2147483647 - 1 } override int maxValue() { result = 2147483647 } + + override string getAPrimaryQlClass() { result = "IntType" } } /** @@ -541,6 +553,8 @@ class LongType extends SignedIntegralType, @long_type { override string toStringWithTypes() { result = "long" } override int getSize() { result = 8 } + + override string getAPrimaryQlClass() { result = "LongType" } } /** @@ -552,6 +566,8 @@ class ByteType extends UnsignedIntegralType, @byte_type { override int getSize() { result = 1 } override int maxValue() { result = 255 } + + override string getAPrimaryQlClass() { result = "ByteType" } } /** @@ -563,6 +579,8 @@ class UShortType extends UnsignedIntegralType, @ushort_type { override int getSize() { result = 2 } override int maxValue() { result = 65535 } + + override string getAPrimaryQlClass() { result = "UShortType" } } /** @@ -572,6 +590,8 @@ class UIntType extends UnsignedIntegralType, @uint_type { override string toStringWithTypes() { result = "uint" } override int getSize() { result = 4 } + + override string getAPrimaryQlClass() { result = "UIntType" } } /** @@ -581,6 +601,8 @@ class ULongType extends UnsignedIntegralType, @ulong_type { override string toStringWithTypes() { result = "ulong" } override int getSize() { result = 8 } + + override string getAPrimaryQlClass() { result = "ULongType" } } /** @@ -597,6 +619,8 @@ class FloatType extends FloatingPointType, @float_type { override string toStringWithTypes() { result = "float" } override int getSize() { result = 4 } + + override string getAPrimaryQlClass() { result = "FloatType" } } /** @@ -606,6 +630,8 @@ class DoubleType extends FloatingPointType, @double_type { override string toStringWithTypes() { result = "double" } override int getSize() { result = 8 } + + override string getAPrimaryQlClass() { result = "DoubleType" } } /** @@ -615,6 +641,8 @@ class DecimalType extends SimpleType, @decimal_type { override string toStringWithTypes() { result = "decimal" } override int getSize() { result = 16 } + + override string getAPrimaryQlClass() { result = "DecimalType" } } /** @@ -775,6 +803,8 @@ class ObjectType extends Class { ObjectType() { this.hasQualifiedName("System.Object") } override string toStringWithTypes() { result = "object" } + + override string getAPrimaryQlClass() { result = "ObjectType" } } /** @@ -784,6 +814,8 @@ class StringType extends Class { StringType() { this.hasQualifiedName("System.String") } override string toStringWithTypes() { result = "string" } + + override string getAPrimaryQlClass() { result = "StringType" } } /** @@ -910,7 +942,9 @@ class FunctionPointerType extends Type, Parameterizable, @function_pointer_type /** * The `null` type. The type of the `null` literal. */ -class NullType extends RefType, @null_type { } +class NullType extends RefType, @null_type { + override string getAPrimaryQlClass() { result = "NullType" } +} /** * A nullable type, for example `int?`. @@ -1124,6 +1158,8 @@ class TupleType extends ValueType, @tuple_type { final override predicate hasQualifiedName(string qualifier, string name) { this.getUnderlyingType().hasQualifiedName(qualifier, name) } + + override string getAPrimaryQlClass() { result = "TupleType" } } /** diff --git a/csharp/ql/lib/semmle/code/csharp/Variable.qll b/csharp/ql/lib/semmle/code/csharp/Variable.qll index 6592320fdd79..aa66e59e7af3 100644 --- a/csharp/ql/lib/semmle/code/csharp/Variable.qll +++ b/csharp/ql/lib/semmle/code/csharp/Variable.qll @@ -338,6 +338,8 @@ class LocalVariable extends LocalScopeVariable, @local_variable { override Type getType() { localvars(this, _, _, _, getTypeRef(result), _) } override Location getALocation() { localvar_location(this, result) } + + override string getAPrimaryQlClass() { result = "LocalVariable" } } /** diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll index 8c53c7ea0c5b..1007d2cefe6c 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll @@ -1084,6 +1084,8 @@ class DiscardExpr extends Expr, @discard_expr { private class UnknownExpr extends Expr, @unknown_expr { override string toString() { result = "Expression" } + + override string getAPrimaryQlClass() { result = "UnknownExpr" } } /** diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/System.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/System.qll index a14fa2268ba5..e7d8521a4244 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/System.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/System.qll @@ -112,6 +112,8 @@ class SystemBooleanStruct extends BoolType { result.getParameter(1).getType() instanceof BoolType and result.getReturnType() instanceof BoolType } + + override string getAPrimaryQlClass() { result = "SystemBooleanStruct" } } /** Data flow for `System.Boolean`. */ @@ -1092,6 +1094,8 @@ class SystemIntPtrType extends ValueType { this = any(SystemNamespace n).getATypeDeclaration() and this.hasName("IntPtr") } + + override string getAPrimaryQlClass() { result = "SystemIntPtrType" } } /** The `System.IDisposable` interface. */ diff --git a/csharp/ql/lib/semmle/code/dotnet/Element.qll b/csharp/ql/lib/semmle/code/dotnet/Element.qll index bb6b2ebd906e..713261dcf361 100644 --- a/csharp/ql/lib/semmle/code/dotnet/Element.qll +++ b/csharp/ql/lib/semmle/code/dotnet/Element.qll @@ -43,8 +43,15 @@ class Element extends @dotnet_element { /** * Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs. + * + * If no primary class can be determined, the result is `"???"`. */ - final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } + final string getPrimaryQlClasses() { + result = strictconcat(this.getAPrimaryQlClass(), ",") + or + not exists(this.getAPrimaryQlClass()) and + result = "???" + } /** * Gets the name of a primary CodeQL class to which this element belongs. @@ -53,11 +60,12 @@ class Element extends @dotnet_element { * which they belong; for example, `AddExpr` is a primary class, but * `BinaryOperation` is not. * - * This predicate always has a result. If no primary class can be - * determined, the result is `"???"`. If multiple primary classes match, - * this predicate can have multiple results. + * If no primary classes match, this predicate has no result. If multiple + * primary classes match, this predicate can have multiple results. + * + * See also `getPrimaryQlClasses`, which is better to use in most cases. */ - string getAPrimaryQlClass() { result = "???" } + string getAPrimaryQlClass() { none() } } /** An element that has a name. */ diff --git a/csharp/ql/lib/semmle/code/dotnet/Namespace.qll b/csharp/ql/lib/semmle/code/dotnet/Namespace.qll index 324448728de2..9a0e9edac00d 100644 --- a/csharp/ql/lib/semmle/code/dotnet/Namespace.qll +++ b/csharp/ql/lib/semmle/code/dotnet/Namespace.qll @@ -39,6 +39,8 @@ class Namespace extends Declaration, @namespace { final override string getName() { namespaces(this, result) } final override string getUndecoratedName() { namespaces(this, result) } + + override string getAPrimaryQlClass() { result = "Namespace" } } /** The global namespace. */ diff --git a/csharp/ql/lib/semmle/code/dotnet/Type.qll b/csharp/ql/lib/semmle/code/dotnet/Type.qll index 81fefa965505..04b6cdaf6eb8 100644 --- a/csharp/ql/lib/semmle/code/dotnet/Type.qll +++ b/csharp/ql/lib/semmle/code/dotnet/Type.qll @@ -89,4 +89,6 @@ class ArrayType extends ValueOrRefType, @dotnet_array_type { final override string getLabel() { result = this.getElementType().getLabel() + "[]" } override string toStringWithTypes() { result = this.getElementType().toStringWithTypes() + "[]" } + + override string getAPrimaryQlClass() { result = "ArrayType" } }