Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0cb12fd
Initial plan for issue
Copilot May 21, 2025
fe08ab0
Fix CLIEvent IsEvent property and XmlDocSig prefix
Copilot May 21, 2025
a7c9a7c
Revert changes to CLIEvent handling to fix test failures
Copilot May 22, 2025
df2821a
Fix CLIEvent IsEvent property and XmlDocSig prefix with test adjustments
Copilot May 22, 2025
ea75396
Merge branch 'main' into copilot/fix-10273
T-Gro May 22, 2025
c7bb6c8
Fix CLIEvent attributes handling in tests while preserving event sema…
Copilot May 23, 2025
416e2c9
Move CLIEvent XmlDocSig prefix handling to GetXmlDocSigOfProp
Copilot May 23, 2025
f431be3
Merge branch 'main' into copilot/fix-10273
T-Gro May 29, 2025
f67e7dd
Generate correct XmlDocSig prefix at source instead of patching
Copilot May 29, 2025
d383cc0
Merge branch 'main' into copilot/fix-10273
T-Gro Jul 22, 2025
8a2d2cb
Fix CLIEvent properties to be recognized as events in Symbol API
Copilot Jul 22, 2025
dd901c7
Merge branch 'main' into copilot/fix-10273
T-Gro Jul 30, 2025
07f10f2
Update test expectations for CLIEvent properties now being recognized…
Copilot Jul 30, 2025
58afe62
Fix test expectations for CLIEvent properties now being recognized as…
Copilot Aug 1, 2025
079024f
Address review feedback: consolidate FSProp pattern matching and move…
Copilot Aug 1, 2025
d8d703d
Fix test expectations for CLIEvent properties now being recognized as…
Copilot Aug 1, 2025
f9eb1e5
Merge branch 'main' into copilot/fix-10273
T-Gro Sep 8, 2025
825cc43
Merge branch 'main' into copilot/fix-10273
T-Gro Sep 18, 2025
1fbc0a8
Fix test expectations for CLIEvent properties recognized as events
Copilot Sep 19, 2025
ee42dcb
Merge branch 'main' into copilot/fix-10273
T-Gro Sep 19, 2025
7f9885d
Merge branch 'main' into copilot/fix-10273
T-Gro Jan 21, 2026
1f620a5
Use CompileAsEvent instead of direct HasFSharpAttribute in XmlDocSig
T-Gro Jan 21, 2026
ca9804a
Fix reviewer feedback: use {caret} marker pattern and remove redundan…
T-Gro Jan 21, 2026
839689d
Add release notes for CLIEvent IsEvent fix #10273
T-Gro Jan 21, 2026
06695ad
clean
T-Gro Jan 22, 2026
a648124
Merge branch 'main' into copilot/fix-10273
T-Gro Jan 22, 2026
203bcbd
Fix release notes: move entry to 11.0.100.md and restore files from main
T-Gro Mar 10, 2026
084913b
Merge branch 'main' into copilot/fix-10273
T-Gro Mar 10, 2026
f3b4180
Merge branch 'main' into copilot/fix-10273
T-Gro Mar 10, 2026
60998ad
Merge main: resolve release notes conflict
T-Gro Mar 13, 2026
8a39d3b
Merge main into copilot/fix-10273
T-Gro Mar 17, 2026
ae69e20
Merge main: resolve conflict in TypedTreeOps.fs, keep optimized Compi…
T-Gro Mar 23, 2026
9452dda
Merge remote-tracking branch 'origin/main' into copilot/fix-10273
T-Gro Mar 24, 2026
c045158
Merge main and remove extra blank lines in TypedTreeOps.fs
T-Gro Mar 24, 2026
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
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### Fixed

