Skip to content

Commit

Permalink
Merge branch 'main' into access-modifies-to-auto-properties
Browse files Browse the repository at this point in the history
  • Loading branch information
vzarytovskii authored Aug 12, 2024
2 parents ba25173 + ed4263f commit 3adaf31
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 20 deletions.
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/9.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* Parser: recover on missing union case field types (PR [#17455](https://github.com/dotnet/fsharp/pull/17455))
* Sink: report function domain type ([PR #17470](https://github.com/dotnet/fsharp/pull/17470))
* Allow access modifies to auto properties getters and setters ([PR 16687](https://github.com/dotnet/fsharp/pull/16687), [PR 16861](https://github.com/dotnet/fsharp/pull/16861), [Language suggestion #430](https://github.com/fsharp/fslang-suggestions/issues/430))
* Render C# nullable-analysis attributes in tooltips ([PR #17485](https://github.com/dotnet/fsharp/pull/17485))

### Changed

Expand Down
49 changes: 45 additions & 4 deletions src/Compiler/Checking/NicePrint.fs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ module internal PrintUtilities =

let squareAngleL x = LeftL.leftBracketAngle ^^ x ^^ RightL.rightBracketAngle

let squareAngleReturn x = LeftL.leftBracketAngle ^^ WordL.keywordReturn ^^ SepL.colon ^^ x ^^ RightL.rightBracketAngle

let angleL x = SepL.leftAngle ^^ x ^^ RightL.rightAngle

let braceL x = wordL leftBrace ^^ x ^^ wordL rightBrace
Expand Down Expand Up @@ -642,6 +644,23 @@ module PrintTypes =
let argsL = bracketL (sepListL RightL.comma (List.map (layoutILAttribElement denv) args))
PrintIL.layoutILType denv [] ty ++ argsL

/// Layout nullness attributes for C# flow-analysis
/// F# does not process them, this way we can at least show them.
and layoutCsharpCodeAnalysisIlAttributes denv (attrs:ILAttributes) (layoutCombinator: Layout -> Layout -> Layout) restL =
let denvShortNames() = { denv with shortTypeNames = true }
let attrsL =
[ for a in attrs.AsArray() do
let name = a.Method.DeclaringType.BasicQualifiedName
if name.StartsWith("System.Diagnostics.CodeAnalysis") then
let parms, _args = decodeILAttribData a
layoutILAttrib (denvShortNames()) (a.Method.DeclaringType, parms)
]
match attrsL with
| [] -> restL
| _ ->
let separated = sepListL RightL.semicolon attrsL
layoutCombinator separated restL

/// Layout '[<attribs>]' above another block
and layoutAttribs denv startOpt isLiteral kind attrs restL =

Expand Down Expand Up @@ -1653,11 +1672,33 @@ module InfoMemberPrinting =
let idL = ConvertValLogicalNameToDisplayLayout false (tagMethod >> tagNavArbValRef minfo.ArbitraryValRef >> wordL) minfo.LogicalName
SepL.dot ^^
PrintTypes.layoutTyparDecls denv idL true minfo.FormalMethodTypars ^^
SepL.leftParen
SepL.leftParen

let layout,paramLayouts =
match denv.showCsharpCodeAnalysisAttributes, minfo with
| true, ILMeth(_g,mi,_e) ->
let methodLayout =
// Render Method attributes and [return:..] attributes on separate lines above (@@) the method definition
PrintTypes.layoutCsharpCodeAnalysisIlAttributes denv (minfo.GetCustomAttrs()) (squareAngleL >> (@@)) layout
|> PrintTypes.layoutCsharpCodeAnalysisIlAttributes denv (mi.RawMetadata.Return.CustomAttrs) (squareAngleReturn >> (@@))
let paramLayouts =
minfo.GetParamDatas (amap, m, minst)
|> List.head
|> List.zip (mi.ParamMetadata)
|> List.map(fun (ilParams,paramData) ->
layoutParamData denv paramData
// Render parameter attributes next to (^^) the parameter definition
|> PrintTypes.layoutCsharpCodeAnalysisIlAttributes denv (ilParams.CustomAttrs) (squareAngleL >> (^^)) )
methodLayout,paramLayouts
| _ ->
layout,
minfo.GetParamDatas (amap, m, minst)
|> List.concat
|> List.map (layoutParamData denv)


let paramDatas = minfo.GetParamDatas (amap, m, minst)
let layout = layout ^^ sepListL RightL.comma ((List.concat >> List.map (layoutParamData denv)) paramDatas)
layout ^^ RightL.rightParen ^^ WordL.colon ^^ PrintTypes.layoutType denv retTy
let layout = layout ^^ sepListL RightL.comma paramLayouts
layout ^^ RightL.rightParen ^^ WordL.colon ^^ PrintTypes.layoutType denv retTy // Todo enrich return type

// Prettify an ILMethInfo
let prettifyILMethInfo (amap: Import.ImportMap) m (minfo: MethInfo) typarInst ilMethInfo =
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Facilities/TextLayoutRender.fs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ module WordL =
let leftAngle = wordL TaggedText.leftAngle
let keywordModule = wordL TaggedText.keywordModule
let keywordNamespace = wordL TaggedText.keywordNamespace
let keywordReturn = wordL TaggedText.keywordReturn

module LeftL =
let leftParen = leftL TaggedText.leftParen
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Facilities/TextLayoutRender.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ module internal WordL =
val leftAngle: Layout
val keywordModule: Layout
val keywordNamespace: Layout
val keywordReturn: Layout

module internal LeftL =
val leftParen: Layout
Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/Service/ServiceDeclarationLists.fs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ module DeclarationListHelpers =
let rec FormatItemDescriptionToToolTipElement displayFullName (infoReader: InfoReader) ad m denv (item: ItemWithInst) symbol (width: int option) =
let g = infoReader.g
let amap = infoReader.amap
let denv = SimplerDisplayEnv denv
let denv = {SimplerDisplayEnv denv with showCsharpCodeAnalysisAttributes = true }
let xml = GetXmlCommentForItem infoReader m item.Item

match item.Item with
Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/TypedTree/TypedTreeOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3145,6 +3145,7 @@ type DisplayEnv =
shortConstraints: bool
useColonForReturnType: bool
showAttributes: bool
showCsharpCodeAnalysisAttributes: bool
showOverrides: bool
showStaticallyResolvedTyparAnnotations: bool
showNullnessAnnotations: bool option
Expand Down Expand Up @@ -3180,6 +3181,7 @@ type DisplayEnv =
suppressMutableKeyword = false
showMemberContainers = false
showAttributes = false
showCsharpCodeAnalysisAttributes = false
showOverrides = true
showStaticallyResolvedTyparAnnotations = true
showNullnessAnnotations = None
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/TypedTree/TypedTreeOps.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,7 @@ type DisplayEnv =
shortConstraints: bool
useColonForReturnType: bool
showAttributes: bool
showCsharpCodeAnalysisAttributes: bool
showOverrides: bool
showStaticallyResolvedTyparAnnotations: bool
showNullnessAnnotations: bool option
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Utilities/sformat.fs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ module TaggedText =
let keywordInline = tagKeyword "inline"
let keywordModule = tagKeyword "module"
let keywordNamespace = tagKeyword "namespace"
let keywordReturn = tagKeyword "return"
let punctuationUnit = tagPunctuation "()"
#endif

Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Utilities/sformat.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ module internal TaggedText =
val internal keywordInline: TaggedText
val internal keywordModule: TaggedText
val internal keywordNamespace: TaggedText
val internal keywordReturn: TaggedText
val internal punctuationUnit: TaggedText

type internal IEnvironment =
Expand Down
84 changes: 69 additions & 15 deletions tests/FSharp.Compiler.Service.Tests/TooltipTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -390,29 +390,83 @@ c.Abc

testToolTipSquashing source 7 5 "c.Abc" [ "c"; "Abc" ] FSharpTokenTag.Identifier

[<Fact>]
let ``Auto property should display a single tool tip`` () =
let source = """
namespace Foo
/// Some comment on class
type Bar() =
/// Some comment on class member
member val Foo = "bla" with get, set
"""
let getCheckResults source options =
let fileName, options =
mkTestFileAndOptions
source
Array.empty
options
let _, checkResults = parseAndCheckFile fileName source options
let (ToolTipText(items)) = checkResults.GetToolTip(7, 18, " member val Foo = \"bla\" with get, set", [ "Foo" ], FSharpTokenTag.Identifier)
Assert.True (items.Length = 1)
checkResults

let assertAndGetSingleToolTipText (ToolTipText(items)) =
Assert.Equal(1,items.Length)
match items.[0] with
| ToolTipElement.Group [ { MainDescription = description } ] ->
let toolTipText =
description
|> Array.map (fun taggedText -> taggedText.Text)
|> String.concat ""

Assert.Equal("property Bar.Foo: string with get, set", toolTipText)
toolTipText
| _ -> failwith $"Expected group, got {items.[0]}"

let normalize (s:string) = s.Replace("\r\n", "\n").Replace("\n\n", "\n")

[<Fact>]
let ``Auto property should display a single tool tip`` () =
let source = """
namespace Foo
/// Some comment on class
type Bar() =
/// Some comment on class member
member val Foo = "bla" with get, set
"""
let checkResults = getCheckResults source Array.empty
checkResults.GetToolTip(7, 18, " member val Foo = \"bla\" with get, set", [ "Foo" ], FSharpTokenTag.Identifier)
|> assertAndGetSingleToolTipText
|> Assert.shouldBeEquivalentTo "property Bar.Foo: string with get, set"

[<FactForNETCOREAPP>]
let ``Should display nullable Csharp code analysis annotations on method argument`` () =

let source = """module Foo
let exists() = System.IO.Path.Exists(null:string)
"""
let checkResults = getCheckResults source [|"--checknulls+";"--langversion:preview"|]
checkResults.GetToolTip(2, 36, "let exists() = System.IO.Path.Exists(null:string)", [ "Exists" ], FSharpTokenTag.Identifier)
|> assertAndGetSingleToolTipText
|> Assert.shouldBeEquivalentTo "System.IO.Path.Exists([<NotNullWhenAttribute (true)>] path: string | null) : bool"


[<FactForNETCOREAPP>]
let ``Should display nullable Csharp code analysis annotations on method return type`` () =

let source = """module Foo
let getPath() = System.IO.Path.GetFileName(null:string)
"""
let checkResults = getCheckResults source [|"--checknulls+";"--langversion:preview"|]
checkResults.GetToolTip(2, 42, "let getPath() = System.IO.Path.GetFileName(null:string)", [ "GetFileName" ], FSharpTokenTag.Identifier)
|> assertAndGetSingleToolTipText
|> Assert.shouldBeEquivalentTo ("""[<return:NotNullIfNotNullAttribute ("path")>]
System.IO.Path.GetFileName(path: string | null) : string | null""" |> normalize)

[<FactForNETCOREAPP>]
let ``Should display nullable Csharp code analysis annotations on TryParse pattern`` () =
let source = """module Foo
let success,version = System.Version.TryParse(null)
"""
let checkResults = getCheckResults source [|"--checknulls+";"--langversion:preview"|]
checkResults.GetToolTip(2, 45, "let success,version = System.Version.TryParse(null)", [ "TryParse" ], FSharpTokenTag.Identifier)
|> assertAndGetSingleToolTipText
|> Assert.shouldBeEquivalentTo ("""System.Version.TryParse([<NotNullWhenAttribute (true)>] input: string | null, [<NotNullWhenAttribute (true)>] result: byref<System.Version | null>) : bool""")

[<FactForNETCOREAPP>]
let ``Display with nullable annotations can be squashed`` () =
let source = """module Foo
let success,version = System.Version.TryParse(null)
"""
let checkResults = getCheckResults source [|"--checknulls+";"--langversion:preview"|]
checkResults.GetToolTip(2, 45, "let success,version = System.Version.TryParse(null)", [ "TryParse" ], FSharpTokenTag.Identifier,width=100)
|> assertAndGetSingleToolTipText
|> Assert.shouldBeEquivalentTo ("""System.Version.TryParse([<NotNullWhenAttribute (true)>] input: string | null,
[<NotNullWhenAttribute (true)>] result: byref<System.Version | null>) : bool""" |> normalize)

0 comments on commit 3adaf31

Please sign in to comment.