Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ internal class Event : CachedSymbol<IEventSymbol>
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!);
Expand All @@ -27,13 +31,13 @@ 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 = Symbol.AddMethod;
var remover = Symbol.RemoveMethod;
var adder = BodyDeclaringSymbol.AddMethod;
var remover = BodyDeclaringSymbol.RemoveMethod;

if (!(adder is null))
if (adder is not null)
Method.Create(Context, adder);

if (!(remover is null))
if (remover is not null)
Method.Create(Context, remover);

PopulateModifiers(trapFile);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ private EventAccessor(Context cx, IMethodSymbol init, IEventSymbol @event)
this.@event = @event;
}

public override bool NeedsPopulation =>
base.NeedsPopulation &&
!Symbol.IsPartialDefinition; // Accessors always have an implementing declaration as well.

/// <summary>
/// Gets the event symbol associated with accessor `symbol`, or `null`
/// if there is no associated symbol.
Expand Down
4 changes: 4 additions & 0 deletions csharp/ql/lib/change-notes/2026-02-16-partial-events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* C# 14: Added support for partial events.
10 changes: 6 additions & 4 deletions csharp/ql/test/library-tests/dispatch/CallGraph.expected
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,9 @@
| 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:707:17:707:20 | Run1 | ViableCallable.cs:702:42:702:44 | get_Property |
| ViableCallable.cs:707:17:707:20 | Run1 | ViableCallable.cs:702:63:702:65 | set_Property |
| ViableCallable.cs:707:17:707:20 | Run1 | ViableCallable.cs:704:49:704:51 | get_Item |
| ViableCallable.cs:707:17:707:20 | Run1 | ViableCallable.cs:704:70:704:72 | set_Item |
| 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 |
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,9 @@
| ViableCallable.cs:683:9:683:16 | call to method M | C22+TestOverloadResolution2<System.Int32>.M(Int32[]) |
| ViableCallable.cs:687:9:687:16 | call to method M | C22+TestOverloadResolution1<System.Int32>.M(List<int>) |
| ViableCallable.cs:687:9:687:16 | call to method M | C22+TestOverloadResolution2<System.Int32>.M(List<int>) |
| ViableCallable.cs:712:9:712:18 | access to property Property | C23+Partial1.set_Property(object) |
| ViableCallable.cs:715:13:715:22 | access to property Property | C23+Partial1.get_Property() |
| ViableCallable.cs:718:9:718:12 | access to indexer | C23+Partial1.set_Item(int, object) |
| ViableCallable.cs:721:13:721:16 | access to indexer | C23+Partial1.get_Item(int) |
| 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) |
8 changes: 8 additions & 0 deletions csharp/ql/test/library-tests/dispatch/ViableCallable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -695,13 +695,15 @@ public partial class Partial1
public partial object Property { get; set; }

public partial object this[int index] { get; set; }
public partial event EventHandler Event;
}

public partial class Partial1
{
public partial object Property { get { return null; } set { } }

public partial object this[int index] { get { return null; } set { } }
public partial event EventHandler Event { add { } remove { } }
}

public void Run1(Partial1 p)
Expand All @@ -719,5 +721,11 @@ public void Run1(Partial1 p)

// Viable callable: Partial1.get_Item(int)
o = p[0];

// Viable callable: Partial1.add_Event
p.Event += (sender, e) => { };

// Viable callable: Partial1.remove_Event
p.Event -= (sender, e) => { };
}
}
14 changes: 7 additions & 7 deletions csharp/ql/test/library-tests/partial/MethodIsPartial.expected
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
| Partial.cs:4:18:4:42 | PartialMethodWithoutBody1 | true |
| Partial.cs:5:17:5:23 | Method2 | false |
| Partial.cs:14:18:14:39 | PartialMethodWithBody1 | true |
| Partial.cs:15:17:15:23 | Method3 | false |
| Partial.cs:34:18:34:42 | PartialMethodWithoutBody2 | true |
| Partial.cs:35:17:35:23 | Method4 | false |
| Partial.cs:40:17:40:23 | Method5 | false |
| 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 |
8 changes: 8 additions & 0 deletions csharp/ql/test/library-tests/partial/Partial.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System;

partial class TwoPartClass
{
partial void PartialMethodWithBody1();
Expand All @@ -7,6 +9,8 @@ public void Method2() { }
public partial object PartialProperty1 { get; set; }
// Declaring declaration.
public partial object this[int index] { get; set; }
// Declaring declaration.
public partial event EventHandler PartialEvent1;
}

partial class TwoPartClass
Expand All @@ -27,6 +31,9 @@ public partial object this[int index]
get { return _backingArray[index]; }
set { _backingArray[index] = value; }
}

// Implementation declaration.
public partial event EventHandler PartialEvent1 { add { } remove { } }
}

