From a83c53ec9ae581eef0b96c88f390524efbd6ef1c Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 20 Feb 2026 09:03:49 +0100 Subject: [PATCH 01/13] C#: Add a partial method example with a body. --- .../partial/MethodIsPartial.expected | 15 +- .../ql/test/library-tests/partial/Partial.cs | 5 + .../library-tests/partial/Partial1.expected | 29 +-- .../library-tests/partial/Partial2.expected | 26 +-- .../partial/PartialAccessors.expected | 24 +-- .../partial/PartialConstructors.expected | 4 +- .../partial/PartialEvents.expected | 4 +- .../partial/PartialIndexers.expected | 4 +- .../partial/PartialMethodBody.expected | 7 +- .../partial/PartialProperties.expected | 4 +- .../library-tests/partial/PrintAst.expected | 191 ++++++++++-------- 11 files changed, 168 insertions(+), 145 deletions(-) diff --git a/csharp/ql/test/library-tests/partial/MethodIsPartial.expected b/csharp/ql/test/library-tests/partial/MethodIsPartial.expected index a0f1f88fb981..497d1e0026df 100644 --- a/csharp/ql/test/library-tests/partial/MethodIsPartial.expected +++ b/csharp/ql/test/library-tests/partial/MethodIsPartial.expected @@ -1,7 +1,8 @@ -| Partial.cs:6:18:6:42 | PartialMethodWithoutBody1 | true | -| Partial.cs:7:17:7:23 | Method2 | false | -| Partial.cs:18:18:18:39 | PartialMethodWithBody1 | true | -| Partial.cs:19:17:19:23 | Method3 | false | -| Partial.cs:41:18:41:42 | PartialMethodWithoutBody2 | true | -| Partial.cs:42:17:42:23 | Method4 | false | -| Partial.cs:47:17:47:23 | Method5 | false | +| Partial.cs:7:18:7:42 | PartialMethodWithoutBody1 | true | +| Partial.cs:8:17:8:23 | Method2 | false | +| Partial.cs:19:18:19:39 | PartialMethodWithBody1 | true | +| Partial.cs:20:27:20:48 | PartialMethodWithBody2 | true | +| Partial.cs:24:17:24:23 | Method3 | false | +| Partial.cs:46:18:46:42 | PartialMethodWithoutBody2 | true | +| Partial.cs:47:17:47:23 | Method4 | false | +| Partial.cs:52:17:52:23 | Method5 | false | diff --git a/csharp/ql/test/library-tests/partial/Partial.cs b/csharp/ql/test/library-tests/partial/Partial.cs index 8dd757fcd24d..b619258170b9 100644 --- a/csharp/ql/test/library-tests/partial/Partial.cs +++ b/csharp/ql/test/library-tests/partial/Partial.cs @@ -3,6 +3,7 @@ partial class TwoPartClass { partial void PartialMethodWithBody1(); + public partial object PartialMethodWithBody2(object obj); partial void PartialMethodWithoutBody1(); public void Method2() { } // Declaring declaration. @@ -16,6 +17,10 @@ public void Method2() { } partial class TwoPartClass { partial void PartialMethodWithBody1() { } + public partial object PartialMethodWithBody2(object obj) + { + return obj; + } public void Method3() { } private object _backingField; // Implementation declaration. diff --git a/csharp/ql/test/library-tests/partial/Partial1.expected b/csharp/ql/test/library-tests/partial/Partial1.expected index fe8f5658f48e..826ed13c4577 100644 --- a/csharp/ql/test/library-tests/partial/Partial1.expected +++ b/csharp/ql/test/library-tests/partial/Partial1.expected @@ -1,17 +1,18 @@ | Partial.cs:3:15:3:26 | TwoPartClass | -| Partial.cs:6:18:6:42 | PartialMethodWithoutBody1 | -| Partial.cs:16:15:16:26 | TwoPartClass | -| Partial.cs:18:18:18:39 | PartialMethodWithBody1 | -| Partial.cs:22:27:22:42 | PartialProperty1 | -| Partial.cs:24:9:24:11 | get_PartialProperty1 | -| Partial.cs:25:9:25:11 | set_PartialProperty1 | -| Partial.cs:29:27:29:30 | Item | -| Partial.cs:31:9:31:11 | get_Item | -| Partial.cs:32:9:32:11 | set_Item | -| Partial.cs:36:39:36:51 | PartialEvent1 | -| Partial.cs:36:55:36:57 | add_PartialEvent1 | -| Partial.cs:36:63:36:68 | remove_PartialEvent1 | -| Partial.cs:39:15:39:33 | OnePartPartialClass | -| Partial.cs:41:18:41:42 | PartialMethodWithoutBody2 | +| Partial.cs:7:18:7:42 | PartialMethodWithoutBody1 | +| Partial.cs:17:15:17:26 | TwoPartClass | +| Partial.cs:19:18:19:39 | PartialMethodWithBody1 | +| Partial.cs:20:27:20:48 | PartialMethodWithBody2 | +| Partial.cs:27:27:27:42 | PartialProperty1 | +| Partial.cs:29:9:29:11 | get_PartialProperty1 | +| Partial.cs:30:9:30:11 | set_PartialProperty1 | +| Partial.cs:34:27:34:30 | Item | +| Partial.cs:36:9:36:11 | get_Item | +| Partial.cs:37:9:37:11 | set_Item | +| Partial.cs:41:39:41:51 | PartialEvent1 | +| Partial.cs:41:55:41:57 | add_PartialEvent1 | +| Partial.cs:41:63:41:68 | remove_PartialEvent1 | +| Partial.cs:44:15:44:33 | OnePartPartialClass | +| Partial.cs:46:18:46:42 | PartialMethodWithoutBody2 | | PartialMultipleFiles1.cs:1:22:1:41 | PartialMultipleFiles | | PartialMultipleFiles2.cs:1:22:1:41 | PartialMultipleFiles | diff --git a/csharp/ql/test/library-tests/partial/Partial2.expected b/csharp/ql/test/library-tests/partial/Partial2.expected index 8d608c26011c..969b37b6ab99 100644 --- a/csharp/ql/test/library-tests/partial/Partial2.expected +++ b/csharp/ql/test/library-tests/partial/Partial2.expected @@ -1,15 +1,17 @@ | Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:3:15:3:26 | | -| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:6:18:6:42 | PartialMethodWithoutBody1 | -| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:7:17:7:23 | Method2 | -| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:18:18:18:39 | PartialMethodWithBody1 | -| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:19:17:19:23 | Method3 | -| Partial.cs:16:15:16:26 | TwoPartClass | Partial.cs:3:15:3:26 | | -| Partial.cs:16:15:16:26 | TwoPartClass | Partial.cs:6:18:6:42 | PartialMethodWithoutBody1 | -| Partial.cs:16:15:16:26 | TwoPartClass | Partial.cs:7:17:7:23 | Method2 | -| Partial.cs:16:15:16:26 | TwoPartClass | Partial.cs:18:18:18:39 | PartialMethodWithBody1 | -| Partial.cs:16:15:16:26 | TwoPartClass | Partial.cs:19:17:19:23 | Method3 | -| Partial.cs:39:15:39:33 | OnePartPartialClass | Partial.cs:39:15:39:33 | | -| Partial.cs:39:15:39:33 | OnePartPartialClass | Partial.cs:41:18:41:42 | PartialMethodWithoutBody2 | -| Partial.cs:39:15:39:33 | OnePartPartialClass | Partial.cs:42:17:42:23 | Method4 | +| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:7:18:7:42 | PartialMethodWithoutBody1 | +| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:8:17:8:23 | Method2 | +| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:19:18:19:39 | PartialMethodWithBody1 | +| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:20:27:20:48 | PartialMethodWithBody2 | +| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:24:17:24:23 | Method3 | +| Partial.cs:17:15:17:26 | TwoPartClass | Partial.cs:3:15:3:26 | | +| Partial.cs:17:15:17:26 | TwoPartClass | Partial.cs:7:18:7:42 | PartialMethodWithoutBody1 | +| Partial.cs:17:15:17:26 | TwoPartClass | Partial.cs:8:17:8:23 | Method2 | +| Partial.cs:17:15:17:26 | TwoPartClass | Partial.cs:19:18:19:39 | PartialMethodWithBody1 | +| Partial.cs:17:15:17:26 | TwoPartClass | Partial.cs:20:27:20:48 | PartialMethodWithBody2 | +| Partial.cs:17:15:17:26 | TwoPartClass | Partial.cs:24:17:24:23 | Method3 | +| Partial.cs:44:15:44:33 | OnePartPartialClass | Partial.cs:44:15:44:33 | | +| Partial.cs:44:15:44:33 | OnePartPartialClass | Partial.cs:46:18:46:42 | PartialMethodWithoutBody2 | +| Partial.cs:44:15:44:33 | OnePartPartialClass | Partial.cs:47:17:47:23 | Method4 | | PartialMultipleFiles1.cs:1:22:1:41 | PartialMultipleFiles | PartialMultipleFiles1.cs:1:22:1:41 | | | PartialMultipleFiles2.cs:1:22:1:41 | PartialMultipleFiles | PartialMultipleFiles1.cs:1:22:1:41 | | diff --git a/csharp/ql/test/library-tests/partial/PartialAccessors.expected b/csharp/ql/test/library-tests/partial/PartialAccessors.expected index a9d70645420b..4e53caaff841 100644 --- a/csharp/ql/test/library-tests/partial/PartialAccessors.expected +++ b/csharp/ql/test/library-tests/partial/PartialAccessors.expected @@ -1,12 +1,12 @@ -| Partial.cs:24:9:24:11 | get_PartialProperty1 | true | -| Partial.cs:25:9:25:11 | set_PartialProperty1 | true | -| Partial.cs:31:9:31:11 | get_Item | true | -| Partial.cs:32:9:32:11 | set_Item | true | -| Partial.cs:36:55:36:57 | add_PartialEvent1 | true | -| Partial.cs:36:63:36:68 | remove_PartialEvent1 | true | -| Partial.cs:48:30:48:32 | get_Property | false | -| Partial.cs:48:35:48:37 | set_Property | false | -| Partial.cs:51:9:51:11 | get_Item | false | -| Partial.cs:52:9:52:11 | set_Item | false | -| Partial.cs:54:31:54:35 | add_Event | false | -| Partial.cs:54:31:54:35 | remove_Event | false | +| Partial.cs:29:9:29:11 | get_PartialProperty1 | true | +| Partial.cs:30:9:30:11 | set_PartialProperty1 | true | +| Partial.cs:36:9:36:11 | get_Item | true | +| Partial.cs:37:9:37:11 | set_Item | true | +| Partial.cs:41:55:41:57 | add_PartialEvent1 | true | +| Partial.cs:41:63:41:68 | remove_PartialEvent1 | true | +| Partial.cs:53:30:53:32 | get_Property | false | +| Partial.cs:53:35:53:37 | set_Property | false | +| Partial.cs:56:9:56:11 | get_Item | false | +| Partial.cs:57:9:57:11 | set_Item | false | +| Partial.cs:59:31:59:35 | add_Event | false | +| Partial.cs:59:31:59:35 | remove_Event | false | diff --git a/csharp/ql/test/library-tests/partial/PartialConstructors.expected b/csharp/ql/test/library-tests/partial/PartialConstructors.expected index 69cabb244f53..907725529019 100644 --- a/csharp/ql/test/library-tests/partial/PartialConstructors.expected +++ b/csharp/ql/test/library-tests/partial/PartialConstructors.expected @@ -1,4 +1,4 @@ | Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:3:15:3:26 | {...} | -| Partial.cs:39:15:39:33 | OnePartPartialClass | Partial.cs:39:15:39:33 | {...} | -| Partial.cs:45:7:45:21 | NonPartialClass | Partial.cs:45:7:45:21 | {...} | +| Partial.cs:44:15:44:33 | OnePartPartialClass | Partial.cs:44:15:44:33 | {...} | +| Partial.cs:50:7:50:21 | NonPartialClass | Partial.cs:50:7:50:21 | {...} | | PartialMultipleFiles1.cs:1:22:1:41 | PartialMultipleFiles | PartialMultipleFiles1.cs:1:22:1:41 | {...} | diff --git a/csharp/ql/test/library-tests/partial/PartialEvents.expected b/csharp/ql/test/library-tests/partial/PartialEvents.expected index b12f8a07a9df..324f3e4e2b01 100644 --- a/csharp/ql/test/library-tests/partial/PartialEvents.expected +++ b/csharp/ql/test/library-tests/partial/PartialEvents.expected @@ -1,2 +1,2 @@ -| Partial.cs:36:39:36:51 | PartialEvent1 | true | -| Partial.cs:54:31:54:35 | Event | false | +| Partial.cs:41:39:41:51 | PartialEvent1 | true | +| Partial.cs:59:31:59:35 | Event | false | diff --git a/csharp/ql/test/library-tests/partial/PartialIndexers.expected b/csharp/ql/test/library-tests/partial/PartialIndexers.expected index be625fc4ad51..5f8c0b50b99c 100644 --- a/csharp/ql/test/library-tests/partial/PartialIndexers.expected +++ b/csharp/ql/test/library-tests/partial/PartialIndexers.expected @@ -1,2 +1,2 @@ -| Partial.cs:29:27:29:30 | Item | true | -| Partial.cs:49:19:49:22 | Item | false | +| Partial.cs:34:27:34:30 | Item | true | +| Partial.cs:54:19:54:22 | Item | false | diff --git a/csharp/ql/test/library-tests/partial/PartialMethodBody.expected b/csharp/ql/test/library-tests/partial/PartialMethodBody.expected index a91a156cb627..c9a2729fb26f 100644 --- a/csharp/ql/test/library-tests/partial/PartialMethodBody.expected +++ b/csharp/ql/test/library-tests/partial/PartialMethodBody.expected @@ -1,3 +1,4 @@ -| Partial.cs:6:18:6:42 | PartialMethodWithoutBody1 | false | -| Partial.cs:18:18:18:39 | PartialMethodWithBody1 | true | -| Partial.cs:41:18:41:42 | PartialMethodWithoutBody2 | false | +| Partial.cs:7:18:7:42 | PartialMethodWithoutBody1 | false | +| Partial.cs:19:18:19:39 | PartialMethodWithBody1 | true | +| Partial.cs:20:27:20:48 | PartialMethodWithBody2 | true | +| Partial.cs:46:18:46:42 | PartialMethodWithoutBody2 | false | diff --git a/csharp/ql/test/library-tests/partial/PartialProperties.expected b/csharp/ql/test/library-tests/partial/PartialProperties.expected index 4686bbf087a4..9d9c5f8a0e18 100644 --- a/csharp/ql/test/library-tests/partial/PartialProperties.expected +++ b/csharp/ql/test/library-tests/partial/PartialProperties.expected @@ -1,2 +1,2 @@ -| Partial.cs:22:27:22:42 | PartialProperty1 | true | -| Partial.cs:48:19:48:26 | Property | false | +| Partial.cs:27:27:27:42 | PartialProperty1 | true | +| Partial.cs:53:19:53:26 | Property | false | diff --git a/csharp/ql/test/library-tests/partial/PrintAst.expected b/csharp/ql/test/library-tests/partial/PrintAst.expected index 315de869fb3d..7a3074774809 100644 --- a/csharp/ql/test/library-tests/partial/PrintAst.expected +++ b/csharp/ql/test/library-tests/partial/PrintAst.expected @@ -1,112 +1,125 @@ Partial.cs: # 3| [Class] TwoPartClass -# 6| 6: [Method] PartialMethodWithoutBody1 -# 6| -1: [TypeMention] Void -# 7| 7: [Method] Method2 +# 7| 6: [Method] PartialMethodWithoutBody1 # 7| -1: [TypeMention] Void -# 7| 4: [BlockStmt] {...} -# 18| 8: [Method] PartialMethodWithBody1 +# 8| 7: [Method] Method2 +# 8| -1: [TypeMention] Void +# 8| 4: [BlockStmt] {...} +# 19| 8: [Method] PartialMethodWithBody1 # 5| -1: [TypeMention] Void -# 18| 4: [BlockStmt] {...} -# 19| 9: [Method] Method3 -# 19| -1: [TypeMention] Void # 19| 4: [BlockStmt] {...} -# 20| 10: [Field] _backingField +# 20| 9: [Method] PartialMethodWithBody2 +# 6| -1: [TypeMention] object # 20| -1: [TypeMention] object -# 22| 11: [Property] PartialProperty1 -# 9| -1: [TypeMention] object -# 22| -1: [TypeMention] object -# 24| 3: [Getter] get_PartialProperty1 -# 24| 4: [BlockStmt] {...} -# 24| 0: [ReturnStmt] return ...; -# 24| 0: [FieldAccess] access to field _backingField -# 25| 4: [Setter] set_PartialProperty1 +#-----| 2: (Parameters) +# 6| 0: [Parameter] obj +# 6| -1: [TypeMention] object +# 20| -1: [TypeMention] object +# 21| 4: [BlockStmt] {...} +# 22| 0: [ReturnStmt] return ...; +# 22| 0: [ParameterAccess] access to parameter obj +# 21| 4: [BlockStmt] {...} +# 22| 0: [ReturnStmt] return ...; +# 22| 0: [ParameterAccess] access to parameter obj +# 24| 10: [Method] Method3 +# 24| -1: [TypeMention] Void +# 24| 4: [BlockStmt] {...} +# 25| 11: [Field] _backingField +# 25| -1: [TypeMention] object +# 27| 12: [Property] PartialProperty1 +# 10| -1: [TypeMention] object +# 27| -1: [TypeMention] object +# 29| 3: [Getter] get_PartialProperty1 +# 29| 4: [BlockStmt] {...} +# 29| 0: [ReturnStmt] return ...; +# 29| 0: [FieldAccess] access to field _backingField +# 30| 4: [Setter] set_PartialProperty1 #-----| 2: (Parameters) -# 25| 0: [Parameter] value -# 25| 4: [BlockStmt] {...} -# 25| 0: [ExprStmt] ...; -# 25| 0: [AssignExpr] ... = ... -# 25| 0: [FieldAccess] access to field _backingField -# 25| 1: [ParameterAccess] access to parameter value -# 27| 12: [Field] _backingArray -# 27| -1: [TypeMention] Object[] -# 27| 1: [TypeMention] object -# 29| 13: [Indexer] Item -# 11| -1: [TypeMention] object -# 29| -1: [TypeMention] object +# 30| 0: [Parameter] value +# 30| 4: [BlockStmt] {...} +# 30| 0: [ExprStmt] ...; +# 30| 0: [AssignExpr] ... = ... +# 30| 0: [FieldAccess] access to field _backingField +# 30| 1: [ParameterAccess] access to parameter value +# 32| 13: [Field] _backingArray +# 32| -1: [TypeMention] Object[] +# 32| 1: [TypeMention] object +# 34| 14: [Indexer] Item +# 12| -1: [TypeMention] object +# 34| -1: [TypeMention] object #-----| 1: (Parameters) -# 11| 0: [Parameter] index -# 11| -1: [TypeMention] int -# 29| -1: [TypeMention] int -# 31| 3: [Getter] get_Item +# 12| 0: [Parameter] index +# 12| -1: [TypeMention] int +# 34| -1: [TypeMention] int +# 36| 3: [Getter] get_Item #-----| 2: (Parameters) -# 29| 0: [Parameter] index -# 31| 4: [BlockStmt] {...} -# 31| 0: [ReturnStmt] return ...; -# 31| 0: [ArrayAccess] access to array element -# 31| -1: [FieldAccess] access to field _backingArray -# 31| 0: [ParameterAccess] access to parameter index -# 32| 4: [Setter] set_Item +# 34| 0: [Parameter] index +# 36| 4: [BlockStmt] {...} +# 36| 0: [ReturnStmt] return ...; +# 36| 0: [ArrayAccess] access to array element +# 36| -1: [FieldAccess] access to field _backingArray +# 36| 0: [ParameterAccess] access to parameter index +# 37| 4: [Setter] set_Item #-----| 2: (Parameters) -# 29| 0: [Parameter] index -# 32| 1: [Parameter] value -# 32| 4: [BlockStmt] {...} -# 32| 0: [ExprStmt] ...; -# 32| 0: [AssignExpr] ... = ... -# 32| 0: [ArrayAccess] access to array element -# 32| -1: [FieldAccess] access to field _backingArray -# 32| 0: [ParameterAccess] access to parameter index -# 32| 1: [ParameterAccess] access to parameter value -# 36| 14: [Event] PartialEvent1 -# 13| -1: [TypeMention] EventHandler -# 36| 3: [AddEventAccessor] add_PartialEvent1 +# 34| 0: [Parameter] index +# 37| 1: [Parameter] value +# 37| 4: [BlockStmt] {...} +# 37| 0: [ExprStmt] ...; +# 37| 0: [AssignExpr] ... = ... +# 37| 0: [ArrayAccess] access to array element +# 37| -1: [FieldAccess] access to field _backingArray +# 37| 0: [ParameterAccess] access to parameter index +# 37| 1: [ParameterAccess] access to parameter value +# 41| 15: [Event] PartialEvent1 +# 14| -1: [TypeMention] EventHandler +# 41| 3: [AddEventAccessor] add_PartialEvent1 #-----| 2: (Parameters) -# 36| 0: [Parameter] value -# 36| 4: [BlockStmt] {...} -# 36| 4: [RemoveEventAccessor] remove_PartialEvent1 +# 41| 0: [Parameter] value +# 41| 4: [BlockStmt] {...} +# 41| 4: [RemoveEventAccessor] remove_PartialEvent1 #-----| 2: (Parameters) -# 36| 0: [Parameter] value -# 36| 4: [BlockStmt] {...} -# 39| [Class] OnePartPartialClass -# 41| 6: [Method] PartialMethodWithoutBody2 -# 41| -1: [TypeMention] Void -# 42| 7: [Method] Method4 -# 42| -1: [TypeMention] Void -# 42| 4: [BlockStmt] {...} -# 45| [Class] NonPartialClass -# 47| 6: [Method] Method5 +# 41| 0: [Parameter] value +# 41| 4: [BlockStmt] {...} +# 44| [Class] OnePartPartialClass +# 46| 6: [Method] PartialMethodWithoutBody2 +# 46| -1: [TypeMention] Void +# 47| 7: [Method] Method4 # 47| -1: [TypeMention] Void # 47| 4: [BlockStmt] {...} -# 48| 7: [Property] Property -# 48| -1: [TypeMention] object -# 48| 3: [Getter] get_Property -# 48| 4: [Setter] set_Property +# 50| [Class] NonPartialClass +# 52| 6: [Method] Method5 +# 52| -1: [TypeMention] Void +# 52| 4: [BlockStmt] {...} +# 53| 7: [Property] Property +# 53| -1: [TypeMention] object +# 53| 3: [Getter] get_Property +# 53| 4: [Setter] set_Property #-----| 2: (Parameters) -# 48| 0: [Parameter] value -# 49| 8: [Indexer] Item -# 49| -1: [TypeMention] object +# 53| 0: [Parameter] value +# 54| 8: [Indexer] Item +# 54| -1: [TypeMention] object #-----| 1: (Parameters) -# 49| 0: [Parameter] index -# 49| -1: [TypeMention] int -# 51| 3: [Getter] get_Item +# 54| 0: [Parameter] index +# 54| -1: [TypeMention] int +# 56| 3: [Getter] get_Item #-----| 2: (Parameters) -# 49| 0: [Parameter] index -# 51| 4: [BlockStmt] {...} -# 51| 0: [ReturnStmt] return ...; -# 51| 0: [NullLiteral] null -# 52| 4: [Setter] set_Item +# 54| 0: [Parameter] index +# 56| 4: [BlockStmt] {...} +# 56| 0: [ReturnStmt] return ...; +# 56| 0: [NullLiteral] null +# 57| 4: [Setter] set_Item #-----| 2: (Parameters) -# 49| 0: [Parameter] index -# 52| 1: [Parameter] value -# 52| 4: [BlockStmt] {...} -# 54| 9: [Event] Event -# 54| -1: [TypeMention] EventHandler -# 54| 3: [AddEventAccessor] add_Event +# 54| 0: [Parameter] index +# 57| 1: [Parameter] value +# 57| 4: [BlockStmt] {...} +# 59| 9: [Event] Event +# 59| -1: [TypeMention] EventHandler +# 59| 3: [AddEventAccessor] add_Event #-----| 2: (Parameters) -# 54| 0: [Parameter] value -# 54| 4: [RemoveEventAccessor] remove_Event +# 59| 0: [Parameter] value +# 59| 4: [RemoveEventAccessor] remove_Event #-----| 2: (Parameters) -# 54| 0: [Parameter] value +# 59| 0: [Parameter] value PartialMultipleFiles1.cs: # 1| [Class] PartialMultipleFiles PartialMultipleFiles2.cs: From 0e543a98436bcfd2809106348cae7e1dead05265 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 20 Feb 2026 09:05:19 +0100 Subject: [PATCH 02/13] C#: Update partial method test to count the number of extracted bodies. --- .../test/library-tests/partial/PartialMethodBody.expected | 8 ++++---- csharp/ql/test/library-tests/partial/PartialMethodBody.ql | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/csharp/ql/test/library-tests/partial/PartialMethodBody.expected b/csharp/ql/test/library-tests/partial/PartialMethodBody.expected index c9a2729fb26f..81f9115d8621 100644 --- a/csharp/ql/test/library-tests/partial/PartialMethodBody.expected +++ b/csharp/ql/test/library-tests/partial/PartialMethodBody.expected @@ -1,4 +1,4 @@ -| Partial.cs:7:18:7:42 | PartialMethodWithoutBody1 | false | -| Partial.cs:19:18:19:39 | PartialMethodWithBody1 | true | -| Partial.cs:20:27:20:48 | PartialMethodWithBody2 | true | -| Partial.cs:46:18:46:42 | PartialMethodWithoutBody2 | false | +| Partial.cs:7:18:7:42 | PartialMethodWithoutBody1 | false | 0 | +| Partial.cs:19:18:19:39 | PartialMethodWithBody1 | true | 1 | +| Partial.cs:20:27:20:48 | PartialMethodWithBody2 | true | 2 | +| Partial.cs:46:18:46:42 | PartialMethodWithoutBody2 | false | 0 | diff --git a/csharp/ql/test/library-tests/partial/PartialMethodBody.ql b/csharp/ql/test/library-tests/partial/PartialMethodBody.ql index 53cb9be250a7..9b01ffa0a690 100644 --- a/csharp/ql/test/library-tests/partial/PartialMethodBody.ql +++ b/csharp/ql/test/library-tests/partial/PartialMethodBody.ql @@ -4,4 +4,4 @@ private boolean hasBody(Method m) { if m.hasBody() then result = true else resul from Method m where m.fromSource() and m.isPartial() -select m, hasBody(m) +select m, hasBody(m), count(m.getBody()) From 7d7bbf2a50c7d46f8a9a911f1976bc4d60e8ff8d Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 20 Feb 2026 09:34:53 +0100 Subject: [PATCH 03/13] C#: Add data flow test for partial method. --- .../dataflow/methods/MethodFlow.expected | 58 +++++++++++++++++++ .../dataflow/methods/MethodFlow.ql | 12 ++++ .../library-tests/dataflow/methods/Methods.cs | 26 +++++++++ 3 files changed, 96 insertions(+) create mode 100644 csharp/ql/test/library-tests/dataflow/methods/MethodFlow.expected create mode 100644 csharp/ql/test/library-tests/dataflow/methods/MethodFlow.ql create mode 100644 csharp/ql/test/library-tests/dataflow/methods/Methods.cs diff --git a/csharp/ql/test/library-tests/dataflow/methods/MethodFlow.expected b/csharp/ql/test/library-tests/dataflow/methods/MethodFlow.expected new file mode 100644 index 000000000000..3b804763a0f7 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/methods/MethodFlow.expected @@ -0,0 +1,58 @@ +models +edges +| Methods.cs:3:48:3:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | provenance | | +| Methods.cs:3:48:3:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | provenance | | +| Methods.cs:3:48:3:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | provenance | | +| Methods.cs:3:48:3:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | provenance | | +| Methods.cs:8:48:8:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | provenance | | +| Methods.cs:8:48:8:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | provenance | | +| Methods.cs:8:48:8:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | provenance | | +| Methods.cs:8:48:8:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | provenance | | +| Methods.cs:17:13:17:13 | access to local variable o : Object | Methods.cs:19:38:19:38 | access to local variable o : Object | provenance | | +| Methods.cs:17:13:17:13 | access to local variable o : Object | Methods.cs:19:38:19:38 | access to local variable o : Object | provenance | | +| Methods.cs:17:17:17:33 | call to method Source : Object | Methods.cs:17:13:17:13 | access to local variable o : Object | provenance | | +| Methods.cs:17:17:17:33 | call to method Source : Object | Methods.cs:17:13:17:13 | access to local variable o : Object | provenance | | +| Methods.cs:19:13:19:18 | access to local variable result : Object | Methods.cs:20:14:20:19 | access to local variable result | provenance | | +| Methods.cs:19:13:19:18 | access to local variable result : Object | Methods.cs:20:14:20:19 | access to local variable result | provenance | | +| Methods.cs:19:22:19:39 | call to method PartialMethod : Object | Methods.cs:19:13:19:18 | access to local variable result : Object | provenance | | +| Methods.cs:19:22:19:39 | call to method PartialMethod : Object | Methods.cs:19:13:19:18 | access to local variable result : Object | provenance | | +| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:3:48:3:48 | o : Object | provenance | | +| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:3:48:3:48 | o : Object | provenance | | +| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:8:48:8:48 | o : Object | provenance | | +| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:8:48:8:48 | o : Object | provenance | | +| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | provenance | | +| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | provenance | | +nodes +| Methods.cs:3:48:3:48 | o : Object | semmle.label | o : Object | +| Methods.cs:3:48:3:48 | o : Object | semmle.label | o : Object | +| Methods.cs:8:48:8:48 | o : Object | semmle.label | o : Object | +| Methods.cs:8:48:8:48 | o : Object | semmle.label | o : Object | +| Methods.cs:10:16:10:16 | access to parameter o : Object | semmle.label | access to parameter o : Object | +| Methods.cs:10:16:10:16 | access to parameter o : Object | semmle.label | access to parameter o : Object | +| Methods.cs:10:16:10:16 | access to parameter o : Object | semmle.label | access to parameter o : Object | +| Methods.cs:10:16:10:16 | access to parameter o : Object | semmle.label | access to parameter o : Object | +| Methods.cs:17:13:17:13 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| Methods.cs:17:13:17:13 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| Methods.cs:17:17:17:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| Methods.cs:17:17:17:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| Methods.cs:19:13:19:18 | access to local variable result : Object | semmle.label | access to local variable result : Object | +| Methods.cs:19:13:19:18 | access to local variable result : Object | semmle.label | access to local variable result : Object | +| Methods.cs:19:22:19:39 | call to method PartialMethod : Object | semmle.label | call to method PartialMethod : Object | +| Methods.cs:19:22:19:39 | call to method PartialMethod : Object | semmle.label | call to method PartialMethod : Object | +| Methods.cs:19:38:19:38 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| Methods.cs:19:38:19:38 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| Methods.cs:20:14:20:19 | access to local variable result | semmle.label | access to local variable result | +| Methods.cs:20:14:20:19 | access to local variable result | semmle.label | access to local variable result | +subpaths +| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:3:48:3:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | +| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:3:48:3:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | +| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:3:48:3:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | +| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:3:48:3:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | +| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:8:48:8:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | +| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:8:48:8:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | +| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:8:48:8:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | +| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:8:48:8:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | +testFailures +#select +| Methods.cs:20:14:20:19 | access to local variable result | Methods.cs:17:17:17:33 | call to method Source : Object | Methods.cs:20:14:20:19 | access to local variable result | $@ | Methods.cs:17:17:17:33 | call to method Source : Object | call to method Source : Object | +| Methods.cs:20:14:20:19 | access to local variable result | Methods.cs:17:17:17:33 | call to method Source : Object | Methods.cs:20:14:20:19 | access to local variable result | $@ | Methods.cs:17:17:17:33 | call to method Source : Object | call to method Source : Object | diff --git a/csharp/ql/test/library-tests/dataflow/methods/MethodFlow.ql b/csharp/ql/test/library-tests/dataflow/methods/MethodFlow.ql new file mode 100644 index 000000000000..9ab95f59caf3 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/methods/MethodFlow.ql @@ -0,0 +1,12 @@ +/** + * @kind path-problem + */ + +import csharp +import utils.test.InlineFlowTest +import DefaultFlowTest +import PathGraph + +from PathNode source, PathNode sink +where flowPath(source, sink) +select sink, source, sink, "$@", source, source.toString() diff --git a/csharp/ql/test/library-tests/dataflow/methods/Methods.cs b/csharp/ql/test/library-tests/dataflow/methods/Methods.cs new file mode 100644 index 000000000000..15e39d5f260c --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/methods/Methods.cs @@ -0,0 +1,26 @@ +public partial class Partial +{ + public partial object PartialMethod(object o); +} + +public partial class Partial +{ + public partial object PartialMethod(object o) + { + return o; + } +} +public class C +{ + public void M() + { + var o = Source(1); + var p = new Partial(); + var result = p.PartialMethod(o); + Sink(result); // $ hasValueFlow=1 + } + + public static void Sink(object o) { } + + static T Source(object source) => throw null; +} From 67441d8930704183c822e718378204c6927693be Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 20 Feb 2026 09:52:26 +0100 Subject: [PATCH 04/13] C#: Cache the Block and ExpressionBody and streamline implementation too look for both when checking whether a body is available. --- .../Entities/Accessor.cs | 2 +- .../Entities/Base/CachedSymbol.cs | 23 +++++++++++-------- .../Entities/Constructor.cs | 6 ++--- .../Entities/EventAccessor.cs | 2 +- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs index fe01e0f9b58d..ed409e23b395 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs @@ -70,7 +70,7 @@ public override void Populate(TextWriter trapFile) Overrides(trapFile); - if (Symbol.FromSource() && Block is null) + if (Symbol.FromSource() && !HasBody) { trapFile.compiler_generated(this); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs index 1a69b0e08b2b..9b9dd7ecc632 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs @@ -89,29 +89,32 @@ protected void BindComments() protected virtual T BodyDeclaringSymbol => Symbol; - public BlockSyntax? Block + private static BlockSyntax? GetBlock(T symbol) { - get - { - return BodyDeclaringSymbol.DeclaringSyntaxReferences + return symbol.DeclaringSyntaxReferences .SelectMany(r => r.GetSyntax().ChildNodes()) .OfType() .FirstOrDefault(); - } } - public ExpressionSyntax? ExpressionBody + private static ExpressionSyntax? GetExpressionBody(T symbol) { - get - { - return BodyDeclaringSymbol.DeclaringSyntaxReferences + return symbol.DeclaringSyntaxReferences .SelectMany(r => r.GetSyntax().ChildNodes()) .OfType() .Select(arrow => arrow.Expression) .FirstOrDefault(); - } } + private BlockSyntax? vBlock; + public BlockSyntax? Block => vBlock ??= GetBlock(BodyDeclaringSymbol); + + private ExpressionSyntax? vExpressionBody; + public ExpressionSyntax? ExpressionBody => vExpressionBody ??= GetExpressionBody(BodyDeclaringSymbol); + + private bool? vHasBody; + public bool HasBody => vHasBody ??= Block is not null || ExpressionBody is not null; + public virtual bool IsSourceDeclaration => Symbol.IsSourceDeclaration(); public override bool NeedsPopulation => Context.Defines(Symbol); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs index 14d9b5480151..48039b4b962e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs @@ -42,7 +42,7 @@ public override void Populate(TextWriter trapFile) return; } - if (MakeSynthetic) + if (MakeSyntheticBody) { // Create a synthetic empty body for primary and default constructors. Statements.SyntheticEmptyBlock.Create(Context, this, 0, Location); @@ -60,7 +60,7 @@ protected override void ExtractInitializers(TextWriter trapFile) // Do not extract initializers for constructed types. // Extract initializers for constructors with a body, primary constructors // and default constructors for classes and structs declared in source code. - if (Block is null && ExpressionBody is null && !MakeSynthetic || Context.OnlyScaffold) + if (!HasBody && !MakeSyntheticBody || Context.OnlyScaffold) { return; } @@ -211,7 +211,7 @@ Symbol.ContainingType.TypeKind is TypeKind.Class or TypeKind.Struct && /// private bool IsBestSourceLocation => ReportingLocation is not null && Context.IsLocationInContext(ReportingLocation); - private bool MakeSynthetic => (IsPrimary || (IsDefault && IsBestSourceLocation)) && !Context.OnlyScaffold; + private bool MakeSyntheticBody => (IsPrimary || (IsDefault && IsBestSourceLocation)) && !Context.OnlyScaffold; [return: NotNullIfNotNull(nameof(constructor))] public static new Constructor? Create(Context cx, IMethodSymbol? constructor) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs index 3e8ab9431be7..05518119858e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs @@ -59,7 +59,7 @@ public override void Populate(TextWriter trapFile) Overrides(trapFile); - if (Symbol.FromSource() && Block is null) + if (Symbol.FromSource() && !HasBody) { trapFile.compiler_generated(this); } From e52db978d157d36d86b90c91ab088385e237ed66 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 20 Feb 2026 13:23:18 +0100 Subject: [PATCH 05/13] C#: Extract partial method declaration. --- .../CodeAnalysisExtensions/SymbolExtensions.cs | 3 +++ .../extractor/Semmle.Extraction.CSharp/Entities/Method.cs | 2 +- .../Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs | 6 ++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs index c108a18f136a..51c668604360 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs @@ -728,6 +728,9 @@ public static bool IsUnboundGenericType(this INamedTypeSymbol type) => public static INamedTypeSymbol? GetNonObjectBaseType(this ITypeSymbol symbol, Context cx) => symbol is ITypeParameterSymbol || SymbolEqualityComparer.Default.Equals(symbol.BaseType, cx.Compilation.ObjectType) ? null : symbol.BaseType; + public static IMethodSymbol GetBodyDeclaringSymbol(this IMethodSymbol method) => + method.PartialImplementationPart ?? method; + [return: NotNullIfNotNull(nameof(symbol))] public static IEntity? CreateEntity(this Context cx, ISymbol symbol) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs index 5a437c3d3585..d2c7a05c4f8f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs @@ -85,7 +85,7 @@ protected virtual void PopulateMethodBody(TextWriter trapFile) else Expression.Create(Context, expr!, this, 0); - NumberOfLines(trapFile, BodyDeclaringSymbol, this); + NumberOfLines(trapFile, Symbol, this); }); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs index 2fb148358e8c..fa7e4473dedf 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs @@ -14,14 +14,12 @@ protected OrdinaryMethod(Context cx, IMethodSymbol init) public override string Name => Symbol.GetName(); - protected override IMethodSymbol BodyDeclaringSymbol => Symbol.PartialImplementationPart ?? Symbol; - public IMethodSymbol SourceDeclaration => Symbol.OriginalDefinition; public override Microsoft.CodeAnalysis.Location ReportingLocation => IsCompilerGeneratedDelegate() ? Symbol.ContainingType.GetSymbolLocation() - : BodyDeclaringSymbol.GetSymbolLocation(); + : Symbol.GetSymbolLocation(); public override bool NeedsPopulation => (base.NeedsPopulation || IsCompilerGeneratedDelegate()) && @@ -77,7 +75,7 @@ Symbol.ContainingType is INamedTypeSymbol nt && cx.ExtractionContext.Logger.LogWarning("Reduced extension method symbols should not be directly extracted."); } - return OrdinaryMethodFactory.Instance.CreateEntityFromSymbol(cx, method); + return OrdinaryMethodFactory.Instance.CreateEntityFromSymbol(cx, method.GetBodyDeclaringSymbol()); } private class OrdinaryMethodFactory : CachedEntityFactory From a49d9447dc5cca04ad214e3ee40c92774a291bef Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 20 Feb 2026 13:26:37 +0100 Subject: [PATCH 06/13] C#: Update test expected output. --- .../dataflow/methods/MethodFlow.expected | 18 ------------------ .../partial/PartialMethodBody.expected | 2 +- .../library-tests/partial/PrintAst.expected | 9 ++------- 3 files changed, 3 insertions(+), 26 deletions(-) diff --git a/csharp/ql/test/library-tests/dataflow/methods/MethodFlow.expected b/csharp/ql/test/library-tests/dataflow/methods/MethodFlow.expected index 3b804763a0f7..c000c4393716 100644 --- a/csharp/ql/test/library-tests/dataflow/methods/MethodFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/methods/MethodFlow.expected @@ -1,11 +1,5 @@ models edges -| Methods.cs:3:48:3:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | provenance | | -| Methods.cs:3:48:3:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | provenance | | -| Methods.cs:3:48:3:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | provenance | | -| Methods.cs:3:48:3:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | provenance | | -| Methods.cs:8:48:8:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | provenance | | -| Methods.cs:8:48:8:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | provenance | | | Methods.cs:8:48:8:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | provenance | | | Methods.cs:8:48:8:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | provenance | | | Methods.cs:17:13:17:13 | access to local variable o : Object | Methods.cs:19:38:19:38 | access to local variable o : Object | provenance | | @@ -16,21 +10,15 @@ edges | Methods.cs:19:13:19:18 | access to local variable result : Object | Methods.cs:20:14:20:19 | access to local variable result | provenance | | | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | Methods.cs:19:13:19:18 | access to local variable result : Object | provenance | | | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | Methods.cs:19:13:19:18 | access to local variable result : Object | provenance | | -| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:3:48:3:48 | o : Object | provenance | | -| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:3:48:3:48 | o : Object | provenance | | | Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:8:48:8:48 | o : Object | provenance | | | Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:8:48:8:48 | o : Object | provenance | | | Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | provenance | | | Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | provenance | | nodes -| Methods.cs:3:48:3:48 | o : Object | semmle.label | o : Object | -| Methods.cs:3:48:3:48 | o : Object | semmle.label | o : Object | | Methods.cs:8:48:8:48 | o : Object | semmle.label | o : Object | | Methods.cs:8:48:8:48 | o : Object | semmle.label | o : Object | | Methods.cs:10:16:10:16 | access to parameter o : Object | semmle.label | access to parameter o : Object | | Methods.cs:10:16:10:16 | access to parameter o : Object | semmle.label | access to parameter o : Object | -| Methods.cs:10:16:10:16 | access to parameter o : Object | semmle.label | access to parameter o : Object | -| Methods.cs:10:16:10:16 | access to parameter o : Object | semmle.label | access to parameter o : Object | | Methods.cs:17:13:17:13 | access to local variable o : Object | semmle.label | access to local variable o : Object | | Methods.cs:17:13:17:13 | access to local variable o : Object | semmle.label | access to local variable o : Object | | Methods.cs:17:17:17:33 | call to method Source : Object | semmle.label | call to method Source : Object | @@ -44,12 +32,6 @@ nodes | Methods.cs:20:14:20:19 | access to local variable result | semmle.label | access to local variable result | | Methods.cs:20:14:20:19 | access to local variable result | semmle.label | access to local variable result | subpaths -| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:3:48:3:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | -| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:3:48:3:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | -| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:3:48:3:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | -| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:3:48:3:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | -| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:8:48:8:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | -| Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:8:48:8:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | | Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:8:48:8:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | | Methods.cs:19:38:19:38 | access to local variable o : Object | Methods.cs:8:48:8:48 | o : Object | Methods.cs:10:16:10:16 | access to parameter o : Object | Methods.cs:19:22:19:39 | call to method PartialMethod : Object | testFailures diff --git a/csharp/ql/test/library-tests/partial/PartialMethodBody.expected b/csharp/ql/test/library-tests/partial/PartialMethodBody.expected index 81f9115d8621..43cbf4a162ad 100644 --- a/csharp/ql/test/library-tests/partial/PartialMethodBody.expected +++ b/csharp/ql/test/library-tests/partial/PartialMethodBody.expected @@ -1,4 +1,4 @@ | Partial.cs:7:18:7:42 | PartialMethodWithoutBody1 | false | 0 | | Partial.cs:19:18:19:39 | PartialMethodWithBody1 | true | 1 | -| Partial.cs:20:27:20:48 | PartialMethodWithBody2 | true | 2 | +| Partial.cs:20:27:20:48 | PartialMethodWithBody2 | true | 1 | | Partial.cs:46:18:46:42 | PartialMethodWithoutBody2 | false | 0 | diff --git a/csharp/ql/test/library-tests/partial/PrintAst.expected b/csharp/ql/test/library-tests/partial/PrintAst.expected index 7a3074774809..49b50086804f 100644 --- a/csharp/ql/test/library-tests/partial/PrintAst.expected +++ b/csharp/ql/test/library-tests/partial/PrintAst.expected @@ -6,21 +6,16 @@ Partial.cs: # 8| -1: [TypeMention] Void # 8| 4: [BlockStmt] {...} # 19| 8: [Method] PartialMethodWithBody1 -# 5| -1: [TypeMention] Void +# 19| -1: [TypeMention] Void # 19| 4: [BlockStmt] {...} # 20| 9: [Method] PartialMethodWithBody2 -# 6| -1: [TypeMention] object # 20| -1: [TypeMention] object #-----| 2: (Parameters) -# 6| 0: [Parameter] obj -# 6| -1: [TypeMention] object +# 20| 0: [Parameter] obj # 20| -1: [TypeMention] object # 21| 4: [BlockStmt] {...} # 22| 0: [ReturnStmt] return ...; # 22| 0: [ParameterAccess] access to parameter obj -# 21| 4: [BlockStmt] {...} -# 22| 0: [ReturnStmt] return ...; -# 22| 0: [ParameterAccess] access to parameter obj # 24| 10: [Method] Method3 # 24| -1: [TypeMention] Void # 24| 4: [BlockStmt] {...} From 0715183647b925a229461770656e9a553639a300 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 20 Feb 2026 16:32:54 +0100 Subject: [PATCH 07/13] C#: Streamline the partial implementation for properties and events. --- .../CodeAnalysisExtensions/SymbolExtensions.cs | 6 ++++++ .../Entities/Base/CachedSymbol.cs | 6 ++---- .../Semmle.Extraction.CSharp/Entities/Event.cs | 10 +++------- .../Semmle.Extraction.CSharp/Entities/Indexer.cs | 6 +++--- .../Semmle.Extraction.CSharp/Entities/Property.cs | 10 +++------- 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs index 51c668604360..fbc1b52c99b3 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs @@ -731,6 +731,12 @@ public static bool IsUnboundGenericType(this INamedTypeSymbol type) => public static IMethodSymbol GetBodyDeclaringSymbol(this IMethodSymbol method) => method.PartialImplementationPart ?? method; + public static IPropertySymbol GetBodyDeclaringSymbol(this IPropertySymbol property) => + property.PartialImplementationPart ?? property; + + public static IEventSymbol GetBodyDeclaringSymbol(this IEventSymbol symbol) => + symbol.PartialImplementationPart ?? symbol; + [return: NotNullIfNotNull(nameof(symbol))] public static IEntity? CreateEntity(this Context cx, ISymbol symbol) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs index 9b9dd7ecc632..f43feb1ed80b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs @@ -87,8 +87,6 @@ protected void BindComments() Context.BindComments(this, FullLocation); } - protected virtual T BodyDeclaringSymbol => Symbol; - private static BlockSyntax? GetBlock(T symbol) { return symbol.DeclaringSyntaxReferences @@ -107,10 +105,10 @@ protected void BindComments() } private BlockSyntax? vBlock; - public BlockSyntax? Block => vBlock ??= GetBlock(BodyDeclaringSymbol); + public BlockSyntax? Block => vBlock ??= GetBlock(Symbol); private ExpressionSyntax? vExpressionBody; - public ExpressionSyntax? ExpressionBody => vExpressionBody ??= GetExpressionBody(BodyDeclaringSymbol); + public ExpressionSyntax? ExpressionBody => vExpressionBody ??= GetExpressionBody(Symbol); private bool? vHasBody; public bool HasBody => vHasBody ??= Block is not null || ExpressionBody is not null; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs index c93df9ccc4f7..bed4a87d5dab 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs @@ -11,10 +11,6 @@ internal class Event : CachedSymbol private Event(Context cx, IEventSymbol init) : base(cx, init) { } - protected override IEventSymbol BodyDeclaringSymbol => Symbol.PartialImplementationPart ?? Symbol; - - public override Microsoft.CodeAnalysis.Location? ReportingLocation => BodyDeclaringSymbol.Locations.BestOrDefault(); - public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(ContainingType!); @@ -31,8 +27,8 @@ public override void Populate(TextWriter trapFile) var type = Type.Create(Context, Symbol.Type); trapFile.events(this, Symbol.GetName(), ContainingType!, type.TypeRef, Create(Context, Symbol.OriginalDefinition)); - var adder = BodyDeclaringSymbol.AddMethod; - var remover = BodyDeclaringSymbol.RemoveMethod; + var adder = Symbol.AddMethod; + var remover = Symbol.RemoveMethod; if (adder is not null) Method.Create(Context, adder); @@ -76,7 +72,7 @@ public override void Populate(TextWriter trapFile) } } - public static Event Create(Context cx, IEventSymbol symbol) => EventFactory.Instance.CreateEntityFromSymbol(cx, symbol); + public static Event Create(Context cx, IEventSymbol symbol) => EventFactory.Instance.CreateEntityFromSymbol(cx, symbol.GetBodyDeclaringSymbol()); private class EventFactory : CachedEntityFactory { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs index 870c2eb76500..10f17abef18f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs @@ -20,8 +20,8 @@ public override void Populate(TextWriter trapFile) var type = Type.Create(Context, Symbol.Type); trapFile.indexers(this, Symbol.GetName(useMetadataName: true), ContainingType!, type.TypeRef, OriginalDefinition); - var getter = BodyDeclaringSymbol.GetMethod; - var setter = BodyDeclaringSymbol.SetMethod; + var getter = Symbol.GetMethod; + var setter = Symbol.SetMethod; if (getter is null && setter is null) Context.ModelError(Symbol, "No indexer accessor defined"); @@ -81,7 +81,7 @@ public override void Populate(TextWriter trapFile) TypeMention.Create(Context, syntax.Type, this, type); } - public static new Indexer Create(Context cx, IPropertySymbol prop) => IndexerFactory.Instance.CreateEntityFromSymbol(cx, prop); + public static new Indexer Create(Context cx, IPropertySymbol prop) => IndexerFactory.Instance.CreateEntityFromSymbol(cx, prop.GetBodyDeclaringSymbol()); public override void WriteId(EscapingTextWriter trapFile) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs index d48d778cb75a..57eb5efc0070 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs @@ -21,10 +21,6 @@ protected Property(Context cx, IPropertySymbol init) private Type Type => type.Value; - protected override IPropertySymbol BodyDeclaringSymbol => Symbol.PartialImplementationPart ?? Symbol; - - public override Microsoft.CodeAnalysis.Location? ReportingLocation => BodyDeclaringSymbol.Locations.BestOrDefault(); - public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(Type); @@ -46,8 +42,8 @@ public override void Populate(TextWriter trapFile) var type = Type; trapFile.properties(this, Symbol.GetName(), ContainingType!, type.TypeRef, Create(Context, Symbol.OriginalDefinition)); - var getter = BodyDeclaringSymbol.GetMethod; - var setter = BodyDeclaringSymbol.SetMethod; + var getter = Symbol.GetMethod; + var setter = Symbol.SetMethod; if (getter is not null) Method.Create(Context, getter); @@ -132,7 +128,7 @@ public static Property Create(Context cx, IPropertySymbol prop) { var isIndexer = prop.IsIndexer || prop.Parameters.Any(); - return isIndexer ? Indexer.Create(cx, prop) : PropertyFactory.Instance.CreateEntityFromSymbol(cx, prop); + return isIndexer ? Indexer.Create(cx, prop) : PropertyFactory.Instance.CreateEntityFromSymbol(cx, prop.GetBodyDeclaringSymbol()); } private class PropertyFactory : CachedEntityFactory From c9832097d7bf173be9484dadc0f8566374d079c6 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 20 Feb 2026 16:33:38 +0100 Subject: [PATCH 08/13] C#: Update test expected output. --- csharp/ql/test/library-tests/partial/PrintAst.expected | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/csharp/ql/test/library-tests/partial/PrintAst.expected b/csharp/ql/test/library-tests/partial/PrintAst.expected index 49b50086804f..dc96317ab03b 100644 --- a/csharp/ql/test/library-tests/partial/PrintAst.expected +++ b/csharp/ql/test/library-tests/partial/PrintAst.expected @@ -22,7 +22,6 @@ Partial.cs: # 25| 11: [Field] _backingField # 25| -1: [TypeMention] object # 27| 12: [Property] PartialProperty1 -# 10| -1: [TypeMention] object # 27| -1: [TypeMention] object # 29| 3: [Getter] get_PartialProperty1 # 29| 4: [BlockStmt] {...} @@ -40,11 +39,9 @@ Partial.cs: # 32| -1: [TypeMention] Object[] # 32| 1: [TypeMention] object # 34| 14: [Indexer] Item -# 12| -1: [TypeMention] object # 34| -1: [TypeMention] object #-----| 1: (Parameters) -# 12| 0: [Parameter] index -# 12| -1: [TypeMention] int +# 34| 0: [Parameter] index # 34| -1: [TypeMention] int # 36| 3: [Getter] get_Item #-----| 2: (Parameters) @@ -66,7 +63,6 @@ Partial.cs: # 37| 0: [ParameterAccess] access to parameter index # 37| 1: [ParameterAccess] access to parameter value # 41| 15: [Event] PartialEvent1 -# 14| -1: [TypeMention] EventHandler # 41| 3: [AddEventAccessor] add_PartialEvent1 #-----| 2: (Parameters) # 41| 0: [Parameter] value From a930cbfbd93b1b837e86a5261355d991455cf1b0 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 23 Feb 2026 15:09:04 +0100 Subject: [PATCH 09/13] C#: Add change note. --- .../ql/lib/change-notes/2026-02-23-partial-extraction-fix.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 csharp/ql/lib/change-notes/2026-02-23-partial-extraction-fix.md diff --git a/csharp/ql/lib/change-notes/2026-02-23-partial-extraction-fix.md b/csharp/ql/lib/change-notes/2026-02-23-partial-extraction-fix.md new file mode 100644 index 000000000000..fc7a9d1dfbf5 --- /dev/null +++ b/csharp/ql/lib/change-notes/2026-02-23-partial-extraction-fix.md @@ -0,0 +1,4 @@ +--- +category: fix +--- +* Fixed an issue where the body of a partial member could be extracted twice. When both a *defining* and an *implementing* declaration exist, only the *implementing* declaration is now extracted. From aa99869413a94d765ce889ef3e17383dd38234f1 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 17 Feb 2026 12:08:13 +0100 Subject: [PATCH 10/13] C#: Update partial tests and expected output. --- .../library-tests/dispatch/CallGraph.expected | 13 +- .../dispatch/GetADynamicTarget.expected | 13 +- .../library-tests/dispatch/ViableCallable.cs | 5 + .../partial/MethodIsPartial.expected | 16 +- .../ql/test/library-tests/partial/Partial.cs | 5 + .../library-tests/partial/Partial1.expected | 31 +-- .../library-tests/partial/Partial2.expected | 28 ++- .../partial/PartialAccessors.expected | 24 +- .../partial/PartialConstructors.expected | 5 +- .../partial/PartialEvents.expected | 4 +- .../partial/PartialIndexers.expected | 4 +- .../partial/PartialMethodBody.expected | 8 +- .../partial/PartialProperties.expected | 4 +- .../library-tests/partial/PrintAst.expected | 205 +++++++++--------- 14 files changed, 192 insertions(+), 173 deletions(-) diff --git a/csharp/ql/test/library-tests/dispatch/CallGraph.expected b/csharp/ql/test/library-tests/dispatch/CallGraph.expected index 2feb959dd863..1deae048d102 100644 --- a/csharp/ql/test/library-tests/dispatch/CallGraph.expected +++ b/csharp/ql/test/library-tests/dispatch/CallGraph.expected @@ -270,9 +270,10 @@ | ViableCallable.cs:679:17:679:20 | Run3 | ViableCallable.cs:637:21:637:21 | M | | ViableCallable.cs:679:17:679:20 | Run3 | ViableCallable.cs:646:21:646:21 | M | | ViableCallable.cs:679:17:679:20 | Run3 | ViableCallable.cs:648:21:648:21 | M | -| ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:703:42:703:44 | get_Property | -| ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:703:63:703:65 | set_Property | -| ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:705:49:705:51 | get_Item | -| ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:705:70:705:72 | set_Item | -| ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:706:51:706:53 | add_Event | -| ViableCallable.cs:709:17:709:20 | Run1 | ViableCallable.cs:706:59:706:64 | remove_Event | +| ViableCallable.cs:711:17:711:20 | Run1 | ViableCallable.cs:695:24:695:31 | Partial1 | +| ViableCallable.cs:711:17:711:20 | Run1 | ViableCallable.cs:705:42:705:44 | get_Property | +| ViableCallable.cs:711:17:711:20 | Run1 | ViableCallable.cs:705:63:705:65 | set_Property | +| ViableCallable.cs:711:17:711:20 | Run1 | ViableCallable.cs:707:49:707:51 | get_Item | +| ViableCallable.cs:711:17:711:20 | Run1 | ViableCallable.cs:707:70:707:72 | set_Item | +| ViableCallable.cs:711:17:711:20 | Run1 | ViableCallable.cs:708:51:708:53 | add_Event | +| ViableCallable.cs:711:17:711:20 | Run1 | ViableCallable.cs:708:59:708:64 | remove_Event | diff --git a/csharp/ql/test/library-tests/dispatch/GetADynamicTarget.expected b/csharp/ql/test/library-tests/dispatch/GetADynamicTarget.expected index 5d6b4be4f878..84dc17b073aa 100644 --- a/csharp/ql/test/library-tests/dispatch/GetADynamicTarget.expected +++ b/csharp/ql/test/library-tests/dispatch/GetADynamicTarget.expected @@ -518,9 +518,10 @@ | ViableCallable.cs:683:9:683:16 | call to method M | C22+TestOverloadResolution2.M(Int32[]) | | ViableCallable.cs:687:9:687:16 | call to method M | C22+TestOverloadResolution1.M(List) | | ViableCallable.cs:687:9:687:16 | call to method M | C22+TestOverloadResolution2.M(List) | -| ViableCallable.cs:714:9:714:18 | access to property Property | C23+Partial1.set_Property(object) | -| ViableCallable.cs:717:13:717:22 | access to property Property | C23+Partial1.get_Property() | -| ViableCallable.cs:720:9:720:12 | access to indexer | C23+Partial1.set_Item(int, object) | -| ViableCallable.cs:723:13:723:16 | access to indexer | C23+Partial1.get_Item(int) | -| ViableCallable.cs:726:9:726:15 | access to event Event | C23+Partial1.add_Event(EventHandler) | -| ViableCallable.cs:729:9:729:15 | access to event Event | C23+Partial1.remove_Event(EventHandler) | +| ViableCallable.cs:716:9:716:18 | access to property Property | C23+Partial1.set_Property(object) | +| ViableCallable.cs:719:13:719:22 | access to property Property | C23+Partial1.get_Property() | +| ViableCallable.cs:722:9:722:12 | access to indexer | C23+Partial1.set_Item(int, object) | +| ViableCallable.cs:725:13:725:16 | access to indexer | C23+Partial1.get_Item(int) | +| ViableCallable.cs:728:9:728:15 | access to event Event | C23+Partial1.add_Event(EventHandler) | +| ViableCallable.cs:731:9:731:15 | access to event Event | C23+Partial1.remove_Event(EventHandler) | +| ViableCallable.cs:734:18:734:43 | object creation of type Partial1 | C23+Partial1.Partial1(object) | diff --git a/csharp/ql/test/library-tests/dispatch/ViableCallable.cs b/csharp/ql/test/library-tests/dispatch/ViableCallable.cs index e904eb01a862..dee8d9b0d1d6 100644 --- a/csharp/ql/test/library-tests/dispatch/ViableCallable.cs +++ b/csharp/ql/test/library-tests/dispatch/ViableCallable.cs @@ -692,6 +692,7 @@ public class C23 { public partial class Partial1 { + public partial Partial1(object obj); public partial object Property { get; set; } public partial object this[int index] { get; set; } @@ -700,6 +701,7 @@ public partial class Partial1 public partial class Partial1 { + public partial Partial1(object obj) { } public partial object Property { get { return null; } set { } } public partial object this[int index] { get { return null; } set { } } @@ -727,5 +729,8 @@ public void Run1(Partial1 p) // Viable callable: Partial1.remove_Event p.Event -= (sender, e) => { }; + + // Viable callable: Partial1.Partial1(object) + var p0 = new Partial1(new object()); } } diff --git a/csharp/ql/test/library-tests/partial/MethodIsPartial.expected b/csharp/ql/test/library-tests/partial/MethodIsPartial.expected index 497d1e0026df..484540d72b2e 100644 --- a/csharp/ql/test/library-tests/partial/MethodIsPartial.expected +++ b/csharp/ql/test/library-tests/partial/MethodIsPartial.expected @@ -1,8 +1,8 @@ -| Partial.cs:7:18:7:42 | PartialMethodWithoutBody1 | true | -| Partial.cs:8:17:8:23 | Method2 | false | -| Partial.cs:19:18:19:39 | PartialMethodWithBody1 | true | -| Partial.cs:20:27:20:48 | PartialMethodWithBody2 | true | -| Partial.cs:24:17:24:23 | Method3 | false | -| Partial.cs:46:18:46:42 | PartialMethodWithoutBody2 | true | -| Partial.cs:47:17:47:23 | Method4 | false | -| Partial.cs:52:17:52:23 | Method5 | false | +| Partial.cs:9:18:9:42 | PartialMethodWithoutBody1 | true | +| Partial.cs:10:17:10:23 | Method2 | false | +| Partial.cs:23:18:23:39 | PartialMethodWithBody1 | true | +| Partial.cs:24:27:24:48 | PartialMethodWithBody2 | true | +| Partial.cs:28:17:28:23 | Method3 | false | +| Partial.cs:50:18:50:42 | PartialMethodWithoutBody2 | true | +| Partial.cs:51:17:51:23 | Method4 | false | +| Partial.cs:57:17:57:23 | Method5 | false | diff --git a/csharp/ql/test/library-tests/partial/Partial.cs b/csharp/ql/test/library-tests/partial/Partial.cs index b619258170b9..6ab87c95e8bb 100644 --- a/csharp/ql/test/library-tests/partial/Partial.cs +++ b/csharp/ql/test/library-tests/partial/Partial.cs @@ -2,6 +2,8 @@ partial class TwoPartClass { + // Declaring declaration. + public partial TwoPartClass(object obj); partial void PartialMethodWithBody1(); public partial object PartialMethodWithBody2(object obj); partial void PartialMethodWithoutBody1(); @@ -16,6 +18,8 @@ public void Method2() { } partial class TwoPartClass { + // Implementation declaration. + public partial TwoPartClass(object obj) { } partial void PartialMethodWithBody1() { } public partial object PartialMethodWithBody2(object obj) { @@ -49,6 +53,7 @@ public void Method4() { } class NonPartialClass { + public NonPartialClass(object obj) { } public void Method5() { } public object Property { get; set; } public object this[int index] diff --git a/csharp/ql/test/library-tests/partial/Partial1.expected b/csharp/ql/test/library-tests/partial/Partial1.expected index 826ed13c4577..8d80261d9ede 100644 --- a/csharp/ql/test/library-tests/partial/Partial1.expected +++ b/csharp/ql/test/library-tests/partial/Partial1.expected @@ -1,18 +1,19 @@ | Partial.cs:3:15:3:26 | TwoPartClass | -| Partial.cs:7:18:7:42 | PartialMethodWithoutBody1 | -| Partial.cs:17:15:17:26 | TwoPartClass | -| Partial.cs:19:18:19:39 | PartialMethodWithBody1 | -| Partial.cs:20:27:20:48 | PartialMethodWithBody2 | -| Partial.cs:27:27:27:42 | PartialProperty1 | -| Partial.cs:29:9:29:11 | get_PartialProperty1 | -| Partial.cs:30:9:30:11 | set_PartialProperty1 | -| Partial.cs:34:27:34:30 | Item | -| Partial.cs:36:9:36:11 | get_Item | -| Partial.cs:37:9:37:11 | set_Item | -| Partial.cs:41:39:41:51 | PartialEvent1 | -| Partial.cs:41:55:41:57 | add_PartialEvent1 | -| Partial.cs:41:63:41:68 | remove_PartialEvent1 | -| Partial.cs:44:15:44:33 | OnePartPartialClass | -| Partial.cs:46:18:46:42 | PartialMethodWithoutBody2 | +| Partial.cs:6:20:6:31 | TwoPartClass | +| Partial.cs:9:18:9:42 | PartialMethodWithoutBody1 | +| Partial.cs:19:15:19:26 | TwoPartClass | +| Partial.cs:23:18:23:39 | PartialMethodWithBody1 | +| Partial.cs:24:27:24:48 | PartialMethodWithBody2 | +| Partial.cs:31:27:31:42 | PartialProperty1 | +| Partial.cs:33:9:33:11 | get_PartialProperty1 | +| Partial.cs:34:9:34:11 | set_PartialProperty1 | +| Partial.cs:38:27:38:30 | Item | +| Partial.cs:40:9:40:11 | get_Item | +| Partial.cs:41:9:41:11 | set_Item | +| Partial.cs:45:39:45:51 | PartialEvent1 | +| Partial.cs:45:55:45:57 | add_PartialEvent1 | +| Partial.cs:45:63:45:68 | remove_PartialEvent1 | +| Partial.cs:48:15:48:33 | OnePartPartialClass | +| Partial.cs:50:18:50:42 | PartialMethodWithoutBody2 | | PartialMultipleFiles1.cs:1:22:1:41 | PartialMultipleFiles | | PartialMultipleFiles2.cs:1:22:1:41 | PartialMultipleFiles | diff --git a/csharp/ql/test/library-tests/partial/Partial2.expected b/csharp/ql/test/library-tests/partial/Partial2.expected index 969b37b6ab99..2d2a7421f7a5 100644 --- a/csharp/ql/test/library-tests/partial/Partial2.expected +++ b/csharp/ql/test/library-tests/partial/Partial2.expected @@ -1,17 +1,15 @@ -| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:3:15:3:26 | | -| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:7:18:7:42 | PartialMethodWithoutBody1 | -| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:8:17:8:23 | Method2 | -| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:19:18:19:39 | PartialMethodWithBody1 | -| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:20:27:20:48 | PartialMethodWithBody2 | -| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:24:17:24:23 | Method3 | -| Partial.cs:17:15:17:26 | TwoPartClass | Partial.cs:3:15:3:26 | | -| Partial.cs:17:15:17:26 | TwoPartClass | Partial.cs:7:18:7:42 | PartialMethodWithoutBody1 | -| Partial.cs:17:15:17:26 | TwoPartClass | Partial.cs:8:17:8:23 | Method2 | -| Partial.cs:17:15:17:26 | TwoPartClass | Partial.cs:19:18:19:39 | PartialMethodWithBody1 | -| Partial.cs:17:15:17:26 | TwoPartClass | Partial.cs:20:27:20:48 | PartialMethodWithBody2 | -| Partial.cs:17:15:17:26 | TwoPartClass | Partial.cs:24:17:24:23 | Method3 | -| Partial.cs:44:15:44:33 | OnePartPartialClass | Partial.cs:44:15:44:33 | | -| Partial.cs:44:15:44:33 | OnePartPartialClass | Partial.cs:46:18:46:42 | PartialMethodWithoutBody2 | -| Partial.cs:44:15:44:33 | OnePartPartialClass | Partial.cs:47:17:47:23 | Method4 | +| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:9:18:9:42 | PartialMethodWithoutBody1 | +| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:10:17:10:23 | Method2 | +| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:23:18:23:39 | PartialMethodWithBody1 | +| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:24:27:24:48 | PartialMethodWithBody2 | +| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:28:17:28:23 | Method3 | +| Partial.cs:19:15:19:26 | TwoPartClass | Partial.cs:9:18:9:42 | PartialMethodWithoutBody1 | +| Partial.cs:19:15:19:26 | TwoPartClass | Partial.cs:10:17:10:23 | Method2 | +| Partial.cs:19:15:19:26 | TwoPartClass | Partial.cs:23:18:23:39 | PartialMethodWithBody1 | +| Partial.cs:19:15:19:26 | TwoPartClass | Partial.cs:24:27:24:48 | PartialMethodWithBody2 | +| Partial.cs:19:15:19:26 | TwoPartClass | Partial.cs:28:17:28:23 | Method3 | +| Partial.cs:48:15:48:33 | OnePartPartialClass | Partial.cs:48:15:48:33 | | +| Partial.cs:48:15:48:33 | OnePartPartialClass | Partial.cs:50:18:50:42 | PartialMethodWithoutBody2 | +| Partial.cs:48:15:48:33 | OnePartPartialClass | Partial.cs:51:17:51:23 | Method4 | | PartialMultipleFiles1.cs:1:22:1:41 | PartialMultipleFiles | PartialMultipleFiles1.cs:1:22:1:41 | | | PartialMultipleFiles2.cs:1:22:1:41 | PartialMultipleFiles | PartialMultipleFiles1.cs:1:22:1:41 | | diff --git a/csharp/ql/test/library-tests/partial/PartialAccessors.expected b/csharp/ql/test/library-tests/partial/PartialAccessors.expected index 4e53caaff841..c09756d252e7 100644 --- a/csharp/ql/test/library-tests/partial/PartialAccessors.expected +++ b/csharp/ql/test/library-tests/partial/PartialAccessors.expected @@ -1,12 +1,12 @@ -| Partial.cs:29:9:29:11 | get_PartialProperty1 | true | -| Partial.cs:30:9:30:11 | set_PartialProperty1 | true | -| Partial.cs:36:9:36:11 | get_Item | true | -| Partial.cs:37:9:37:11 | set_Item | true | -| Partial.cs:41:55:41:57 | add_PartialEvent1 | true | -| Partial.cs:41:63:41:68 | remove_PartialEvent1 | true | -| Partial.cs:53:30:53:32 | get_Property | false | -| Partial.cs:53:35:53:37 | set_Property | false | -| Partial.cs:56:9:56:11 | get_Item | false | -| Partial.cs:57:9:57:11 | set_Item | false | -| Partial.cs:59:31:59:35 | add_Event | false | -| Partial.cs:59:31:59:35 | remove_Event | false | +| Partial.cs:33:9:33:11 | get_PartialProperty1 | true | +| Partial.cs:34:9:34:11 | set_PartialProperty1 | true | +| Partial.cs:40:9:40:11 | get_Item | true | +| Partial.cs:41:9:41:11 | set_Item | true | +| Partial.cs:45:55:45:57 | add_PartialEvent1 | true | +| Partial.cs:45:63:45:68 | remove_PartialEvent1 | true | +| Partial.cs:58:30:58:32 | get_Property | false | +| Partial.cs:58:35:58:37 | set_Property | false | +| Partial.cs:61:9:61:11 | get_Item | false | +| Partial.cs:62:9:62:11 | set_Item | false | +| Partial.cs:64:31:64:35 | add_Event | false | +| Partial.cs:64:31:64:35 | remove_Event | false | diff --git a/csharp/ql/test/library-tests/partial/PartialConstructors.expected b/csharp/ql/test/library-tests/partial/PartialConstructors.expected index 907725529019..e4c399091c05 100644 --- a/csharp/ql/test/library-tests/partial/PartialConstructors.expected +++ b/csharp/ql/test/library-tests/partial/PartialConstructors.expected @@ -1,4 +1,3 @@ -| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:3:15:3:26 | {...} | -| Partial.cs:44:15:44:33 | OnePartPartialClass | Partial.cs:44:15:44:33 | {...} | -| Partial.cs:50:7:50:21 | NonPartialClass | Partial.cs:50:7:50:21 | {...} | +| Partial.cs:48:15:48:33 | OnePartPartialClass | Partial.cs:48:15:48:33 | {...} | +| Partial.cs:56:12:56:26 | NonPartialClass | Partial.cs:56:40:56:42 | {...} | | PartialMultipleFiles1.cs:1:22:1:41 | PartialMultipleFiles | PartialMultipleFiles1.cs:1:22:1:41 | {...} | diff --git a/csharp/ql/test/library-tests/partial/PartialEvents.expected b/csharp/ql/test/library-tests/partial/PartialEvents.expected index 324f3e4e2b01..4c7e610c37d3 100644 --- a/csharp/ql/test/library-tests/partial/PartialEvents.expected +++ b/csharp/ql/test/library-tests/partial/PartialEvents.expected @@ -1,2 +1,2 @@ -| Partial.cs:41:39:41:51 | PartialEvent1 | true | -| Partial.cs:59:31:59:35 | Event | false | +| Partial.cs:45:39:45:51 | PartialEvent1 | true | +| Partial.cs:64:31:64:35 | Event | false | diff --git a/csharp/ql/test/library-tests/partial/PartialIndexers.expected b/csharp/ql/test/library-tests/partial/PartialIndexers.expected index 5f8c0b50b99c..425a9897a478 100644 --- a/csharp/ql/test/library-tests/partial/PartialIndexers.expected +++ b/csharp/ql/test/library-tests/partial/PartialIndexers.expected @@ -1,2 +1,2 @@ -| Partial.cs:34:27:34:30 | Item | true | -| Partial.cs:54:19:54:22 | Item | false | +| Partial.cs:38:27:38:30 | Item | true | +| Partial.cs:59:19:59:22 | Item | false | diff --git a/csharp/ql/test/library-tests/partial/PartialMethodBody.expected b/csharp/ql/test/library-tests/partial/PartialMethodBody.expected index 43cbf4a162ad..c15189262cad 100644 --- a/csharp/ql/test/library-tests/partial/PartialMethodBody.expected +++ b/csharp/ql/test/library-tests/partial/PartialMethodBody.expected @@ -1,4 +1,4 @@ -| Partial.cs:7:18:7:42 | PartialMethodWithoutBody1 | false | 0 | -| Partial.cs:19:18:19:39 | PartialMethodWithBody1 | true | 1 | -| Partial.cs:20:27:20:48 | PartialMethodWithBody2 | true | 1 | -| Partial.cs:46:18:46:42 | PartialMethodWithoutBody2 | false | 0 | +| Partial.cs:9:18:9:42 | PartialMethodWithoutBody1 | false | 0 | +| Partial.cs:23:18:23:39 | PartialMethodWithBody1 | true | 1 | +| Partial.cs:24:27:24:48 | PartialMethodWithBody2 | true | 1 | +| Partial.cs:50:18:50:42 | PartialMethodWithoutBody2 | false | 0 | diff --git a/csharp/ql/test/library-tests/partial/PartialProperties.expected b/csharp/ql/test/library-tests/partial/PartialProperties.expected index 9d9c5f8a0e18..5510fd53ad64 100644 --- a/csharp/ql/test/library-tests/partial/PartialProperties.expected +++ b/csharp/ql/test/library-tests/partial/PartialProperties.expected @@ -1,2 +1,2 @@ -| Partial.cs:27:27:27:42 | PartialProperty1 | true | -| Partial.cs:53:19:53:26 | Property | false | +| Partial.cs:31:27:31:42 | PartialProperty1 | true | +| Partial.cs:58:19:58:26 | Property | false | diff --git a/csharp/ql/test/library-tests/partial/PrintAst.expected b/csharp/ql/test/library-tests/partial/PrintAst.expected index dc96317ab03b..742bb02e8447 100644 --- a/csharp/ql/test/library-tests/partial/PrintAst.expected +++ b/csharp/ql/test/library-tests/partial/PrintAst.expected @@ -1,116 +1,125 @@ Partial.cs: # 3| [Class] TwoPartClass -# 7| 6: [Method] PartialMethodWithoutBody1 -# 7| -1: [TypeMention] Void -# 8| 7: [Method] Method2 -# 8| -1: [TypeMention] Void -# 8| 4: [BlockStmt] {...} -# 19| 8: [Method] PartialMethodWithBody1 -# 19| -1: [TypeMention] Void -# 19| 4: [BlockStmt] {...} -# 20| 9: [Method] PartialMethodWithBody2 -# 20| -1: [TypeMention] object +# 6| 4: [InstanceConstructor] TwoPartClass #-----| 2: (Parameters) -# 20| 0: [Parameter] obj -# 20| -1: [TypeMention] object -# 21| 4: [BlockStmt] {...} -# 22| 0: [ReturnStmt] return ...; -# 22| 0: [ParameterAccess] access to parameter obj -# 24| 10: [Method] Method3 -# 24| -1: [TypeMention] Void -# 24| 4: [BlockStmt] {...} -# 25| 11: [Field] _backingField -# 25| -1: [TypeMention] object -# 27| 12: [Property] PartialProperty1 -# 27| -1: [TypeMention] object -# 29| 3: [Getter] get_PartialProperty1 -# 29| 4: [BlockStmt] {...} -# 29| 0: [ReturnStmt] return ...; -# 29| 0: [FieldAccess] access to field _backingField -# 30| 4: [Setter] set_PartialProperty1 +# 6| 0: [Parameter] obj +# 6| -1: [TypeMention] object +# 9| 5: [Method] PartialMethodWithoutBody1 +# 9| -1: [TypeMention] Void +# 10| 6: [Method] Method2 +# 10| -1: [TypeMention] Void +# 10| 4: [BlockStmt] {...} +# 23| 7: [Method] PartialMethodWithBody1 +# 23| -1: [TypeMention] Void +# 23| 4: [BlockStmt] {...} +# 24| 8: [Method] PartialMethodWithBody2 +# 24| -1: [TypeMention] object +#-----| 2: (Parameters) +# 24| 0: [Parameter] obj +# 24| -1: [TypeMention] object +# 25| 4: [BlockStmt] {...} +# 26| 0: [ReturnStmt] return ...; +# 26| 0: [ParameterAccess] access to parameter obj +# 28| 9: [Method] Method3 +# 28| -1: [TypeMention] Void +# 28| 4: [BlockStmt] {...} +# 29| 10: [Field] _backingField +# 29| -1: [TypeMention] object +# 31| 11: [Property] PartialProperty1 +# 31| -1: [TypeMention] object +# 33| 3: [Getter] get_PartialProperty1 +# 33| 4: [BlockStmt] {...} +# 33| 0: [ReturnStmt] return ...; +# 33| 0: [FieldAccess] access to field _backingField +# 34| 4: [Setter] set_PartialProperty1 #-----| 2: (Parameters) -# 30| 0: [Parameter] value -# 30| 4: [BlockStmt] {...} -# 30| 0: [ExprStmt] ...; -# 30| 0: [AssignExpr] ... = ... -# 30| 0: [FieldAccess] access to field _backingField -# 30| 1: [ParameterAccess] access to parameter value -# 32| 13: [Field] _backingArray -# 32| -1: [TypeMention] Object[] -# 32| 1: [TypeMention] object -# 34| 14: [Indexer] Item -# 34| -1: [TypeMention] object +# 34| 0: [Parameter] value +# 34| 4: [BlockStmt] {...} +# 34| 0: [ExprStmt] ...; +# 34| 0: [AssignExpr] ... = ... +# 34| 0: [FieldAccess] access to field _backingField +# 34| 1: [ParameterAccess] access to parameter value +# 36| 12: [Field] _backingArray +# 36| -1: [TypeMention] Object[] +# 36| 1: [TypeMention] object +# 38| 13: [Indexer] Item +# 38| -1: [TypeMention] object #-----| 1: (Parameters) -# 34| 0: [Parameter] index -# 34| -1: [TypeMention] int -# 36| 3: [Getter] get_Item -#-----| 2: (Parameters) -# 34| 0: [Parameter] index -# 36| 4: [BlockStmt] {...} -# 36| 0: [ReturnStmt] return ...; -# 36| 0: [ArrayAccess] access to array element -# 36| -1: [FieldAccess] access to field _backingArray -# 36| 0: [ParameterAccess] access to parameter index -# 37| 4: [Setter] set_Item +# 38| 0: [Parameter] index +# 38| -1: [TypeMention] int +# 40| 3: [Getter] get_Item #-----| 2: (Parameters) -# 34| 0: [Parameter] index -# 37| 1: [Parameter] value -# 37| 4: [BlockStmt] {...} -# 37| 0: [ExprStmt] ...; -# 37| 0: [AssignExpr] ... = ... -# 37| 0: [ArrayAccess] access to array element -# 37| -1: [FieldAccess] access to field _backingArray -# 37| 0: [ParameterAccess] access to parameter index -# 37| 1: [ParameterAccess] access to parameter value -# 41| 15: [Event] PartialEvent1 -# 41| 3: [AddEventAccessor] add_PartialEvent1 +# 38| 0: [Parameter] index +# 40| 4: [BlockStmt] {...} +# 40| 0: [ReturnStmt] return ...; +# 40| 0: [ArrayAccess] access to array element +# 40| -1: [FieldAccess] access to field _backingArray +# 40| 0: [ParameterAccess] access to parameter index +# 41| 4: [Setter] set_Item #-----| 2: (Parameters) -# 41| 0: [Parameter] value +# 38| 0: [Parameter] index +# 41| 1: [Parameter] value # 41| 4: [BlockStmt] {...} -# 41| 4: [RemoveEventAccessor] remove_PartialEvent1 +# 41| 0: [ExprStmt] ...; +# 41| 0: [AssignExpr] ... = ... +# 41| 0: [ArrayAccess] access to array element +# 41| -1: [FieldAccess] access to field _backingArray +# 41| 0: [ParameterAccess] access to parameter index +# 41| 1: [ParameterAccess] access to parameter value +# 45| 14: [Event] PartialEvent1 +# 45| 3: [AddEventAccessor] add_PartialEvent1 #-----| 2: (Parameters) -# 41| 0: [Parameter] value -# 41| 4: [BlockStmt] {...} -# 44| [Class] OnePartPartialClass -# 46| 6: [Method] PartialMethodWithoutBody2 -# 46| -1: [TypeMention] Void -# 47| 7: [Method] Method4 -# 47| -1: [TypeMention] Void -# 47| 4: [BlockStmt] {...} -# 50| [Class] NonPartialClass -# 52| 6: [Method] Method5 -# 52| -1: [TypeMention] Void -# 52| 4: [BlockStmt] {...} -# 53| 7: [Property] Property -# 53| -1: [TypeMention] object -# 53| 3: [Getter] get_Property -# 53| 4: [Setter] set_Property +# 45| 0: [Parameter] value +# 45| 4: [BlockStmt] {...} +# 45| 4: [RemoveEventAccessor] remove_PartialEvent1 +#-----| 2: (Parameters) +# 45| 0: [Parameter] value +# 45| 4: [BlockStmt] {...} +# 48| [Class] OnePartPartialClass +# 50| 6: [Method] PartialMethodWithoutBody2 +# 50| -1: [TypeMention] Void +# 51| 7: [Method] Method4 +# 51| -1: [TypeMention] Void +# 51| 4: [BlockStmt] {...} +# 54| [Class] NonPartialClass +# 56| 5: [InstanceConstructor] NonPartialClass +#-----| 2: (Parameters) +# 56| 0: [Parameter] obj +# 56| -1: [TypeMention] object +# 56| 4: [BlockStmt] {...} +# 57| 6: [Method] Method5 +# 57| -1: [TypeMention] Void +# 57| 4: [BlockStmt] {...} +# 58| 7: [Property] Property +# 58| -1: [TypeMention] object +# 58| 3: [Getter] get_Property +# 58| 4: [Setter] set_Property #-----| 2: (Parameters) -# 53| 0: [Parameter] value -# 54| 8: [Indexer] Item -# 54| -1: [TypeMention] object +# 58| 0: [Parameter] value +# 59| 8: [Indexer] Item +# 59| -1: [TypeMention] object #-----| 1: (Parameters) -# 54| 0: [Parameter] index -# 54| -1: [TypeMention] int -# 56| 3: [Getter] get_Item +# 59| 0: [Parameter] index +# 59| -1: [TypeMention] int +# 61| 3: [Getter] get_Item #-----| 2: (Parameters) -# 54| 0: [Parameter] index -# 56| 4: [BlockStmt] {...} -# 56| 0: [ReturnStmt] return ...; -# 56| 0: [NullLiteral] null -# 57| 4: [Setter] set_Item +# 59| 0: [Parameter] index +# 61| 4: [BlockStmt] {...} +# 61| 0: [ReturnStmt] return ...; +# 61| 0: [NullLiteral] null +# 62| 4: [Setter] set_Item #-----| 2: (Parameters) -# 54| 0: [Parameter] index -# 57| 1: [Parameter] value -# 57| 4: [BlockStmt] {...} -# 59| 9: [Event] Event -# 59| -1: [TypeMention] EventHandler -# 59| 3: [AddEventAccessor] add_Event +# 59| 0: [Parameter] index +# 62| 1: [Parameter] value +# 62| 4: [BlockStmt] {...} +# 64| 9: [Event] Event +# 64| -1: [TypeMention] EventHandler +# 64| 3: [AddEventAccessor] add_Event #-----| 2: (Parameters) -# 59| 0: [Parameter] value -# 59| 4: [RemoveEventAccessor] remove_Event +# 64| 0: [Parameter] value +# 64| 4: [RemoveEventAccessor] remove_Event #-----| 2: (Parameters) -# 59| 0: [Parameter] value +# 64| 0: [Parameter] value PartialMultipleFiles1.cs: # 1| [Class] PartialMultipleFiles PartialMultipleFiles2.cs: From 88b36fc912b11ecb860e82f407c66765d40c7e2e Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 17 Feb 2026 15:29:45 +0100 Subject: [PATCH 11/13] C#: Add dataflow test for partial constructors. --- .../constructors/ConstructorFlow.expected | 1 + .../dataflow/constructors/Constructors.cs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/csharp/ql/test/library-tests/dataflow/constructors/ConstructorFlow.expected b/csharp/ql/test/library-tests/dataflow/constructors/ConstructorFlow.expected index 678dca279ff0..158f68baf88c 100644 --- a/csharp/ql/test/library-tests/dataflow/constructors/ConstructorFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/constructors/ConstructorFlow.expected @@ -274,6 +274,7 @@ subpaths | Constructors.cs:143:25:143:26 | access to local variable o1 : Object | Constructors.cs:137:29:137:32 | Obj1 : Object | Constructors.cs:137:19:137:20 | this [Return] : R1 [property Obj1] : Object | Constructors.cs:143:18:143:31 | object creation of type R1 : R1 [property Obj1] : Object | | Constructors.cs:143:29:143:30 | access to local variable o2 : Object | Constructors.cs:137:42:137:45 | Obj2 : Object | Constructors.cs:137:19:137:20 | this [Return] : R1 [property Obj2] : Object | Constructors.cs:143:18:143:31 | object creation of type R1 : R1 [property Obj2] : Object | testFailures +| Constructors.cs:164:29:164:48 | // ... | Missing result: hasValueFlow=11 | #select | Constructors.cs:15:18:15:19 | access to field s1 | Constructors.cs:5:29:5:45 | call to method Source : Object | Constructors.cs:15:18:15:19 | access to field s1 | $@ | Constructors.cs:5:29:5:45 | call to method Source : Object | call to method Source : Object | | Constructors.cs:33:18:33:19 | access to field s1 | Constructors.cs:21:29:21:45 | call to method Source : Object | Constructors.cs:33:18:33:19 | access to field s1 | $@ | Constructors.cs:21:29:21:45 | call to method Source : Object | call to method Source : Object | diff --git a/csharp/ql/test/library-tests/dataflow/constructors/Constructors.cs b/csharp/ql/test/library-tests/dataflow/constructors/Constructors.cs index 9eae5079d847..94beb58544b3 100644 --- a/csharp/ql/test/library-tests/dataflow/constructors/Constructors.cs +++ b/csharp/ql/test/library-tests/dataflow/constructors/Constructors.cs @@ -145,6 +145,25 @@ public void M7() Sink(r1.Obj2); // $ hasValueFlow=10 } + public partial class CPartial + { + public object Obj { get; } + + public partial CPartial(object o); + } + + public partial class CPartial + { + public partial CPartial(object o) => Obj = o; + } + + public void M8() + { + var o = Source(11); + var cPartial = new CPartial(o); + Sink(cPartial.Obj); // $ hasValueFlow=11 + } + public static void Sink(object o) { } public static T Source(object source) => throw null; From db376cd0844fdca3ded1d10b63e578bb74753f21 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 17 Feb 2026 14:45:59 +0100 Subject: [PATCH 12/13] C#: Partial constructor declaration support. --- .../Entities/Constructor.cs | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs index 48039b4b962e..58c94cf047a1 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -12,7 +13,9 @@ namespace Semmle.Extraction.CSharp.Entities internal class Constructor : Method { private readonly List declaringReferenceSyntax; - + private readonly Lazy ordinaryConstructorSyntaxLazy; + private readonly Lazy primaryConstructorSyntaxLazy; + private readonly Lazy primaryBaseLazy; private Constructor(Context cx, IMethodSymbol init) : base(cx, init) { @@ -20,8 +23,28 @@ private Constructor(Context cx, IMethodSymbol init) Symbol.DeclaringSyntaxReferences .Select(r => r.GetSyntax()) .ToList(); + ordinaryConstructorSyntaxLazy = new Lazy(() => + declaringReferenceSyntax + .OfType() + .FirstOrDefault()); + primaryConstructorSyntaxLazy = new Lazy(() => + declaringReferenceSyntax + .OfType() + .FirstOrDefault(t => t is ClassDeclarationSyntax or StructDeclarationSyntax or RecordDeclarationSyntax)); + primaryBaseLazy = new Lazy(() => + PrimaryConstructorSyntax? + .BaseList? + .Types + .OfType() + .FirstOrDefault()); } + private ConstructorDeclarationSyntax? OrdinaryConstructorSyntax => ordinaryConstructorSyntaxLazy.Value; + + private TypeDeclarationSyntax? PrimaryConstructorSyntax => primaryConstructorSyntaxLazy.Value; + + private PrimaryConstructorBaseTypeSyntax? PrimaryBase => primaryBaseLazy.Value; + public override void Populate(TextWriter trapFile) { PopulateMethod(trapFile); @@ -176,23 +199,6 @@ private void ExtractSourceInitializer(TextWriter trapFile, ITypeSymbol? type, IM init.PopulateArguments(trapFile, arguments, 0); } - private ConstructorDeclarationSyntax? OrdinaryConstructorSyntax => - declaringReferenceSyntax - .OfType() - .FirstOrDefault(); - - private TypeDeclarationSyntax? PrimaryConstructorSyntax => - declaringReferenceSyntax - .OfType() - .FirstOrDefault(t => t is ClassDeclarationSyntax or StructDeclarationSyntax or RecordDeclarationSyntax); - - private PrimaryConstructorBaseTypeSyntax? PrimaryBase => - PrimaryConstructorSyntax? - .BaseList? - .Types - .OfType() - .FirstOrDefault(); - private bool IsPrimary => PrimaryConstructorSyntax is not null; // This is a default constructor in a class or struct declared in source. @@ -223,7 +229,7 @@ Symbol.ContainingType.TypeKind is TypeKind.Class or TypeKind.Struct && { case MethodKind.StaticConstructor: case MethodKind.Constructor: - return ConstructorFactory.Instance.CreateEntityFromSymbol(cx, constructor); + return ConstructorFactory.Instance.CreateEntityFromSymbol(cx, constructor.GetBodyDeclaringSymbol()); default: throw new InternalError(constructor, "Attempt to create a Constructor from a symbol that isn't a constructor"); } From e1d4fef4e9d1fdaa2649fca047515bb88e76e149 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 17 Feb 2026 15:05:49 +0100 Subject: [PATCH 13/13] C#: Update test expected output. --- .../constructors/ConstructorFlow.expected | 24 ++++++++++++++++++- .../library-tests/dispatch/CallGraph.expected | 2 +- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/csharp/ql/test/library-tests/dataflow/constructors/ConstructorFlow.expected b/csharp/ql/test/library-tests/dataflow/constructors/ConstructorFlow.expected index 158f68baf88c..6e063d49ef62 100644 --- a/csharp/ql/test/library-tests/dataflow/constructors/ConstructorFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/constructors/ConstructorFlow.expected @@ -122,6 +122,16 @@ edges | Constructors.cs:143:29:143:30 | access to local variable o2 : Object | Constructors.cs:143:18:143:31 | object creation of type R1 : R1 [property Obj2] : Object | provenance | | | Constructors.cs:144:14:144:15 | access to local variable r1 : R1 [property Obj1] : Object | Constructors.cs:144:14:144:20 | access to property Obj1 | provenance | | | Constructors.cs:145:14:145:15 | access to local variable r1 : R1 [property Obj2] : Object | Constructors.cs:145:14:145:20 | access to property Obj2 | provenance | | +| Constructors.cs:157:40:157:40 | o : Object | Constructors.cs:157:52:157:52 | access to parameter o : Object | provenance | | +| Constructors.cs:157:46:157:48 | [post] this access : CPartial [property Obj] : Object | Constructors.cs:157:24:157:31 | this [Return] : CPartial [property Obj] : Object | provenance | | +| Constructors.cs:157:52:157:52 | access to parameter o : Object | Constructors.cs:157:46:157:48 | [post] this access : CPartial [property Obj] : Object | provenance | | +| Constructors.cs:162:13:162:13 | access to local variable o : Object | Constructors.cs:163:37:163:37 | access to local variable o : Object | provenance | | +| Constructors.cs:162:17:162:34 | call to method Source : Object | Constructors.cs:162:13:162:13 | access to local variable o : Object | provenance | | +| Constructors.cs:163:13:163:20 | access to local variable cPartial : CPartial [property Obj] : Object | Constructors.cs:164:14:164:21 | access to local variable cPartial : CPartial [property Obj] : Object | provenance | | +| Constructors.cs:163:24:163:38 | object creation of type CPartial : CPartial [property Obj] : Object | Constructors.cs:163:13:163:20 | access to local variable cPartial : CPartial [property Obj] : Object | provenance | | +| Constructors.cs:163:37:163:37 | access to local variable o : Object | Constructors.cs:157:40:157:40 | o : Object | provenance | | +| Constructors.cs:163:37:163:37 | access to local variable o : Object | Constructors.cs:163:24:163:38 | object creation of type CPartial : CPartial [property Obj] : Object | provenance | | +| Constructors.cs:164:14:164:21 | access to local variable cPartial : CPartial [property Obj] : Object | Constructors.cs:164:14:164:25 | access to property Obj | provenance | | nodes | Constructors.cs:3:18:3:26 | [post] this access : C_no_ctor [field s1] : Object | semmle.label | [post] this access : C_no_ctor [field s1] : Object | | Constructors.cs:5:24:5:25 | [post] this access : C_no_ctor [field s1] : Object | semmle.label | [post] this access : C_no_ctor [field s1] : Object | @@ -255,6 +265,17 @@ nodes | Constructors.cs:144:14:144:20 | access to property Obj1 | semmle.label | access to property Obj1 | | Constructors.cs:145:14:145:15 | access to local variable r1 : R1 [property Obj2] : Object | semmle.label | access to local variable r1 : R1 [property Obj2] : Object | | Constructors.cs:145:14:145:20 | access to property Obj2 | semmle.label | access to property Obj2 | +| Constructors.cs:157:24:157:31 | this [Return] : CPartial [property Obj] : Object | semmle.label | this [Return] : CPartial [property Obj] : Object | +| Constructors.cs:157:40:157:40 | o : Object | semmle.label | o : Object | +| Constructors.cs:157:46:157:48 | [post] this access : CPartial [property Obj] : Object | semmle.label | [post] this access : CPartial [property Obj] : Object | +| Constructors.cs:157:52:157:52 | access to parameter o : Object | semmle.label | access to parameter o : Object | +| Constructors.cs:162:13:162:13 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| Constructors.cs:162:17:162:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| Constructors.cs:163:13:163:20 | access to local variable cPartial : CPartial [property Obj] : Object | semmle.label | access to local variable cPartial : CPartial [property Obj] : Object | +| Constructors.cs:163:24:163:38 | object creation of type CPartial : CPartial [property Obj] : Object | semmle.label | object creation of type CPartial : CPartial [property Obj] : Object | +| Constructors.cs:163:37:163:37 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| Constructors.cs:164:14:164:21 | access to local variable cPartial : CPartial [property Obj] : Object | semmle.label | access to local variable cPartial : CPartial [property Obj] : Object | +| Constructors.cs:164:14:164:25 | access to property Obj | semmle.label | access to property Obj | subpaths | Constructors.cs:44:18:44:19 | this access : C2 [parameter o21param] : Object | Constructors.cs:46:23:46:27 | this access : C2 [parameter o21param] : Object | Constructors.cs:46:23:46:27 | [post] this access : C2 [field Obj21] : Object | Constructors.cs:44:18:44:19 | [post] this access : C2 [field Obj21] : Object | | Constructors.cs:64:37:64:37 | access to parameter o : Object | Constructors.cs:57:54:57:55 | o2 : Object | Constructors.cs:59:13:59:14 | access to parameter o1 : Object | Constructors.cs:64:27:64:34 | access to parameter o22param : Object | @@ -273,8 +294,8 @@ subpaths | Constructors.cs:132:29:132:30 | access to local variable o2 : Object | Constructors.cs:121:38:121:40 | oc2 : Object | Constructors.cs:121:16:121:17 | this [Return] : C4 [property Obj2] : Object | Constructors.cs:132:18:132:31 | object creation of type C4 : C4 [property Obj2] : Object | | Constructors.cs:143:25:143:26 | access to local variable o1 : Object | Constructors.cs:137:29:137:32 | Obj1 : Object | Constructors.cs:137:19:137:20 | this [Return] : R1 [property Obj1] : Object | Constructors.cs:143:18:143:31 | object creation of type R1 : R1 [property Obj1] : Object | | Constructors.cs:143:29:143:30 | access to local variable o2 : Object | Constructors.cs:137:42:137:45 | Obj2 : Object | Constructors.cs:137:19:137:20 | this [Return] : R1 [property Obj2] : Object | Constructors.cs:143:18:143:31 | object creation of type R1 : R1 [property Obj2] : Object | +| Constructors.cs:163:37:163:37 | access to local variable o : Object | Constructors.cs:157:40:157:40 | o : Object | Constructors.cs:157:24:157:31 | this [Return] : CPartial [property Obj] : Object | Constructors.cs:163:24:163:38 | object creation of type CPartial : CPartial [property Obj] : Object | testFailures -| Constructors.cs:164:29:164:48 | // ... | Missing result: hasValueFlow=11 | #select | Constructors.cs:15:18:15:19 | access to field s1 | Constructors.cs:5:29:5:45 | call to method Source : Object | Constructors.cs:15:18:15:19 | access to field s1 | $@ | Constructors.cs:5:29:5:45 | call to method Source : Object | call to method Source : Object | | Constructors.cs:33:18:33:19 | access to field s1 | Constructors.cs:21:29:21:45 | call to method Source : Object | Constructors.cs:33:18:33:19 | access to field s1 | $@ | Constructors.cs:21:29:21:45 | call to method Source : Object | call to method Source : Object | @@ -289,3 +310,4 @@ testFailures | Constructors.cs:134:14:134:20 | access to property Obj2 | Constructors.cs:131:18:131:34 | call to method Source : Object | Constructors.cs:134:14:134:20 | access to property Obj2 | $@ | Constructors.cs:131:18:131:34 | call to method Source : Object | call to method Source : Object | | Constructors.cs:144:14:144:20 | access to property Obj1 | Constructors.cs:141:18:141:34 | call to method Source : Object | Constructors.cs:144:14:144:20 | access to property Obj1 | $@ | Constructors.cs:141:18:141:34 | call to method Source : Object | call to method Source : Object | | Constructors.cs:145:14:145:20 | access to property Obj2 | Constructors.cs:142:18:142:35 | call to method Source : Object | Constructors.cs:145:14:145:20 | access to property Obj2 | $@ | Constructors.cs:142:18:142:35 | call to method Source : Object | call to method Source : Object | +| Constructors.cs:164:14:164:25 | access to property Obj | Constructors.cs:162:17:162:34 | call to method Source : Object | Constructors.cs:164:14:164:25 | access to property Obj | $@ | Constructors.cs:162:17:162:34 | call to method Source : Object | call to method Source : Object | diff --git a/csharp/ql/test/library-tests/dispatch/CallGraph.expected b/csharp/ql/test/library-tests/dispatch/CallGraph.expected index 1deae048d102..31e2a99ae24c 100644 --- a/csharp/ql/test/library-tests/dispatch/CallGraph.expected +++ b/csharp/ql/test/library-tests/dispatch/CallGraph.expected @@ -270,7 +270,7 @@ | ViableCallable.cs:679:17:679:20 | Run3 | ViableCallable.cs:637:21:637:21 | M | | ViableCallable.cs:679:17:679:20 | Run3 | ViableCallable.cs:646:21:646:21 | M | | ViableCallable.cs:679:17:679:20 | Run3 | ViableCallable.cs:648:21:648:21 | M | -| ViableCallable.cs:711:17:711:20 | Run1 | ViableCallable.cs:695:24:695:31 | Partial1 | +| ViableCallable.cs:711:17:711:20 | Run1 | ViableCallable.cs:704:24:704:31 | Partial1 | | ViableCallable.cs:711:17:711:20 | Run1 | ViableCallable.cs:705:42:705:44 | get_Property | | ViableCallable.cs:711:17:711:20 | Run1 | ViableCallable.cs:705:63:705:65 | set_Property | | ViableCallable.cs:711:17:711:20 | Run1 | ViableCallable.cs:707:49:707:51 | get_Item |