diff --git a/src/Compiler/SyntaxTree/ParseHelpers.fs b/src/Compiler/SyntaxTree/ParseHelpers.fs index f3e3fcdfdaf..6aa973d275e 100644 --- a/src/Compiler/SyntaxTree/ParseHelpers.fs +++ b/src/Compiler/SyntaxTree/ParseHelpers.fs @@ -468,6 +468,7 @@ let mkSynMemberDefnGetSet { LetKeyword = None EqualsRange = mEquals + ExternKeyword = None } let binding = @@ -542,6 +543,7 @@ let mkSynMemberDefnGetSet { LetKeyword = None EqualsRange = mEquals + ExternKeyword = None } let binding = @@ -629,6 +631,7 @@ let mkSynMemberDefnGetSet { LetKeyword = None EqualsRange = mEquals + ExternKeyword = None } let bindingOuter = diff --git a/src/Compiler/SyntaxTree/SyntaxTrivia.fs b/src/Compiler/SyntaxTree/SyntaxTrivia.fs index e4b58c85c00..fa4ebc784c8 100644 --- a/src/Compiler/SyntaxTree/SyntaxTrivia.fs +++ b/src/Compiler/SyntaxTree/SyntaxTrivia.fs @@ -159,12 +159,14 @@ type SynTypeDefnSigTrivia = type SynBindingTrivia = { LetKeyword: range option + ExternKeyword: range option EqualsRange: range option } static member Zero: SynBindingTrivia = { LetKeyword = None + ExternKeyword = None EqualsRange = None } diff --git a/src/Compiler/SyntaxTree/SyntaxTrivia.fsi b/src/Compiler/SyntaxTree/SyntaxTrivia.fsi index 3e0dea3db0b..b6f0532d73c 100644 --- a/src/Compiler/SyntaxTree/SyntaxTrivia.fsi +++ b/src/Compiler/SyntaxTree/SyntaxTrivia.fsi @@ -246,6 +246,9 @@ type SynBindingTrivia = /// The syntax range of the `let` keyword. LetKeyword: range option + /// The syntax range of the `extern` keyword. + ExternKeyword: range option + /// The syntax range of the `=` token. EqualsRange: range option } diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index 94df75aada1..b732754d7fc 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -1868,7 +1868,7 @@ memberCore: let xmlDoc = grabXmlDocAtRangeStart(parseState, attrs, rangeStart) let memberFlags = Some (memFlagsBuilder SynMemberKind.Member) let mWholeBindLhs = (mBindLhs, attrs) ||> unionRangeWithListBy (fun (a: SynAttributeList) -> a.Range) - let trivia: SynBindingTrivia = { LetKeyword = None; EqualsRange = Some mEquals } + let trivia: SynBindingTrivia = { LetKeyword = None; EqualsRange = Some mEquals; ExternKeyword = None } let binding = mkSynBinding (xmlDoc, bindingPat) (vis, $1, false, mWholeBindLhs, DebugPointAtBinding.NoneAtInvisible, optReturnType, $5, mRhs, [], attrs, memberFlags, trivia) let memberRange = unionRanges rangeStart mRhs |> unionRangeWithXmlDoc xmlDoc [ SynMemberDefn.Member (binding, memberRange) ]) } @@ -1985,7 +1985,7 @@ classDefnMember: let declPat = SynPat.LongIdent (SynLongIdent([mkSynId (rhs parseState 3) "new"], [], [None]), None, Some noInferredTypars, SynArgPats.Pats [$4], vis, rhs parseState 3) // Check that 'SynPatForConstructorDecl' matches this correctly assert (match declPat with SynPatForConstructorDecl _ -> true | _ -> false) - let synBindingTrivia: SynBindingTrivia = { LetKeyword = None; EqualsRange = Some mEquals } + let synBindingTrivia: SynBindingTrivia = { LetKeyword = None; EqualsRange = Some mEquals; ExternKeyword = None } [ SynMemberDefn.Member(SynBinding (None, SynBindingKind.Normal, false, false, $1, xmlDoc, valSynData, declPat, None, expr, mWholeBindLhs, DebugPointAtBinding.NoneAtInvisible, synBindingTrivia), m) ] } | opt_attributes opt_declVisibility STATIC typeKeyword tyconDefn @@ -2743,7 +2743,8 @@ hardwhiteDefnBindingsTerminator: /* An 'extern' DllImport function definition in C-style syntax */ cPrototype: | EXTERN cRetType opt_access ident opt_HIGH_PRECEDENCE_APP LPAREN externArgs rparen - { let rty, vis, nm, args = $2, $3, $4, $7 + { let mExtern = rhs parseState 1 + let rty, vis, nm, args = $2, $3, $4, $7 let nmm = rhs parseState 3 let argsm = rhs parseState 6 let mBindLhs = lhs parseState @@ -2760,10 +2761,11 @@ cPrototype: let bindingPat = SynPat.LongIdent (SynLongIdent([nm], [], [None]), None, Some noInferredTypars, SynArgPats.Pats [SynPat.Tuple(false, args, argsm)], vis, nmm) let mWholeBindLhs = (mBindLhs, attrs) ||> unionRangeWithListBy (fun (a: SynAttributeList) -> a.Range) let xmlDoc = grabXmlDoc(parseState, attrs, 1) + let trivia = { LetKeyword = None; ExternKeyword = Some mExtern; EqualsRange = None } let binding = mkSynBinding (xmlDoc, bindingPat) - (vis, false, false, mWholeBindLhs, DebugPointAtBinding.NoneAtInvisible, Some rty, rhsExpr, mRhs, [], attrs, None, SynBindingTrivia.Zero) + (vis, false, false, mWholeBindLhs, DebugPointAtBinding.NoneAtInvisible, Some rty, rhsExpr, mRhs, [], attrs, None, trivia) [], [binding]) } /* A list of arguments in an 'extern' DllImport function definition */ @@ -2882,7 +2884,7 @@ localBinding: let mWhole = (unionRanges mLetKwd mRhs, attrs) ||> unionRangeWithListBy (fun (a: SynAttributeList) -> a.Range) let spBind = if IsDebugPointBinding bindingPat expr then DebugPointAtBinding.Yes mWhole else DebugPointAtBinding.NoneAtLet let mWholeBindLhs = (mBindLhs, attrs) ||> unionRangeWithListBy (fun (a: SynAttributeList) -> a.Range) - let trivia: SynBindingTrivia = { LetKeyword = Some mLetKwd; EqualsRange = Some mEquals } + let trivia: SynBindingTrivia = { LetKeyword = Some mLetKwd; EqualsRange = Some mEquals; ExternKeyword = None } mkSynBinding (xmlDoc, bindingPat) (vis, $1, $2, mWholeBindLhs, spBind, optReturnType, expr, mRhs, opts, attrs, None, trivia)) localBindingRange, localBindingBuilder } @@ -2897,7 +2899,7 @@ localBinding: let zeroWidthAtEnd = mEquals.EndRange let rhsExpr = arbExpr("localBinding1", zeroWidthAtEnd) let spBind = if IsDebugPointBinding bindingPat rhsExpr then DebugPointAtBinding.Yes mWhole else DebugPointAtBinding.NoneAtLet - let trivia: SynBindingTrivia = { LetKeyword = Some mLetKwd; EqualsRange = Some mEquals } + let trivia: SynBindingTrivia = { LetKeyword = Some mLetKwd; EqualsRange = Some mEquals; ExternKeyword = None } mkSynBinding (xmlDoc, bindingPat) (vis, $1, $2, mBindLhs, spBind, optReturnType, rhsExpr, mRhs, [], attrs, None, trivia)) mWhole, localBindingBuilder } @@ -2910,7 +2912,7 @@ localBinding: let localBindingBuilder = (fun xmlDoc attrs vis mLetKwd -> let spBind = DebugPointAtBinding.Yes (unionRanges mLetKwd mRhs) - let trivia = { LetKeyword = Some mLetKwd; EqualsRange = None } + let trivia = { LetKeyword = Some mLetKwd; EqualsRange = None; ExternKeyword = None } let rhsExpr = arbExpr("localBinding2", mRhs) mkSynBinding (xmlDoc, bindingPat) (vis, $1, $2, mBindLhs, spBind, optReturnType, rhsExpr, mRhs, [], attrs, None, trivia)) mWhole, localBindingBuilder } diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj index d6db862529d..202bd49532e 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj @@ -122,6 +122,9 @@ SyntaxTree\AttributeTests.fs + + SyntaxTree\ExternTests.fs + FileSystemTests.fs 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 a6713fdbc26..1d2367fb0a6 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected @@ -9419,11 +9419,13 @@ FSharp.Compiler.SyntaxTrivia.SynBindingTrivia FSharp.Compiler.SyntaxTrivia.SynBindingTrivia: FSharp.Compiler.SyntaxTrivia.SynBindingTrivia Zero FSharp.Compiler.SyntaxTrivia.SynBindingTrivia: FSharp.Compiler.SyntaxTrivia.SynBindingTrivia get_Zero() FSharp.Compiler.SyntaxTrivia.SynBindingTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] EqualsRange +FSharp.Compiler.SyntaxTrivia.SynBindingTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] ExternKeyword FSharp.Compiler.SyntaxTrivia.SynBindingTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] LetKeyword FSharp.Compiler.SyntaxTrivia.SynBindingTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] get_EqualsRange() +FSharp.Compiler.SyntaxTrivia.SynBindingTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] get_ExternKeyword() FSharp.Compiler.SyntaxTrivia.SynBindingTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] get_LetKeyword() FSharp.Compiler.SyntaxTrivia.SynBindingTrivia: System.String ToString() -FSharp.Compiler.SyntaxTrivia.SynBindingTrivia: Void .ctor(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range]) +FSharp.Compiler.SyntaxTrivia.SynBindingTrivia: Void .ctor(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range]) FSharp.Compiler.SyntaxTrivia.SynEnumCaseTrivia FSharp.Compiler.SyntaxTrivia.SynEnumCaseTrivia: FSharp.Compiler.Text.Range EqualsRange FSharp.Compiler.SyntaxTrivia.SynEnumCaseTrivia: FSharp.Compiler.Text.Range get_EqualsRange() diff --git a/tests/service/SyntaxTreeTests/ExternTests.fs b/tests/service/SyntaxTreeTests/ExternTests.fs new file mode 100644 index 00000000000..0599307d994 --- /dev/null +++ b/tests/service/SyntaxTreeTests/ExternTests.fs @@ -0,0 +1,21 @@ +module FSharp.Compiler.Service.Tests.SyntaxTreeTests.ExternTests + +open FSharp.Compiler.Service.Tests.Common +open FSharp.Compiler.Syntax +open FSharp.Compiler.SyntaxTrivia +open NUnit.Framework + +[] +let ``extern keyword is present in trivia`` () = + let parseResults = getParseResults "extern void GetProcessHeap()" + + match parseResults with + | ParsedInput.ImplFile(ParsedImplFileInput(contents = [ + SynModuleOrNamespace.SynModuleOrNamespace(decls = [ + SynModuleDecl.Let(bindings = [ + SynBinding(trivia = { ExternKeyword = Some mExtern }) + ]) + ]) + ])) -> + assertRange (1, 0) (1, 6) mExtern + | _ -> Assert.Fail $"Could not get valid AST, got {parseResults}"