diff --git a/src/Compiler/Checking/NicePrint.fs b/src/Compiler/Checking/NicePrint.fs index 072dc76a100..db54e06faf6 100644 --- a/src/Compiler/Checking/NicePrint.fs +++ b/src/Compiler/Checking/NicePrint.fs @@ -1795,7 +1795,7 @@ module TastDefinitionPrinting = let overallL = staticL ^^ WordL.keywordMember ^^ (nameL |> addColonL) ^^ typL layoutXmlDocOfEventInfo denv infoReader einfo overallL - let layoutPropInfo denv (infoReader: InfoReader) m (pinfo: PropInfo) = + let layoutPropInfo denv (infoReader: InfoReader) m (pinfo: PropInfo) : Layout list = let amap = infoReader.amap let isPublicGetterSetter (getter: MethInfo) (setter: MethInfo) = @@ -1804,13 +1804,33 @@ module TastDefinitionPrinting = | Some gRef, Some sRef -> isPublicAccess gRef.Accessibility && isPublicAccess sRef.Accessibility | _ -> false + let (|MixedAccessibilityGetterAndSetter|_|) (pinfo: PropInfo) = + if not (pinfo.HasGetter && pinfo.HasSetter) then + None + else + match pinfo.GetterMethod.ArbitraryValRef, pinfo.SetterMethod.ArbitraryValRef with + | Some getValRef, Some setValRef -> + if getValRef.Accessibility = setValRef.Accessibility then + None + else + Some (getValRef, setValRef) + | _ -> None + match pinfo.ArbitraryValRef with | Some vref -> - let propL = PrintTastMemberOrVals.prettyLayoutOfValOrMemberNoInst denv infoReader vref - if pinfo.HasGetter && pinfo.HasSetter && not pinfo.IsIndexer && isPublicGetterSetter pinfo.GetterMethod pinfo.SetterMethod then - propL ^^ wordL (tagKeyword "with") ^^ wordL (tagText "get, set") - else - propL + match pinfo with + | MixedAccessibilityGetterAndSetter(getValRef, setValRef) -> + let getSuffix = if pinfo.IsIndexer then emptyL else wordL (tagKeyword "with") ^^ wordL (tagText "get") + [ + PrintTastMemberOrVals.prettyLayoutOfValOrMemberNoInst denv infoReader getValRef ^^ getSuffix + PrintTastMemberOrVals.prettyLayoutOfValOrMemberNoInst denv infoReader setValRef + ] + | _ -> + let propL = PrintTastMemberOrVals.prettyLayoutOfValOrMemberNoInst denv infoReader vref + if pinfo.HasGetter && pinfo.HasSetter && not pinfo.IsIndexer && isPublicGetterSetter pinfo.GetterMethod pinfo.SetterMethod then + [ propL ^^ wordL (tagKeyword "with") ^^ wordL (tagText "get, set") ] + else + [ propL ] | None -> let modifierAndMember = @@ -1822,7 +1842,7 @@ module TastDefinitionPrinting = let nameL = ConvertValLogicalNameToDisplayLayout false (tagProperty >> tagNavArbValRef pinfo.ArbitraryValRef >> wordL) pinfo.PropertyName let typL = layoutType denv (pinfo.GetPropertyType(amap, m)) let overallL = modifierAndMember ^^ (nameL |> addColonL) ^^ typL - layoutXmlDocOfPropInfo denv infoReader pinfo overallL + [ layoutXmlDocOfPropInfo denv infoReader pinfo overallL ] let layoutTyconDefn (denv: DisplayEnv) (infoReader: InfoReader) ad m simplified isFirstType (tcref: TyconRef) = let g = denv.g @@ -1993,7 +2013,9 @@ module TastDefinitionPrinting = let propLs = props - |> List.map (fun x -> (true, x.IsStatic, x.PropertyName, 0, 0), layoutPropInfo denv infoReader m x) + |> List.collect (fun x -> + layoutPropInfo denv infoReader m x + |> List.map (fun layout -> (true, x.IsStatic, x.PropertyName, 0, 0), layout)) |> List.sortBy fst |> List.map snd diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 93ff861fe68..0145bcfa609 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -252,6 +252,7 @@ + diff --git a/tests/FSharp.Compiler.ComponentTests/Signatures/MemberTests.fs b/tests/FSharp.Compiler.ComponentTests/Signatures/MemberTests.fs new file mode 100644 index 00000000000..2452a900881 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Signatures/MemberTests.fs @@ -0,0 +1,52 @@ +module Signatures.MemberTests + +open Xunit +open FsUnit +open FSharp.Test.Compiler +open Signatures.TestHelpers + +[] +let ``Verify that the visibility difference between the getter and setter results in two distinct member signatures`` () = + FSharp + """ +module Foo + +type Foo() = + member f.X with internal get (key1, key2) = true and public set (key1, key2) value = () +""" + |> printSignatures + |> should + equal + """ +module Foo + +type Foo = + + new: unit -> Foo + + member internal X: key1: obj * key2: obj -> bool with get + + member X: key1: obj * key2: obj -> obj with set""" + +[] +let ``Getter should have explicit with get suffix`` () = + FSharp + """ +module Foo + +type Foo() = + member f.Y with public get () = 'y' and internal set y = ignore y +""" + |> printSignatures + |> should + equal + """ +module Foo + +type Foo = + + new: unit -> Foo + + member Y: char with get + + member internal Y: char with set""" diff --git a/tests/FSharp.Compiler.ComponentTests/Signatures/TestCasesForGenerationRoundTrip/setters.fsx b/tests/FSharp.Compiler.ComponentTests/Signatures/TestCasesForGenerationRoundTrip/setters.fsx new file mode 100644 index 00000000000..47f7601baba --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Signatures/TestCasesForGenerationRoundTrip/setters.fsx @@ -0,0 +1,3 @@ +type Foo() = + member f.X with internal get (key1, key2) = true and public set (key1, key2) value = () + member f.Y with public get () = 'y' and internal set y = ignore y