Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Report auto property #15589

Merged
merged 22 commits into from
Jul 20, 2023
Merged

Report auto property #15589

merged 22 commits into from
Jul 20, 2023

Conversation

nojaf
Copy link
Contributor

@nojaf nojaf commented Jul 12, 2023

Related to #15586 and tries to address fsharp/fsharp-compiler-docs#79.

As mentioned in #15586, the SynMemberDefn.AutoProperty will be split into three parts when both get,set are present.

Consider the following example:

type X() =
    member val Y = 1 with get,set

This will generate two bindings: get_Y and set_Y.
Both will currently use the Y range for the Item in NameResolution.
When you later ask for the symbol use of Y, you now get the get_Y from checkResults.GetSymbolUseAtLocation.

There is no clever symbol detection going on here,

member this.GetSymbolUseAtLocation(line, colAtEndOfNames, lineText, names) =
this.GetSymbolUsesAtLocation(line, colAtEndOfNames, lineText, names)
|> List.tryHead

it just returns the first value. Where actually two symbols were found.

In this PR, when both get,set are present, the respective get_Y and set_Y members will now use the keyword (get or set) range for the NameResolution Item.

And a new property Item will be reported for the range of Y. That property will contain both the Getter and Setter:

let mfv = checkResults.GetSymbolUsesAtLocation(..., [ "Y" ]).Symbol :?> FSharpMemberOrFunctionOrValue
Assert.True mfv.IsProperty
Assert.True mfv.HasGetterMethod
Assert.True mfv.HasSetterMethod
Assert.True (mfv.GetterMethod.CompiledName.StartsWith("get_"))
Assert.True (mfv.SetterMethod.CompiledName.StartsWith("set_"))

The same approach is possible for SynMemberDefn.GetSetMember where we can produce a Item.Property if both members are present.

The result of this change is that checkResults.GetSymbolUseAtLocation will report a single symbol for the property name and not just return the getter. And this solves the original problem with the tooltip. And it also has a positive effect on the new GetValSignatureText API.

@auduchinok
Copy link
Member

auduchinok commented Jul 13, 2023

@nojaf Is there a chance that this change could also help to improve things for non-auto properties too? 😇

type T1() =
    member x.P = 0
    member x.P with set (i: int) = ()
type T2() =
    member this.P
        with get _ = 1
        and set _ _ = ()

@auduchinok
Copy link
Member

auduchinok commented Jul 13, 2023

There's also seems to be a crazy way to mix auto properties with normal ones:

type T3() =
    member val P = 1
    member this.P with set (i: int) = ()

@nojaf
Copy link
Contributor Author

nojaf commented Jul 13, 2023

Is there a chance that this change could also help to improve things for non-auto properties too?

Not with the current changes but I hope to use the same mechanism for SynMemberDefn.GetSetProperty.
I'll take a look at it, hopefully, the same trick could work as well there.

There's also seems to be a crazy way to mix auto properties with normal ones:

Because of course, why not 🥳🙈. I don't think my current change would impact that.
I do think it is correct to have two symbols in this case.

@auduchinok
Copy link
Member

auduchinok commented Jul 13, 2023

I do think it is correct to have two symbols in this case.

Apparently, it's considered to be a single property in the signature and compiled code. 😞

It looks like the setter from the second declaration is added to the auto property. Ideally, the same property should be reported at both P identifier ranges, and accessors should be reported if defined explicitly via get/set at their ranges.

@nojaf nojaf marked this pull request as ready for review July 17, 2023 09:07
@nojaf nojaf requested a review from a team as a code owner July 17, 2023 09:07
@nojaf
Copy link
Contributor Author

nojaf commented Jul 17, 2023

Ready for review.

@nojaf nojaf force-pushed the report-auto-property branch from 5fec4c0 to 81b0fc6 Compare July 17, 2023 12:20
@@ -9035,15 +9035,17 @@ FSharp.Compiler.Syntax.SynUnionCaseKind: FSharp.Compiler.Syntax.SynUnionCaseKind
FSharp.Compiler.Syntax.SynUnionCaseKind: Int32 Tag
FSharp.Compiler.Syntax.SynUnionCaseKind: Int32 get_Tag()
FSharp.Compiler.Syntax.SynUnionCaseKind: System.String ToString()
FSharp.Compiler.Syntax.SynValData: FSharp.Compiler.Syntax.SynValData NewSynValData(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynMemberFlags], FSharp.Compiler.Syntax.SynValInfo, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident])
FSharp.Compiler.Syntax.SynValData: FSharp.Compiler.Syntax.SynValData NewSynValData(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynMemberFlags], FSharp.Compiler.Syntax.SynValInfo, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strictly speaking yes, but every FCS release has these.
You could say the same thing about the recent changes in https://github.com/dotnet/fsharp/commits/main/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl

Copy link
Member

@vzarytovskii vzarytovskii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one in particular is a bit more scary to me.

@0101 @auduchinok @T-Gro please take a look

Copy link
Member

@vzarytovskii vzarytovskii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one in particular is a bit more scary to me.

@0101 @auduchinok @T-Gro please take a look

Copy link
Member

@psfinaki psfinaki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few remarks, mostly minor things and some learning opportunities for me.

Thanks a lot!

match mGetSetOpt with
| Some (GetSetKeywords.GetSet(set = mSet)) -> Ident(id.idText, mSet)
| _ -> id
if isStatic then [id] else [ident ("__", mMemberPortion);id]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a high level, how do things bubble up if it's static actually?

@@ -11418,8 +11419,7 @@ and AnalyzeRecursiveInstanceMemberDecl
// the definition of these symbols.
//
// See https://github.com/fsharp/FSharp.Compiler.Service/issues/79.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the comments above can go now?

| DifferentGetterAndSetter(getValRef, setValRef) ->
let g = NicePrint.stringValOrMember displayEnv cenv.infoReader getValRef
let s = NicePrint.stringValOrMember displayEnv cenv.infoReader setValRef
$"{g}\n{s}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't know if we have any consistent approach on that but maybe consider using Environment.NewLine

if p.HasGetter && p.HasSetter then "with get, set"
elif p.HasGetter then "with get"
elif p.HasSetter then "with set"
else ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this shouldn't happen?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that is unreachable code indeed.

@@ -458,19 +464,290 @@ type Foo =
| symbol -> Assert.Fail $"Expected {symbol} to be FSharpMemberOrFunctionOrValue"

[<Test>]
let ``AutoProperty with get,set has two symbols`` () =
let ``AutoProperty with get,set has a single symbol!`` () =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like the exclamation mark - feels like the emotional core of the PR :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

5 participants