mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Merge pull request #14363 from michaelnebel/csharp/sunsetqlstubgen
C#: Sunset QL based stub generator.
This commit is contained in:
2
.github/workflows/csharp-qltest.yml
vendored
2
.github/workflows/csharp-qltest.yml
vendored
@@ -91,7 +91,7 @@ jobs:
|
||||
run: |
|
||||
# Generate (Asp)NetCore stubs
|
||||
STUBS_PATH=stubs_output
|
||||
python3 ql/src/Stubs/make_stubs_nuget.py webapp Swashbuckle.AspNetCore.Swagger 6.5.0 "$STUBS_PATH"
|
||||
python3 scripts/stubs/make_stubs_nuget.py webapp Swashbuckle.AspNetCore.Swagger 6.5.0 "$STUBS_PATH"
|
||||
rm -rf ql/test/resources/stubs/_frameworks
|
||||
# Update existing stubs in the repo with the freshly generated ones
|
||||
mv "$STUBS_PATH/output/stubs/_frameworks" ql/test/resources/stubs/
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* Tool to generate C# stubs from a qltest snapshot.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import Stubs
|
||||
|
||||
/** All public declarations from assemblies. */
|
||||
class AllExternalPublicDeclarations extends GeneratedDeclaration {
|
||||
AllExternalPublicDeclarations() { this.fromLibrary() }
|
||||
}
|
||||
|
||||
from Assembly a
|
||||
select a.getFullName(), a.getName(), a.getVersion().toString(), a.getFile().getAbsolutePath(),
|
||||
generatedCode(a)
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* Tool to generate C# stubs from a qltest snapshot.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import Stubs
|
||||
|
||||
/** All public declarations from source. */
|
||||
class AllDeclarations extends GeneratedDeclaration {
|
||||
AllDeclarations() { not this.fromLibrary() }
|
||||
}
|
||||
|
||||
/** Exclude types from these standard assemblies. */
|
||||
private class DefaultLibs extends ExcludedAssembly {
|
||||
DefaultLibs() {
|
||||
this.getName() = "System.Private.CoreLib" or
|
||||
this.getName() = "mscorlib" or
|
||||
this.getName() = "System.Runtime"
|
||||
}
|
||||
}
|
||||
|
||||
select concat(generatedCode(_) + "\n\n")
|
||||
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
* Tool to generate C# stubs from a qltest snapshot.
|
||||
*
|
||||
* It finds all declarations used in the source code,
|
||||
* and generates minimal C# stubs containing those declarations
|
||||
* and their dependencies.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import Stubs
|
||||
|
||||
/** Declarations used by source code. */
|
||||
class UsedInSource extends GeneratedDeclaration {
|
||||
UsedInSource() {
|
||||
(
|
||||
this = any(Access a).getTarget()
|
||||
or
|
||||
this = any(Call c).getTarget()
|
||||
or
|
||||
this = any(TypeMention tm).getType()
|
||||
or
|
||||
exists(Virtualizable v | v.fromSource() |
|
||||
this = v.getImplementee() or this = v.getOverridee()
|
||||
)
|
||||
or
|
||||
this = any(Attribute a).getType().getAConstructor()
|
||||
) and
|
||||
this.fromLibrary()
|
||||
}
|
||||
}
|
||||
|
||||
/** Exclude types from these standard assemblies. */
|
||||
private class DefaultLibs extends ExcludedAssembly {
|
||||
DefaultLibs() {
|
||||
this.getName() = "System.Private.CoreLib" or
|
||||
this.getName() = "mscorlib" or
|
||||
this.getName() = "System.Runtime"
|
||||
}
|
||||
}
|
||||
|
||||
select concat(generatedCode(_) + "\n\n")
|
||||
@@ -1,965 +0,0 @@
|
||||
/**
|
||||
* Generates C# stubs for use in test code.
|
||||
*
|
||||
* Extend the abstract class `GeneratedDeclaration` with the declarations that should be generated.
|
||||
* This will generate stubs for all the required dependencies as well.
|
||||
*
|
||||
* Use
|
||||
* ```ql
|
||||
* select generatedCode()
|
||||
* ```
|
||||
* to retrieve the generated C# code.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.commons.QualifiedName
|
||||
private import semmle.code.csharp.frameworks.System
|
||||
private import semmle.code.dotnet.DotNet as DotNet // added to handle VoidType as a ValueOrRefType
|
||||
|
||||
/** An element that should be in the generated code. */
|
||||
abstract class GeneratedElement extends Element { }
|
||||
|
||||
/** A member that should be in the generated code. */
|
||||
abstract class GeneratedMember extends Member, GeneratedElement { }
|
||||
|
||||
/** Class representing all `struct`s, such as user defined ones and built-in ones, like `int`. */
|
||||
private class StructExt extends Type {
|
||||
StructExt() {
|
||||
this instanceof Struct or
|
||||
this instanceof SimpleType or
|
||||
this instanceof VoidType or
|
||||
this instanceof SystemIntPtrType
|
||||
}
|
||||
}
|
||||
|
||||
/** A type that should be in the generated code. */
|
||||
abstract private class GeneratedType extends Type, GeneratedElement {
|
||||
GeneratedType() {
|
||||
(
|
||||
this instanceof Interface
|
||||
or
|
||||
this instanceof Class
|
||||
or
|
||||
this instanceof StructExt
|
||||
or
|
||||
this instanceof Enum
|
||||
or
|
||||
this instanceof DelegateType
|
||||
) and
|
||||
not this instanceof ConstructedType and
|
||||
not this.getALocation() instanceof ExcludedAssembly
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this type is defined in multiple assemblies, and at least one of
|
||||
* them is in the `Microsoft.NETCore.App.Ref` folder. In this case, we only stub
|
||||
* the type in the assembly in `Microsoft.NETCore.App.Ref`. In case there are
|
||||
* multiple assemblies in this folder, then we prefer `System.Runtime`.
|
||||
*/
|
||||
private predicate isDuplicate(Assembly assembly) {
|
||||
// type exists in multiple assemblies
|
||||
count(this.getALocation().(Assembly)) > 1 and
|
||||
// at least one of them is in the `Microsoft.NETCore.App.Ref` folder
|
||||
this.getALocation()
|
||||
.(Assembly)
|
||||
.getFile()
|
||||
.getAbsolutePath()
|
||||
.matches("%Microsoft.NETCore.App.Ref%") and
|
||||
exists(int i |
|
||||
i =
|
||||
count(Assembly a |
|
||||
this.getALocation() = a and
|
||||
a.getFile().getAbsolutePath().matches("%Microsoft.NETCore.App.Ref%")
|
||||
)
|
||||
|
|
||||
i = 1 and
|
||||
// assemblies not in `Microsoft.NETCore.App.Ref` folder are considered duplicates
|
||||
not assembly.getFile().getAbsolutePath().matches("%Microsoft.NETCore.App.Ref%")
|
||||
or
|
||||
i > 1 and
|
||||
// one of the assemblies is named `System.Runtime`
|
||||
this.getALocation().(Assembly).getName() = "System.Runtime" and
|
||||
// all others are considered duplicates
|
||||
assembly.getName() != "System.Runtime"
|
||||
)
|
||||
}
|
||||
|
||||
predicate isInAssembly(Assembly assembly) { this.getALocation() = assembly }
|
||||
|
||||
private string stubKeyword() {
|
||||
this instanceof Interface and result = "interface"
|
||||
or
|
||||
this instanceof StructExt and result = "struct"
|
||||
or
|
||||
this instanceof Class and result = "class"
|
||||
or
|
||||
this instanceof Enum and result = "enum"
|
||||
or
|
||||
this instanceof DelegateType and result = "delegate"
|
||||
}
|
||||
|
||||
private string stubAbstractModifier() {
|
||||
if this.(Class).isAbstract() then result = "abstract " else result = ""
|
||||
}
|
||||
|
||||
private string stubStaticModifier() {
|
||||
if this.isStatic() then result = "static " else result = ""
|
||||
}
|
||||
|
||||
private string stubPartialModifier() {
|
||||
if
|
||||
count(Assembly a | this.getALocation() = a) <= 1 or
|
||||
this instanceof Enum
|
||||
then result = ""
|
||||
else result = "partial "
|
||||
}
|
||||
|
||||
private string stubAttributes() {
|
||||
if this.(ValueOrRefType).getAnAttribute().getType().hasQualifiedName("System", "FlagsAttribute")
|
||||
then result = "[System.Flags]\n"
|
||||
else result = ""
|
||||
}
|
||||
|
||||
/** Gets the entire C# stub code for this type. */
|
||||
pragma[nomagic]
|
||||
final string getStub(Assembly assembly) {
|
||||
this.isInAssembly(assembly) and
|
||||
if this.isDuplicate(assembly)
|
||||
then
|
||||
result =
|
||||
"/* Duplicate type '" + this.getName() + "' is not stubbed in this assembly '" +
|
||||
assembly.toString() + "'. */\n\n"
|
||||
else (
|
||||
not this instanceof DelegateType and
|
||||
result =
|
||||
this.stubAttributes() + stubUnsafe(this) + stubAccessibility(this) +
|
||||
this.stubAbstractModifier() + this.stubStaticModifier() + this.stubPartialModifier() +
|
||||
this.stubKeyword() + " " + this.getUndecoratedName() + stubGenericArguments(this) +
|
||||
this.stubBaseTypesString() + stubTypeParametersConstraints(this) + "\n{\n" +
|
||||
this.stubPrivateConstructor() + this.stubMembers(assembly) + "}\n\n"
|
||||
or
|
||||
result =
|
||||
this.stubAttributes() + stubUnsafe(this) + stubAccessibility(this) + this.stubKeyword() +
|
||||
" " + stubClassName(this.(DelegateType).getReturnType()) + " " + this.getUndecoratedName()
|
||||
+ stubGenericArguments(this) + "(" + stubParameters(this) + ");\n\n"
|
||||
)
|
||||
}
|
||||
|
||||
private ValueOrRefType getAnInterestingBaseType() {
|
||||
result = this.(ValueOrRefType).getABaseType() and
|
||||
not result instanceof ObjectType and
|
||||
not result.hasQualifiedName("System", "ValueType") and
|
||||
(not result instanceof Interface or result.(Interface).isEffectivelyPublic())
|
||||
}
|
||||
|
||||
private string stubBaseTypesString() {
|
||||
if this instanceof Enum
|
||||
then result = " : " + this.(Enum).getUnderlyingType().toStringWithTypes()
|
||||
else
|
||||
if exists(this.getAnInterestingBaseType())
|
||||
then
|
||||
result =
|
||||
" : " +
|
||||
concat(int i, ValueOrRefType t |
|
||||
t = this.getAnInterestingBaseType() and
|
||||
(if t instanceof Class then i = 0 else i = 1)
|
||||
|
|
||||
stubClassName(t), ", " order by i, t.getQualifiedName()
|
||||
)
|
||||
else result = ""
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
private string stubMembers(Assembly assembly) {
|
||||
result =
|
||||
concat(GeneratedMember m |
|
||||
m = this.getAGeneratedMember(assembly)
|
||||
|
|
||||
stubMember(m, assembly)
|
||||
order by
|
||||
m.getQualifiedNameWithTypes(), stubExplicitImplementation(m)
|
||||
)
|
||||
}
|
||||
|
||||
string stubPrivateConstructor() {
|
||||
if
|
||||
this instanceof Interface
|
||||
or
|
||||
this.isStatic()
|
||||
or
|
||||
this.isAbstract()
|
||||
or
|
||||
exists(this.(ValueOrRefType).getAConstructor())
|
||||
or
|
||||
not exists(this.getAnInterestingBaseType())
|
||||
or
|
||||
not exists(this.getAnInterestingBaseType().getAConstructor())
|
||||
or
|
||||
exists(Constructor bc |
|
||||
bc = this.getAnInterestingBaseType().getAConstructor() and
|
||||
bc.getNumberOfParameters() = 0 and
|
||||
not bc.isStatic()
|
||||
)
|
||||
then result = ""
|
||||
else
|
||||
result =
|
||||
" private " + this.getUndecoratedName() + "() : base(" +
|
||||
stubDefaultArguments(getBaseConstructor(this), this) + ")" + " => throw null;\n"
|
||||
}
|
||||
|
||||
private GeneratedMember getAGeneratedMember() { result.getDeclaringType() = this }
|
||||
|
||||
pragma[noinline]
|
||||
private GeneratedMember getAGeneratedMember(Assembly assembly) {
|
||||
result = this.getAGeneratedMember() and assembly = result.getALocation()
|
||||
}
|
||||
|
||||
final Type getAGeneratedType() {
|
||||
result = this.getAnInterestingBaseType()
|
||||
or
|
||||
result = this.getAGeneratedMember().(Callable).getReturnType()
|
||||
or
|
||||
result = this.getAGeneratedMember().(Callable).getAParameter().getType()
|
||||
or
|
||||
result = this.getAGeneratedMember().(Property).getType()
|
||||
or
|
||||
result = this.getAGeneratedMember().(Field).getType()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A declaration that should be generated.
|
||||
* This is extended in client code to identify the actual
|
||||
* declarations that should be generated.
|
||||
*/
|
||||
abstract class GeneratedDeclaration extends Modifiable {
|
||||
GeneratedDeclaration() { this.isEffectivelyPublic() }
|
||||
}
|
||||
|
||||
private class IndirectType extends GeneratedType {
|
||||
IndirectType() {
|
||||
this.(ValueOrRefType).getASubType() instanceof GeneratedType
|
||||
or
|
||||
this.(ValueOrRefType).getAChildType() instanceof GeneratedType
|
||||
or
|
||||
this.(UnboundGenericType).getAConstructedGeneric().getASubType() instanceof GeneratedType
|
||||
or
|
||||
exists(GeneratedType t |
|
||||
this = getAContainedType(t.getAGeneratedType()).getUnboundDeclaration()
|
||||
)
|
||||
or
|
||||
exists(GeneratedDeclaration decl |
|
||||
decl.(Member).getDeclaringType().getUnboundDeclaration() = this
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class RootGeneratedType extends GeneratedType {
|
||||
RootGeneratedType() { this = any(GeneratedDeclaration decl).getUnboundDeclaration() }
|
||||
}
|
||||
|
||||
private Type getAContainedType(Type t) {
|
||||
result = t
|
||||
or
|
||||
result = getAContainedType(t.(ConstructedType).getATypeArgument())
|
||||
}
|
||||
|
||||
private class RootGeneratedMember extends GeneratedMember {
|
||||
RootGeneratedMember() { this = any(GeneratedDeclaration d).getUnboundDeclaration() }
|
||||
}
|
||||
|
||||
private predicate declarationExists(Virtualizable m) {
|
||||
m instanceof GeneratedMember
|
||||
or
|
||||
m.getLocation() instanceof ExcludedAssembly
|
||||
}
|
||||
|
||||
private class InheritedMember extends GeneratedMember, Virtualizable {
|
||||
InheritedMember() {
|
||||
declarationExists(this.getImplementee+())
|
||||
or
|
||||
declarationExists(this.getAnImplementor+())
|
||||
or
|
||||
declarationExists(this.getOverridee+())
|
||||
or
|
||||
declarationExists(this.getAnOverrider+())
|
||||
}
|
||||
}
|
||||
|
||||
private class ExtraGeneratedConstructor extends GeneratedMember, Constructor {
|
||||
ExtraGeneratedConstructor() {
|
||||
not this.isStatic() and
|
||||
not this.isEffectivelyPublic() and
|
||||
this.getDeclaringType() instanceof GeneratedType and
|
||||
(
|
||||
// if the base class has no 0 parameter constructor
|
||||
not exists(Constructor c |
|
||||
c = this.getDeclaringType().getBaseClass().getAMember() and
|
||||
c.getNumberOfParameters() = 0 and
|
||||
not c.isStatic()
|
||||
)
|
||||
or
|
||||
// if this constructor might be called from a (generic) derived class
|
||||
exists(Class c |
|
||||
this.getDeclaringType() = c.getBaseClass().getUnboundDeclaration() and
|
||||
this = getBaseConstructor(c).getUnboundDeclaration()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A namespace that contains at least one generated type. */
|
||||
private class GeneratedNamespace extends Namespace, GeneratedElement {
|
||||
GeneratedNamespace() {
|
||||
this.getATypeDeclaration() instanceof GeneratedType
|
||||
or
|
||||
this.getAChildNamespace() instanceof GeneratedNamespace
|
||||
}
|
||||
|
||||
private string getPreamble() {
|
||||
if this.isGlobalNamespace()
|
||||
then result = ""
|
||||
else result = "namespace " + this.getName() + "\n{\n"
|
||||
}
|
||||
|
||||
private string getPostAmble() { if this.isGlobalNamespace() then result = "" else result = "}\n" }
|
||||
|
||||
final string getStubs(Assembly assembly) {
|
||||
result =
|
||||
this.getPreamble() + this.getTypeStubs(assembly) + this.getSubNamespaceStubs(assembly) +
|
||||
this.getPostAmble()
|
||||
}
|
||||
|
||||
/** Gets the `n`th generated child namespace, indexed from 0. */
|
||||
pragma[nomagic]
|
||||
final GeneratedNamespace getChildNamespace(int n) {
|
||||
result.getParentNamespace() = this and
|
||||
result.getName() =
|
||||
rank[n + 1](GeneratedNamespace g | g.getParentNamespace() = this | g.getName())
|
||||
}
|
||||
|
||||
final int getChildNamespaceCount() {
|
||||
result = count(GeneratedNamespace g | g.getParentNamespace() = this)
|
||||
}
|
||||
|
||||
private predicate isInAssembly(Assembly assembly) {
|
||||
any(GeneratedType gt | gt.(DotNet::ValueOrRefType).getDeclaringNamespace() = this)
|
||||
.isInAssembly(assembly)
|
||||
or
|
||||
this.getChildNamespace(_).isInAssembly(assembly)
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
string getSubNamespaceStubs(Assembly assembly) {
|
||||
this.isInAssembly(assembly) and
|
||||
result =
|
||||
concat(GeneratedNamespace child, int i |
|
||||
child = this.getChildNamespace(i) and child.isInAssembly(assembly)
|
||||
|
|
||||
child.getStubs(assembly) order by i
|
||||
)
|
||||
}
|
||||
|
||||
string getTypeStubs(Assembly assembly) {
|
||||
this.isInAssembly(assembly) and
|
||||
result =
|
||||
concat(GeneratedType gt |
|
||||
gt.(DotNet::ValueOrRefType).getDeclaringNamespace() = this and gt.isInAssembly(assembly)
|
||||
|
|
||||
gt.getStub(assembly) order by gt.getName()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify assemblies to exclude.
|
||||
* Do not generate any types from these assemblies.
|
||||
*/
|
||||
abstract class ExcludedAssembly extends Assembly { }
|
||||
|
||||
private Virtualizable getAccessibilityDeclaringVirtualizable(Virtualizable v) {
|
||||
if not v.isOverride()
|
||||
then result = v
|
||||
else
|
||||
if not v.getOverridee().getLocation() instanceof ExcludedAssembly
|
||||
then result = getAccessibilityDeclaringVirtualizable(v.getOverridee())
|
||||
else result = v
|
||||
}
|
||||
|
||||
private string stubAccessibility(Member m) {
|
||||
if
|
||||
m.getDeclaringType() instanceof Interface
|
||||
or
|
||||
exists(getExplicitImplementedInterface(m))
|
||||
or
|
||||
m instanceof Constructor and m.isStatic()
|
||||
then result = ""
|
||||
else
|
||||
if m.isPublic()
|
||||
then result = "public "
|
||||
else
|
||||
if m.isProtected()
|
||||
then
|
||||
if m.isPrivate() or getAccessibilityDeclaringVirtualizable(m).isPrivate()
|
||||
then result = "protected private "
|
||||
else
|
||||
if m.isInternal() or getAccessibilityDeclaringVirtualizable(m).isInternal()
|
||||
then result = "protected internal "
|
||||
else result = "protected "
|
||||
else
|
||||
if m.isPrivate()
|
||||
then result = "private "
|
||||
else
|
||||
if m.isInternal()
|
||||
then result = "internal "
|
||||
else result = "unknown-accessibility"
|
||||
}
|
||||
|
||||
private string stubModifiers(Member m) {
|
||||
result = stubUnsafe(m) + stubAccessibility(m) + stubStaticOrConst(m) + stubOverride(m)
|
||||
}
|
||||
|
||||
private string stubUnsafe(Member m) {
|
||||
if m.(Modifiable).isUnsafe() then result = "unsafe " else result = ""
|
||||
}
|
||||
|
||||
private string stubStaticOrConst(Member m) {
|
||||
if m.(Modifiable).isStatic()
|
||||
then result = "static "
|
||||
else
|
||||
if m.(Modifiable).isConst()
|
||||
then result = "const "
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubOverride(Member m) {
|
||||
if m.getDeclaringType() instanceof Interface and not m.isStatic()
|
||||
then result = ""
|
||||
else
|
||||
if m.(Virtualizable).isVirtual()
|
||||
then result = "virtual "
|
||||
else
|
||||
if m.(Virtualizable).isAbstract()
|
||||
then
|
||||
if m.(Virtualizable).isOverride()
|
||||
then result = "abstract override "
|
||||
else result = "abstract "
|
||||
else
|
||||
if m.(Virtualizable).isOverride()
|
||||
then result = "override "
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubQualifiedNamePrefix(ValueOrRefType t) {
|
||||
if t.getParent() instanceof GlobalNamespace
|
||||
then result = ""
|
||||
else
|
||||
if t.getParent() instanceof Namespace
|
||||
then result = t.getDeclaringNamespace().getFullName() + "."
|
||||
else result = stubClassName(t.getDeclaringType()) + "."
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
private string stubClassName(Type t) {
|
||||
if t instanceof ObjectType
|
||||
then result = "object"
|
||||
else
|
||||
if t instanceof StringType
|
||||
then result = "string"
|
||||
else
|
||||
if t instanceof IntType
|
||||
then result = "int"
|
||||
else
|
||||
if t instanceof BoolType
|
||||
then result = "bool"
|
||||
else
|
||||
if t instanceof VoidType
|
||||
then result = "void"
|
||||
else
|
||||
if t instanceof FloatType
|
||||
then result = "float"
|
||||
else
|
||||
if t instanceof DoubleType
|
||||
then result = "double"
|
||||
else
|
||||
if t instanceof NullableType
|
||||
then result = stubClassName(t.(NullableType).getUnderlyingType()) + "?"
|
||||
else
|
||||
if t instanceof TypeParameter
|
||||
then result = t.getName()
|
||||
else
|
||||
if t instanceof ArrayType
|
||||
then result = stubClassName(t.(ArrayType).getElementType()) + "[]"
|
||||
else
|
||||
if t instanceof PointerType
|
||||
then result = stubClassName(t.(PointerType).getReferentType()) + "*"
|
||||
else
|
||||
if t instanceof TupleType
|
||||
then
|
||||
exists(TupleType tt | tt = t |
|
||||
if tt.getArity() < 2
|
||||
then result = stubClassName(tt.getUnderlyingType())
|
||||
else
|
||||
result =
|
||||
"(" +
|
||||
concat(int i, Type element |
|
||||
element = tt.getElementType(i)
|
||||
|
|
||||
stubClassName(element), "," order by i
|
||||
) + ")"
|
||||
)
|
||||
else
|
||||
if t instanceof FunctionPointerType
|
||||
then
|
||||
exists(
|
||||
FunctionPointerType fpt, CallingConvention callconvention,
|
||||
string calltext
|
||||
|
|
||||
fpt = t
|
||||
|
|
||||
callconvention = fpt.getCallingConvention() and
|
||||
(
|
||||
if callconvention instanceof UnmanagedCallingConvention
|
||||
then calltext = "unmanaged"
|
||||
else calltext = ""
|
||||
) and
|
||||
result =
|
||||
"delegate* " + calltext + "<" +
|
||||
concat(int i, Parameter p |
|
||||
p = fpt.getParameter(i)
|
||||
|
|
||||
stubClassName(p.getType()) + "," order by i
|
||||
) + stubClassName(fpt.getReturnType()) + ">"
|
||||
)
|
||||
else
|
||||
if t instanceof ValueOrRefType
|
||||
then
|
||||
result =
|
||||
stubQualifiedNamePrefix(t) + t.getUndecoratedName() +
|
||||
stubGenericArguments(t)
|
||||
else result = "<error>"
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
private string stubGenericArguments(Type t) {
|
||||
if t instanceof UnboundGenericType
|
||||
then
|
||||
result =
|
||||
"<" +
|
||||
concat(int n |
|
||||
exists(t.(UnboundGenericType).getTypeParameter(n))
|
||||
|
|
||||
t.(UnboundGenericType).getTypeParameter(n).getName(), "," order by n
|
||||
) + ">"
|
||||
else
|
||||
if t instanceof ConstructedType
|
||||
then
|
||||
result =
|
||||
"<" +
|
||||
concat(int n |
|
||||
exists(t.(ConstructedType).getTypeArgument(n))
|
||||
|
|
||||
stubClassName(t.(ConstructedType).getTypeArgument(n)), "," order by n
|
||||
) + ">"
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubGenericMethodParams(Method m) {
|
||||
if m instanceof UnboundGenericMethod
|
||||
then
|
||||
result =
|
||||
"<" +
|
||||
concat(int n, TypeParameter param |
|
||||
param = m.(UnboundGenericMethod).getTypeParameter(n)
|
||||
|
|
||||
param.getName(), "," order by n
|
||||
) + ">"
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubConstraints(TypeParameterConstraints tpc, int i) {
|
||||
tpc.hasConstructorConstraint() and result = "new()" and i = 4
|
||||
or
|
||||
tpc.hasUnmanagedTypeConstraint() and result = "unmanaged" and i = 0
|
||||
or
|
||||
tpc.hasValueTypeConstraint() and
|
||||
result = "struct" and
|
||||
i = 0 and
|
||||
not tpc.hasUnmanagedTypeConstraint() and
|
||||
not stubClassName(tpc.getATypeConstraint().(Class)) = "System.Enum"
|
||||
or
|
||||
tpc.hasRefTypeConstraint() and result = "class" and i = 0
|
||||
or
|
||||
result = tpc.getATypeConstraint().(TypeParameter).getName() and i = 3
|
||||
or
|
||||
result = stubClassName(tpc.getATypeConstraint().(Interface)) and i = 2
|
||||
or
|
||||
result = stubClassName(tpc.getATypeConstraint().(Class)) and i = 1
|
||||
}
|
||||
|
||||
private string stubTypeParameterConstraints(TypeParameter tp) {
|
||||
if
|
||||
tp.getDeclaringGeneric().(Virtualizable).isOverride() or
|
||||
tp.getDeclaringGeneric().(Virtualizable).implementsExplicitInterface()
|
||||
then
|
||||
if tp.getConstraints().hasValueTypeConstraint()
|
||||
then result = " where " + tp.getName() + ": struct"
|
||||
else
|
||||
if tp.getConstraints().hasRefTypeConstraint()
|
||||
then result = " where " + tp.getName() + ": class"
|
||||
else result = ""
|
||||
else
|
||||
exists(TypeParameterConstraints tpc | tpc = tp.getConstraints() |
|
||||
result =
|
||||
" where " + tp.getName() + ": " +
|
||||
strictconcat(string s, int i | s = stubConstraints(tpc, i) | s, ", " order by i)
|
||||
)
|
||||
}
|
||||
|
||||
private string stubTypeParametersConstraints(Declaration d) {
|
||||
if d instanceof UnboundGeneric
|
||||
then
|
||||
result =
|
||||
concat(TypeParameter tp |
|
||||
tp = d.(UnboundGeneric).getATypeParameter()
|
||||
|
|
||||
stubTypeParameterConstraints(tp), " "
|
||||
)
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubImplementation(Virtualizable c) {
|
||||
if c.isAbstract() then result = "" else result = " => throw null"
|
||||
}
|
||||
|
||||
private predicate isKeyword(string s) {
|
||||
s =
|
||||
[
|
||||
"abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked",
|
||||
"class", "const", "continue", "decimal", "default", "delegate", "do", "double", "else",
|
||||
"enum", "event", "explicit", "extern", "false", "finally", "fixed", "float", "for", "foreach",
|
||||
"goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock", "long",
|
||||
"namespace", "new", "null", "object", "operator", "out", "override", "params", "private",
|
||||
"protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof",
|
||||
"stackalloc", "static", "string", "struct", "switch", "this", "throw", "true", "try",
|
||||
"typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void",
|
||||
"volatile", "while"
|
||||
]
|
||||
}
|
||||
|
||||
bindingset[s]
|
||||
private string escapeIfKeyword(string s) { if isKeyword(s) then result = "@" + s else result = s }
|
||||
|
||||
private string stubParameters(Parameterizable p) {
|
||||
result =
|
||||
concat(int i, Parameter param |
|
||||
param = p.getParameter(i) and not param.getType() instanceof ArglistType
|
||||
|
|
||||
stubParameterModifiers(param) + stubClassName(param.getType()) + " " +
|
||||
escapeIfKeyword(param.getName()) + stubDefaultValue(param), ", "
|
||||
order by
|
||||
i
|
||||
)
|
||||
}
|
||||
|
||||
private string stubDefaultArguments(Constructor baseCtor, ValueOrRefType callingType) {
|
||||
baseCtor = getBaseConstructor(callingType) and
|
||||
baseCtor.getNumberOfParameters() > 0 and
|
||||
result =
|
||||
concat(int i, Parameter param |
|
||||
param = baseCtor.getParameter(i) and not param.getType() instanceof ArglistType
|
||||
|
|
||||
"default(" + stubClassName(param.getType()) + ")", ", " order by i
|
||||
)
|
||||
}
|
||||
|
||||
private string stubParameterModifiers(Parameter p) {
|
||||
if p.isOut()
|
||||
then result = "out "
|
||||
else
|
||||
if p.isRef()
|
||||
then result = "ref "
|
||||
else
|
||||
if p.isParams()
|
||||
then result = "params "
|
||||
else
|
||||
if p.isIn()
|
||||
then result = "" // Only C# 7.1 so ignore
|
||||
else
|
||||
if p.hasExtensionMethodModifier()
|
||||
then result = "this "
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubDefaultValue(Parameter p) {
|
||||
if p.hasDefaultValue()
|
||||
then result = " = default(" + stubClassName(p.getType()) + ")"
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubEventAccessors(Event e) {
|
||||
if exists(e.(Virtualizable).getExplicitlyImplementedInterface())
|
||||
then result = " { add => throw null; remove => throw null; }"
|
||||
else result = ";"
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an interface that `c` explicitly implements, if either of the
|
||||
* following also holds.
|
||||
* (1) `c` is not static.
|
||||
* (2) `c` is static and an implementation of a generic with type constraints.
|
||||
* (3) `c` is static and there is another member with the same name
|
||||
* but different return type.
|
||||
*
|
||||
* We use these rules as explicit interfaces are needed in some cases
|
||||
* for compilation purposes (both to distinguish members but also to ensure
|
||||
* type constraints are satisfied). We can't always use explicit interface
|
||||
* implementation due to the generic math support, because then in some cases
|
||||
* we will only be able to access a static via a type variable with type
|
||||
* constraints (C# 11 language feature).
|
||||
*/
|
||||
private Interface getExplicitImplementedInterface(Virtualizable c) {
|
||||
result = unique(Interface i | i = c.getExplicitlyImplementedInterface()) and
|
||||
(
|
||||
not c.isStatic()
|
||||
or
|
||||
c.isStatic() and
|
||||
(
|
||||
not c instanceof Method
|
||||
or
|
||||
c instanceof Method and
|
||||
(
|
||||
exists(TypeParameter t | t = c.getImplementee().(UnboundGeneric).getATypeParameter() |
|
||||
exists(t.getConstraints().getATypeConstraint())
|
||||
)
|
||||
or
|
||||
exists(Member m |
|
||||
(not m.isStatic() or m.(Method).getReturnType() != c.(Method).getReturnType()) and
|
||||
m.getName() = c.getName() and
|
||||
m.getDeclaringType() = c.getDeclaringType()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private string stubExplicitImplementation(Member c) {
|
||||
if exists(getExplicitImplementedInterface(c))
|
||||
then result = stubClassName(getExplicitImplementedInterface(c)) + "."
|
||||
else result = ""
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private string stubMethod(Method m, Assembly assembly) {
|
||||
m instanceof GeneratedMember and
|
||||
m.getALocation() = assembly and
|
||||
if not m.getDeclaringType() instanceof Enum
|
||||
then
|
||||
result =
|
||||
" " + stubModifiers(m) + stubClassName(m.getReturnType()) + " " +
|
||||
stubExplicitImplementation(m) + escapeIfKeyword(m.getUndecoratedName()) +
|
||||
stubGenericMethodParams(m) + "(" + stubParameters(m) + ")" +
|
||||
stubTypeParametersConstraints(m) + stubImplementation(m) + ";\n"
|
||||
else result = " // Stub generator skipped method: " + m.getName() + "\n"
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private string stubOperator(Operator o, Assembly assembly) {
|
||||
o instanceof GeneratedMember and
|
||||
o.getALocation() = assembly and
|
||||
if o instanceof ConversionOperator
|
||||
then
|
||||
result =
|
||||
" " + stubModifiers(o) + stubExplicit(o) + stubExplicitImplementation(o) + "operator " +
|
||||
stubChecked(o) + stubClassName(o.getReturnType()) + "(" + stubParameters(o) + ")" +
|
||||
stubImplementation(o) + ";\n"
|
||||
else
|
||||
if not o.getDeclaringType() instanceof Enum
|
||||
then
|
||||
result =
|
||||
" " + stubModifiers(o) + stubClassName(o.getReturnType()) + " " +
|
||||
stubExplicitImplementation(o) + "operator " + o.getName() + "(" + stubParameters(o) + ")" +
|
||||
stubImplementation(o) + ";\n"
|
||||
else result = " // Stub generator skipped operator: " + o.getName() + "\n"
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private string stubEnumConstant(EnumConstant ec, Assembly assembly) {
|
||||
ec instanceof GeneratedMember and
|
||||
ec.getALocation() = assembly and
|
||||
result = " " + escapeIfKeyword(ec.getName()) + " = " + ec.getValue() + ",\n"
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private string stubProperty(Property p, Assembly assembly) {
|
||||
p instanceof GeneratedMember and
|
||||
p.getALocation() = assembly and
|
||||
result =
|
||||
" " + stubModifiers(p) + stubClassName(p.getType()) + " " + stubExplicitImplementation(p) +
|
||||
escapeIfKeyword(p.getName()) + " { " + stubGetter(p) + stubSetter(p) + "}\n"
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private string stubConstructor(Constructor c, Assembly assembly) {
|
||||
c instanceof GeneratedMember and
|
||||
c.getALocation() = assembly and
|
||||
if c.getDeclaringType() instanceof Enum
|
||||
then result = ""
|
||||
else
|
||||
if
|
||||
not c.getDeclaringType() instanceof StructExt or
|
||||
c.getNumberOfParameters() > 0
|
||||
then
|
||||
result =
|
||||
" " + stubModifiers(c) + escapeIfKeyword(c.getName()) + "(" + stubParameters(c) + ")" +
|
||||
stubConstructorInitializer(c) + " => throw null;\n"
|
||||
else result = " // Stub generator skipped constructor \n"
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private string stubIndexer(Indexer i, Assembly assembly) {
|
||||
i instanceof GeneratedMember and
|
||||
i.getALocation() = assembly and
|
||||
result =
|
||||
" " + stubIndexerNameAttribute(i) + stubModifiers(i) + stubClassName(i.getType()) + " " +
|
||||
stubExplicitImplementation(i) + "this[" + stubParameters(i) + "] { " + stubGetter(i) +
|
||||
stubSetter(i) + "}\n"
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private string stubField(Field f, Assembly assembly) {
|
||||
f instanceof GeneratedMember and
|
||||
f.getALocation() = assembly and
|
||||
not f instanceof EnumConstant and // EnumConstants are already stubbed
|
||||
exists(string impl |
|
||||
(if f.isConst() then impl = " = default" else impl = "") and
|
||||
result =
|
||||
" " + stubModifiers(f) + stubClassName(f.getType()) + " " + escapeIfKeyword(f.getName()) +
|
||||
impl + ";\n"
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private string stubEvent(Event e, Assembly assembly) {
|
||||
e instanceof GeneratedMember and
|
||||
e.getALocation() = assembly and
|
||||
result =
|
||||
" " + stubModifiers(e) + "event " + stubClassName(e.getType()) + " " +
|
||||
stubExplicitImplementation(e) + escapeIfKeyword(e.getName()) + stubEventAccessors(e) + "\n"
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private string stubMember(GeneratedMember m, Assembly assembly) {
|
||||
result = stubMethod(m, assembly)
|
||||
or
|
||||
result = stubOperator(m, assembly)
|
||||
or
|
||||
result = stubEnumConstant(m, assembly)
|
||||
or
|
||||
result = stubProperty(m, assembly)
|
||||
or
|
||||
result = stubConstructor(m, assembly)
|
||||
or
|
||||
result = stubIndexer(m, assembly)
|
||||
or
|
||||
result = stubField(m, assembly)
|
||||
or
|
||||
result = stubEvent(m, assembly)
|
||||
or
|
||||
not m instanceof Method and
|
||||
not m instanceof Operator and
|
||||
not m instanceof EnumConstant and
|
||||
not m instanceof Property and
|
||||
not m instanceof Constructor and
|
||||
not m instanceof Indexer and
|
||||
not m instanceof Field and
|
||||
not m instanceof Event and
|
||||
m.getALocation() = assembly and
|
||||
(
|
||||
result = m.(GeneratedType).getStub(assembly) + "\n"
|
||||
or
|
||||
not m instanceof GeneratedType and
|
||||
result = " // ERR: Stub generator didn't handle member: " + m.getName() + "\n"
|
||||
)
|
||||
}
|
||||
|
||||
private string stubIndexerNameAttribute(Indexer i) {
|
||||
if i.getName() != "Item"
|
||||
then result = "[System.Runtime.CompilerServices.IndexerName(\"" + i.getName() + "\")]\n "
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private Constructor getBaseConstructor(ValueOrRefType type) {
|
||||
result =
|
||||
min(Constructor bc |
|
||||
type.getBaseClass().getAMember() = bc and
|
||||
// not the `static` constructor
|
||||
not bc.isStatic() and
|
||||
// not a `private` constructor, unless it's `private protected`, or if the derived class is nested
|
||||
(not bc.isPrivate() or bc.isProtected() or bc.getDeclaringType() = type.getDeclaringType+())
|
||||
|
|
||||
bc order by bc.getNumberOfParameters(), stubParameters(bc)
|
||||
)
|
||||
}
|
||||
|
||||
private string stubConstructorInitializer(Constructor c) {
|
||||
exists(Constructor baseCtor |
|
||||
baseCtor = getBaseConstructor(c.getDeclaringType()) and
|
||||
if baseCtor.getNumberOfParameters() = 0 or c.isStatic()
|
||||
then result = ""
|
||||
else result = " : base(" + stubDefaultArguments(baseCtor, c.getDeclaringType()) + ")"
|
||||
)
|
||||
or
|
||||
// abstract base class might not have a constructor
|
||||
not exists(Constructor baseCtor |
|
||||
c.getDeclaringType().getBaseClass().getAMember() = baseCtor and not baseCtor.isStatic()
|
||||
) and
|
||||
result = ""
|
||||
}
|
||||
|
||||
private string stubExplicit(ConversionOperator op) {
|
||||
op instanceof ImplicitConversionOperator and result = "implicit "
|
||||
or
|
||||
(
|
||||
op instanceof ExplicitConversionOperator
|
||||
or
|
||||
op instanceof CheckedExplicitConversionOperator
|
||||
) and
|
||||
result = "explicit "
|
||||
}
|
||||
|
||||
private string stubChecked(Operator o) {
|
||||
if o instanceof CheckedExplicitConversionOperator then result = "checked " else result = ""
|
||||
}
|
||||
|
||||
private string stubGetter(DeclarationWithGetSetAccessors p) {
|
||||
if exists(p.getGetter())
|
||||
then if p.isAbstract() then result = "get; " else result = "get => throw null; "
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubSetter(DeclarationWithGetSetAccessors p) {
|
||||
if exists(p.getSetter())
|
||||
then if p.isAbstract() then result = "set; " else result = "set => throw null; "
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubSemmleExtractorOptions() {
|
||||
result =
|
||||
concat(string s |
|
||||
exists(CommentLine comment |
|
||||
s =
|
||||
"// original-extractor-options:" +
|
||||
comment.getText().regexpCapture("\\w*semmle-extractor-options:(.*)", 1) + "\n"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the generated C# code. */
|
||||
string generatedCode(Assembly assembly) {
|
||||
result =
|
||||
"// This file contains auto-generated code.\n" //
|
||||
+ "// Generated from `" + assembly.getFullName() + "`.\n" //
|
||||
+ stubSemmleExtractorOptions() + "\n" //
|
||||
+ any(GeneratedNamespace ns | ns.isGlobalNamespace()).getStubs(assembly)
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
# Tool to generate 'stub.cs' files inside C# qltest projects.
|
||||
# The purpose of this is to stub out assemblies from the qltest case
|
||||
# so that the test is self-contained and will still run without them.
|
||||
#
|
||||
# To do this, it
|
||||
# 1. Performs a regular qltest to generate a snapshot
|
||||
# 2. Runs a QL query on the snapshot to find out which symbols are needed,
|
||||
# then uses QL to generate a string of C# code.
|
||||
# 3. Re-runs the test to ensure that it still compiles and passes.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import helpers
|
||||
|
||||
print('Script to generate stub.cs files for C# qltest projects')
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Please supply a qltest directory.")
|
||||
exit(1)
|
||||
|
||||
testDir = sys.argv[1]
|
||||
|
||||
if not os.path.isdir(testDir):
|
||||
print("Directory", testDir, "does not exist")
|
||||
exit(1)
|
||||
|
||||
# Does it contain a .ql file and a .cs file?
|
||||
|
||||
foundCS = False
|
||||
foundQL = False
|
||||
|
||||
for file in os.listdir(testDir):
|
||||
if file.endswith(".cs"):
|
||||
foundCS = True
|
||||
if file.endswith(".ql") or file.endswith(".qlref"):
|
||||
foundQL = True
|
||||
|
||||
if not foundQL:
|
||||
print("Test directory does not contain .ql files. Please specify a working qltest directory.")
|
||||
exit(1)
|
||||
|
||||
if not foundCS:
|
||||
print("Test directory does not contain .cs files. Please specify a working qltest directory.")
|
||||
exit(1)
|
||||
|
||||
csharpQueries = os.path.abspath(os.path.dirname(sys.argv[0]))
|
||||
outputFile = os.path.join(testDir, 'stubs.cs')
|
||||
bqrsFile = os.path.join(testDir, 'stubs.bqrs')
|
||||
|
||||
print("Stubbing qltest in", testDir)
|
||||
|
||||
if os.path.isfile(outputFile):
|
||||
os.remove(outputFile) # It would interfere with the test.
|
||||
print("Removed previous", outputFile)
|
||||
|
||||
helpers.run_cmd(['codeql', 'test', 'run', '--keep-databases', testDir],
|
||||
"codeql test failed. Please fix up the test before proceeding.")
|
||||
|
||||
dbDir = os.path.join(testDir, os.path.basename(testDir) + ".testproj")
|
||||
|
||||
if not os.path.isdir(dbDir):
|
||||
print("Expected database directory " + dbDir + " not found.")
|
||||
exit(1)
|
||||
|
||||
helpers.run_cmd(['codeql', 'query', 'run', os.path.join(
|
||||
csharpQueries, 'MinimalStubsFromSource.ql'), '--database', dbDir, '--output', bqrsFile], 'Failed to run the query to generate output file.')
|
||||
|
||||
helpers.run_cmd(['codeql', 'bqrs', 'decode', bqrsFile, '--output',
|
||||
outputFile, '--format=text', '--no-titles'], 'Failed to run the query to generate output file.')
|
||||
|
||||
helpers.trim_output_file(outputFile)
|
||||
|
||||
if os.path.isfile(bqrsFile):
|
||||
os.remove(bqrsFile) # Cleanup
|
||||
print("Removed temp BQRS file", bqrsFile)
|
||||
|
||||
cmd = ['codeql', 'test', 'run', testDir]
|
||||
print('Running ' + ' '.join(cmd))
|
||||
if subprocess.check_call(cmd):
|
||||
print('\nTest failed. You may need to fix up', outputFile)
|
||||
print('It may help to view', outputFile, ' in Visual Studio')
|
||||
print("Next steps:")
|
||||
print('1. Look at the compilation errors, and fix up',
|
||||
outputFile, 'so that the test compiles')
|
||||
print('2. Re-run codeql test run "' + testDir + '"')
|
||||
print('3. git add "' + outputFile + '"')
|
||||
exit(1)
|
||||
|
||||
print("\nStub generation successful! Next steps:")
|
||||
print('1. Edit "semmle-extractor-options" in the .cs files to remove unused references')
|
||||
print('2. Re-run codeql test run "' + testDir + '"')
|
||||
print('3. git add "' + outputFile + '"')
|
||||
print('4. Commit your changes.')
|
||||
|
||||
exit(0)
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
Stubs/AllStubsFromSource.ql
|
||||
@@ -1,249 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
public class Class1
|
||||
{
|
||||
public struct Struct1
|
||||
{
|
||||
public ValueTuple<int> t1;
|
||||
public (int, int) t2;
|
||||
|
||||
public int i;
|
||||
public const int j = 42;
|
||||
|
||||
public void Method(Struct1 s = new Struct1()) => throw null;
|
||||
}
|
||||
|
||||
public interface Interface1
|
||||
{
|
||||
void Method1();
|
||||
}
|
||||
|
||||
internal protected interface Interface2
|
||||
{
|
||||
void Method2();
|
||||
int this[int i] { get; }
|
||||
}
|
||||
|
||||
private protected interface Interface3
|
||||
{
|
||||
void Method3();
|
||||
}
|
||||
|
||||
public class Class11 : Interface1, Interface2, Interface3
|
||||
{
|
||||
public Class11(int i) => throw null;
|
||||
|
||||
public void Method1() => throw null;
|
||||
|
||||
void Interface2.Method2() => throw null;
|
||||
|
||||
int Interface2.this[int i] => throw null;
|
||||
|
||||
void Interface3.Method3() => throw null;
|
||||
}
|
||||
|
||||
public delegate void Delegate1<T>(T i, int j);
|
||||
|
||||
public event Delegate1<int> Event1 { add { } remove { } }
|
||||
|
||||
public class Class12 : Class11
|
||||
{
|
||||
public Class12(int i, float j) : base(1) => throw null;
|
||||
}
|
||||
|
||||
public class GenericType<T>
|
||||
{
|
||||
public class X { }
|
||||
}
|
||||
|
||||
public GenericType<int>.X Prop { get; }
|
||||
|
||||
public abstract class Class13
|
||||
{
|
||||
protected internal virtual void M() => throw null;
|
||||
public virtual void M1<T>() where T : Class13 => throw null;
|
||||
public abstract void M2();
|
||||
}
|
||||
|
||||
public abstract class Class14 : Class13
|
||||
{
|
||||
protected internal override void M() => throw null;
|
||||
public override void M1<T>() => throw null;
|
||||
public abstract override void M2();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class Class2
|
||||
{
|
||||
public void M() => throw null;
|
||||
}
|
||||
|
||||
public class Class3
|
||||
{
|
||||
public object Item { get; set; }
|
||||
[System.Runtime.CompilerServices.IndexerName("MyItem")]
|
||||
public object this[string index] { get { return null; } set { } }
|
||||
}
|
||||
|
||||
public class Class4
|
||||
{
|
||||
unsafe public void M(int* p) => throw null;
|
||||
}
|
||||
|
||||
public interface IInterface1
|
||||
{
|
||||
void M1() => throw null;
|
||||
void M2();
|
||||
}
|
||||
|
||||
public class Class5 : IInterface1
|
||||
{
|
||||
public void M2() => throw null;
|
||||
}
|
||||
|
||||
public class Class6<T> where T : class, IInterface1
|
||||
{
|
||||
public Class6(int i) => throw null;
|
||||
|
||||
public virtual void M1<T>() where T : class, IInterface1, new() => throw null;
|
||||
}
|
||||
|
||||
public class Class7 : Class6<Class5>
|
||||
{
|
||||
public Class7(int i) : base(i) => throw null;
|
||||
|
||||
public override void M1<T>() where T : class => throw null;
|
||||
}
|
||||
|
||||
public class Class8
|
||||
{
|
||||
public const int @this = 10;
|
||||
}
|
||||
|
||||
public class Class9
|
||||
{
|
||||
private Class9(int i) => throw null;
|
||||
|
||||
public class Nested : Class9
|
||||
{
|
||||
internal Nested(int i) : base(i) => throw null;
|
||||
}
|
||||
|
||||
public Class9.Nested NestedInstance { get; } = new Class9.Nested(1);
|
||||
}
|
||||
|
||||
public class Class10
|
||||
{
|
||||
unsafe public void M1(delegate* unmanaged<System.IntPtr, void> f) => throw null;
|
||||
}
|
||||
|
||||
public interface IInterface2<T> where T : IInterface2<T>
|
||||
{
|
||||
static abstract T operator +(T left, T right);
|
||||
static virtual T operator -(T left, T right) => throw null;
|
||||
static abstract T operator *(T left, T right);
|
||||
static virtual T operator /(T left, T right) => throw null;
|
||||
static abstract explicit operator short(T n);
|
||||
static abstract explicit operator int(T n);
|
||||
void M1();
|
||||
void M2();
|
||||
}
|
||||
|
||||
public interface IInterface3<T> where T : IInterface3<T>
|
||||
{
|
||||
static abstract T operator +(T left, T right);
|
||||
static virtual T operator -(T left, T right) => throw null;
|
||||
static abstract explicit operator short(T n);
|
||||
void M1();
|
||||
}
|
||||
|
||||
public class Class11 : IInterface2<Class11>, IInterface3<Class11>
|
||||
{
|
||||
public static Class11 operator +(Class11 left, Class11 right) => throw null;
|
||||
public static Class11 operator -(Class11 left, Class11 right) => throw null;
|
||||
static Class11 IInterface2<Class11>.operator *(Class11 left, Class11 right) => throw null;
|
||||
static Class11 IInterface2<Class11>.operator /(Class11 left, Class11 right) => throw null;
|
||||
public void M1() => throw null;
|
||||
void IInterface2<Class11>.M2() => throw null;
|
||||
public static explicit operator short(Class11 n) => 0;
|
||||
static explicit IInterface2<Class11>.operator int(Class11 n) => 0;
|
||||
}
|
||||
|
||||
public unsafe class MyUnsafeClass
|
||||
{
|
||||
public static void M1(delegate*<void> f) => throw null;
|
||||
public static void M2(int*[] x) => throw null;
|
||||
public static char* M3() => throw null;
|
||||
public static void M4(int x) => throw null;
|
||||
}
|
||||
|
||||
public enum Enum1
|
||||
{
|
||||
None1,
|
||||
Some11,
|
||||
Some12
|
||||
}
|
||||
|
||||
public enum Enum2
|
||||
{
|
||||
None2 = 2,
|
||||
Some21 = 1,
|
||||
Some22 = 3
|
||||
}
|
||||
|
||||
public enum Enum3
|
||||
{
|
||||
Some32,
|
||||
Some31,
|
||||
None3
|
||||
}
|
||||
|
||||
public enum Enum4
|
||||
{
|
||||
Some41 = 7,
|
||||
None4 = 2,
|
||||
Some42 = 6
|
||||
}
|
||||
|
||||
public enum EnumLong : long
|
||||
{
|
||||
Some = 223372036854775807,
|
||||
None = 10
|
||||
}
|
||||
}
|
||||
|
||||
namespace A1
|
||||
{
|
||||
namespace B1
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class C1 { }
|
||||
}
|
||||
|
||||
namespace A2
|
||||
{
|
||||
namespace B2
|
||||
{
|
||||
public class C2 { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace A3
|
||||
{
|
||||
public class C3 { }
|
||||
}
|
||||
|
||||
namespace A4
|
||||
{
|
||||
namespace B4
|
||||
{
|
||||
public class D4 { }
|
||||
}
|
||||
|
||||
public class C4 { }
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
Stubs/MinimalStubsFromSource.ql
|
||||
@@ -1,123 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.UI.WebControls;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
public class RegexHandler
|
||||
{
|
||||
private static readonly string JAVA_CLASS_REGEX = "^(([a-z])+.)+[A-Z]([a-z])+$";
|
||||
|
||||
public void ProcessRequest()
|
||||
{
|
||||
string userInput = "";
|
||||
|
||||
// BAD:
|
||||
// Artificial regexes
|
||||
new Regex("^([a-z]+)+$").Match(userInput);
|
||||
new Regex("^([a-z]*)*$").Replace(userInput, "");
|
||||
// Known exponential blowup regex for e-mail address validation
|
||||
// Problematic part is: ([a-zA-Z0-9]+))*
|
||||
new Regex("^([a-zA-Z0-9])(([\\-.]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$").Match(userInput);
|
||||
// Known exponential blowup regex for Java class name validation
|
||||
// Problematic part is: (([a-z])+.)+
|
||||
new Regex(JAVA_CLASS_REGEX).Match(userInput);
|
||||
// Static use
|
||||
Regex.Match(userInput, JAVA_CLASS_REGEX);
|
||||
// GOOD:
|
||||
new Regex("^(([a-b]+[c-z]+)+$").Match(userInput);
|
||||
new Regex("^([a-z]+)+$", RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1)).Match(userInput);
|
||||
Regex.Match(userInput, JAVA_CLASS_REGEX, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1));
|
||||
// Known possible FP.
|
||||
new Regex("^[a-z0-9]+([_.-][a-z0-9]+)*$").Match(userInput);
|
||||
}
|
||||
}
|
||||
|
||||
// The only purpose of this class is to make sure the extractor extracts the
|
||||
// relevant library methods
|
||||
public class LibraryTypeDataFlow
|
||||
{
|
||||
void M()
|
||||
{
|
||||
int i;
|
||||
int.Parse("");
|
||||
int.TryParse("", out i);
|
||||
|
||||
bool b;
|
||||
bool.Parse("");
|
||||
bool.TryParse("", out b);
|
||||
|
||||
Uri uri = null;
|
||||
uri.ToString();
|
||||
|
||||
StringReader sr = new StringReader("");
|
||||
|
||||
string s = new string(new[] { 'a' });
|
||||
string.Join("", "", "", "");
|
||||
|
||||
StringBuilder sb = new StringBuilder("");
|
||||
|
||||
Lazy<int> l = new Lazy<int>(() => 42);
|
||||
|
||||
IEnumerable ie = null;
|
||||
ie.GetEnumerator();
|
||||
ie.AsParallel();
|
||||
ie.AsQueryable();
|
||||
IEnumerable<int> ieint = null;
|
||||
ieint.Select(x => x);
|
||||
List<int> list = null;
|
||||
list.Find(x => x > 0);
|
||||
Stack<int> stack = null;
|
||||
stack.Peek();
|
||||
ArrayList al = null;
|
||||
ArrayList.FixedSize(al);
|
||||
SortedList sl = null;
|
||||
sl.GetByIndex(0);
|
||||
|
||||
Convert.ToInt32("0");
|
||||
|
||||
DataContract dc = null;
|
||||
s = dc.AString;
|
||||
|
||||
KeyValuePair<int, string> kvp = new KeyValuePair<int, string>(0, "");
|
||||
|
||||
IEnumerator ienum = null;
|
||||
object o = ienum.Current;
|
||||
|
||||
IEnumerator<int> ienumint = null;
|
||||
i = ienumint.Current;
|
||||
|
||||
var task = new Task(() => { });
|
||||
Task.WhenAll<int>(null, null);
|
||||
Task.WhenAny<int>(null, null);
|
||||
Task.Factory.ContinueWhenAll((Task[])null, (Func<Task[], int>)null);
|
||||
|
||||
var task2 = new Task<int>(() => 42);
|
||||
Task<string>.Factory.ContinueWhenAny<int>(new Task<int>[] { task2 }, t => t.Result.ToString());
|
||||
|
||||
Encoding.Unicode.GetString(Encoding.Unicode.GetBytes(""));
|
||||
|
||||
Path.Combine("", "");
|
||||
Path.GetDirectoryName("");
|
||||
Path.GetExtension("");
|
||||
Path.GetFileName("");
|
||||
Path.GetFileNameWithoutExtension("");
|
||||
Path.GetPathRoot("");
|
||||
HttpContextBase context = null;
|
||||
string name = context.Request.QueryString["name"];
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class DataContract
|
||||
{
|
||||
[DataMember]
|
||||
public string AString { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
semmle-extractor-options: /r:System.Text.RegularExpressions.dll /r:System.Collections.Specialized.dll /r:System.Net.dll /r:System.Web.dll /r:System.Net.HttpListener.dll /r:System.Collections.Specialized.dll /r:System.Private.Uri.dll /r:System.Runtime.Extensions.dll /r:System.Linq.Parallel.dll /r:System.Collections.Concurrent.dll /r:System.Linq.Expressions.dll /r:System.Collections.dll /r:System.Linq.Queryable.dll /r:System.Linq.dll /r:System.Collections.NonGeneric.dll /r:System.ObjectModel.dll /r:System.ComponentModel.TypeConverter.dll /r:System.IO.Compression.dll /r:System.IO.Pipes.dll /r:System.Net.Primitives.dll /r:System.Net.Security.dll /r:System.Security.Cryptography.Primitives.dll /r:System.Text.RegularExpressions.dll ${testdir}/../../../resources/stubs/System.Web.cs /r:System.Runtime.Serialization.Primitives.dll
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Test
|
||||
{
|
||||
public class Class1
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
| Test.cs:0:0:0:0 | Test.cs |
|
||||
@@ -1,5 +0,0 @@
|
||||
import csharp
|
||||
|
||||
from File f
|
||||
where f.fromSource()
|
||||
select f
|
||||
@@ -1,3 +0,0 @@
|
||||
semmle-extractor-options: --load-sources-from-project:../../../resources/stubs/_frameworks/Microsoft.AspNetCore.App/Microsoft.AspNetCore.App.csproj
|
||||
semmle-extractor-options: /nostdlib
|
||||
semmle-extractor-options: /noconfig
|
||||
@@ -5,10 +5,10 @@ Stubs can be generated from Nuget packages with the `make_stubs_nuget.py` script
|
||||
The following calls generate stubs for `Newtonsoft.Json`:
|
||||
|
||||
```
|
||||
python make_stubs_nuget.py Newtonsoft.Json
|
||||
python make_stubs_nuget.py Newtonsoft.Json latest
|
||||
python make_stubs_nuget.py Newtonsoft.Json 13.0.1
|
||||
python make_stubs_nuget.py Newtonsoft.Json 13.0.1 /Users/tmp/working-dir
|
||||
python3 make_stubs_nuget.py classlib Newtonsoft.Json
|
||||
python3 make_stubs_nuget.py classlib Newtonsoft.Json latest
|
||||
python3 make_stubs_nuget.py classlib Newtonsoft.Json 13.0.1
|
||||
python3 make_stubs_nuget.py classlib Newtonsoft.Json 13.0.1 /Users/tmp/working-dir
|
||||
```
|
||||
|
||||
The output stubs are found in the `[DIR]/output/stubs` folder and can be copied over to `csharp/ql/test/resources/stubs`.
|
||||
@@ -82,7 +82,7 @@ print("\n* Creating new global.json file and setting SDK to " + sdk_version)
|
||||
run_cmd(['dotnet', 'new', 'globaljson', '--force', '--sdk-version', sdk_version, '--output', workDir])
|
||||
|
||||
print("\n* Running stub generator")
|
||||
helpers.run_cmd_cwd(['dotnet', 'run', '--project', thisDir + '/../../../extractor/Semmle.Extraction.CSharp.DependencyStubGenerator/Semmle.Extraction.CSharp.DependencyStubGenerator.csproj'], projectDirIn)
|
||||
helpers.run_cmd_cwd(['dotnet', 'run', '--project', thisDir + '/../../extractor/Semmle.Extraction.CSharp.DependencyStubGenerator/Semmle.Extraction.CSharp.DependencyStubGenerator.csproj'], projectDirIn)
|
||||
|
||||
print("\n* Creating new raw output project")
|
||||
rawSrcOutputDirName = 'src'
|
||||
Reference in New Issue
Block a user