partial class OnePartPartialClass
Expand All @@ -44,4 +51,5 @@ public object this[int index]
get { return null; }
set { }
}
public event EventHandler Event;
}
27 changes: 15 additions & 12 deletions csharp/ql/test/library-tests/partial/Partial1.expected
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
| Partial.cs:1:15:1:26 | TwoPartClass |
| Partial.cs:4:18:4:42 | PartialMethodWithoutBody1 |
| Partial.cs:12:15:12:26 | TwoPartClass |
| Partial.cs:14:18:14:39 | PartialMethodWithBody1 |
| Partial.cs:18:27:18:42 | PartialProperty1 |
| Partial.cs:20:9:20:11 | get_PartialProperty1 |
| Partial.cs:21:9:21:11 | set_PartialProperty1 |
| Partial.cs:25:27:25:30 | Item |
| Partial.cs:27:9:27:11 | get_Item |
| Partial.cs:28:9:28:11 | set_Item |
| Partial.cs:32:15:32:33 | OnePartPartialClass |
| Partial.cs:34:18:34:42 | PartialMethodWithoutBody2 |
| 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 |
| PartialMultipleFiles1.cs:1:22:1:41 | PartialMultipleFiles |
| PartialMultipleFiles2.cs:1:22:1:41 | PartialMultipleFiles |
26 changes: 13 additions & 13 deletions csharp/ql/test/library-tests/partial/Partial2.expected
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
| Partial.cs:1:15:1:26 | TwoPartClass | Partial.cs:1:15:1:26 | <object initializer> |
| Partial.cs:1:15:1:26 | TwoPartClass | Partial.cs:4:18:4:42 | PartialMethodWithoutBody1 |
| Partial.cs:1:15:1:26 | TwoPartClass | Partial.cs:5:17:5:23 | Method2 |
| Partial.cs:1:15:1:26 | TwoPartClass | Partial.cs:14:18:14:39 | PartialMethodWithBody1 |
| Partial.cs:1:15:1:26 | TwoPartClass | Partial.cs:15:17:15:23 | Method3 |
| Partial.cs:12:15:12:26 | TwoPartClass | Partial.cs:1:15:1:26 | <object initializer> |
| Partial.cs:12:15:12:26 | TwoPartClass | Partial.cs:4:18:4:42 | PartialMethodWithoutBody1 |
| Partial.cs:12:15:12:26 | TwoPartClass | Partial.cs:5:17:5:23 | Method2 |
| Partial.cs:12:15:12:26 | TwoPartClass | Partial.cs:14:18:14:39 | PartialMethodWithBody1 |
| Partial.cs:12:15:12:26 | TwoPartClass | Partial.cs:15:17:15:23 | Method3 |
| Partial.cs:32:15:32:33 | OnePartPartialClass | Partial.cs:32:15:32:33 | <object initializer> |
| Partial.cs:32:15:32:33 | OnePartPartialClass | Partial.cs:34:18:34:42 | PartialMethodWithoutBody2 |
| Partial.cs:32:15:32:33 | OnePartPartialClass | Partial.cs:35:17:35:23 | Method4 |
| Partial.cs:3:15:3:26 | TwoPartClass | Partial.cs:3:15:3:26 | <object initializer> |
| 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 | <object initializer> |
| 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 | <object initializer> |
| 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 |
| PartialMultipleFiles1.cs:1:22:1:41 | PartialMultipleFiles | PartialMultipleFiles1.cs:1:22:1:41 | <object initializer> |
| PartialMultipleFiles2.cs:1:22:1:41 | PartialMultipleFiles | PartialMultipleFiles1.cs:1:22:1:41 | <object initializer> |
20 changes: 12 additions & 8 deletions csharp/ql/test/library-tests/partial/PartialAccessors.expected
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
| Partial.cs:20:9:20:11 | get_PartialProperty1 | true |
| Partial.cs:21:9:21:11 | set_PartialProperty1 | true |
| Partial.cs:27:9:27:11 | get_Item | true |
| Partial.cs:28:9:28:11 | set_Item | true |
| Partial.cs:41:30:41:32 | get_Property | false |
| Partial.cs:41:35:41:37 | set_Property | false |
| Partial.cs:44:9:44:11 | get_Item | false |
| Partial.cs:45:9:45:11 | set_Item | false |
| 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 |
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
| Partial.cs:1:15:1:26 | TwoPartClass | Partial.cs:1:15:1:26 | {...} |
| Partial.cs:32:15:32:33 | OnePartPartialClass | Partial.cs:32:15:32:33 | {...} |
| Partial.cs:38:7:38:21 | NonPartialClass | Partial.cs:38:7:38:21 | {...} |
| 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 | {...} |
| PartialMultipleFiles1.cs:1:22:1:41 | PartialMultipleFiles | PartialMultipleFiles1.cs:1:22:1:41 | {...} |
2 changes: 2 additions & 0 deletions csharp/ql/test/library-tests/partial/PartialEvents.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
| Partial.cs:36:39:36:51 | PartialEvent1 | true |
| Partial.cs:54:31:54:35 | Event | false |
7 changes: 7 additions & 0 deletions csharp/ql/test/library-tests/partial/PartialEvents.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import csharp

private boolean isPartial(Event e) { if e.isPartial() then result = true else result = false }

from Event e
where e.fromSource()
select e, isPartial(e)
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
| Partial.cs:25:27:25:30 | Item | true |
| Partial.cs:42:19:42:22 | Item | false |
| Partial.cs:29:27:29:30 | Item | true |
| Partial.cs:49:19:49:22 | Item | false |
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
| Partial.cs:4:18:4:42 | PartialMethodWithoutBody1 | false |
| Partial.cs:14:18:14:39 | PartialMethodWithBody1 | true |
| Partial.cs:34:18:34:42 | PartialMethodWithoutBody2 | false |
| Partial.cs:6:18:6:42 | PartialMethodWithoutBody1 | false |
| Partial.cs:18:18:18:39 | PartialMethodWithBody1 | true |
| Partial.cs:41:18:41:42 | PartialMethodWithoutBody2 | false |
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
| Partial.cs:18:27:18:42 | PartialProperty1 | true |
| Partial.cs:41:19:41:26 | Property | false |
| Partial.cs:22:27:22:42 | PartialProperty1 | true |
| Partial.cs:48:19:48:26 | Property | false |
Loading