From c0386a986cdf920e4ee504c27fb982e0f6a8f2a7 Mon Sep 17 00:00:00 2001 From: nojaf Date: Tue, 14 Jun 2022 14:18:30 +0200 Subject: [PATCH 1/3] Initial draft of SynMemberDefn.ReadWriteMember. --- src/Compiler/Checking/CheckDeclarations.fs | 4 +- .../Service/ServiceInterfaceStubGenerator.fs | 3 + src/Compiler/Service/ServiceNavigation.fs | 15 + src/Compiler/Service/ServiceParseTreeWalk.fs | 3 + src/Compiler/Service/ServiceXmlDocParser.fs | 7 + src/Compiler/SyntaxTree/SyntaxTree.fs | 26 ++ src/Compiler/SyntaxTree/SyntaxTree.fsi | 26 ++ src/Compiler/SyntaxTree/SyntaxTreeOps.fs | 63 ++++ src/Compiler/SyntaxTree/SyntaxTreeOps.fsi | 9 + src/Compiler/SyntaxTree/SyntaxTrivia.fs | 14 + src/Compiler/SyntaxTree/SyntaxTrivia.fsi | 20 ++ src/Compiler/pars.fsy | 283 +++++------------- ...erService.SurfaceArea.netstandard.expected | 58 ++++ tests/service/Symbols.fs | 38 ++- 14 files changed, 350 insertions(+), 219 deletions(-) diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index 825be7fdd8..1de2155882 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -2530,6 +2530,7 @@ let TcMutRecDefns_Phase2 (cenv: cenv) envInitial bindsm scopem mutRecNSInfo (env | SynMemberDefn.LetBindings _ | SynMemberDefn.AutoProperty _ | SynMemberDefn.Member _ + | SynMemberDefn.ReadWriteMember _ | SynMemberDefn.Open _ -> Some(TyconBindingDefn(containerInfo, newslotsOK, declKind, memb, memb.Range)) @@ -4832,7 +4833,8 @@ module TcDeclarations = cspec |> List.filter (fun memb -> match memb with | SynMemberDefn.Interface _ - | SynMemberDefn.Member _ + | SynMemberDefn.Member _ + | SynMemberDefn.ReadWriteMember _ | SynMemberDefn.LetBindings _ | SynMemberDefn.ImplicitCtor _ | SynMemberDefn.AutoProperty _ diff --git a/src/Compiler/Service/ServiceInterfaceStubGenerator.fs b/src/Compiler/Service/ServiceInterfaceStubGenerator.fs index e54f5922bc..970d6d493c 100644 --- a/src/Compiler/Service/ServiceInterfaceStubGenerator.fs +++ b/src/Compiler/Service/ServiceInterfaceStubGenerator.fs @@ -817,6 +817,9 @@ module InterfaceStubGenerator = else Option.bind (List.tryPick walkSynMemberDefn) members | SynMemberDefn.Member (binding, _range) -> walkBinding binding + | SynMemberDefn.ReadWriteMember (read = read; write = write) -> + walkExpr read.Expression + |> Option.orElseWith (fun () -> walkExpr write.Expression) | SynMemberDefn.NestedType (typeDef, _access, _range) -> walkSynTypeDefn typeDef | SynMemberDefn.ValField (_field, _range) -> None | SynMemberDefn.LetBindings (bindings, _isStatic, _isRec, _range) -> List.tryPick walkBinding bindings diff --git a/src/Compiler/Service/ServiceNavigation.fs b/src/Compiler/Service/ServiceNavigation.fs index 61dffe2213..552ea57bb1 100755 --- a/src/Compiler/Service/ServiceNavigation.fs +++ b/src/Compiler/Service/ServiceNavigation.fs @@ -997,6 +997,21 @@ module NavigateTo = walkSynMemberDefn m container | None -> () | SynMemberDefn.Member (binding, _) -> addBinding binding None container + | SynMemberDefn.ReadWriteMember (identifier = pat) -> + let kind = mapMemberKind SynMemberKind.PropertyGetSet + + match pat with + | SynPat.LongIdent(longDotId = SynLongIdent ([ _; id ], _, _)) -> + // instance members + addIdent kind id false container + | SynPat.LongIdent(longDotId = SynLongIdent ([ id ], _, _)) -> + // functions + addIdent kind id false container + | SynPat.Named (SynIdent (id, _), _, _, _) + | SynPat.As (_, SynPat.Named (SynIdent (id, _), _, _, _), _) -> + // values + addIdent kind id false container + | _ -> () | SynMemberDefn.NestedType (typeDef, _, _) -> walkSynTypeDefn typeDef container | SynMemberDefn.ValField (field, _) -> addField field false container | SynMemberDefn.LetBindings (bindings, _, _, _) -> diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fs b/src/Compiler/Service/ServiceParseTreeWalk.fs index 4cfc7c9937..4ddb2b1d6b 100755 --- a/src/Compiler/Service/ServiceParseTreeWalk.fs +++ b/src/Compiler/Service/ServiceParseTreeWalk.fs @@ -948,6 +948,9 @@ module SyntaxTraversal = | SynMemberDefn.Inherit (synType, _identOption, range) -> traverseInherit (synType, range) | SynMemberDefn.ValField (_synField, _range) -> None | SynMemberDefn.NestedType (synTypeDefn, _synAccessOption, _range) -> traverseSynTypeDefn path synTypeDefn + | SynMemberDefn.ReadWriteMember (read = read; write = write) -> + traverseSynExpr path read.Expression + |> Option.orElseWith (fun () -> traverseSynExpr path write.Expression) and traverseSynMatchClause origPath mc = let defaultTraverse mc = diff --git a/src/Compiler/Service/ServiceXmlDocParser.fs b/src/Compiler/Service/ServiceXmlDocParser.fs index dac6183874..fd18a687c5 100644 --- a/src/Compiler/Service/ServiceXmlDocParser.fs +++ b/src/Compiler/Service/ServiceXmlDocParser.fs @@ -187,6 +187,13 @@ module XmlDocParsing = let indent = indentOf line XmlDocable(line, indent, []) + | SynMemberDefn.ReadWriteMember (xmlDoc = xmlDoc; identifier = synPat; range = range) -> + if isEmptyXmlDoc xmlDoc then + let line = range.StartLine + let indent = indentOf line + let paramNames = digNamesFrom synPat + XmlDocable(line, indent, paramNames) + | SynMemberDefn.Open _ | SynMemberDefn.ImplicitCtor _ | SynMemberDefn.ImplicitInherit _ diff --git a/src/Compiler/SyntaxTree/SyntaxTree.fs b/src/Compiler/SyntaxTree/SyntaxTree.fs index fa965bc174..d07a592ef7 100644 --- a/src/Compiler/SyntaxTree/SyntaxTree.fs +++ b/src/Compiler/SyntaxTree/SyntaxTree.fs @@ -1367,6 +1367,18 @@ type SynMemberDefn = | Member of memberDefn: SynBinding * range: range + | ReadWriteMember of + accessibility: SynAccess option * + attributes: SynAttributes * + memberFlags: (SynMemberKind -> SynMemberFlags) * + xmlDoc: PreXmlDoc * + identifier: SynPat * + returnTypeOpt: SynReturnInfo option * + read: SynMemberDefnPropertyInfo * + write: SynMemberDefnPropertyInfo * + range: range * + trivia: SynMemberDefnReadWriteMemberTrivia + | ImplicitCtor of accessibility: SynAccess option * attributes: SynAttributes * @@ -1407,6 +1419,7 @@ type SynMemberDefn = member d.Range = match d with | SynMemberDefn.Member (range = m) + | SynMemberDefn.ReadWriteMember (range = m) | SynMemberDefn.Interface (range = m) | SynMemberDefn.Open (range = m) | SynMemberDefn.LetBindings (range = m) @@ -1420,6 +1433,19 @@ type SynMemberDefn = type SynMemberDefns = SynMemberDefn list +type SynMemberDefnPropertyInfo = + { + IsInline: bool + Attributes: SynAttributes + /// 'get' or 'set + IsWrite: bool + Pattern: SynPat + ReturnInfo: SynReturnInfo option + Expression: SynExpr + Range: range + Trivia: SynMemberDefnPropertyInfoTrivia + } + [] type SynModuleDecl = diff --git a/src/Compiler/SyntaxTree/SyntaxTree.fsi b/src/Compiler/SyntaxTree/SyntaxTree.fsi index 9f18a1488d..a0b1dd02e9 100644 --- a/src/Compiler/SyntaxTree/SyntaxTree.fsi +++ b/src/Compiler/SyntaxTree/SyntaxTree.fsi @@ -1554,6 +1554,19 @@ type SynMemberDefn = /// A 'member' definition within a type | Member of memberDefn: SynBinding * range: range + /// A 'member' definition with a get and set portion + | ReadWriteMember of + accessibility: SynAccess option * + attributes: SynAttributes * + memberFlags: (SynMemberKind -> SynMemberFlags) * + xmlDoc: PreXmlDoc * + identifier: SynPat * + returnTypeOpt: SynReturnInfo option * + read: SynMemberDefnPropertyInfo * + write: SynMemberDefnPropertyInfo * + range: range * + trivia: SynMemberDefnReadWriteMemberTrivia + /// An implicit constructor definition | ImplicitCtor of accessibility: SynAccess option * @@ -1605,6 +1618,19 @@ type SynMemberDefn = type SynMemberDefns = SynMemberDefn list +type SynMemberDefnPropertyInfo = + { + IsInline: bool + Attributes: SynAttributes + /// 'get' or 'set + IsWrite: bool + Pattern: SynPat + ReturnInfo: SynReturnInfo option + Expression: SynExpr + Range: range + Trivia: SynMemberDefnPropertyInfoTrivia + } + /// Represents a definition within a module [] type SynModuleDecl = diff --git a/src/Compiler/SyntaxTree/SyntaxTreeOps.fs b/src/Compiler/SyntaxTree/SyntaxTreeOps.fs index 3af3910f0c..18be2a7f22 100644 --- a/src/Compiler/SyntaxTree/SyntaxTreeOps.fs +++ b/src/Compiler/SyntaxTree/SyntaxTreeOps.fs @@ -985,3 +985,66 @@ let mkDynamicArgExpr expr = SynExpr.Const(con, con.Range ident.idRange) | SynExpr.Paren (expr = e) -> e | e -> e + +let mkSynMemberGetOrSet + (grabXmlDocAtRangeStart: SynAttributes -> range -> PreXmlDoc) + (optInline: bool) + (propertyNameBindingPat: SynPat) + (mBindPattern: range) + (mWith: range) + (memberDefnPropertyInfo: SynMemberDefnPropertyInfo) + : SynAccess option -> (SynMemberKind -> SynMemberFlags) -> SynAttributeList list -> range -> SynMemberDefn list = + fun vis memFlagsBuilder attrs rangeStart -> + let optInline = optInline || memberDefnPropertyInfo.IsInline + let attrs = attrs @ memberDefnPropertyInfo.Attributes + + let memberKind = + if memberDefnPropertyInfo.IsWrite then + SynMemberKind.PropertySet + else + SynMemberKind.PropertyGet + + let xmlDoc = grabXmlDocAtRangeStart attrs rangeStart + + let binding = + let mLhs = unionRanges mBindPattern memberDefnPropertyInfo.Pattern.Range + + let headPat = + match propertyNameBindingPat with + | SynPat.LongIdent (longDotId, _, _, typarDecls, _, access, _) -> + let getSet = if memberDefnPropertyInfo.IsWrite then "set" else "get" + + let m = + unionRanges memberDefnPropertyInfo.Trivia.GetSetRange memberDefnPropertyInfo.Pattern.Range + + SynPat.LongIdent( + longDotId, + Some(PropertyKeyword.With mWith), + Some(ident (getSet, memberDefnPropertyInfo.Trivia.GetSetRange)), + typarDecls, + SynArgPats.Pats([ memberDefnPropertyInfo.Pattern ]), + access, + m + ) + | pat -> pat + + mkSynBinding + (xmlDoc, headPat) + (vis, + optInline, + false, + mBindPattern, + DebugPointAtBinding.NoneAtInvisible, + memberDefnPropertyInfo.ReturnInfo, + memberDefnPropertyInfo.Expression, + mLhs, + [], + attrs, + Some(memFlagsBuilder memberKind), + SynBindingTrivia.Zero) + + let memberRange = + unionRanges rangeStart memberDefnPropertyInfo.Expression.Range + |> unionRangeWithXmlDoc xmlDoc + + [ SynMemberDefn.Member(binding, memberRange) ] diff --git a/src/Compiler/SyntaxTree/SyntaxTreeOps.fsi b/src/Compiler/SyntaxTree/SyntaxTreeOps.fsi index dfff0392bc..f7ed679ef6 100644 --- a/src/Compiler/SyntaxTree/SyntaxTreeOps.fsi +++ b/src/Compiler/SyntaxTree/SyntaxTreeOps.fsi @@ -340,3 +340,12 @@ val (|SynPipeRight3|_|): SynExpr -> (SynExpr * SynExpr * SynExpr * SynExpr) opti val prependIdentInLongIdentWithTrivia: ident: SynIdent -> dotm: range -> lid: SynLongIdent -> SynLongIdent val mkDynamicArgExpr: expr: SynExpr -> SynExpr + +val mkSynMemberGetOrSet: + grabXmlDocAtRangeStart: (SynAttributes -> range -> PreXmlDoc) -> + optInline: bool -> + propertyNameBindingPat: SynPat -> + mBindPattern: range -> + mWith: range -> + memberDefnPropertyInfo: SynMemberDefnPropertyInfo -> + (SynAccess option -> (SynMemberKind -> SynMemberFlags) -> SynAttributeList list -> range -> SynMemberDefn list) diff --git a/src/Compiler/SyntaxTree/SyntaxTrivia.fs b/src/Compiler/SyntaxTree/SyntaxTrivia.fs index 31ff458204..9323015535 100644 --- a/src/Compiler/SyntaxTree/SyntaxTrivia.fs +++ b/src/Compiler/SyntaxTree/SyntaxTrivia.fs @@ -230,3 +230,17 @@ type SynValSigTrivia = WithKeyword = None EqualsRange = None } + +[] +type SynMemberDefnReadWriteMemberTrivia = + { + WithKeyword: range + AndKeyword: range + } + +[] +type SynMemberDefnPropertyInfoTrivia = + { + GetSetRange: range + EqualsRange: range + } diff --git a/src/Compiler/SyntaxTree/SyntaxTrivia.fsi b/src/Compiler/SyntaxTree/SyntaxTrivia.fsi index 5f7fc64dfd..b79347753a 100644 --- a/src/Compiler/SyntaxTree/SyntaxTrivia.fsi +++ b/src/Compiler/SyntaxTree/SyntaxTrivia.fsi @@ -324,3 +324,23 @@ type SynValSigTrivia = } static member Zero: SynValSigTrivia + +/// Represents additional information for SynMemberDefn.ReadWriteMember +[] +type SynMemberDefnReadWriteMemberTrivia = + { + /// The syntax range of the `with` keyword + WithKeyword: range + /// The syntax range of the `and` keyword + AndKeyword: range + } + +/// Represents additional information for SynMemberDefnPropertyInfo +[] +type SynMemberDefnPropertyInfoTrivia = + { + /// The syntax range of the `get` or `set` keyword + GetSetRange: range + /// The syntax range of the `=` token after the read pattern + EqualsRange: range + } diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index e0f78e337b..8c758f9bc7 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -1822,32 +1822,65 @@ classDefnMembersAtLeastOne: /* The "with get, set" part of a member definition */ classDefnMemberGetSet: - | WITH classDefnMemberGetSetElements + | WITH classDefnMemberGetSetElement AND classDefnMemberGetSetElement + { let mWithKwd = rhs parseState 1 + let mAndKwd = rhs parseState 3 + mWithKwd, $2, mAndKwd, $4 } + + | OWITH classDefnMemberGetSetElement AND classDefnMemberGetSetElement OEND + { let mWithKwd = rhs parseState 1 + let mAndKwd = rhs parseState 3 + mWithKwd, $2, mAndKwd, $4 } + + | OWITH classDefnMemberGetSetElement AND classDefnMemberGetSetElement error + { let mWithKwd = rhs parseState 1 + let mAndKwd = rhs parseState 3 + reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsUnmatchedWith()) + mWithKwd, $2, mAndKwd, $4 } + +/* The "with get" part of a member definition */ +classDefnMemberGet: + | WITH classDefnMemberGetSetElement { let mWithKwd = rhs parseState 1 mWithKwd, $2 } - | OWITH classDefnMemberGetSetElements OEND + | OWITH classDefnMemberGetSetElement OEND { let mWithKwd = rhs parseState 1 mWithKwd, $2 } - | OWITH classDefnMemberGetSetElements error + | OWITH classDefnMemberGetSetElement error { let mWithKwd = rhs parseState 1 reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsUnmatchedWith()) mWithKwd, $2 } -/* The "get, set" part of a member definition */ -classDefnMemberGetSetElements: - | classDefnMemberGetSetElement - { [$1], None } - | classDefnMemberGetSetElement AND classDefnMemberGetSetElement - { let mAnd = rhs parseState 2 - [$1;$3], Some mAnd } +classDefnMemberGetSetPattern: + | LPAREN rparen + { let m = rhs2 parseState 1 2 + SynPat.Const(SynConst.Unit, m) } + + | atomicPattern + { $1 } classDefnMemberGetSetElement: - | opt_inline opt_attributes bindingPattern opt_topReturnTypeWithTypeConstraints EQUALS typedSequentialExprBlock - { let mEquals = rhs parseState 5 - let mRhs = ($6 : SynExpr).Range - ($1, $2, $3, $4, Some mEquals, $6, mRhs) } + | opt_inline opt_attributes ident classDefnMemberGetSetPattern opt_topReturnTypeWithTypeConstraints EQUALS typedSequentialExprBlock + { let mSet = rhs parseState 3 + let getOrSet = $3.idText + printfn "getOrSet %s" getOrSet + if getOrSet <> "get" && getOrSet <> "set" then raiseParseErrorAt mSet (FSComp.SR.parsGetAndOrSetRequired()) else + + let attrs = $2 |> List.map (fun attrList -> { attrList with Attributes = attrList.Attributes |> List.map (fun a -> { a with AppliesToGetterAndSetter = true } ) }) + let mSet = rhs parseState 3 + let mEquals = rhs parseState 6 + let m = rhs2 parseState 1 7 + let trivia: SynMemberDefnPropertyInfoTrivia = { GetSetRange = mSet; EqualsRange = mEquals } + { IsInline = $1 + Attributes = attrs + IsWrite = getOrSet = "set" + Pattern = $4 + ReturnInfo = $5 + Expression = $7 + Range = m + Trivia = trivia } } /* The core of a member definition */ @@ -1867,203 +1900,33 @@ memberCore: let memberRange = unionRanges rangeStart mRhs |> unionRangeWithXmlDoc xmlDoc [ SynMemberDefn.Member (binding, memberRange) ]) } - /* Properties with explicit get/set, also indexer properties */ + /* Properties with explicit get and set, also indexer properties */ | opt_inline bindingPattern opt_topReturnTypeWithTypeConstraints classDefnMemberGetSet - { let mWith, (classDefnMemberGetSetElements, mAnd) = $4 - let mWhole = (rhs parseState 2, classDefnMemberGetSetElements) ||> unionRangeWithListBy (fun (_, _, _, _, _, _, m2) -> m2) - let propertyNameBindingPat, _ = $2 - let optPropertyType = $3 - let isMutable = false - (fun visNoLongerUsed memFlagsBuilder attrs rangeStart -> - let mutable hasGet = false - let mutable hasSet = false - let xmlDoc = grabXmlDocAtRangeStart(parseState, attrs, rangeStart) - - let tryMkSynMemberDefnMember - (withPropertyKeyword: PropertyKeyword option) - (optInline, (optAttrs: SynAttributeList list), (bindingPat, mBindLhs), optReturnType, mEquals, expr, mExpr) - = - let optInline = $1 || optInline - // optional attributes are only applied to getters and setters - // the "top level" attrs will be applied to both - let optAttrs = - optAttrs |> List.map (fun attrList -> - { attrList with Attributes = attrList.Attributes |> List.map (fun a -> { a with AppliesToGetterAndSetter = true } ) }) - - let attrs = attrs @ optAttrs - - let trivia: SynBindingTrivia = { LetKeyword = None; EqualsRange = mEquals } - let binding = mkSynBinding (xmlDoc, bindingPat) (visNoLongerUsed, optInline, isMutable, mBindLhs, DebugPointAtBinding.NoneAtInvisible, optReturnType, expr, mExpr, [], attrs, Some (memFlagsBuilder SynMemberKind.Member), trivia) - let (SynBinding (vis, _, isInline, _, attrs, doc, valSynData, pv, _, _, mBindLhs, spBind, _)) = binding - let memberKind = - let getset = - let rec go p = - match p with - | SynPat.LongIdent (longDotId=SynLongIdent([id], _, _)) -> id.idText - | SynPat.Named (SynIdent(nm, _), _, _, _) | SynPat.As (_, SynPat.Named (SynIdent(nm, _), _, _, _), _) -> nm.idText - | SynPat.Typed (p, _, _) -> go p - | SynPat.Attrib (p, _, _) -> go p - | _ -> raiseParseErrorAt mBindLhs (FSComp.SR.parsInvalidDeclarationSyntax()) - go pv - if getset = "get" then - if hasGet then - reportParseErrorAt mBindLhs (FSComp.SR.parsGetAndOrSetRequired()) - None - else - hasGet <- true - Some SynMemberKind.PropertyGet - else if getset = "set" then - if hasSet then - reportParseErrorAt mBindLhs (FSComp.SR.parsGetAndOrSetRequired()) - None - else - hasSet <- true - Some SynMemberKind.PropertySet - else - raiseParseErrorAt mBindLhs (FSComp.SR.parsGetAndOrSetRequired()) - - match memberKind with - | None -> None - | Some memberKind -> - - // REVIEW: It's hard not to ignore the optPropertyType type annotation for 'set' properties. To apply it, - // we should apply it to the last argument, but at this point we've already pushed the patterns that - // make up the arguments onto the RHS. So we just always give a warning. - - begin match optPropertyType with - | Some _ -> errorR(Error(FSComp.SR.parsTypeAnnotationsOnGetSet(), mBindLhs)) - | None -> () - end - - let optReturnType = - match (memberKind, optReturnType) with - | SynMemberKind.PropertySet, _ -> optReturnType - | _, None -> optPropertyType - | _ -> optReturnType - - // REDO with the correct member kind - let trivia: SynBindingTrivia = { LetKeyword = None; EqualsRange = mEquals } - let binding = mkSynBinding (PreXmlDoc.Empty, bindingPat) (vis, isInline, isMutable, mBindLhs, DebugPointAtBinding.NoneAtInvisible, optReturnType, expr, mExpr, [], attrs, Some(memFlagsBuilder memberKind), trivia) - - let (SynBinding (vis, _, isInline, _, attrs, doc, valSynData, pv, rhsRetInfo, rhsExpr, mBindLhs, spBind, trivia)) = binding - let mWholeBindLhs = (mBindLhs, attrs) ||> unionRangeWithListBy (fun (a: SynAttributeList) -> a.Range) - - let (SynValData(_, valSynInfo, _)) = valSynData - - // Setters have all arguments tupled in their internal TAST form, though they don't appear to be - // tupled from the syntax - let memFlags : SynMemberFlags = memFlagsBuilder memberKind - - let valSynInfo = - let adjustValueArg valueArg = - match valueArg with - | [_] -> valueArg - | _ -> SynInfo.unnamedTopArg - - match memberKind, valSynInfo, memFlags.IsInstance with - | SynMemberKind.PropertyGet, SynValInfo ([], _ret), false - | SynMemberKind.PropertyGet, SynValInfo ([_], _ret), true -> - raiseParseErrorAt mWholeBindLhs (FSComp.SR.parsGetterMustHaveAtLeastOneArgument()) - - | SynMemberKind.PropertyGet, SynValInfo (thisArg :: indexOrUnitArgs :: rest, ret), true -> - if not rest.IsEmpty then - reportParseErrorAt mWholeBindLhs (FSComp.SR.parsGetterAtMostOneArgument ()) - SynValInfo ([thisArg; indexOrUnitArgs], ret) - - | SynMemberKind.PropertyGet, SynValInfo (indexOrUnitArgs :: rest, ret), false -> - if not rest.IsEmpty then - reportParseErrorAt mWholeBindLhs (FSComp.SR.parsGetterAtMostOneArgument ()) - SynValInfo ([indexOrUnitArgs], ret) - - | SynMemberKind.PropertySet, SynValInfo ([thisArg;valueArg], ret), true -> - SynValInfo ([thisArg; adjustValueArg valueArg], ret) - - | SynMemberKind.PropertySet, SynValInfo (thisArg :: indexArgs :: valueArg :: rest, ret), true -> - if not rest.IsEmpty then - reportParseErrorAt mWholeBindLhs (FSComp.SR.parsSetterAtMostTwoArguments ()) - SynValInfo ([thisArg; indexArgs @ adjustValueArg valueArg], ret) - - | SynMemberKind.PropertySet, SynValInfo ([valueArg], ret), false -> - SynValInfo ([adjustValueArg valueArg], ret) - - | SynMemberKind.PropertySet, SynValInfo (indexArgs :: valueArg :: rest, ret), _ -> - if not rest.IsEmpty then - reportParseErrorAt mWholeBindLhs (FSComp.SR.parsSetterAtMostTwoArguments ()) - SynValInfo ([indexArgs @ adjustValueArg valueArg], ret) - - | _ -> - // should be unreachable, cover just in case - raiseParseErrorAt mWholeBindLhs (FSComp.SR.parsInvalidProperty ()) - - let valSynData = SynValData(Some(memFlags), valSynInfo, None) - - // Fold together the information from the first lambda pattern and the get/set binding - // This uses the 'this' variable from the first and the patterns for the get/set binding, - // replacing the get/set identifier. A little gross. - - let bindingPatAdjusted, xmlDocAdjusted = - - let trivia: SynBindingTrivia = { LetKeyword = None; EqualsRange = mEquals } - let bindingOuter = mkSynBinding (xmlDoc, propertyNameBindingPat) (vis, optInline, isMutable, mWholeBindLhs, spBind, optReturnType, expr, mExpr, [], attrs, Some(memFlagsBuilder SynMemberKind.Member), trivia) - - let (SynBinding (_, _, _, _, _, doc2, _, bindingPatOuter, _, _, _, _, _)) = bindingOuter - - let lidOuter, lidVisOuter = - match bindingPatOuter with - | SynPat.LongIdent (lid, _, None, None, SynArgPats.Pats [], lidVisOuter, m) -> lid, lidVisOuter - | SynPat.Named (SynIdent(id, _), _, visOuter, m) | SynPat.As(_, SynPat.Named (SynIdent(id, _), _, visOuter, m), _) -> SynLongIdent([id], [], [None]), visOuter - | p -> raiseParseErrorAt mWholeBindLhs (FSComp.SR.parsInvalidDeclarationSyntax()) - - // Merge the visibility from the outer point with the inner point, e.g. - // member this.Size with get () = m_size - - let mergeLidVisOuter lidVisInner = - match lidVisInner, lidVisOuter with - | None, None -> None - | Some lidVisInner, None | None, Some lidVisInner -> Some lidVisInner - | Some _, Some _ -> - errorR(Error(FSComp.SR.parsMultipleAccessibilitiesForGetSet(), mWholeBindLhs)) - lidVisInner - - // Replace the "get" or the "set" with the right name - let rec go p = - match p with - | SynPat.LongIdent (longDotId=SynLongIdent([id], _, _); typarDecls=tyargs; argPats=SynArgPats.Pats args; accessibility=lidVisInner; range=m) -> - // Setters have all arguments tupled in their internal form, though they don't - // appear to be tupled from the syntax. Somewhat unfortunate - let args = - if id.idText = "set" then - match args with - | [SynPat.Paren(SynPat.Tuple (false, indexPats, _), indexPatRange);valuePat] when id.idText = "set" -> - [SynPat.Tuple(false, indexPats@[valuePat], unionRanges indexPatRange valuePat.Range)] - | [indexPat;valuePat] -> - [SynPat.Tuple(false, args, unionRanges indexPat.Range valuePat.Range)] - | [valuePat] -> - [valuePat] - | _ -> - raiseParseErrorAt m (FSComp.SR.parsSetSyntax()) - else - args - SynPat.LongIdent (lidOuter, withPropertyKeyword, Some(id), tyargs, SynArgPats.Pats args, mergeLidVisOuter lidVisInner, m) - | SynPat.Named (_, _, lidVisInner, m) - | SynPat.As (_, SynPat.Named (_, _, lidVisInner, m), _) -> SynPat.LongIdent (lidOuter, None, None, None, SynArgPats.Pats [], mergeLidVisOuter lidVisInner, m) - | SynPat.Typed (p, ty, m) -> SynPat.Typed(go p, ty, m) - | SynPat.Attrib (p, attribs, m) -> SynPat.Attrib(go p, attribs, m) - | SynPat.Wild(m) -> SynPat.Wild(m) - | _ -> raiseParseErrorAt mWholeBindLhs (FSComp.SR.parsInvalidDeclarationSyntax()) - - go pv, PreXmlDoc.Merge doc2 doc - - let binding = SynBinding (vis, SynBindingKind.Normal, isInline, isMutable, attrs, xmlDocAdjusted, valSynData, bindingPatAdjusted, rhsRetInfo, rhsExpr, mWholeBindLhs, spBind, trivia) - let memberRange = unionRanges rangeStart mWhole |> unionRangeWithXmlDoc xmlDocAdjusted - Some (SynMemberDefn.Member (binding, memberRange)) - - // Iterate over 1 or 2 'get'/'set' entries - match classDefnMemberGetSetElements with - | [ h ] -> List.choose id [ tryMkSynMemberDefnMember (Some (PropertyKeyword.With mWith)) h ] - | [ g ; s ] -> List.choose id [ tryMkSynMemberDefnMember (Some (PropertyKeyword.With mWith)) g ; tryMkSynMemberDefnMember (Option.map PropertyKeyword.And mAnd) s ] - | _ -> []) - } + { let propertyNameBindingPat, mBindPattern = $2 + let mWith, classDefnMemberGetSetElement1, mAnd, classDefnMemberGetSetElement2 = $4 + let classDefnMemberGet = if classDefnMemberGetSetElement1.IsWrite then classDefnMemberGetSetElement2 else classDefnMemberGetSetElement1 + let classDefnMemberSet = if classDefnMemberGetSetElement2.IsWrite then classDefnMemberGetSetElement2 else classDefnMemberGetSetElement1 + let mWhole = unionRanges mBindPattern classDefnMemberGetSetElement2.Range + let trivia = { WithKeyword = mWith; AndKeyword = mAnd } + + (fun vis memFlagsBuilder attrs rangeStart -> + let xmlDoc = grabXmlDocAtRangeStart(parseState, attrs, rangeStart) + let memberRange = unionRanges rangeStart mWhole |> unionRangeWithXmlDoc xmlDoc + [ SynMemberDefn.ReadWriteMember(vis, attrs, memFlagsBuilder, xmlDoc, propertyNameBindingPat, $3, classDefnMemberGet, classDefnMemberSet, memberRange, trivia) ]) } + + /* Properties with explicit get or set */ + | opt_inline bindingPattern opt_topReturnTypeWithTypeConstraints classDefnMemberGet + { let propertyNameBindingPat, mBindPattern = $2 + let mWith, memberDefnPropertyInfo = $4 + + mkSynMemberGetOrSet + (fun attrs rangeStart -> grabXmlDocAtRangeStart (parseState, attrs, rangeStart)) + $1 + propertyNameBindingPat + mBindPattern + mWith + memberDefnPropertyInfo + } abstractMemberFlags: diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected b/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected index 6b3cb7e4f8..7623343624 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected @@ -7423,6 +7423,26 @@ FSharp.Compiler.Syntax.SynMemberDefn+Open: FSharp.Compiler.Syntax.SynOpenDeclTar FSharp.Compiler.Syntax.SynMemberDefn+Open: FSharp.Compiler.Syntax.SynOpenDeclTarget target FSharp.Compiler.Syntax.SynMemberDefn+Open: FSharp.Compiler.Text.Range get_range() FSharp.Compiler.Syntax.SynMemberDefn+Open: FSharp.Compiler.Text.Range range +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo get_read() +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo get_write() +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo read +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo write +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: FSharp.Compiler.Syntax.SynPat get_identifier() +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: FSharp.Compiler.Syntax.SynPat identifier +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: FSharp.Compiler.SyntaxTrivia.SynMemberDefnReadWriteMemberTrivia get_trivia() +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: FSharp.Compiler.SyntaxTrivia.SynMemberDefnReadWriteMemberTrivia trivia +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: FSharp.Compiler.Text.Range get_range() +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: FSharp.Compiler.Text.Range range +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: FSharp.Compiler.Xml.PreXmlDoc get_xmlDoc() +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: FSharp.Compiler.Xml.PreXmlDoc xmlDoc +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynAttributeList] attributes +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynAttributeList] get_attributes() +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SynMemberKind,FSharp.Compiler.Syntax.SynMemberFlags] get_memberFlags() +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SynMemberKind,FSharp.Compiler.Syntax.SynMemberFlags] memberFlags +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynAccess] accessibility +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynAccess] get_accessibility() +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynReturnInfo] get_returnTypeOpt() +FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynReturnInfo] returnTypeOpt FSharp.Compiler.Syntax.SynMemberDefn+Tags: Int32 AbstractSlot FSharp.Compiler.Syntax.SynMemberDefn+Tags: Int32 AutoProperty FSharp.Compiler.Syntax.SynMemberDefn+Tags: Int32 ImplicitCtor @@ -7433,6 +7453,7 @@ FSharp.Compiler.Syntax.SynMemberDefn+Tags: Int32 LetBindings FSharp.Compiler.Syntax.SynMemberDefn+Tags: Int32 Member FSharp.Compiler.Syntax.SynMemberDefn+Tags: Int32 NestedType FSharp.Compiler.Syntax.SynMemberDefn+Tags: Int32 Open +FSharp.Compiler.Syntax.SynMemberDefn+Tags: Int32 ReadWriteMember FSharp.Compiler.Syntax.SynMemberDefn+Tags: Int32 ValField FSharp.Compiler.Syntax.SynMemberDefn+ValField: FSharp.Compiler.Syntax.SynField fieldInfo FSharp.Compiler.Syntax.SynMemberDefn+ValField: FSharp.Compiler.Syntax.SynField get_fieldInfo() @@ -7448,6 +7469,7 @@ FSharp.Compiler.Syntax.SynMemberDefn: Boolean IsLetBindings FSharp.Compiler.Syntax.SynMemberDefn: Boolean IsMember FSharp.Compiler.Syntax.SynMemberDefn: Boolean IsNestedType FSharp.Compiler.Syntax.SynMemberDefn: Boolean IsOpen +FSharp.Compiler.Syntax.SynMemberDefn: Boolean IsReadWriteMember FSharp.Compiler.Syntax.SynMemberDefn: Boolean IsValField FSharp.Compiler.Syntax.SynMemberDefn: Boolean get_IsAbstractSlot() FSharp.Compiler.Syntax.SynMemberDefn: Boolean get_IsAutoProperty() @@ -7459,6 +7481,7 @@ FSharp.Compiler.Syntax.SynMemberDefn: Boolean get_IsLetBindings() FSharp.Compiler.Syntax.SynMemberDefn: Boolean get_IsMember() FSharp.Compiler.Syntax.SynMemberDefn: Boolean get_IsNestedType() FSharp.Compiler.Syntax.SynMemberDefn: Boolean get_IsOpen() +FSharp.Compiler.Syntax.SynMemberDefn: Boolean get_IsReadWriteMember() FSharp.Compiler.Syntax.SynMemberDefn: Boolean get_IsValField() FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewAbstractSlot(FSharp.Compiler.Syntax.SynValSig, FSharp.Compiler.Syntax.SynMemberFlags, FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewAutoProperty(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynAttributeList], Boolean, FSharp.Compiler.Syntax.Ident, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynType], FSharp.Compiler.Syntax.SynMemberKind, Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SynMemberKind,FSharp.Compiler.Syntax.SynMemberFlags], FSharp.Compiler.Xml.PreXmlDoc, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynAccess], FSharp.Compiler.Text.Range, FSharp.Compiler.Syntax.SynExpr, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range], FSharp.Compiler.Text.Range) @@ -7470,6 +7493,7 @@ FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewLe FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewMember(FSharp.Compiler.Syntax.SynBinding, FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewNestedType(FSharp.Compiler.Syntax.SynTypeDefn, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynAccess], FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewOpen(FSharp.Compiler.Syntax.SynOpenDeclTarget, FSharp.Compiler.Text.Range) +FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewReadWriteMember(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynAccess], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynAttributeList], Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SynMemberKind,FSharp.Compiler.Syntax.SynMemberFlags], FSharp.Compiler.Xml.PreXmlDoc, FSharp.Compiler.Syntax.SynPat, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynReturnInfo], FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo, FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo, FSharp.Compiler.Text.Range, FSharp.Compiler.SyntaxTrivia.SynMemberDefnReadWriteMemberTrivia) FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewValField(FSharp.Compiler.Syntax.SynField, FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn+AbstractSlot FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn+AutoProperty @@ -7481,6 +7505,7 @@ FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn+LetBi FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn+Member FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn+NestedType FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn+Open +FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn+ReadWriteMember FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn+Tags FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn+ValField FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Text.Range Range @@ -7488,6 +7513,25 @@ FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Text.Range get_Range() FSharp.Compiler.Syntax.SynMemberDefn: Int32 Tag FSharp.Compiler.Syntax.SynMemberDefn: Int32 get_Tag() FSharp.Compiler.Syntax.SynMemberDefn: System.String ToString() +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: Boolean IsInline +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: Boolean IsWrite +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: Boolean get_IsInline() +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: Boolean get_IsWrite() +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: FSharp.Compiler.Syntax.SynExpr Expression +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: FSharp.Compiler.Syntax.SynExpr get_Expression() +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: FSharp.Compiler.Syntax.SynPat Pattern +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: FSharp.Compiler.Syntax.SynPat get_Pattern() +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: FSharp.Compiler.SyntaxTrivia.SynMemberDefnPropertyInfoTrivia Trivia +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: FSharp.Compiler.SyntaxTrivia.SynMemberDefnPropertyInfoTrivia get_Trivia() +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: FSharp.Compiler.Text.Range Range +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: FSharp.Compiler.Text.Range get_Range() +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynAttributeList] Attributes +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynAttributeList] get_Attributes() +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynReturnInfo] ReturnInfo +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynReturnInfo] get_ReturnInfo() +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: System.String ToString() +FSharp.Compiler.Syntax.SynMemberDefnPropertyInfo: Void .ctor(Boolean, Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynAttributeList], Boolean, FSharp.Compiler.Syntax.SynPat, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynReturnInfo], FSharp.Compiler.Syntax.SynExpr, FSharp.Compiler.Text.Range, FSharp.Compiler.SyntaxTrivia.SynMemberDefnPropertyInfoTrivia) FSharp.Compiler.Syntax.SynMemberFlags FSharp.Compiler.Syntax.SynMemberFlags: Boolean Equals(System.Object) FSharp.Compiler.Syntax.SynMemberFlags: Boolean IsDispatchSlot @@ -9391,6 +9435,20 @@ FSharp.Compiler.SyntaxTrivia.SynMatchClauseTrivia: Microsoft.FSharp.Core.FSharpO FSharp.Compiler.SyntaxTrivia.SynMatchClauseTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] get_BarRange() FSharp.Compiler.SyntaxTrivia.SynMatchClauseTrivia: System.String ToString() FSharp.Compiler.SyntaxTrivia.SynMatchClauseTrivia: Void .ctor(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range]) +FSharp.Compiler.SyntaxTrivia.SynMemberDefnPropertyInfoTrivia +FSharp.Compiler.SyntaxTrivia.SynMemberDefnPropertyInfoTrivia: FSharp.Compiler.Text.Range EqualsRange +FSharp.Compiler.SyntaxTrivia.SynMemberDefnPropertyInfoTrivia: FSharp.Compiler.Text.Range GetSetRange +FSharp.Compiler.SyntaxTrivia.SynMemberDefnPropertyInfoTrivia: FSharp.Compiler.Text.Range get_EqualsRange() +FSharp.Compiler.SyntaxTrivia.SynMemberDefnPropertyInfoTrivia: FSharp.Compiler.Text.Range get_GetSetRange() +FSharp.Compiler.SyntaxTrivia.SynMemberDefnPropertyInfoTrivia: System.String ToString() +FSharp.Compiler.SyntaxTrivia.SynMemberDefnPropertyInfoTrivia: Void .ctor(FSharp.Compiler.Text.Range, FSharp.Compiler.Text.Range) +FSharp.Compiler.SyntaxTrivia.SynMemberDefnReadWriteMemberTrivia +FSharp.Compiler.SyntaxTrivia.SynMemberDefnReadWriteMemberTrivia: FSharp.Compiler.Text.Range AndKeyword +FSharp.Compiler.SyntaxTrivia.SynMemberDefnReadWriteMemberTrivia: FSharp.Compiler.Text.Range WithKeyword +FSharp.Compiler.SyntaxTrivia.SynMemberDefnReadWriteMemberTrivia: FSharp.Compiler.Text.Range get_AndKeyword() +FSharp.Compiler.SyntaxTrivia.SynMemberDefnReadWriteMemberTrivia: FSharp.Compiler.Text.Range get_WithKeyword() +FSharp.Compiler.SyntaxTrivia.SynMemberDefnReadWriteMemberTrivia: System.String ToString() +FSharp.Compiler.SyntaxTrivia.SynMemberDefnReadWriteMemberTrivia: Void .ctor(FSharp.Compiler.Text.Range, FSharp.Compiler.Text.Range) FSharp.Compiler.SyntaxTrivia.SynMemberFlagsTrivia FSharp.Compiler.SyntaxTrivia.SynMemberFlagsTrivia: FSharp.Compiler.SyntaxTrivia.SynMemberFlagsTrivia Zero FSharp.Compiler.SyntaxTrivia.SynMemberFlagsTrivia: FSharp.Compiler.SyntaxTrivia.SynMemberFlagsTrivia get_Zero() diff --git a/tests/service/Symbols.fs b/tests/service/Symbols.fs index 059646cd92..36922119f0 100644 --- a/tests/service/Symbols.fs +++ b/tests/service/Symbols.fs @@ -666,7 +666,7 @@ type Foo() = ) ]) ]) ])) -> assertRange (4, 31) (4, 35) mWith - | _ -> Assert.Fail "Could not get valid AST" + | _ -> Assert.Fail $"Could not get valid AST, got {parseResults}" [] let ``write-only property in SynMemberDefn.Member contains the range of the with keyword`` () = @@ -687,7 +687,7 @@ type Foo() = ) ]) ]) ])) -> assertRange (4, 36) (4, 40) mWith - | _ -> Assert.Fail "Could not get valid AST" + | _ -> Assert.Fail $"Could not get valid AST, got {parseResults}" [] let ``read/write property in SynMemberDefn.Member contains the range of the with keyword`` () = @@ -705,14 +705,36 @@ type Foo() = | ParsedInput.ImplFile (ParsedImplFileInput (modules = [ SynModuleOrNamespace.SynModuleOrNamespace(decls = [ SynModuleDecl.Types( typeDefns = [ SynTypeDefn(typeRepr = - SynTypeDefnRepr.ObjectModel(members=[ _ - SynMemberDefn.Member(memberDefn=SynBinding(headPat=SynPat.LongIdent(propertyKeyword=Some(PropertyKeyword.With mWith)))) - SynMemberDefn.Member(memberDefn=SynBinding(headPat=SynPat.LongIdent(propertyKeyword=Some(PropertyKeyword.And mAnd)))) ]) - ) ]) - ]) ])) -> + SynTypeDefnRepr.ObjectModel(members = [ + SynMemberDefn.ImplicitCtor _ + SynMemberDefn.ReadWriteMember( + None, [], _, _, + SynPat.LongIdent(longDotId = SynLongIdent _), None, + { Pattern = SynPat.Const(constant = SynConst.Unit) + Expression = SynExpr.Ident _ + Range = mGetter + Trivia = { GetSetRange = mGet; EqualsRange = mReadEquals } }, + { Pattern = SynPat.Paren(pat = SynPat.Named _) + Expression = SynExpr.LongIdentSet _ + Range = mSetter + Trivia = { GetSetRange = mSet; EqualsRange = mWriteEquals } }, + mMember, + { WithKeyword = mWith; AndKeyword = mAnd } + ) + ])) + ]) ]) ])) -> + assertRange (5, 8) (5, 12) mWith + assertRange (5, 13) (5, 16) mGet + assertRange (5, 20) (5, 21) mReadEquals + assertRange (5, 13) (5, 37) mGetter assertRange (6, 8) (6, 11) mAnd - | _ -> Assert.Fail "Could not get valid AST" + assertRange (6, 12) (6, 15) mSet + assertRange (6, 24) (6, 25) mWriteEquals + assertRange (6, 12) (6, 50) mSetter + assertRange (4, 4) (6, 50) mMember + + | _ -> Assert.Fail $"Could not get valid AST, got {parseResults}" [] let ``SynTypeDefn with XmlDoc contains the range of the type keyword`` () = From 51529b3aa821c3a9a55f408510e84cdfaad66846 Mon Sep 17 00:00:00 2001 From: nojaf Date: Tue, 14 Jun 2022 15:38:23 +0200 Subject: [PATCH 2/3] Use `bindingPattern` again to capture the pattern in classDefnMemberGetSetElement. --- src/Compiler/SyntaxTree/SyntaxTreeOps.fs | 25 +++++++++++++++ src/Compiler/SyntaxTree/SyntaxTreeOps.fsi | 2 ++ src/Compiler/pars.fsy | 38 +++++++++++------------ tests/service/Symbols.fs | 21 +++++++++++++ 4 files changed, 66 insertions(+), 20 deletions(-) diff --git a/src/Compiler/SyntaxTree/SyntaxTreeOps.fs b/src/Compiler/SyntaxTree/SyntaxTreeOps.fs index 18be2a7f22..6c33ae8763 100644 --- a/src/Compiler/SyntaxTree/SyntaxTreeOps.fs +++ b/src/Compiler/SyntaxTree/SyntaxTreeOps.fs @@ -1048,3 +1048,28 @@ let mkSynMemberGetOrSet |> unionRangeWithXmlDoc xmlDoc [ SynMemberDefn.Member(binding, memberRange) ] + +let (|GetOrSetPattern|GetOrSetWithoutArgument|NoGetSetWord|) (pat: SynPat) : Choice<(string * SynPat), string, unit> = + let isGetOrSet v = + if v = "get" || v = "set" then Some v else None + + let stripParensFromUnit p = + match p with + | SynPat.Paren(pat = SynPat.Const(constant = SynConst.Unit) as patUnit) -> patUnit + | _ -> p + + let rec visit p = + match p with + | SynPat.LongIdent (longDotId = SynLongIdent ([ id ], _, _); argPats = SynArgPats.Pats [ singleArg ]) -> + isGetOrSet id.idText, Some(stripParensFromUnit singleArg) + | SynPat.Named (SynIdent (nm, _), _, _, _) + | SynPat.As (_, SynPat.Named (SynIdent (nm, _), _, _, _), _) -> isGetOrSet nm.idText, None + | SynPat.Typed (p, _, _) -> visit p + | SynPat.Attrib (p, _, _) -> visit p + | _ -> None, None + + match visit pat with + | None, None -> NoGetSetWord + | Some v, None -> GetOrSetWithoutArgument v + | None, Some _ -> NoGetSetWord + | Some getOrSet, Some arg -> GetOrSetPattern(getOrSet, arg) diff --git a/src/Compiler/SyntaxTree/SyntaxTreeOps.fsi b/src/Compiler/SyntaxTree/SyntaxTreeOps.fsi index f7ed679ef6..cde0f3c2ed 100644 --- a/src/Compiler/SyntaxTree/SyntaxTreeOps.fsi +++ b/src/Compiler/SyntaxTree/SyntaxTreeOps.fsi @@ -349,3 +349,5 @@ val mkSynMemberGetOrSet: mWith: range -> memberDefnPropertyInfo: SynMemberDefnPropertyInfo -> (SynAccess option -> (SynMemberKind -> SynMemberFlags) -> SynAttributeList list -> range -> SynMemberDefn list) + +val (|GetOrSetPattern|GetOrSetWithoutArgument|NoGetSetWord|): pat: SynPat -> Choice<(string * SynPat), string, unit> diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index 8c758f9bc7..533c2237d3 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -1853,32 +1853,30 @@ classDefnMemberGet: reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsUnmatchedWith()) mWithKwd, $2 } -classDefnMemberGetSetPattern: - | LPAREN rparen - { let m = rhs2 parseState 1 2 - SynPat.Const(SynConst.Unit, m) } - - | atomicPattern - { $1 } - classDefnMemberGetSetElement: - | opt_inline opt_attributes ident classDefnMemberGetSetPattern opt_topReturnTypeWithTypeConstraints EQUALS typedSequentialExprBlock - { let mSet = rhs parseState 3 - let getOrSet = $3.idText - printfn "getOrSet %s" getOrSet - if getOrSet <> "get" && getOrSet <> "set" then raiseParseErrorAt mSet (FSComp.SR.parsGetAndOrSetRequired()) else + | opt_inline opt_attributes bindingPattern opt_topReturnTypeWithTypeConstraints EQUALS typedSequentialExprBlock + { let mGetSet = + let mPat = rhs parseState 3 + mkFileIndexRange mPat.FileIndex mPat.Start (mkPos mPat.StartLine (mPat.StartColumn + 3)) + + match fst $3 with + | NoGetSetWord -> + raiseParseErrorAt mGetSet (FSComp.SR.parsGetAndOrSetRequired()) + | GetOrSetWithoutArgument gs -> + // TODO: handle correctly + raiseParseErrorAt mGetSet (FSComp.SR.parsGetAndOrSetRequired()) + | GetOrSetPattern (getOrSet, pat) -> let attrs = $2 |> List.map (fun attrList -> { attrList with Attributes = attrList.Attributes |> List.map (fun a -> { a with AppliesToGetterAndSetter = true } ) }) - let mSet = rhs parseState 3 - let mEquals = rhs parseState 6 - let m = rhs2 parseState 1 7 - let trivia: SynMemberDefnPropertyInfoTrivia = { GetSetRange = mSet; EqualsRange = mEquals } + let mEquals = rhs parseState 4 + let m = rhs2 parseState 1 6 + let trivia: SynMemberDefnPropertyInfoTrivia = { GetSetRange = mGetSet; EqualsRange = mEquals } { IsInline = $1 Attributes = attrs IsWrite = getOrSet = "set" - Pattern = $4 - ReturnInfo = $5 - Expression = $7 + Pattern = pat + ReturnInfo = $4 + Expression = $6 Range = m Trivia = trivia } } diff --git a/tests/service/Symbols.fs b/tests/service/Symbols.fs index 36922119f0..6e177e580b 100644 --- a/tests/service/Symbols.fs +++ b/tests/service/Symbols.fs @@ -668,6 +668,27 @@ type Foo() = assertRange (4, 31) (4, 35) mWith | _ -> Assert.Fail $"Could not get valid AST, got {parseResults}" + [] + let ``read-only property in SynMemberDefn.Member where get and unit are adjacent`` () = + let parseResults = + getParseResults + """ +type Foo() = + // notice there is no space between `get` and `()` + member this.MyReadProperty with get() = myInternalValue +""" + + match parseResults with + | ParsedInput.ImplFile (ParsedImplFileInput (modules = [ SynModuleOrNamespace.SynModuleOrNamespace(decls = [ + SynModuleDecl.Types( + typeDefns = [ SynTypeDefn(typeRepr = + SynTypeDefnRepr.ObjectModel(members=[ _ + SynMemberDefn.Member(memberDefn=SynBinding(headPat=SynPat.LongIdent(propertyKeyword=Some(PropertyKeyword.With mWith)))) ]) + ) ]) + ]) ])) -> + assertRange (4, 31) (4, 35) mWith + | _ -> Assert.Fail $"Could not get valid AST, got {parseResults}" + [] let ``write-only property in SynMemberDefn.Member contains the range of the with keyword`` () = let parseResults = From 038a1ce1bee5378cbb2d0871e924c0515e5c36c4 Mon Sep 17 00:00:00 2001 From: nojaf Date: Tue, 14 Jun 2022 15:45:53 +0200 Subject: [PATCH 3/3] Create range of SynMemberDefnPropertyInfo by joining pattern and expression. --- src/Compiler/pars.fsy | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index 533c2237d3..18d39696b3 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -1859,7 +1859,9 @@ classDefnMemberGetSetElement: let mPat = rhs parseState 3 mkFileIndexRange mPat.FileIndex mPat.Start (mkPos mPat.StartLine (mPat.StartColumn + 3)) - match fst $3 with + let pat, mPat = $3 + + match pat with | NoGetSetWord -> raiseParseErrorAt mGetSet (FSComp.SR.parsGetAndOrSetRequired()) | GetOrSetWithoutArgument gs -> @@ -1869,7 +1871,7 @@ classDefnMemberGetSetElement: let attrs = $2 |> List.map (fun attrList -> { attrList with Attributes = attrList.Attributes |> List.map (fun a -> { a with AppliesToGetterAndSetter = true } ) }) let mEquals = rhs parseState 4 - let m = rhs2 parseState 1 6 + let m = unionRanges mPat $6.Range let trivia: SynMemberDefnPropertyInfoTrivia = { GetSetRange = mGetSet; EqualsRange = mEquals } { IsInline = $1 Attributes = attrs