* Fix CLIEvent properties to be correctly recognized as events: `IsEvent` returns `true` and `XmlDocSig` uses `E:` prefix instead of `P:`. ([Issue #10273](https://github.com/dotnet/fsharp/issues/10273), [PR #18584](https://github.com/dotnet/fsharp/pull/18584))
* Fix extra sequence point at the end of match expressions. ([Issue #12052](https://github.com/dotnet/fsharp/issues/12052), [PR #19278](https://github.com/dotnet/fsharp/pull/19278))
* Fix wrong sequence point range for `return`/`yield`/`return!`/`yield!` inside computation expressions. ([Issue #19248](https://github.com/dotnet/fsharp/issues/19248), [PR #19278](https://github.com/dotnet/fsharp/pull/19278))
* Fix extra out-of-order sequence point for `use` in `task` computation expressions. ([Issue #19255](https://github.com/dotnet/fsharp/issues/19255), [PR #19278](https://github.com/dotnet/fsharp/pull/19278))
Expand Down
5 changes: 4 additions & 1 deletion src/Compiler/Checking/infos.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2049,7 +2049,10 @@ type PropInfo =
/// Indicates if this is an F# property compiled as a CLI event, e.g. a [<CLIEvent>] property.
member x.IsFSharpEventProperty =
match x with
| FSProp(g, _, Some vref, None) -> vref.IsFSharpEventProperty g
| FSProp(g, _, getterOpt, setterOpt) ->
match getterOpt, setterOpt with
| Some vref, _ | None, Some vref -> vref.IsFSharpEventProperty g
| None, None -> false
#if !NO_TYPEPROVIDERS
| ProvidedProp _ -> false
#endif
Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/Symbols/Symbols.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1906,6 +1906,8 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) =
member _.IsEvent =
match d with
| E _ -> true
| P p when p.IsFSharpEventProperty -> true
| V v when v.IsFSharpEventProperty cenv.g -> true
| _ -> false

member _.EventForFSharpProperty =
Expand Down
4 changes: 3 additions & 1 deletion src/Compiler/TypedTree/TypedTreeOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9689,7 +9689,9 @@ let XmlDocSigOfVal g full path (v: Val) =
| SynMemberKind.Member -> "M:", v.CompiledName g.CompilerGlobalState
| SynMemberKind.PropertyGetSet
| SynMemberKind.PropertySet
| SynMemberKind.PropertyGet -> "P:", v.PropertyName
| SynMemberKind.PropertyGet ->
let prefix = if attribsHaveValFlag g WellKnownValAttributes.CLIEventAttribute v.Attribs then "E:" else "P:"
prefix, v.PropertyName
Comment thread
T-Gro marked this conversation as resolved.

let path = if v.HasDeclaringEntity then prependPath path v.DeclaringEntity.CompiledName else path

Expand Down
4 changes: 2 additions & 2 deletions tests/FSharp.Compiler.Service.Tests/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -311,11 +311,11 @@ let attribsOfSymbol (symbol: FSharpSymbol) =
if v.IsDispatchSlot then yield "slot"
if v.IsModuleValueOrMember && not v.IsMember then yield "val"
if v.IsMember then yield "member"
if v.IsProperty then yield "prop"
if v.IsProperty && not v.IsEvent then yield "prop"
if v.IsEvent then yield "event"
if v.IsExtensionMember then yield "extmem"
if v.IsPropertyGetterMethod then yield "getter"
if v.IsPropertySetterMethod then yield "setter"
if v.IsEvent then yield "event"
if v.EventForFSharpProperty.IsSome then yield "clievent"
if v.IsEventAddMethod then yield "add"
if v.IsEventRemoveMethod then yield "remove"
Expand Down
52 changes: 26 additions & 26 deletions tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -991,7 +991,7 @@ let ``Test project3 all symbols in signature`` () =
("member set_InterfacePropertySet", ["slot"; "member"; "setter"]);
("property InterfacePropertySet", ["slot"; "member"; "prop"]);
("property InterfaceProperty", ["slot"; "member"; "prop"]);
("property InterfaceEvent", ["slot"; "member"; "prop"; "clievent"]);
("event InterfaceEvent", ["slot"; "member"; "event"; "clievent"]);
("CFoo", ["class"]);
("member .ctor", ["member"; "ctor"]);
("member AbstractClassMethod", ["slot"; "member"]);
Expand All @@ -1002,7 +1002,7 @@ let ``Test project3 all symbols in signature`` () =
("member set_AbstractClassPropertySet", ["slot"; "member"; "setter"]);
("property AbstractClassPropertySet", ["slot"; "member"; "prop"]);
("property AbstractClassProperty", ["slot"; "member"; "prop"]);
("property AbstractClassEvent", ["slot"; "member"; "prop"; "clievent"]);
("event AbstractClassEvent", ["slot"; "member"; "event"; "clievent"]);
("CBaseFoo", ["class"]); ("member .ctor", ["member"; "ctor"]);
("member BaseClassMethod", ["slot"; "member"]);
("member BaseClassMethod", ["member"; "overridemem"]);
Expand All @@ -1020,8 +1020,8 @@ let ``Test project3 all symbols in signature`` () =
("property BaseClassPropertySet", ["slot"; "member"; "prop"]);
("property BaseClassProperty", ["member"; "prop"; "overridemem"]);
("property BaseClassProperty", ["slot"; "member"; "prop"]);
("property BaseClassEvent", ["member"; "prop"; "overridemem"]);
("property BaseClassEvent", ["slot"; "member"; "prop"]);
("event BaseClassEvent", ["member"; "event"; "overridemem"]);
("event BaseClassEvent", ["slot"; "member"; "event"]);
("IFooImpl", ["class"]); ("member .ctor", ["member"; "ctor"]);
("member InterfaceMethod", ["member"; "overridemem"; "intfmem"]);
("member add_InterfaceEvent", ["member"; "overridemem"; "intfmem"]);
Expand All @@ -1038,7 +1038,7 @@ let ``Test project3 all symbols in signature`` () =
("member set_AbstractClassPropertySet", ["member"; "setter"; "overridemem"]);
("property AbstractClassPropertySet", ["member"; "prop"; "overridemem"]);
("property AbstractClassProperty", ["member"; "prop"; "overridemem"]);
("property AbstractClassEvent", ["member"; "prop"; "clievent"; "overridemem"]);
("event AbstractClassEvent", ["member"; "event"; "clievent"; "overridemem"]);
("CBaseFooImpl", ["class"]); ("member .ctor", ["member"; "ctor"]);
("member BaseClassMethod", ["member"; "overridemem"]);
("member add_BaseClassEvent", ["member"; "add"; "overridemem"]);
Expand All @@ -1048,7 +1048,7 @@ let ``Test project3 all symbols in signature`` () =
("member set_BaseClassPropertySet", ["member"; "setter"; "overridemem"]);
("property BaseClassPropertySet", ["member"; "prop"; "overridemem"]);
("property BaseClassProperty", ["member"; "prop"; "overridemem"]);
("property BaseClassEvent", ["member"; "prop"; "clievent"; "overridemem"])]
("event BaseClassEvent", ["member"; "event"; "clievent"; "overridemem"])]
|> List.iter (fun x ->
if results |> List.exists (fun y -> x = y) |> not then
failwithf "%A does not exist in the collection." x
Expand Down Expand Up @@ -1122,10 +1122,10 @@ let ``Test project3 all uses of all signature symbols`` () =
("file1", ((61, 20), (61, 37)), ["override"], ["slot"; "member"; "prop"]);
("file1", ((76, 23), (76, 44)), [], ["slot"; "member"; "prop"]);
("file1", ((34, 20), (34, 37)), ["override"], ["slot"; "member"; "prop"])]);
("property InterfaceEvent",
[("file1", ((8, 13), (8, 27)), ["defn"], ["slot"; "member"; "prop"; "clievent"]);
("file1", ((65, 20), (65, 34)), ["override"], ["slot"; "member"; "prop"; "clievent"]);
("file1", ((38, 20), (38, 34)), ["override"], ["slot"; "member"; "prop"; "clievent"])]);
("event InterfaceEvent",
[("file1", ((8, 13), (8, 27)), ["defn"], ["slot"; "member"; "event"; "clievent"]);
("file1", ((65, 20), (65, 34)), ["override"], ["slot"; "member"; "event"; "clievent"]);
("file1", ((38, 20), (38, 34)), ["override"], ["slot"; "member"; "event"; "clievent"])]);
("CFoo",
[("file1", ((11, 5), (11, 9)), ["defn"], ["class"]);
("file1", ((41, 12), (41, 16)), ["type"], ["class"]);
Expand Down Expand Up @@ -1170,10 +1170,10 @@ let ``Test project3 all uses of all signature symbols`` () =
[("file1", ((12, 13), (12, 34)), ["defn"], ["slot"; "member"; "prop"]);
("file1", ((70, 22), (70, 43)), ["override"], ["slot"; "member"; "prop"]);
("file1", ((43, 18), (43, 39)), ["override"], ["slot"; "member"; "prop"])]);
("property AbstractClassEvent",
[("file1", ((16, 13), (16, 31)), ["defn"], ["slot"; "member"; "prop"; "clievent"]);
("file1", ((74, 22), (74, 40)), ["override"], ["slot"; "member"; "prop"; "clievent"]);
("file1", ((47, 18), (47, 36)), ["override"], ["slot"; "member"; "prop"; "clievent"])]);
("event AbstractClassEvent",
[("file1", ((16, 13), (16, 31)), ["defn"], ["slot"; "member"; "event"; "clievent"]);
("file1", ((74, 22), (74, 40)), ["override"], ["slot"; "member"; "event"; "clievent"]);
("file1", ((47, 18), (47, 36)), ["override"], ["slot"; "member"; "event"; "clievent"])]);
("CBaseFoo",
[("file1", ((18, 5), (18, 13)), ["defn"], ["class"]);
("file1", ((50, 12), (50, 20)), ["type"], ["class"]);
Expand Down Expand Up @@ -1230,12 +1230,12 @@ let ``Test project3 all uses of all signature symbols`` () =
[("file1", ((20, 13), (20, 30)), ["defn"], ["slot"; "member"; "prop"]);
("file1", ((25, 15), (25, 32)), ["override"], ["slot"; "member"; "prop"]);
("file1", ((52, 18), (52, 35)), ["override"], ["slot"; "member"; "prop"])]);
("property BaseClassEvent",
[("file1", ((29, 15), (29, 29)), ["defn"], ["member"; "prop"; "overridemem"])]);
("property BaseClassEvent",
[("file1", ((24, 13), (24, 27)), ["defn"], ["slot"; "member"; "prop"]);
("file1", ((29, 15), (29, 29)), ["override"], ["slot"; "member"; "prop"]);
("file1", ((56, 18), (56, 32)), ["override"], ["slot"; "member"; "prop"])]);
("event BaseClassEvent",
[("file1", ((29, 15), (29, 29)), ["defn"], ["member"; "event"; "overridemem"])]);
("event BaseClassEvent",
[("file1", ((24, 13), (24, 27)), ["defn"], ["slot"; "member"; "event"]);
("file1", ((29, 15), (29, 29)), ["override"], ["slot"; "member"; "event"]);
("file1", ((56, 18), (56, 32)), ["override"], ["slot"; "member"; "event"])]);
("IFooImpl", [("file1", ((31, 5), (31, 13)), ["defn"], ["class"])]);
("member .ctor", [("file1", ((31, 5), (31, 13)), ["defn"], ["member"; "ctor"])]);
("member InterfaceMethod",
Expand Down Expand Up @@ -1268,8 +1268,8 @@ let ``Test project3 all uses of all signature symbols`` () =
[("file1", ((44, 18), (44, 42)), ["defn"], ["member"; "prop"; "overridemem"])]);
("property AbstractClassProperty",
[("file1", ((43, 18), (43, 39)), ["defn"], ["member"; "prop"; "overridemem"])]);
("property AbstractClassEvent",
[("file1", ((47, 18), (47, 36)), ["defn"], ["member"; "prop"; "clievent"; "overridemem"])]);
("event AbstractClassEvent",
[("file1", ((47, 18), (47, 36)), ["defn"], ["member"; "event"; "clievent"; "overridemem"])]);
("CBaseFooImpl", [("file1", ((49, 5), (49, 17)), ["defn"], ["class"])]);
("member .ctor", [("file1", ((49, 5), (49, 17)), ["defn"], ["member"; "ctor"])]);
("member BaseClassMethod",
Expand All @@ -1288,8 +1288,8 @@ let ``Test project3 all uses of all signature symbols`` () =
[("file1", ((53, 18), (53, 38)), ["defn"], ["member"; "prop"; "overridemem"])]);
("property BaseClassProperty",
[("file1", ((52, 18), (52, 35)), ["defn"], ["member"; "prop"; "overridemem"])]);
("property BaseClassEvent",
[("file1", ((56, 18), (56, 32)), ["defn"], ["member"; "prop"; "clievent"; "overridemem"])])]
("event BaseClassEvent",
[("file1", ((56, 18), (56, 32)), ["defn"], ["member"; "event"; "clievent"; "overridemem"])])]
set allUsesOfAllSymbols - set expected |> shouldEqual Set.empty
set expected - set allUsesOfAllSymbols |> shouldEqual Set.empty
(set expected = set allUsesOfAllSymbols) |> shouldEqual true
Expand Down Expand Up @@ -4065,13 +4065,13 @@ let ``Test project28 all symbols in signature`` () =
("FSharpMemberOrFunctionOrValue", "TestEvent2", "M:M.XmlDocSigTest.TestEvent2(System.Object)");
("FSharpMemberOrFunctionOrValue", "add_AnEvent", "M:M.XmlDocSigTest.add_AnEvent(Microsoft.FSharp.Control.FSharpHandler{System.Tuple{M.XmlDocSigTest,System.Object}})");
("FSharpMemberOrFunctionOrValue", "AProperty", "P:M.XmlDocSigTest.AProperty");
("FSharpMemberOrFunctionOrValue", "AnEvent", "P:M.XmlDocSigTest.AnEvent");
("FSharpMemberOrFunctionOrValue", "AnEvent", "E:M.XmlDocSigTest.AnEvent");
("FSharpMemberOrFunctionOrValue", "AnotherEvent", "P:M.XmlDocSigTest.AnotherEvent");
("FSharpMemberOrFunctionOrValue", "AnotherProperty", "P:M.XmlDocSigTest.AnotherProperty");
("FSharpMemberOrFunctionOrValue", "remove_AnEvent", "M:M.XmlDocSigTest.remove_AnEvent(Microsoft.FSharp.Control.FSharpHandler{System.Tuple{M.XmlDocSigTest,System.Object}})");
("FSharpMemberOrFunctionOrValue", "AnotherProperty", "P:M.XmlDocSigTest.AnotherProperty");
("FSharpMemberOrFunctionOrValue", "AnotherEvent", "P:M.XmlDocSigTest.AnotherEvent");
("FSharpMemberOrFunctionOrValue", "AnEvent", "P:M.XmlDocSigTest.AnEvent");
("FSharpMemberOrFunctionOrValue", "AnEvent", "E:M.XmlDocSigTest.AnEvent");
("FSharpMemberOrFunctionOrValue", "AProperty", "P:M.XmlDocSigTest.AProperty");
("FSharpField", "event1", "P:M.XmlDocSigTest.event1");
("FSharpField", "event2", "P:M.XmlDocSigTest.event2");
Expand Down
13 changes: 13 additions & 0 deletions tests/FSharp.Compiler.Service.Tests/Symbols.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1286,6 +1286,19 @@ type T() =
)

Assert.False hasPropertySymbols

[<Fact>]
let ``CLIEvent is recognized as event`` () =
let symbolUse = Checker.getSymbolUse """
type T() =
[<CLIEvent>]
member this.Ev{caret}ent = Event<int>().Publish
"""
match symbolUse.Symbol with
| :? FSharpMemberOrFunctionOrValue as mfv ->
Assert.True mfv.IsEvent
Assert.StartsWith("E:", mfv.XmlDocSig)
| _ -> failwith "Expected FSharpMemberOrFunctionOrValue"

[<Fact>]
let ``CLIEvent 01 - Synthetic range`` () =
Expand Down
Loading