Skip to content

Commit 40d3112

Browse files
authored
Merge pull request github#12264 from michaelnebel/csharp/nugetnet7
C#: Stub generator improvements.
2 parents 3a4c0a2 + 676c352 commit 40d3112

File tree

5 files changed

+107
-32
lines changed

5 files changed

+107
-32
lines changed

csharp/ql/src/Stubs/Stubs.qll

Lines changed: 72 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,6 @@ abstract private class GeneratedType extends Type, GeneratedElement {
120120
else result = ""
121121
}
122122

123-
private string stubComment() {
124-
exists(string qualifier, string name |
125-
this.hasQualifiedName(qualifier, name) and
126-
result =
127-
"// Generated from `" + getQualifiedName(qualifier, name) + "` in `" +
128-
concat(this.getALocation().toString(), "; ") + "`\n"
129-
)
130-
}
131-
132123
/** Gets the entire C# stub code for this type. */
133124
pragma[nomagic]
134125
final string getStub(Assembly assembly) {
@@ -141,17 +132,16 @@ abstract private class GeneratedType extends Type, GeneratedElement {
141132
else (
142133
not this instanceof DelegateType and
143134
result =
144-
this.stubComment() + this.stubAttributes() + stubAccessibility(this) +
145-
this.stubAbstractModifier() + this.stubStaticModifier() + this.stubPartialModifier() +
146-
this.stubKeyword() + " " + this.getUndecoratedName() + stubGenericArguments(this) +
147-
this.stubBaseTypesString() + stubTypeParametersConstraints(this) + "\n{\n" +
148-
this.stubPrivateConstructor() + this.stubMembers(assembly) + "}\n\n"
135+
this.stubAttributes() + stubAccessibility(this) + this.stubAbstractModifier() +
136+
this.stubStaticModifier() + this.stubPartialModifier() + this.stubKeyword() + " " +
137+
this.getUndecoratedName() + stubGenericArguments(this) + this.stubBaseTypesString() +
138+
stubTypeParametersConstraints(this) + "\n{\n" + this.stubPrivateConstructor() +
139+
this.stubMembers(assembly) + "}\n\n"
149140
or
150141
result =
151-
this.stubComment() + this.stubAttributes() + stubUnsafe(this) + stubAccessibility(this) +
152-
this.stubKeyword() + " " + stubClassName(this.(DelegateType).getReturnType()) + " " +
153-
this.getUndecoratedName() + stubGenericArguments(this) + "(" + stubParameters(this) +
154-
");\n\n"
142+
this.stubAttributes() + stubUnsafe(this) + stubAccessibility(this) + this.stubKeyword() +
143+
" " + stubClassName(this.(DelegateType).getReturnType()) + " " + this.getUndecoratedName()
144+
+ stubGenericArguments(this) + "(" + stubParameters(this) + ");\n\n"
155145
)
156146
}
157147

@@ -400,7 +390,7 @@ private string stubAccessibility(Member m) {
400390
if
401391
m.getDeclaringType() instanceof Interface
402392
or
403-
exists(m.(Virtualizable).getExplicitlyImplementedInterface())
393+
exists(getExplicitImplementedInterface(m))
404394
or
405395
m instanceof Constructor and m.isStatic()
406396
then result = ""
@@ -443,7 +433,7 @@ private string stubStaticOrConst(Member m) {
443433
}
444434

445435
private string stubOverride(Member m) {
446-
if m.getDeclaringType() instanceof Interface
436+
if m.getDeclaringType() instanceof Interface and not m.isStatic()
447437
then result = ""
448438
else
449439
if m.(Virtualizable).isVirtual()
@@ -713,9 +703,49 @@ private string stubEventAccessors(Event e) {
713703
else result = ";"
714704
}
715705

706+
/**
707+
* Returns an interface that `c` explicitly implements, if either of the
708+
* following also holds.
709+
* (1) `c` is not static.
710+
* (2) `c` is static and an implementation of a generic with type constraints.
711+
* (3) `c` is static and there is another member with the same name
712+
* but different return type.
713+
*
714+
* We use these rules as explicit interfaces are needed in some cases
715+
* for compilation purposes (both to distinguish members but also to ensure
716+
* type constraints are satisfied). We can't always use explicit interface
717+
* implementation due to the generic math support, because then in some cases
718+
* we will only be able to access a static via a type variable with type
719+
* constraints (C# 11 language feature).
720+
*/
721+
private Interface getExplicitImplementedInterface(Virtualizable c) {
722+
result = unique(Interface i | i = c.getExplicitlyImplementedInterface()) and
723+
(
724+
not c.isStatic()
725+
or
726+
c.isStatic() and
727+
(
728+
not c instanceof Method
729+
or
730+
c instanceof Method and
731+
(
732+
exists(TypeParameter t | t = c.getImplementee().(UnboundGeneric).getATypeParameter() |
733+
exists(t.getConstraints().getATypeConstraint())
734+
)
735+
or
736+
exists(Member m |
737+
(not m.isStatic() or m.(Method).getReturnType() != c.(Method).getReturnType()) and
738+
m.getName() = c.getName() and
739+
m.getDeclaringType() = c.getDeclaringType()
740+
)
741+
)
742+
)
743+
)
744+
}
745+
716746
private string stubExplicitImplementation(Member c) {
717-
if exists(c.(Virtualizable).getExplicitlyImplementedInterface())
718-
then result = stubClassName(c.(Virtualizable).getExplicitlyImplementedInterface()) + "."
747+
if exists(getExplicitImplementedInterface(c))
748+
then result = stubClassName(getExplicitImplementedInterface(c)) + "."
719749
else result = ""
720750
}
721751

@@ -740,14 +770,16 @@ private string stubOperator(Operator o, Assembly assembly) {
740770
if o instanceof ConversionOperator
741771
then
742772
result =
743-
" " + stubModifiers(o) + stubExplicit(o) + "operator " + stubClassName(o.getReturnType()) +
744-
"(" + stubParameters(o) + ") => throw null;\n"
773+
" " + stubModifiers(o) + stubExplicit(o) + stubExplicitImplementation(o) + "operator " +
774+
stubChecked(o) + stubClassName(o.getReturnType()) + "(" + stubParameters(o) + ")" +
775+
stubImplementation(o) + ";\n"
745776
else
746777
if not o.getDeclaringType() instanceof Enum
747778
then
748779
result =
749-
" " + stubModifiers(o) + stubClassName(o.getReturnType()) + " operator " + o.getName() +
750-
"(" + stubParameters(o) + ") => throw null;\n"
780+
" " + stubModifiers(o) + stubClassName(o.getReturnType()) + " " +
781+
stubExplicitImplementation(o) + "operator " + o.getName() + "(" + stubParameters(o) + ")" +
782+
stubImplementation(o) + ";\n"
751783
else result = " // Stub generator skipped operator: " + o.getName() + "\n"
752784
}
753785

@@ -888,7 +920,16 @@ private string stubConstructorInitializer(Constructor c) {
888920
private string stubExplicit(ConversionOperator op) {
889921
op instanceof ImplicitConversionOperator and result = "implicit "
890922
or
891-
op instanceof ExplicitConversionOperator and result = "explicit "
923+
(
924+
op instanceof ExplicitConversionOperator
925+
or
926+
op instanceof CheckedExplicitConversionOperator
927+
) and
928+
result = "explicit "
929+
}
930+
931+
private string stubChecked(Operator o) {
932+
if o instanceof CheckedExplicitConversionOperator then result = "checked " else result = ""
892933
}
893934

894935
private string stubGetter(DeclarationWithGetSetAccessors p) {
@@ -917,6 +958,8 @@ private string stubSemmleExtractorOptions() {
917958
/** Gets the generated C# code. */
918959
string generatedCode(Assembly assembly) {
919960
result =
920-
"// This file contains auto-generated code.\n" + stubSemmleExtractorOptions() + "\n" +
921-
any(GeneratedNamespace ns | ns.isGlobalNamespace()).getStubs(assembly)
961+
"// This file contains auto-generated code.\n" //
962+
+ "// Generated from `" + assembly.getFullName() + "`.\n" //
963+
+ stubSemmleExtractorOptions() + "\n" //
964+
+ any(GeneratedNamespace ns | ns.isGlobalNamespace()).getStubs(assembly)
922965
}

csharp/ql/src/Stubs/make_stubs_nuget.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def run_cmd(cmd, msg="Failed to run command"):
109109
print("\n --> Generated stub files: " + rawSrcOutputDir)
110110

111111
print("\n* Formatting files")
112-
run_cmd(['dotnet', 'format', rawSrcOutputDir])
112+
run_cmd(['dotnet', 'format', 'whitespace', rawSrcOutputDir])
113113

114114
print("\n --> Generated (formatted) stub files: " + rawSrcOutputDir)
115115

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
| // This file contains auto-generated code.\n\nnamespace A1\n{\n// Generated from `A1.C1` in `Test.cs:185:18:185:19; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class C1\n{\n}\n\n}\nnamespace A2\n{\nnamespace B2\n{\n// Generated from `A2.B2.C2` in `Test.cs:192:22:192:23; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class C2\n{\n}\n\n}\n}\nnamespace A3\n{\n// Generated from `A3.C3` in `Test.cs:198:18:198:19; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class C3\n{\n}\n\n}\nnamespace A4\n{\n// Generated from `A4.C4` in `Test.cs:208:18:208:19; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class C4\n{\n}\n\nnamespace B4\n{\n// Generated from `A4.B4.D4` in `Test.cs:205:22:205:23; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class D4\n{\n}\n\n}\n}\nnamespace Test\n{\n// Generated from `Test.Class1` in `Test.cs:5:18:5:23; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class Class1\n{\n// Generated from `Test.Class1+Class11` in `Test.cs:34:22:34:28; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class Class11 : Test.Class1.Interface1, Test.Class1.Interface2\n{\n int Test.Class1.Interface2.this[int i] { get => throw null; }\n public void Method1() => throw null;\n void Test.Class1.Interface2.Method2() => throw null;\n}\n\n\n// Generated from `Test.Class1+Class12` in `Test.cs:51:22:51:28; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class Class12 : Test.Class1.Class11\n{\n}\n\n\n// Generated from `Test.Class1+Class13` in `Test.cs:63:31:63:37; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic abstract class Class13\n{\n protected internal virtual void M() => throw null;\n public virtual void M1<T>() where T: Test.Class1.Class13 => throw null;\n public abstract void M2();\n}\n\n\n// Generated from `Test.Class1+Class14` in `Test.cs:70:31:70:37; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic abstract class Class14 : Test.Class1.Class13\n{\n protected internal override void M() => throw null;\n public override void M1<T>() => throw null;\n public abstract override void M2();\n}\n\n\n// Generated from `Test.Class1+Delegate1<>` in `Test.cs:47:30:47:41; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic delegate void Delegate1<T>(T i, int j);\n\n\n// Generated from `Test.Class1+GenericType<>` in `Test.cs:56:22:56:35; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class GenericType<T>\n{\n// Generated from `Test.Class1+GenericType<>+X` in `Test.cs:58:26:58:26; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class X\n{\n}\n\n\n}\n\n\n// Generated from `Test.Class1+Interface1` in `Test.cs:18:26:18:35; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic interface Interface1\n{\n void Method1();\n}\n\n\n// Generated from `Test.Class1+Interface2` in `Test.cs:23:38:23:47; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\nprotected internal interface Interface2\n{\n int this[int i] { get; }\n void Method2();\n}\n\n\n// Generated from `Test.Class1+Struct1` in `Test.cs:7:23:7:29; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic struct Struct1\n{\n public void Method(Test.Class1.Struct1 s = default(Test.Class1.Struct1)) => throw null;\n public int i;\n public static int j = default;\n public System.ValueTuple<int> t1;\n public (int,int) t2;\n}\n\n\n public event Test.Class1.Delegate1<int> Event1;\n public Test.Class1.GenericType<int>.X Prop { get => throw null; }\n}\n\n// Generated from `Test.Class10` in `Test.cs:138:18:138:24; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class Class10\n{\n unsafe public void M1(delegate* unmanaged<System.IntPtr,void> f) => throw null;\n}\n\n// Generated from `Test.Class3` in `Test.cs:84:18:84:23; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class Class3\n{\n public object Item { get => throw null; set => throw null; }\n [System.Runtime.CompilerServices.IndexerName("MyItem")]\n public object this[string index] { get => throw null; set => throw null; }\n}\n\n// Generated from `Test.Class4` in `Test.cs:91:18:91:23; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class Class4\n{\n unsafe public void M(int* p) => throw null;\n}\n\n// Generated from `Test.Class5` in `Test.cs:102:18:102:23; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class Class5 : Test.IInterface1\n{\n public void M2() => throw null;\n}\n\n// Generated from `Test.Class6<>` in `Test.cs:107:18:107:26; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class Class6<T> where T: class, Test.IInterface1\n{\n public virtual void M1<T>() where T: class, Test.IInterface1, new() => throw null;\n}\n\n// Generated from `Test.Class7` in `Test.cs:114:18:114:23; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class Class7 : Test.Class6<Test.Class5>\n{\n public override void M1<T>() where T: class => throw null;\n}\n\n// Generated from `Test.Class8` in `Test.cs:121:18:121:23; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class Class8\n{\n public static int @this = default;\n}\n\n// Generated from `Test.Class9` in `Test.cs:126:18:126:23; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class Class9\n{\n// Generated from `Test.Class9+Nested` in `Test.cs:130:22:130:27; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic class Nested : Test.Class9\n{\n}\n\n\n public Test.Class9.Nested NestedInstance { get => throw null; }\n}\n\n// Generated from `Test.Enum1` in `Test.cs:143:17:143:21; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic enum Enum1 : int\n{\n None1 = 0,\n Some11 = 1,\n Some12 = 2,\n}\n\n// Generated from `Test.Enum2` in `Test.cs:150:17:150:21; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic enum Enum2 : int\n{\n None2 = 2,\n Some21 = 1,\n Some22 = 3,\n}\n\n// Generated from `Test.Enum3` in `Test.cs:157:17:157:21; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic enum Enum3 : int\n{\n None3 = 2,\n Some31 = 1,\n Some32 = 0,\n}\n\n// Generated from `Test.Enum4` in `Test.cs:164:17:164:21; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic enum Enum4 : int\n{\n None4 = 2,\n Some41 = 7,\n Some42 = 6,\n}\n\n// Generated from `Test.EnumLong` in `Test.cs:171:17:171:24; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic enum EnumLong : long\n{\n None = 10,\n Some = 223372036854775807,\n}\n\n// Generated from `Test.IInterface1` in `Test.cs:96:22:96:32; Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`\npublic interface IInterface1\n{\n void M1() => throw null;\n void M2();\n}\n\n}\n\n\n |
1+
| // This file contains auto-generated code.\n// Generated from `Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`.\n\nnamespace A1\n{\npublic class C1\n{\n}\n\n}\nnamespace A2\n{\nnamespace B2\n{\npublic class C2\n{\n}\n\n}\n}\nnamespace A3\n{\npublic class C3\n{\n}\n\n}\nnamespace A4\n{\npublic class C4\n{\n}\n\nnamespace B4\n{\npublic class D4\n{\n}\n\n}\n}\nnamespace Test\n{\npublic class Class1\n{\npublic class Class11 : Test.Class1.Interface1, Test.Class1.Interface2\n{\n int Test.Class1.Interface2.this[int i] { get => throw null; }\n public void Method1() => throw null;\n void Test.Class1.Interface2.Method2() => throw null;\n}\n\n\npublic class Class12 : Test.Class1.Class11\n{\n}\n\n\npublic abstract class Class13\n{\n protected internal virtual void M() => throw null;\n public virtual void M1<T>() where T: Test.Class1.Class13 => throw null;\n public abstract void M2();\n}\n\n\npublic abstract class Class14 : Test.Class1.Class13\n{\n protected internal override void M() => throw null;\n public override void M1<T>() => throw null;\n public abstract override void M2();\n}\n\n\npublic delegate void Delegate1<T>(T i, int j);\n\n\npublic class GenericType<T>\n{\npublic class X\n{\n}\n\n\n}\n\n\npublic interface Interface1\n{\n void Method1();\n}\n\n\nprotected internal interface Interface2\n{\n int this[int i] { get; }\n void Method2();\n}\n\n\npublic struct Struct1\n{\n public void Method(Test.Class1.Struct1 s = default(Test.Class1.Struct1)) => throw null;\n public int i;\n public static int j = default;\n public System.ValueTuple<int> t1;\n public (int,int) t2;\n}\n\n\n public event Test.Class1.Delegate1<int> Event1;\n public Test.Class1.GenericType<int>.X Prop { get => throw null; }\n}\n\npublic class Class10\n{\n unsafe public void M1(delegate* unmanaged<System.IntPtr,void> f) => throw null;\n}\n\npublic class Class11 : Test.IInterface2<Test.Class11>, Test.IInterface3<Test.Class11>\n{\n static Test.Class11 Test.IInterface2<Test.Class11>.operator *(Test.Class11 left, Test.Class11 right) => throw null;\n public static Test.Class11 operator +(Test.Class11 left, Test.Class11 right) => throw null;\n public static Test.Class11 operator -(Test.Class11 left, Test.Class11 right) => throw null;\n static Test.Class11 Test.IInterface2<Test.Class11>.operator /(Test.Class11 left, Test.Class11 right) => throw null;\n public void M1() => throw null;\n void Test.IInterface2<Test.Class11>.M2() => throw null;\n public static explicit operator System.Int16(Test.Class11 n) => throw null;\n static explicit Test.IInterface2<Test.Class11>.operator int(Test.Class11 n) => throw null;\n}\n\npublic class Class3\n{\n public object Item { get => throw null; set => throw null; }\n [System.Runtime.CompilerServices.IndexerName("MyItem")]\n public object this[string index] { get => throw null; set => throw null; }\n}\n\npublic class Class4\n{\n unsafe public void M(int* p) => throw null;\n}\n\npublic class Class5 : Test.IInterface1\n{\n public void M2() => throw null;\n}\n\npublic class Class6<T> where T: class, Test.IInterface1\n{\n public virtual void M1<T>() where T: class, Test.IInterface1, new() => throw null;\n}\n\npublic class Class7 : Test.Class6<Test.Class5>\n{\n public override void M1<T>() where T: class => throw null;\n}\n\npublic class Class8\n{\n public static int @this = default;\n}\n\npublic class Class9\n{\npublic class Nested : Test.Class9\n{\n}\n\n\n public Test.Class9.Nested NestedInstance { get => throw null; }\n}\n\npublic enum Enum1 : int\n{\n None1 = 0,\n Some11 = 1,\n Some12 = 2,\n}\n\npublic enum Enum2 : int\n{\n None2 = 2,\n Some21 = 1,\n Some22 = 3,\n}\n\npublic enum Enum3 : int\n{\n None3 = 2,\n Some31 = 1,\n Some32 = 0,\n}\n\npublic enum Enum4 : int\n{\n None4 = 2,\n Some41 = 7,\n Some42 = 6,\n}\n\npublic enum EnumLong : long\n{\n None = 10,\n Some = 223372036854775807,\n}\n\npublic interface IInterface1\n{\n void M1() => throw null;\n void M2();\n}\n\npublic interface IInterface2<T> where T: Test.IInterface2<T>\n{\n static abstract T operator *(T left, T right);\n static abstract T operator +(T left, T right);\n static virtual T operator -(T left, T right) => throw null;\n static virtual T operator /(T left, T right) => throw null;\n void M1();\n void M2();\n static abstract explicit operator System.Int16(T n);\n static abstract explicit operator int(T n);\n}\n\npublic interface IInterface3<T> where T: Test.IInterface3<T>\n{\n static abstract T operator +(T left, T right);\n static virtual T operator -(T left, T right) => throw null;\n void M1();\n static abstract explicit operator System.Int16(T n);\n}\n\n}\n\n\n |

0 commit comments

Comments
 (0)