From d984929551f52d9689021dc3ed5481daaa798219 Mon Sep 17 00:00:00 2001 From: Petr Date: Thu, 15 Jun 2023 15:05:59 +0200 Subject: [PATCH 1/7] Democratizing code fixes - part two (#15398) --- .../CodeFix/AddInstanceMemberParameter.fs | 24 ++-- .../AddMissingRecToMutuallyRecFunctions.fs | 85 +++++++------- .../FSharp.Editor/CodeFix/ChangeToUpcast.fs | 64 +++++------ .../FSharp.Editor/CodeFix/CodeFixHelpers.fs | 12 +- .../CodeFix/ConvertToAnonymousRecord.fs | 32 +++--- .../FSharp.Editor/CodeFix/IFSharpCodeFix.fs | 11 +- .../CodeFix/RemoveReturnOrYield.fs | 56 ++++----- .../CodeFix/WrapExpressionInParentheses.fs | 33 +++--- .../AddInstanceMemberParameterTests.fs | 13 ++- ...ddMissingRecToMutuallyRecFunctionsTests.fs | 50 +++++++++ .../CodeFixes/ChangeToUpcastTests.fs | 43 +++++-- .../CodeFixes/CodeFixTestFramework.fs | 17 +-- .../ConvertToAnonymousRecordTests.fs | 30 +++-- ...ExpressionInParenthesesFixProviderTests.fs | 39 +++++++ .../CodeFixes/RemoveReturnOrYieldTests.fs | 106 ++++++++++++++++++ .../FSharp.Editor.Tests.fsproj | 3 + 16 files changed, 433 insertions(+), 185 deletions(-) create mode 100644 vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddMissingRecToMutuallyRecFunctionsTests.fs create mode 100644 vsintegration/tests/FSharp.Editor.Tests/CodeFixes/FSharpWrapExpressionInParenthesesFixProviderTests.fs create mode 100644 vsintegration/tests/FSharp.Editor.Tests/CodeFixes/RemoveReturnOrYieldTests.fs diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs index e98976184a..36c0ad6f59 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs @@ -3,7 +3,6 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition -open System.Threading.Tasks open System.Collections.Immutable open Microsoft.CodeAnalysis.Text @@ -17,16 +16,17 @@ type internal FSharpAddInstanceMemberParameterCodeFixProvider() = static let title = SR.AddMissingInstanceMemberParameter() - interface IFSharpCodeFix with - member _.GetChangesAsync _ span = - let changes = [ TextChange(TextSpan(span.Start, 0), "x.") ] - CancellableTask.singleton (title, changes) - override _.FixableDiagnosticIds = ImmutableArray.Create("FS0673") - override this.RegisterCodeFixesAsync context : Task = - cancellableTask { - let! title, changes = (this :> IFSharpCodeFix).GetChangesAsync context.Document context.Span - context.RegisterFsharpFix(CodeFix.AddInstanceMemberParameter, title, changes) - } - |> CancellableTask.startAsTask context.CancellationToken + override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) + + interface IFSharpCodeFixProvider with + member _.GetCodeFixIfAppliesAsync _ span = + let codeFix = + { + Name = CodeFix.AddInstanceMemberParameter + Message = title + Changes = [ TextChange(TextSpan(span.Start, 0), "x.") ] + } + + CancellableTask.singleton (Some codeFix) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs index ec9c7dad66..73c7b9c741 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs @@ -5,16 +5,11 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System open System.Collections.Immutable open System.Composition -open System.Threading open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes -open FSharp.Compiler -open FSharp.Compiler.CodeAnalysis - -open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.CodeActions +open CancellableTasks [] type internal FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider [] () = @@ -24,48 +19,48 @@ type internal FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider [ liftAsync + override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) - let! sourceText = context.Document.GetTextAsync(context.CancellationToken) + interface IFSharpCodeFixProvider with + member _.GetCodeFixIfAppliesAsync document span = + cancellableTask { + let! cancellationToken = CancellableTask.getCurrentCancellationToken () - let funcStartPos = - let rec loop ch pos = - if not (Char.IsWhiteSpace(ch)) then - pos - else - loop sourceText.[pos + 1] (pos + 1) + let! defines, langVersion = + document.GetFSharpCompilationDefinesAndLangVersionAsync( + nameof (FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider) + ) - loop sourceText.[context.Span.End + 1] (context.Span.End + 1) + let! sourceText = document.GetTextAsync(cancellationToken) - let! funcLexerSymbol = - Tokenizer.getSymbolAtPosition ( - context.Document.Id, - sourceText, - funcStartPos, - context.Document.FilePath, - defines, - SymbolLookupKind.Greedy, - false, - false, - Some langVersion, - context.CancellationToken - ) + let funcStartPos = + let rec loop ch pos = + if not (Char.IsWhiteSpace(ch)) then + pos + else + loop sourceText.[pos + 1] (pos + 1) - let! funcNameSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, funcLexerSymbol.Range) - let funcName = sourceText.GetSubText(funcNameSpan).ToString() + loop sourceText.[span.End + 1] (span.End + 1) - do - context.RegisterFsharpFix( - CodeFix.AddMissingRecToMutuallyRecFunctions, - String.Format(titleFormat, funcName), - [| TextChange(TextSpan(context.Span.End, 0), " rec") |] - ) - } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + return + Tokenizer.getSymbolAtPosition ( + document.Id, + sourceText, + funcStartPos, + document.FilePath, + defines, + SymbolLookupKind.Greedy, + false, + false, + Some langVersion, + cancellationToken + ) + |> Option.bind (fun funcLexerSymbol -> RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, funcLexerSymbol.Range)) + |> Option.map (fun funcNameSpan -> sourceText.GetSubText(funcNameSpan).ToString()) + |> Option.map (fun funcName -> + { + Name = CodeFix.AddMissingRecToMutuallyRecFunctions + Message = String.Format(titleFormat, funcName) + Changes = [ TextChange(TextSpan(span.End, 0), " rec") ] + }) + } diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ChangeToUpcast.fs b/vsintegration/src/FSharp.Editor/CodeFix/ChangeToUpcast.fs index 7136009a92..26d693a875 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ChangeToUpcast.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ChangeToUpcast.fs @@ -3,7 +3,6 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition -open System.Threading.Tasks open System.Collections.Immutable open Microsoft.CodeAnalysis.Text @@ -17,8 +16,10 @@ type internal FSharpChangeToUpcastCodeFixProvider() = override _.FixableDiagnosticIds = ImmutableArray.Create("FS3198") - interface IFSharpCodeFix with - member _.GetChangesAsync document span = + override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) + + interface IFSharpCodeFixProvider with + member _.GetCodeFixIfAppliesAsync document span = cancellableTask { let! cancellationToken = CancellableTask.getCurrentCancellationToken () @@ -29,33 +30,32 @@ type internal FSharpChangeToUpcastCodeFixProvider() = let isDowncastOperator = text.Contains(":?>") let isDowncastKeyword = text.Contains("downcast") - let changes = - [ - if - (isDowncastOperator || isDowncastKeyword) - && not (isDowncastOperator && isDowncastKeyword) - then - let replacement = - if isDowncastOperator then - text.Replace(":?>", ":>") - else - text.Replace("downcast", "upcast") - - TextChange(span, replacement) - ] - - let title = - if isDowncastOperator then - SR.UseUpcastOperator() - else - SR.UseUpcastKeyword() - - return title, changes + if + (isDowncastOperator || isDowncastKeyword) + && not (isDowncastOperator && isDowncastKeyword) + then + let replacement = + if isDowncastOperator then + text.Replace(":?>", ":>") + else + text.Replace("downcast", "upcast") + + let changes = [ TextChange(span, replacement) ] + + let title = + if isDowncastOperator then + SR.UseUpcastOperator() + else + SR.UseUpcastKeyword() + + let codeFix = + { + Name = CodeFix.ChangeToUpcast + Message = title + Changes = changes + } + + return Some codeFix + else + return None } - - override this.RegisterCodeFixesAsync context : Task = - cancellableTask { - let! title, changes = (this :> IFSharpCodeFix).GetChangesAsync context.Document context.Span - context.RegisterFsharpFix(CodeFix.ChangeToUpcast, title, changes) - } - |> CancellableTask.startAsTask context.CancellationToken diff --git a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs index 924bf6d38f..13ce3fc309 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs @@ -2,9 +2,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor -open System open System.Threading -open System.Threading.Tasks open System.Collections.Immutable open System.Diagnostics @@ -15,6 +13,8 @@ open Microsoft.CodeAnalysis.CodeFixes open Microsoft.CodeAnalysis.CodeActions open Microsoft.VisualStudio.FSharp.Editor.Telemetry +open CancellableTasks + [] module internal CodeFixHelpers = let private reportCodeFixTelemetry @@ -80,3 +80,11 @@ module internal CodeFixExtensions = let diag = diagnostics |> Option.defaultValue ctx.Diagnostics ctx.RegisterCodeFix(codeAction, diag) + + member ctx.RegisterFsharpFix(codeFix: IFSharpCodeFixProvider) = + cancellableTask { + match! codeFix.GetCodeFixIfAppliesAsync ctx.Document ctx.Span with + | Some codeFix -> ctx.RegisterFsharpFix(codeFix.Name, codeFix.Message, codeFix.Changes) + | None -> () + } + |> CancellableTask.startAsTask ctx.CancellationToken diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs index da39b93e9c..1b2fbe4921 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs @@ -3,7 +3,6 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition -open System.Threading.Tasks open System.Collections.Immutable open Microsoft.CodeAnalysis.Text @@ -19,8 +18,10 @@ type internal FSharpConvertToAnonymousRecordCodeFixProvider [ Option.bind (fun range -> RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range)) |> Option.map (fun span -> - [ - TextChange(TextSpan(span.Start + 1, 0), "|") - TextChange(TextSpan(span.End - 1, 0), "|") - ]) - |> Option.defaultValue [] - - return title, changes + { + Name = CodeFix.ConvertToAnonymousRecord + Message = title + Changes = + [ + TextChange(TextSpan(span.Start + 1, 0), "|") + TextChange(TextSpan(span.End - 1, 0), "|") + ] + }) } - - override this.RegisterCodeFixesAsync context : Task = - cancellableTask { - let! title, changes = (this :> IFSharpCodeFix).GetChangesAsync context.Document context.Span - return context.RegisterFsharpFix(CodeFix.ConvertToAnonymousRecord, title, changes) - } - |> CancellableTask.startAsTask context.CancellationToken diff --git a/vsintegration/src/FSharp.Editor/CodeFix/IFSharpCodeFix.fs b/vsintegration/src/FSharp.Editor/CodeFix/IFSharpCodeFix.fs index e0a4fa23a6..36a4ff2db5 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/IFSharpCodeFix.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/IFSharpCodeFix.fs @@ -7,5 +7,12 @@ open Microsoft.CodeAnalysis.Text open CancellableTasks -type IFSharpCodeFix = - abstract member GetChangesAsync: document: Document -> span: TextSpan -> CancellableTask +type FSharpCodeFix = + { + Name: string + Message: string + Changes: TextChange list + } + +type IFSharpCodeFixProvider = + abstract member GetCodeFixIfAppliesAsync: document: Document -> span: TextSpan -> CancellableTask diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs index f69688fde1..16f33212ef 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs @@ -8,40 +8,44 @@ open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes +open CancellableTasks + [] type internal FSharpRemoveReturnOrYieldCodeFixProvider [] () = inherit CodeFixProvider() - override _.FixableDiagnosticIds = ImmutableArray.Create("FS0748", "FS0747") + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0747", "FS0748") + + override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) - override _.RegisterCodeFixesAsync context = - asyncMaybe { - let! parseResults = - context.Document.GetFSharpParseResultsAsync(nameof (FSharpRemoveReturnOrYieldCodeFixProvider)) - |> liftAsync + interface IFSharpCodeFixProvider with + member _.GetCodeFixIfAppliesAsync document span = + cancellableTask { + let! cancellationToken = CancellableTask.getCurrentCancellationToken () - let! sourceText = context.Document.GetTextAsync(context.CancellationToken) + let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpRemoveReturnOrYieldCodeFixProvider)) - let errorRange = - RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) + let! sourceText = document.GetTextAsync(cancellationToken) - let! exprRange = parseResults.TryRangeOfExprInYieldOrReturn errorRange.Start - let! exprSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, exprRange) + let errorRange = + RoslynHelpers.TextSpanToFSharpRange(document.FilePath, span, sourceText) - let title = - let text = sourceText.GetSubText(context.Span).ToString() + return + parseResults.TryRangeOfExprInYieldOrReturn errorRange.Start + |> Option.bind (fun exprRange -> RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, exprRange)) + |> Option.map (fun exprSpan -> [ TextChange(span, sourceText.GetSubText(exprSpan).ToString()) ]) + |> Option.map (fun changes -> + let title = + let text = sourceText.GetSubText(span).ToString() - if text.StartsWith("return!") then SR.RemoveReturnBang() - elif text.StartsWith("return") then SR.RemoveReturn() - elif text.StartsWith("yield!") then SR.RemoveYieldBang() - else SR.RemoveYield() + if text.StartsWith("return!") then SR.RemoveReturnBang() + elif text.StartsWith("return") then SR.RemoveReturn() + elif text.StartsWith("yield!") then SR.RemoveYieldBang() + else SR.RemoveYield() - do - context.RegisterFsharpFix( - CodeFix.RemoveReturnOrYield, - title, - [| TextChange(context.Span, sourceText.GetSubText(exprSpan).ToString()) |] - ) - } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + { + Name = CodeFix.RemoveReturnOrYield + Message = title + Changes = changes + }) + } diff --git a/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs b/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs index 70ed64f505..2c04415d2b 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs @@ -3,13 +3,12 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition -open System.Threading -open System.Threading.Tasks open System.Collections.Immutable -open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes -open Microsoft.CodeAnalysis.CodeActions +open Microsoft.CodeAnalysis.Text + +open CancellableTasks [] type internal FSharpWrapExpressionInParenthesesFixProvider() = @@ -19,13 +18,19 @@ type internal FSharpWrapExpressionInParenthesesFixProvider() = override _.FixableDiagnosticIds = ImmutableArray.Create("FS0597") - override this.RegisterCodeFixesAsync context : Task = - backgroundTask { - let changes = - [ - TextChange(TextSpan(context.Span.Start, 0), "(") - TextChange(TextSpan(context.Span.End + 1, 0), ")") - ] - - context.RegisterFsharpFix(CodeFix.AddParentheses, title, changes) - } + override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) + + interface IFSharpCodeFixProvider with + member _.GetCodeFixIfAppliesAsync _ span = + let codeFix = + { + Name = CodeFix.AddParentheses + Message = title + Changes = + [ + TextChange(TextSpan(span.Start, 0), "(") + TextChange(TextSpan(span.End, 0), ")") + ] + } + + CancellableTask.singleton (Some codeFix) diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddInstanceMemberParameterTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddInstanceMemberParameterTests.fs index 1fbd6b4a39..6737045f7f 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddInstanceMemberParameterTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddInstanceMemberParameterTests.fs @@ -19,15 +19,16 @@ type UsefulTestHarness() = """ let expected = - { - Title = "Add missing instance member parameter" - FixedCode = - """ + Some + { + Message = "Add missing instance member parameter" + FixedCode = + """ type UsefulTestHarness() = member x.FortyTwo = 42 """ - } + } - let actual = codeFix |> fix code diagnostic + let actual = codeFix |> tryFix code diagnostic Assert.Equal(expected, actual) diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddMissingRecToMutuallyRecFunctionsTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddMissingRecToMutuallyRecFunctionsTests.fs new file mode 100644 index 0000000000..04e1bbd492 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddMissingRecToMutuallyRecFunctionsTests.fs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +module FSharp.Editor.Tests.CodeFixes.AddMissingRecToMutuallyRecFunctionsTests + +open Microsoft.VisualStudio.FSharp.Editor +open Xunit + +open CodeFixTestFramework + +let private codeFix = FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider() +let private diagnostic = 0576 // The declaration form 'let ... and ...' for non-recursive bindings is not used in F# code... + +// TODO: write some negative test cases here + +[] +let ``Fixes FS0576`` () = + let code = + """ +let isEven n = + match n with + | 0 -> true + | _ -> isOdd (n - 1) + +and isOdd n = + match n with + | 0 -> false + | _ -> isEven (n - 1) +""" + + let expected = + Some + { + Message = "Make 'isEven' recursive" + FixedCode = + """ +let rec isEven n = + match n with + | 0 -> true + | _ -> isOdd (n - 1) + +and isOdd n = + match n with + | 0 -> false + | _ -> isEven (n - 1) +""" + } + + let actual = codeFix |> tryFix code diagnostic + + Assert.Equal(expected, actual) diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ChangeToUpcastTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ChangeToUpcastTests.fs index 9b0ead993d..d47f320d28 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ChangeToUpcastTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ChangeToUpcastTests.fs @@ -24,18 +24,19 @@ let Thing : IFoo = Foo() :?> IFoo """ let expected = - { - Title = "Use ':>' operator" - FixedCode = - """ + Some + { + Message = "Use ':>' operator" + FixedCode = + """ type IFoo = abstract member Bar : unit -> unit type Foo() = interface IFoo with member __.Bar () = () let Thing : IFoo = Foo() :> IFoo """ - } + } - let actual = codeFix |> fix code diagnostic + let actual = codeFix |> tryFix code diagnostic Assert.Equal(expected, actual) @@ -50,17 +51,35 @@ let Thing : IFoo = downcast Foo() """ let expected = - { - Title = "Use 'upcast'" - FixedCode = - """ + Some + { + Message = "Use 'upcast'" + FixedCode = + """ type IFoo = abstract member Bar : unit -> unit type Foo() = interface IFoo with member __.Bar () = () let Thing : IFoo = upcast Foo() """ - } + } - let actual = codeFix |> fix code diagnostic + let actual = codeFix |> tryFix code diagnostic + + Assert.Equal(expected, actual) + +[] +// TODO: that's a weird thing, we should rather rewrite the code of the code fix +let ``Doesn't fix FS3198 when both`` () = + let code = + """ +type IdowncastFoo = abstract member Bar : unit -> unit +type Foo() = interface IdowncastFoo with member __.Bar () = () + +let Thing : IdowncastFoo = Foo() :?> IdowncastFoo +""" + + let expected = None + + let actual = codeFix |> tryFix code diagnostic Assert.Equal(expected, actual) diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs index 04998935ea..616bc907fe 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs @@ -11,7 +11,7 @@ open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks open FSharp.Editor.Tests.Helpers -type TestCodeFix = { Title: string; FixedCode: string } +type TestCodeFix = { Message: string; FixedCode: string } let getRelevantDiagnostic (document: Document) errorNumber = cancellableTask { @@ -23,7 +23,7 @@ let getRelevantDiagnostic (document: Document) errorNumber = |> Seq.exactlyOne } -let fix (code: string) diagnostic (fixProvider: IFSharpCodeFix) = +let tryFix (code: string) diagnostic (fixProvider: IFSharpCodeFixProvider) = cancellableTask { let sourceText = SourceText.From code let document = RoslynTestHelpers.GetFsDocument code @@ -33,14 +33,15 @@ let fix (code: string) diagnostic (fixProvider: IFSharpCodeFix) = let diagnosticSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, diagnostic.Range) - let! title, changes = fixProvider.GetChangesAsync document diagnosticSpan - let fixedSourceText = sourceText.WithChanges changes + let! result = fixProvider.GetCodeFixIfAppliesAsync document diagnosticSpan return - { - Title = title - FixedCode = fixedSourceText.ToString() - } + (result + |> Option.map (fun codeFix -> + { + Message = codeFix.Message + FixedCode = (sourceText.WithChanges codeFix.Changes).ToString() + })) } |> CancellableTask.start CancellationToken.None |> fun task -> task.Result diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertToAnonymousRecordTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertToAnonymousRecordTests.fs index 9f8008356b..33207bddaa 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertToAnonymousRecordTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertToAnonymousRecordTests.fs @@ -8,24 +8,38 @@ open Xunit open CodeFixTestFramework let private codeFix = FSharpConvertToAnonymousRecordCodeFixProvider() -let private diagnostic = 0039 // The record label is not defined... +let private diagnostic = 0039 // ... is not defined... [] -let ``Fixes FS0039`` () = +let ``Fixes FS0039 for records`` () = let code = """ let band = { Name = "The Velvet Underground" } """ let expected = - { - Title = "Convert to Anonymous Record" - FixedCode = - """ + Some + { + Message = "Convert to Anonymous Record" + FixedCode = + """ let band = {| Name = "The Velvet Underground" |} """ - } + } - let actual = codeFix |> fix code diagnostic + let actual = codeFix |> tryFix code diagnostic + + Assert.Equal(expected, actual) + +[] +let ``Doesn't fix FS0039 for random undefined identifiers`` () = + let code = + """ +let x = someUndefinedFunction 42 +""" + + let expected = None + + let actual = codeFix |> tryFix code diagnostic Assert.Equal(expected, actual) diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/FSharpWrapExpressionInParenthesesFixProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/FSharpWrapExpressionInParenthesesFixProviderTests.fs new file mode 100644 index 0000000000..32009583e0 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/FSharpWrapExpressionInParenthesesFixProviderTests.fs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +module FSharp.Editor.Tests.CodeFixes.FSharpWrapExpressionInParenthesesFixProviderTests + +open Microsoft.VisualStudio.FSharp.Editor +open Xunit + +open CodeFixTestFramework + +let private codeFix = FSharpWrapExpressionInParenthesesFixProvider() +let private diagnostic = 0597 // ... arguments involving function or method applications should be parenthesized + +// Test case is taken from the original PR: +// https://github.com/dotnet/fsharp/pull/10460 + +[] +let ``Fixes FS0597`` () = + let code = + """ +let rng = System.Random() + +printfn "Hello %d" rng.Next(5) +""" + + let expected = + Some + { + Message = "Wrap expression in parentheses" + FixedCode = + """ +let rng = System.Random() + +printfn "Hello %d" (rng.Next(5)) +""" + } + + let actual = codeFix |> tryFix code diagnostic + + Assert.Equal(expected, actual) diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/RemoveReturnOrYieldTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/RemoveReturnOrYieldTests.fs new file mode 100644 index 0000000000..83c841f198 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/RemoveReturnOrYieldTests.fs @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +module FSharp.Editor.Tests.CodeFixes.RemoveReturnOrYieldTests + +open Microsoft.VisualStudio.FSharp.Editor +open Xunit + +open CodeFixTestFramework + +let private codeFix = FSharpRemoveReturnOrYieldCodeFixProvider() +let private yieldDiagnostic = 0747 // This construct may only be used within list, array and sequence expressions... +let private returnDiagnostic = 0748 // This construct may only be used with computation expressions... + +// TODO: write some negative tests here + +[] +let ``Fixes FS0747 - yield`` () = + let code = + """ +let answer question = + yield 42 +""" + + let expected = + Some + { + Message = "Remove 'yield'" + FixedCode = + """ +let answer question = + 42 +""" + } + + let actual = codeFix |> tryFix code yieldDiagnostic + + Assert.Equal(expected, actual) + +[] +let ``Fixes FS0747 - yield!`` () = + let code = + """ +let answer question = + yield! 42 +""" + + let expected = + Some + { + Message = "Remove 'yield!'" + FixedCode = + """ +let answer question = + 42 +""" + } + + let actual = codeFix |> tryFix code yieldDiagnostic + + Assert.Equal(expected, actual) + +[] +let ``Fixes FS0748 - return`` () = + let code = + """ +let answer question = + return 42 +""" + + let expected = + Some + { + Message = "Remove 'return'" + FixedCode = + """ +let answer question = + 42 +""" + } + + let actual = codeFix |> tryFix code returnDiagnostic + + Assert.Equal(expected, actual) + +[] +let ``Fixes FS0748 - return!`` () = + let code = + """ +let answer question = + return! 42 +""" + + let expected = + Some + { + Message = "Remove 'return!'" + FixedCode = + """ +let answer question = + 42 +""" + } + + let actual = codeFix |> tryFix code returnDiagnostic + + Assert.Equal(expected, actual) diff --git a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj index 232e394455..f539937e07 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj +++ b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj @@ -36,6 +36,9 @@ + + + From af18bf0f2278a11ea09b439df0c07471b2accec8 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Thu, 15 Jun 2023 16:31:45 +0200 Subject: [PATCH 2/7] Parser: recover on unfinished 'do' members with new indentation rules (#15392) --- src/Compiler/SyntaxTree/LexFilter.fs | 33 ++++++++----- src/Compiler/pars.fsy | 6 +++ .../data/SyntaxTree/Expression/Do 03.fs.bsl | 12 ++--- .../data/SyntaxTree/Expression/For 03.fs.bsl | 14 +++--- .../SyntaxTree/Expression/While 03.fs.bsl | 5 +- .../SyntaxTree/Expression/While 04.fs.bsl | 5 +- .../data/SyntaxTree/Member/Do 03.fs.bsl | 2 +- tests/service/data/SyntaxTree/Member/Do 04.fs | 7 +++ .../data/SyntaxTree/Member/Do 04.fs.bsl | 48 +++++++++++++++++++ .../data/SyntaxTree/ModuleMember/Do 01.fs | 5 ++ .../data/SyntaxTree/ModuleMember/Do 01.fs.bsl | 17 +++++++ .../ModuleMember/{Do 04.fs => Do 02.fs} | 0 .../{Do 04.fs.bsl => Do 02.fs.bsl} | 7 +-- .../data/SyntaxTree/ModuleMember/Do 03.fs.bsl | 11 ----- 14 files changed, 125 insertions(+), 47 deletions(-) create mode 100644 tests/service/data/SyntaxTree/Member/Do 04.fs create mode 100644 tests/service/data/SyntaxTree/Member/Do 04.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Do 01.fs create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Do 01.fs.bsl rename tests/service/data/SyntaxTree/ModuleMember/{Do 04.fs => Do 02.fs} (100%) rename tests/service/data/SyntaxTree/ModuleMember/{Do 04.fs.bsl => Do 02.fs.bsl} (56%) delete mode 100644 tests/service/data/SyntaxTree/ModuleMember/Do 03.fs.bsl diff --git a/src/Compiler/SyntaxTree/LexFilter.fs b/src/Compiler/SyntaxTree/LexFilter.fs index 6befda9a8e..c90539ad3f 100644 --- a/src/Compiler/SyntaxTree/LexFilter.fs +++ b/src/Compiler/SyntaxTree/LexFilter.fs @@ -748,6 +748,9 @@ type LexFilterImpl ( // ignore Vanilla because a SeqBlock is always coming | _, CtxtVanilla _ :: rest -> undentationLimit strict rest + | CtxtSeqBlock(FirstInSeqBlock, _, _), (CtxtDo _ as limitCtxt) :: CtxtSeqBlock _ :: (CtxtTypeDefns _ | CtxtModuleBody _) :: _ -> + PositionWithColumn(limitCtxt.StartPos, limitCtxt.StartCol + 1) + | _, CtxtSeqBlock _ :: rest when not strict -> undentationLimit strict rest | _, CtxtParen _ :: rest when not strict -> undentationLimit strict rest @@ -1587,7 +1590,7 @@ type LexFilterImpl ( | _ -> delayToken tokenTup pushCtxt tokenTup (CtxtNamespaceBody namespaceTokenPos) - pushCtxtSeqBlockAt false tokenTup tokenTup AddBlockEnd + pushCtxtSeqBlockAt false false tokenTup tokenTup AddBlockEnd hwTokenFetch false // Transition rule. CtxtModuleHead ~~~> push CtxtModuleBody; push CtxtSeqBlock @@ -2162,7 +2165,7 @@ type LexFilterImpl ( | (DO | DO_BANG), _ -> if debug then dprintf "DO: pushing CtxtSeqBlock, tokenStartPos = %a\n" outputPos tokenStartPos pushCtxt tokenTup (CtxtDo tokenStartPos) - pushCtxtSeqBlock tokenTup AddBlockEnd + tryPushCtxtSeqBlock tokenTup AddBlockEnd returnToken tokenLexbufState (match token with DO -> ODO | DO_BANG -> ODO_BANG | _ -> failwith "unreachable") // The r.h.s. of an infix token begins a new block. @@ -2562,24 +2565,28 @@ type LexFilterImpl ( false and pushCtxtSeqBlock fallbackToken addBlockEnd = - pushCtxtSeqBlockAt strictIndentation fallbackToken (peekNextTokenTup()) addBlockEnd + pushCtxtSeqBlockAt strictIndentation true fallbackToken (peekNextTokenTup ()) addBlockEnd + + and tryPushCtxtSeqBlock fallbackToken addBlockEnd = + pushCtxtSeqBlockAt strictIndentation false fallbackToken (peekNextTokenTup ()) addBlockEnd - and pushCtxtSeqBlockAt strict (fallbackToken: TokenTup) (tokenTup: TokenTup) addBlockEnd = + and pushCtxtSeqBlockAt strict (useFallback: bool) (fallbackToken: TokenTup) (tokenTup: TokenTup) addBlockEnd = let pushed = tryPushCtxt strict tokenTup (CtxtSeqBlock(FirstInSeqBlock, startPosOfTokenTup tokenTup, addBlockEnd)) - if not pushed then + if not pushed && useFallback then // The upcoming token isn't sufficiently indented to start the new context. // The parser expects proper contexts structure, so we push a new recovery context at the fallback token position. pushCtxt fallbackToken (CtxtSeqBlock(NotFirstInSeqBlock, startPosOfTokenTup fallbackToken, addBlockEnd)) - let addBlockBegin = - match addBlockEnd with - | AddBlockEnd -> true - | _ -> false + if pushed || useFallback then + let addBlockBegin = + match addBlockEnd with + | AddBlockEnd -> true + | _ -> false - if addBlockBegin then - if debug then dprintf "--> insert OBLOCKBEGIN \n" - let ctxtToken = if pushed then tokenTup else fallbackToken - delayToken(pool.UseLocation(ctxtToken, OBLOCKBEGIN)) + if addBlockBegin then + if debug then dprintf "--> insert OBLOCKBEGIN \n" + let ctxtToken = if pushed then tokenTup else fallbackToken + delayToken(pool.UseLocation(ctxtToken, OBLOCKBEGIN)) let rec swTokenFetch() = let tokenTup = popNextTokenTup() diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index 8a472afd18..c65fd54b1d 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -2787,6 +2787,12 @@ hardwhiteDoBinding: // associated with the module, 'main' function or assembly depending on their target BindingSetPreAttrs(mDo, false, false, (fun attrs vis -> attrs, [mkSynDoBinding (vis, mDo, $2, mAll)]), mAll), $2 } + | ODO ODECLEND + { let mDo = rhs parseState 1 + reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsExpectingExpression ()) + let seqPt = DebugPointAtBinding.NoneAtDo + let expr = arbExpr ("hardwhiteDoBinding1", mDo.EndRange) + BindingSetPreAttrs(mDo, false, false, (fun attrs vis -> attrs, [mkSynDoBinding (vis, mDo, expr, mDo)]), mDo), expr } /* The bindings in a class type definition */ classDefnBindings: diff --git a/tests/service/data/SyntaxTree/Expression/Do 03.fs.bsl b/tests/service/data/SyntaxTree/Expression/Do 03.fs.bsl index f60cb8f91f..99ae567f06 100644 --- a/tests/service/data/SyntaxTree/Expression/Do 03.fs.bsl +++ b/tests/service/data/SyntaxTree/Expression/Do 03.fs.bsl @@ -12,12 +12,12 @@ ImplFile (None, SynValInfo ([], SynArgInfo ([], false, None)), None), Wild (3,4--3,5), None, Do - (ArbitraryAfterError - ("typedSequentialExprBlock1", (4,6--4,6)), (4,4--4,6)), - (3,4--3,5), Yes (3,0--4,6), { LeadingKeyword = Let (3,0--3,3) - InlineKeyword = None - EqualsRange = Some (3,6--3,7) })], - (3,0--4,6)); Expr (Const (Int32 1, (6,0--6,1)), (6,0--6,1))], + (ArbitraryAfterError ("hardwhiteDoBinding1", (4,6--4,6)), + (4,4--4,6)), (3,4--3,5), Yes (3,0--4,6), + { LeadingKeyword = Let (3,0--3,3) + InlineKeyword = None + EqualsRange = Some (3,6--3,7) })], (3,0--4,6)); + Expr (Const (Int32 1, (6,0--6,1)), (6,0--6,1))], PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, (1,0--6,1), { LeadingKeyword = Module (1,0--1,6) })], (true, true), { ConditionalDirectives = [] diff --git a/tests/service/data/SyntaxTree/Expression/For 03.fs.bsl b/tests/service/data/SyntaxTree/Expression/For 03.fs.bsl index 2c93eb12bb..adaf09a1ea 100644 --- a/tests/service/data/SyntaxTree/Expression/For 03.fs.bsl +++ b/tests/service/data/SyntaxTree/Expression/For 03.fs.bsl @@ -14,16 +14,16 @@ ImplFile ForEach (Yes (4,4--4,7), Yes (4,10--4,12), SeqExprOnly false, true, Wild (4,8--4,9), Const (Int32 1, (4,13--4,14)), - ArbitraryAfterError - ("typedSequentialExprBlock1", (4,17--4,17)), (4,4--4,17)), - (3,4--3,5), NoneAtLet, { LeadingKeyword = Let (3,0--3,3) - InlineKeyword = None - EqualsRange = Some (3,6--3,7) })], - (3,0--4,17)); Expr (Const (Int32 3, (6,0--6,1)), (6,0--6,1))], + ArbitraryAfterError ("forLoopBody2a", (6,0--6,1)), + (4,4--4,17)), (3,4--3,5), NoneAtLet, + { LeadingKeyword = Let (3,0--3,3) + InlineKeyword = None + EqualsRange = Some (3,6--3,7) })], (3,0--4,17)); + Expr (Const (Int32 3, (6,0--6,1)), (6,0--6,1))], PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, (1,0--6,1), { LeadingKeyword = Module (1,0--1,6) })], (true, true), { ConditionalDirectives = [] CodeComments = [] }, set [])) (6,0)-(6,1) parse error Possible incorrect indentation: this token is offside of context started at position (4:5). Try indenting this token further or using standard formatting conventions. -(6,0)-(6,1) parse error Expecting expression +(6,0)-(6,1) parse error Incomplete structured construct at or before this point in expression diff --git a/tests/service/data/SyntaxTree/Expression/While 03.fs.bsl b/tests/service/data/SyntaxTree/Expression/While 03.fs.bsl index e74fa7f848..b10e7fab89 100644 --- a/tests/service/data/SyntaxTree/Expression/While 03.fs.bsl +++ b/tests/service/data/SyntaxTree/Expression/While 03.fs.bsl @@ -13,8 +13,7 @@ ImplFile Wild (3,4--3,5), None, While (Yes (4,4--4,11), Const (Int32 1, (4,10--4,11)), - ArbitraryAfterError - ("typedSequentialExprBlock1", (4,14--4,14)), (4,4--4,14)), + ArbitraryAfterError ("whileBody1", (6,0--6,1)), (4,4--4,14)), (3,4--3,5), NoneAtLet, { LeadingKeyword = Let (3,0--3,3) InlineKeyword = None EqualsRange = Some (3,6--3,7) })], @@ -25,4 +24,4 @@ ImplFile CodeComments = [] }, set [])) (6,0)-(6,1) parse error Possible incorrect indentation: this token is offside of context started at position (4:5). Try indenting this token further or using standard formatting conventions. -(6,0)-(6,1) parse error Expecting expression +(6,0)-(6,1) parse error Incomplete structured construct at or before this point in expression diff --git a/tests/service/data/SyntaxTree/Expression/While 04.fs.bsl b/tests/service/data/SyntaxTree/Expression/While 04.fs.bsl index 1286e6a723..538e8312b7 100644 --- a/tests/service/data/SyntaxTree/Expression/While 04.fs.bsl +++ b/tests/service/data/SyntaxTree/Expression/While 04.fs.bsl @@ -13,8 +13,7 @@ ImplFile Wild (3,4--3,5), None, While (Yes (4,4--4,11), Const (Int32 1, (4,10--4,11)), - ArbitraryAfterError - ("typedSequentialExprBlock1", (4,14--4,14)), (4,4--4,14)), + ArbitraryAfterError ("whileBody1", (5,0--5,0)), (4,4--4,14)), (3,4--3,5), NoneAtLet, { LeadingKeyword = Let (3,0--3,3) InlineKeyword = None EqualsRange = Some (3,6--3,7) })], @@ -25,4 +24,4 @@ ImplFile CodeComments = [] }, set [])) (5,0)-(5,0) parse error Possible incorrect indentation: this token is offside of context started at position (4:5). Try indenting this token further or using standard formatting conventions. -(5,0)-(5,0) parse error Expecting expression +(5,0)-(5,0) parse error Incomplete structured construct at or before this point in expression diff --git a/tests/service/data/SyntaxTree/Member/Do 03.fs.bsl b/tests/service/data/SyntaxTree/Member/Do 03.fs.bsl index 6634a59f3f..f80e296c39 100644 --- a/tests/service/data/SyntaxTree/Member/Do 03.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Do 03.fs.bsl @@ -19,7 +19,7 @@ ImplFile SynValInfo ([], SynArgInfo ([], false, None)), None), Const (Unit, (5,11--5,13)), None, ArbitraryAfterError - ("typedSequentialExprBlock1", (5,13--5,13)), + ("hardwhiteDoBinding1", (5,13--5,13)), (5,11--5,13), NoneAtDo, { LeadingKeyword = StaticDo ((5,4--5,10), (5,11--5,13)) diff --git a/tests/service/data/SyntaxTree/Member/Do 04.fs b/tests/service/data/SyntaxTree/Member/Do 04.fs new file mode 100644 index 0000000000..487fb8c7e5 --- /dev/null +++ b/tests/service/data/SyntaxTree/Member/Do 04.fs @@ -0,0 +1,7 @@ +module Module + +type T = + + do + + do () diff --git a/tests/service/data/SyntaxTree/Member/Do 04.fs.bsl b/tests/service/data/SyntaxTree/Member/Do 04.fs.bsl new file mode 100644 index 0000000000..9d6eb8b189 --- /dev/null +++ b/tests/service/data/SyntaxTree/Member/Do 04.fs.bsl @@ -0,0 +1,48 @@ +ImplFile + (ParsedImplFileInput + ("/root/Member/Do 04.fs", false, QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Types + ([SynTypeDefn + (SynComponentInfo + ([], None, [], [T], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), + false, None, (3,5--3,6)), + ObjectModel + (Unspecified, + [LetBindings + ([SynBinding + (None, Do, false, false, [], PreXmlDocEmpty, + SynValData + (None, + SynValInfo ([], SynArgInfo ([], false, None)), + None), Const (Unit, (5,4--5,6)), None, + ArbitraryAfterError + ("hardwhiteDoBinding1", (5,6--5,6)), (5,4--5,6), + NoneAtDo, { LeadingKeyword = Do (5,4--5,6) + InlineKeyword = None + EqualsRange = None })], false, false, + (5,4--5,6)); + LetBindings + ([SynBinding + (None, Do, false, false, [], PreXmlDocEmpty, + SynValData + (None, + SynValInfo ([], SynArgInfo ([], false, None)), + None), Const (Unit, (7,4--7,9)), None, + Const (Unit, (7,7--7,9)), (7,4--7,9), NoneAtDo, + { LeadingKeyword = Do (7,4--7,6) + InlineKeyword = None + EqualsRange = None })], false, false, (7,4--7,9))], + (5,4--7,9)), [], None, (3,5--7,9), + { LeadingKeyword = Type (3,0--3,4) + EqualsRange = Some (3,7--3,8) + WithKeyword = None })], (3,0--7,9))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--7,9), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(7,4)-(7,6) parse error Possible incorrect indentation: this token is offside of context started at position (5:5). Try indenting this token further or using standard formatting conventions. +(7,4)-(7,6) parse error Expecting expression diff --git a/tests/service/data/SyntaxTree/ModuleMember/Do 01.fs b/tests/service/data/SyntaxTree/ModuleMember/Do 01.fs new file mode 100644 index 0000000000..7053ee15c8 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Do 01.fs @@ -0,0 +1,5 @@ +module Module + +do + +2 diff --git a/tests/service/data/SyntaxTree/ModuleMember/Do 01.fs.bsl b/tests/service/data/SyntaxTree/ModuleMember/Do 01.fs.bsl new file mode 100644 index 0000000000..b3d783470b --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Do 01.fs.bsl @@ -0,0 +1,17 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleMember/Do 01.fs", false, QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Expr + (Do + (ArbitraryAfterError ("hardwhiteDoBinding1", (3,2--3,2)), + (3,0--3,2)), (3,0--3,2)); + Expr (Const (Int32 2, (5,0--5,1)), (5,0--5,1))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--5,1), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(5,0)-(5,1) parse error Possible incorrect indentation: this token is offside of context started at position (3:1). Try indenting this token further or using standard formatting conventions. +(5,0)-(5,1) parse error Expecting expression diff --git a/tests/service/data/SyntaxTree/ModuleMember/Do 04.fs b/tests/service/data/SyntaxTree/ModuleMember/Do 02.fs similarity index 100% rename from tests/service/data/SyntaxTree/ModuleMember/Do 04.fs rename to tests/service/data/SyntaxTree/ModuleMember/Do 02.fs diff --git a/tests/service/data/SyntaxTree/ModuleMember/Do 04.fs.bsl b/tests/service/data/SyntaxTree/ModuleMember/Do 02.fs.bsl similarity index 56% rename from tests/service/data/SyntaxTree/ModuleMember/Do 04.fs.bsl rename to tests/service/data/SyntaxTree/ModuleMember/Do 02.fs.bsl index 6738b7e6e9..24bb18d6df 100644 --- a/tests/service/data/SyntaxTree/ModuleMember/Do 04.fs.bsl +++ b/tests/service/data/SyntaxTree/ModuleMember/Do 02.fs.bsl @@ -1,16 +1,17 @@ ImplFile (ParsedImplFileInput - ("/root/ModuleMember/Do 04.fs", false, QualifiedNameOfFile Module, [], [], + ("/root/ModuleMember/Do 02.fs", false, QualifiedNameOfFile Module, [], [], [SynModuleOrNamespace ([Module], false, NamedModule, [Expr (Do - (ArbitraryAfterError ("typedSequentialExprBlock1", (4,4--4,4)), - (3,0--4,4)), (3,0--4,4)); + (ArbitraryAfterError ("hardwhiteDoBinding1", (3,2--3,2)), + (3,0--3,2)), (3,0--3,2)); Expr (Const (Int32 2, (6,0--6,1)), (6,0--6,1))], PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, (1,0--6,1), { LeadingKeyword = Module (1,0--1,6) })], (true, true), { ConditionalDirectives = [] CodeComments = [] }, set [])) +(4,0)-(4,4) parse error Possible incorrect indentation: this token is offside of context started at position (3:1). Try indenting this token further or using standard formatting conventions. (4,0)-(4,4) parse error Expecting expression diff --git a/tests/service/data/SyntaxTree/ModuleMember/Do 03.fs.bsl b/tests/service/data/SyntaxTree/ModuleMember/Do 03.fs.bsl deleted file mode 100644 index 1bc84f9a9d..0000000000 --- a/tests/service/data/SyntaxTree/ModuleMember/Do 03.fs.bsl +++ /dev/null @@ -1,11 +0,0 @@ -ImplFile - (ParsedImplFileInput - ("/root/ModuleMember/Do 03.fs", false, QualifiedNameOfFile Module, [], [], - [SynModuleOrNamespace - ([Module], false, NamedModule, - [Expr (Do (Const (Int32 1, (4,2--4,3)), (3,0--4,3)), (3,0--4,3)); - Expr (Const (Int32 2, (7,0--7,1)), (7,0--7,1))], - PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, - (1,0--7,1), { LeadingKeyword = Module (1,0--1,6) })], (true, true), - { ConditionalDirectives = [] - CodeComments = [] }, set [])) From 61dffdd52ee6411924b47485bf2cc240bbb8ebf5 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Fri, 16 Jun 2023 12:40:32 +0200 Subject: [PATCH 3/7] Parser: recover on unfinished `open` (#15377) --- src/Compiler/pars.fsy | 21 +++++++++++---- .../data/SyntaxTree/ModuleMember/Open 01.fs | 4 +++ .../SyntaxTree/ModuleMember/Open 01.fs.bsl | 17 ++++++++++++ .../data/SyntaxTree/ModuleMember/Open 02.fs | 4 +++ .../SyntaxTree/ModuleMember/Open 02.fs.bsl | 17 ++++++++++++ .../data/SyntaxTree/ModuleMember/Open 03.fs | 5 ++++ .../SyntaxTree/ModuleMember/Open 03.fs.bsl | 14 ++++++++++ .../data/SyntaxTree/ModuleMember/Open 04.fs | 5 ++++ .../SyntaxTree/ModuleMember/Open 04.fs.bsl | 14 ++++++++++ .../data/SyntaxTree/ModuleMember/Open 05.fs | 5 ++++ .../SyntaxTree/ModuleMember/Open 05.fs.bsl | 27 +++++++++++++++++++ .../data/SyntaxTree/ModuleMember/Open 06.fs | 4 +++ .../SyntaxTree/ModuleMember/Open 06.fs.bsl | 18 +++++++++++++ .../data/SyntaxTree/ModuleMember/Open 07.fs | 5 ++++ .../SyntaxTree/ModuleMember/Open 07.fs.bsl | 15 +++++++++++ .../data/SyntaxTree/ModuleMember/Open 08.fs | 4 +++ .../SyntaxTree/ModuleMember/Open 08.fs.bsl | 13 +++++++++ 17 files changed, 187 insertions(+), 5 deletions(-) create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Open 01.fs create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Open 01.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Open 02.fs create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Open 02.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Open 03.fs create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Open 03.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Open 04.fs create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Open 04.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Open 05.fs create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Open 05.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Open 06.fs create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Open 06.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Open 07.fs create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Open 07.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Open 08.fs create mode 100644 tests/service/data/SyntaxTree/ModuleMember/Open 08.fs.bsl diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index c65fd54b1d..e35eb962f6 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -706,7 +706,7 @@ moduleSpfn: SynModuleSigDecl.Exception(synExnDefn, mWhole) } | openDecl - { SynModuleSigDecl.Open($1, (rhs parseState 1)) } + { SynModuleSigDecl.Open $1 } valSpfn: | opt_attributes opt_access VAL opt_attributes opt_inline opt_mutable opt_access nameop opt_explicitValTyparDecls COLON topTypeWithTypeConstraints optLiteralValueSpfn @@ -1320,15 +1320,26 @@ moduleDefn: /* 'open' declarations */ | openDecl - { [ SynModuleDecl.Open($1, (rhs parseState 1)) ] } + { [ SynModuleDecl.Open $1 ] } openDecl: - /* 'open' declarations */ | OPEN path - { SynOpenDeclTarget.ModuleOrNamespace($2, (rhs parseState 2)) } + { let mOpen = rhs parseState 1 + let mPath = $2.Range + SynOpenDeclTarget.ModuleOrNamespace($2, mPath), unionRanges mOpen mPath } + + | OPEN recover + { let mOpen = rhs parseState 1 + SynOpenDeclTarget.ModuleOrNamespace(SynLongIdent([], [], []), mOpen.EndRange), mOpen } | OPEN typeKeyword appType - { SynOpenDeclTarget.Type($3, (rhs parseState 3)) } + { let mOpen = rhs parseState 1 + let mPath = $3.Range + SynOpenDeclTarget.Type($3, mPath), unionRanges mOpen mPath } + + | OPEN typeKeyword recover + { let m = rhs2 parseState 1 2 + SynOpenDeclTarget.ModuleOrNamespace(SynLongIdent([], [], []), m.EndRange), m } /* The right-hand-side of a module abbreviation definition */ /* This occurs on the right of a module abbreviation (#light encloses the r.h.s. with OBLOCKBEGIN/OBLOCKEND) */ diff --git a/tests/service/data/SyntaxTree/ModuleMember/Open 01.fs b/tests/service/data/SyntaxTree/ModuleMember/Open 01.fs new file mode 100644 index 0000000000..a6746f4ead --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Open 01.fs @@ -0,0 +1,4 @@ +module Module + +open +open Ns1 diff --git a/tests/service/data/SyntaxTree/ModuleMember/Open 01.fs.bsl b/tests/service/data/SyntaxTree/ModuleMember/Open 01.fs.bsl new file mode 100644 index 0000000000..9ba9b81eb5 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Open 01.fs.bsl @@ -0,0 +1,17 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleMember/Open 01.fs", false, QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Open + (ModuleOrNamespace (SynLongIdent ([], [], []), (3,4--3,4)), + (3,0--3,4)); + Open + (ModuleOrNamespace (SynLongIdent ([Ns1], [], [None]), (4,5--4,8)), + (4,0--4,8))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--4,8), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,5)-(4,0) parse error Incomplete structured construct at or before this point in open declaration. Expected identifier, 'global', 'type' or other token. diff --git a/tests/service/data/SyntaxTree/ModuleMember/Open 02.fs b/tests/service/data/SyntaxTree/ModuleMember/Open 02.fs new file mode 100644 index 0000000000..6546212aeb --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Open 02.fs @@ -0,0 +1,4 @@ +module Module + +open type +open Ns1 diff --git a/tests/service/data/SyntaxTree/ModuleMember/Open 02.fs.bsl b/tests/service/data/SyntaxTree/ModuleMember/Open 02.fs.bsl new file mode 100644 index 0000000000..2a17ef664a --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Open 02.fs.bsl @@ -0,0 +1,17 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleMember/Open 02.fs", false, QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Open + (ModuleOrNamespace (SynLongIdent ([], [], []), (3,9--3,9)), + (3,0--3,9)); + Open + (ModuleOrNamespace (SynLongIdent ([Ns1], [], [None]), (4,5--4,8)), + (4,0--4,8))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--4,8), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,10)-(4,0) parse error Incomplete structured construct at or before this point in open declaration diff --git a/tests/service/data/SyntaxTree/ModuleMember/Open 03.fs b/tests/service/data/SyntaxTree/ModuleMember/Open 03.fs new file mode 100644 index 0000000000..15a614bc78 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Open 03.fs @@ -0,0 +1,5 @@ +module Module + +open type + +() diff --git a/tests/service/data/SyntaxTree/ModuleMember/Open 03.fs.bsl b/tests/service/data/SyntaxTree/ModuleMember/Open 03.fs.bsl new file mode 100644 index 0000000000..549a2c50a7 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Open 03.fs.bsl @@ -0,0 +1,14 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleMember/Open 03.fs", false, QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Open + (ModuleOrNamespace (SynLongIdent ([], [], []), (3,9--3,9)), + (3,0--3,9)); Expr (Const (Unit, (5,0--5,2)), (5,0--5,2))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--5,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,10)-(5,0) parse error Incomplete structured construct at or before this point in open declaration diff --git a/tests/service/data/SyntaxTree/ModuleMember/Open 04.fs b/tests/service/data/SyntaxTree/ModuleMember/Open 04.fs new file mode 100644 index 0000000000..798a6aef2e --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Open 04.fs @@ -0,0 +1,5 @@ +module Module + +open type + +ignore diff --git a/tests/service/data/SyntaxTree/ModuleMember/Open 04.fs.bsl b/tests/service/data/SyntaxTree/ModuleMember/Open 04.fs.bsl new file mode 100644 index 0000000000..26532b885c --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Open 04.fs.bsl @@ -0,0 +1,14 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleMember/Open 04.fs", false, QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Open + (ModuleOrNamespace (SynLongIdent ([], [], []), (3,9--3,9)), + (3,0--3,9)); Expr (Ident ignore, (5,0--5,6))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--5,6), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,10)-(5,0) parse error Incomplete structured construct at or before this point in open declaration diff --git a/tests/service/data/SyntaxTree/ModuleMember/Open 05.fs b/tests/service/data/SyntaxTree/ModuleMember/Open 05.fs new file mode 100644 index 0000000000..0999fac7c7 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Open 05.fs @@ -0,0 +1,5 @@ +module Module + +open + +type T = int diff --git a/tests/service/data/SyntaxTree/ModuleMember/Open 05.fs.bsl b/tests/service/data/SyntaxTree/ModuleMember/Open 05.fs.bsl new file mode 100644 index 0000000000..d6339de665 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Open 05.fs.bsl @@ -0,0 +1,27 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleMember/Open 05.fs", false, QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Open + (ModuleOrNamespace (SynLongIdent ([], [], []), (3,4--3,4)), + (3,0--3,4)); + Types + ([SynTypeDefn + (SynComponentInfo + ([], None, [], [T], + PreXmlDoc ((5,0), FSharp.Compiler.Xml.XmlDocCollector), + false, None, (5,5--5,6)), + Simple + (TypeAbbrev + (Ok, LongIdent (SynLongIdent ([int], [], [None])), + (5,9--5,12)), (5,9--5,12)), [], None, (5,5--5,12), + { LeadingKeyword = Type (5,0--5,4) + EqualsRange = Some (5,7--5,8) + WithKeyword = None })], (5,0--5,12))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--5,12), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,5)-(5,0) parse error Incomplete structured construct at or before this point in open declaration. Expected identifier, 'global', 'type' or other token. diff --git a/tests/service/data/SyntaxTree/ModuleMember/Open 06.fs b/tests/service/data/SyntaxTree/ModuleMember/Open 06.fs new file mode 100644 index 0000000000..77b1a3f007 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Open 06.fs @@ -0,0 +1,4 @@ +module Module + +open Ns1. +open Ns2 diff --git a/tests/service/data/SyntaxTree/ModuleMember/Open 06.fs.bsl b/tests/service/data/SyntaxTree/ModuleMember/Open 06.fs.bsl new file mode 100644 index 0000000000..44453c403f --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Open 06.fs.bsl @@ -0,0 +1,18 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleMember/Open 06.fs", false, QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Open + (ModuleOrNamespace + (SynLongIdent ([Ns1], [(3,8--3,9)], [None]), (3,5--3,9)), + (3,0--3,9)); + Open + (ModuleOrNamespace (SynLongIdent ([Ns2], [], [None]), (4,5--4,8)), + (4,0--4,8))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--4,8), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,10)-(4,0) parse error Incomplete structured construct at or before this point in open declaration diff --git a/tests/service/data/SyntaxTree/ModuleMember/Open 07.fs b/tests/service/data/SyntaxTree/ModuleMember/Open 07.fs new file mode 100644 index 0000000000..66d705cb2d --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Open 07.fs @@ -0,0 +1,5 @@ +module Module + +open Ns1. + +ignore diff --git a/tests/service/data/SyntaxTree/ModuleMember/Open 07.fs.bsl b/tests/service/data/SyntaxTree/ModuleMember/Open 07.fs.bsl new file mode 100644 index 0000000000..38af89635b --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Open 07.fs.bsl @@ -0,0 +1,15 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleMember/Open 07.fs", false, QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Open + (ModuleOrNamespace + (SynLongIdent ([Ns1], [(3,8--3,9)], [None]), (3,5--3,9)), + (3,0--3,9)); Expr (Ident ignore, (5,0--5,6))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--5,6), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,10)-(5,0) parse error Incomplete structured construct at or before this point in open declaration diff --git a/tests/service/data/SyntaxTree/ModuleMember/Open 08.fs b/tests/service/data/SyntaxTree/ModuleMember/Open 08.fs new file mode 100644 index 0000000000..998eb9b5f9 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Open 08.fs @@ -0,0 +1,4 @@ +module Module + +open Ns1. + Ns2 diff --git a/tests/service/data/SyntaxTree/ModuleMember/Open 08.fs.bsl b/tests/service/data/SyntaxTree/ModuleMember/Open 08.fs.bsl new file mode 100644 index 0000000000..5968d7456b --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleMember/Open 08.fs.bsl @@ -0,0 +1,13 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleMember/Open 08.fs", false, QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Open + (ModuleOrNamespace + (SynLongIdent ([Ns1; Ns2], [(3,8--3,9)], [None; None]), + (3,5--4,4)), (3,0--4,4))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--4,4), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) From 9c55d323c2dae199ea83dd946cf9859163b390dd Mon Sep 17 00:00:00 2001 From: Petr Date: Fri, 16 Jun 2023 13:32:23 +0200 Subject: [PATCH 4/7] Some cleanup in the editor code (#15412) * Small cleanup in the editor code * up * fantomas --- .../AddInstanceMemberParameter.fs | 2 +- .../AddMissingEqualsToTypeDefinition.fs | 2 +- .../AddMissingFunKeyword.fs | 7 +- .../AddMissingRecToMutuallyRecFunctions.fs | 6 +- ...eywordToDisposableConstructorInvocation.fs | 3 +- .../AddOpenCodeFixProvider.fs | 12 +- ...peAnnotationToObjectOfIndeterminateType.fs | 9 +- .../ChangePrefixNegationToInfixSubtraction.fs | 2 +- .../ChangeRefCellDerefToNotExpression.fs | 4 +- .../{CodeFix => CodeFixes}/ChangeToUpcast.fs | 2 +- .../{CodeFix => CodeFixes}/CodeFixHelpers.fs | 0 .../ConvertCSharpLambdaToFSharpLambda.fs | 4 +- .../ConvertCSharpUsingToFSharpOpen.fs | 3 +- .../ConvertToAnonymousRecord.fs | 4 +- .../ConvertToNotEqualsEqualityExpression.fs | 2 +- ...ConvertToSingleEqualsEqualityExpression.fs | 2 +- .../FixIndexerAccess.fs | 2 +- .../{CodeFix => CodeFixes}/IFSharpCodeFix.fs | 0 .../ImplementInterfaceCodeFixProvider.fs | 6 +- .../MakeDeclarationMutable.fs | 6 +- .../MakeOuterBindingRecursive.fs | 4 +- .../MissingReferenceCodeFixProvider.fs | 0 .../ProposeUppercaseLabel.fs | 2 +- .../RemoveReturnOrYield.fs | 4 +- ...SuperflousCaptureForUnionCaseWithNoData.fs | 3 +- .../RemoveUnusedBinding.fs | 7 +- .../RemoveUnusedOpens.fs | 5 +- .../RenameParamToMatchSignature.fs | 4 +- .../RenameUnusedValue.fs | 6 +- .../ReplaceWithSuggestion.fs | 5 +- .../{CodeFix => CodeFixes}/SimplifyName.fs | 5 +- .../UseMutationWhenValueIsMutable.fs | 6 +- .../UseTripleQuotedInterpolation.fs | 4 +- .../WrapExpressionInParentheses.fs | 2 +- .../src/FSharp.Editor/FSharp.Editor.fsproj | 68 +- .../AddInstanceMemberParameterTests.fs | 2 +- ...ddMissingRecToMutuallyRecFunctionsTests.fs | 2 +- .../CodeFixes/ChangeToUpcastTests.fs | 2 +- .../ConvertToAnonymousRecordTests.fs | 2 +- .../CodeFixes/RemoveReturnOrYieldTests.fs | 2 +- ...fs => WrapExpressionInParenthesesTests.fs} | 2 +- .../FSharp.Editor.Tests.fsproj | 2 +- .../Helpers/RoslynHelpers.fs | 16 + .../Hints/HintTestFramework.fs | 26 - .../Hints/InlineParameterNameHintTests.fs | 949 +++++++++--------- .../Hints/InlineReturnTypeHintTests.fs | 19 +- .../Hints/InlineTypeHintTests.fs | 399 ++++---- .../Hints/OverallHintExperienceTests.fs | 168 ++-- 48 files changed, 878 insertions(+), 916 deletions(-) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/AddInstanceMemberParameter.fs (94%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/AddMissingEqualsToTypeDefinition.fs (95%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/AddMissingFunKeyword.fs (91%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/AddMissingRecToMutuallyRecFunctions.fs (91%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/AddNewKeywordToDisposableConstructorInvocation.fs (93%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/AddOpenCodeFixProvider.fs (95%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/AddTypeAnnotationToObjectOfIndeterminateType.fs (92%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/ChangePrefixNegationToInfixSubtraction.fs (95%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/ChangeRefCellDerefToNotExpression.fs (86%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/ChangeToUpcast.fs (97%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/CodeFixHelpers.fs (100%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/ConvertCSharpLambdaToFSharpLambda.fs (92%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/ConvertCSharpUsingToFSharpOpen.fs (96%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/ConvertToAnonymousRecord.fs (92%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/ConvertToNotEqualsEqualityExpression.fs (94%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/ConvertToSingleEqualsEqualityExpression.fs (94%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/FixIndexerAccess.fs (98%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/IFSharpCodeFix.fs (100%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/ImplementInterfaceCodeFixProvider.fs (98%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/MakeDeclarationMutable.fs (93%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/MakeOuterBindingRecursive.fs (93%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/MissingReferenceCodeFixProvider.fs (100%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/ProposeUppercaseLabel.fs (93%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/RemoveReturnOrYield.fs (93%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/RemoveSuperflousCaptureForUnionCaseWithNoData.fs (95%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/RemoveUnusedBinding.fs (93%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/RemoveUnusedOpens.fs (92%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/RenameParamToMatchSignature.fs (95%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/RenameUnusedValue.fs (96%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/ReplaceWithSuggestion.fs (93%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/SimplifyName.fs (93%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/UseMutationWhenValueIsMutable.fs (93%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/UseTripleQuotedInterpolation.fs (91%) rename vsintegration/src/FSharp.Editor/{CodeFix => CodeFixes}/WrapExpressionInParentheses.fs (94%) rename vsintegration/tests/FSharp.Editor.Tests/CodeFixes/{FSharpWrapExpressionInParenthesesFixProviderTests.fs => WrapExpressionInParenthesesTests.fs} (93%) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddInstanceMemberParameter.fs similarity index 94% rename from vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/AddInstanceMemberParameter.fs index 36c0ad6f59..4e757dbe61 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddInstanceMemberParameter.fs @@ -11,7 +11,7 @@ open Microsoft.CodeAnalysis.CodeFixes open CancellableTasks [] -type internal FSharpAddInstanceMemberParameterCodeFixProvider() = +type internal AddInstanceMemberParameterCodeFixProvider() = inherit CodeFixProvider() static let title = SR.AddMissingInstanceMemberParameter() diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingEqualsToTypeDefinition.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingEqualsToTypeDefinition.fs similarity index 95% rename from vsintegration/src/FSharp.Editor/CodeFix/AddMissingEqualsToTypeDefinition.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/AddMissingEqualsToTypeDefinition.fs index 96d434ca3c..c71d4cbcb4 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingEqualsToTypeDefinition.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingEqualsToTypeDefinition.fs @@ -11,7 +11,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes [] -type internal FSharpAddMissingEqualsToTypeDefinitionCodeFixProvider() = +type internal AddMissingEqualsToTypeDefinitionCodeFixProvider() = inherit CodeFixProvider() static let title = SR.AddMissingEqualsToTypeDefinition() diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs similarity index 91% rename from vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs index 5c07cb6e79..65854292e7 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs @@ -9,11 +9,8 @@ open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes -open FSharp.Compiler -open FSharp.Compiler.CodeAnalysis - [] -type internal FSharpAddMissingFunKeywordCodeFixProvider [] () = +type internal AddMissingFunKeywordCodeFixProvider [] () = inherit CodeFixProvider() static let title = SR.AddMissingFunKeyword() @@ -29,7 +26,7 @@ type internal FSharpAddMissingFunKeywordCodeFixProvider [] do! Option.guard (textOfError = "->") let! defines, langVersion = - document.GetFSharpCompilationDefinesAndLangVersionAsync(nameof (FSharpAddMissingFunKeywordCodeFixProvider)) + document.GetFSharpCompilationDefinesAndLangVersionAsync(nameof (AddMissingFunKeywordCodeFixProvider)) |> liftAsync let adjustedPosition = diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingRecToMutuallyRecFunctions.fs similarity index 91% rename from vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/AddMissingRecToMutuallyRecFunctions.fs index 73c7b9c741..5713ca8e68 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingRecToMutuallyRecFunctions.fs @@ -12,7 +12,7 @@ open Microsoft.CodeAnalysis.CodeFixes open CancellableTasks [] -type internal FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider [] () = +type internal AddMissingRecToMutuallyRecFunctionsCodeFixProvider [] () = inherit CodeFixProvider() static let titleFormat = SR.MakeOuterBindingRecursive() @@ -27,9 +27,7 @@ type internal FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider [] -type internal FSharpAddNewKeywordCodeFixProvider() = +type internal AddNewKeywordCodeFixProvider() = inherit CodeFixProvider() static let title = SR.AddNewKeyword() diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddOpenCodeFixProvider.fs similarity index 95% rename from vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/AddOpenCodeFixProvider.fs index 38f96d81ed..a9b71bac3f 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddOpenCodeFixProvider.fs @@ -2,24 +2,18 @@ namespace Microsoft.VisualStudio.FSharp.Editor -open System open System.Composition -open System.Threading open System.Threading.Tasks open System.Collections.Immutable -open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes -open Microsoft.CodeAnalysis.CodeActions -open FSharp.Compiler -open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.EditorServices open FSharp.Compiler.Text [] -type internal FSharpAddOpenCodeFixProvider [] (assemblyContentProvider: AssemblyContentProvider) = +type internal AddOpenCodeFixProvider [] (assemblyContentProvider: AssemblyContentProvider) = inherit CodeFixProvider() let fixUnderscoresInMenuText (text: string) = text.Replace("_", "__") @@ -73,14 +67,14 @@ type internal FSharpAddOpenCodeFixProvider [] (assemblyCon let! sourceText = document.GetTextAsync(context.CancellationToken) let! parseResults, checkResults = - document.GetFSharpParseAndCheckResultsAsync(nameof (FSharpAddOpenCodeFixProvider)) + document.GetFSharpParseAndCheckResultsAsync(nameof (AddOpenCodeFixProvider)) |> liftAsync let line = sourceText.Lines.GetLineFromPosition(context.Span.End) let linePos = sourceText.Lines.GetLinePosition(context.Span.End) let! defines, langVersion = - document.GetFSharpCompilationDefinesAndLangVersionAsync(nameof (FSharpAddOpenCodeFixProvider)) + document.GetFSharpCompilationDefinesAndLangVersionAsync(nameof (AddOpenCodeFixProvider)) |> liftAsync let! symbol = diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddTypeAnnotationToObjectOfIndeterminateType.fs similarity index 92% rename from vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/AddTypeAnnotationToObjectOfIndeterminateType.fs index 4e73d9bc13..5f048d4d9d 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddTypeAnnotationToObjectOfIndeterminateType.fs @@ -4,21 +4,18 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System open System.Composition -open System.Threading open System.Threading.Tasks open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes -open FSharp.Compiler open FSharp.Compiler.EditorServices open FSharp.Compiler.Text open FSharp.Compiler.Symbols -open Microsoft.CodeAnalysis.CodeActions [] -type internal FSharpAddTypeAnnotationToObjectOfIndeterminateTypeFixProvider [] () = +type internal AddTypeAnnotationToObjectOfIndeterminateTypeFixProvider [] () = inherit CodeFixProvider() static let title = SR.AddTypeAnnotation() @@ -42,11 +39,11 @@ type internal FSharpAddTypeAnnotationToObjectOfIndeterminateTypeFixProvider [ liftAsync let decl = diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ChangePrefixNegationToInfixSubtraction.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ChangePrefixNegationToInfixSubtraction.fs similarity index 95% rename from vsintegration/src/FSharp.Editor/CodeFix/ChangePrefixNegationToInfixSubtraction.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/ChangePrefixNegationToInfixSubtraction.fs index 7f0a69ef4b..62fda0ff51 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ChangePrefixNegationToInfixSubtraction.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ChangePrefixNegationToInfixSubtraction.fs @@ -11,7 +11,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes [] -type internal FSharpChangePrefixNegationToInfixSubtractionodeFixProvider() = +type internal ChangePrefixNegationToInfixSubtractionodeFixProvider() = inherit CodeFixProvider() static let title = SR.ChangePrefixNegationToInfixSubtraction() diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ChangeRefCellDerefToNotExpression.fs similarity index 86% rename from vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/ChangeRefCellDerefToNotExpression.fs index e084a871a3..41e5701100 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ChangeRefCellDerefToNotExpression.fs @@ -10,7 +10,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes [] -type internal FSharpChangeRefCellDerefToNotExpressionCodeFixProvider [] () = +type internal ChangeRefCellDerefToNotExpressionCodeFixProvider [] () = inherit CodeFixProvider() static let title = SR.UseNotForNegation() @@ -22,7 +22,7 @@ type internal FSharpChangeRefCellDerefToNotExpressionCodeFixProvider [ liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ChangeToUpcast.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ChangeToUpcast.fs similarity index 97% rename from vsintegration/src/FSharp.Editor/CodeFix/ChangeToUpcast.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/ChangeToUpcast.fs index 26d693a875..4d17fba03a 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ChangeToUpcast.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ChangeToUpcast.fs @@ -11,7 +11,7 @@ open Microsoft.CodeAnalysis.CodeFixes open CancellableTasks [] -type internal FSharpChangeToUpcastCodeFixProvider() = +type internal ChangeToUpcastCodeFixProvider() = inherit CodeFixProvider() override _.FixableDiagnosticIds = ImmutableArray.Create("FS3198") diff --git a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs b/vsintegration/src/FSharp.Editor/CodeFixes/CodeFixHelpers.fs similarity index 100% rename from vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/CodeFixHelpers.fs diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs similarity index 92% rename from vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs index 7e8f1c71e7..978f8e328f 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs @@ -9,7 +9,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes [] -type internal FSharpConvertCSharpLambdaToFSharpLambdaCodeFixProvider [] () = +type internal ConvertCSharpLambdaToFSharpLambdaCodeFixProvider [] () = inherit CodeFixProvider() static let title = SR.UseFSharpLambda() @@ -19,7 +19,7 @@ type internal FSharpConvertCSharpLambdaToFSharpLambdaCodeFixProvider [ liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpUsingToFSharpOpen.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpUsingToFSharpOpen.fs similarity index 96% rename from vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpUsingToFSharpOpen.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpUsingToFSharpOpen.fs index a1d45db3e4..0619a7e021 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpUsingToFSharpOpen.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpUsingToFSharpOpen.fs @@ -6,12 +6,11 @@ open System open System.Composition open System.Collections.Immutable -open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes [] -type internal FSharpConvertCSharpUsingToFSharpOpen [] () = +type internal ConvertCSharpUsingToFSharpOpenCodeFixProvider [] () = inherit CodeFixProvider() static let title = SR.ConvertCSharpUsingToFSharpOpen() diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertToAnonymousRecord.fs similarity index 92% rename from vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/ConvertToAnonymousRecord.fs index 1b2fbe4921..098045b929 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertToAnonymousRecord.fs @@ -11,7 +11,7 @@ open Microsoft.CodeAnalysis.CodeFixes open CancellableTasks [] -type internal FSharpConvertToAnonymousRecordCodeFixProvider [] () = +type internal ConvertToAnonymousRecordCodeFixProvider [] () = inherit CodeFixProvider() static let title = SR.ConvertToAnonymousRecord() @@ -25,7 +25,7 @@ type internal FSharpConvertToAnonymousRecordCodeFixProvider [] -type internal FSharpConvertToNotEqualsEqualityExpressionCodeFixProvider() = +type internal ConvertToNotEqualsEqualityExpressionCodeFixProvider() = inherit CodeFixProvider() static let title = SR.ConvertToNotEqualsEqualityExpression() diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToSingleEqualsEqualityExpression.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertToSingleEqualsEqualityExpression.fs similarity index 94% rename from vsintegration/src/FSharp.Editor/CodeFix/ConvertToSingleEqualsEqualityExpression.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/ConvertToSingleEqualsEqualityExpression.fs index f38be2aed0..a243c7c63a 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToSingleEqualsEqualityExpression.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertToSingleEqualsEqualityExpression.fs @@ -10,7 +10,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes [] -type internal FSharpConvertToSingleEqualsEqualityExpressionCodeFixProvider() = +type internal ConvertToSingleEqualsEqualityExpressionCodeFixProvider() = inherit CodeFixProvider() static let title = SR.ConvertToSingleEqualsEqualityExpression() diff --git a/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs b/vsintegration/src/FSharp.Editor/CodeFixes/FixIndexerAccess.fs similarity index 98% rename from vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/FixIndexerAccess.fs index 71b5655509..400982edd7 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/FixIndexerAccess.fs @@ -13,7 +13,7 @@ open Microsoft.CodeAnalysis.CodeFixes open FSharp.Compiler.Diagnostics [] -type internal LegacyFsharpFixAddDotToIndexerAccess() = +type internal LegacyFixAddDotToIndexerAccessCodeFixProvider() = inherit CodeFixProvider() static let title = CompilerDiagnostics.GetErrorMessage FSharpDiagnosticKind.AddIndexerDot diff --git a/vsintegration/src/FSharp.Editor/CodeFix/IFSharpCodeFix.fs b/vsintegration/src/FSharp.Editor/CodeFixes/IFSharpCodeFix.fs similarity index 100% rename from vsintegration/src/FSharp.Editor/CodeFix/IFSharpCodeFix.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/IFSharpCodeFix.fs diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ImplementInterfaceCodeFixProvider.fs similarity index 98% rename from vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/ImplementInterfaceCodeFixProvider.fs index d0630bbba0..ad0ca1c0f7 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ImplementInterfaceCodeFixProvider.fs @@ -32,7 +32,7 @@ type internal InterfaceState = } [] -type internal FSharpImplementInterfaceCodeFixProvider [] () = +type internal ImplementInterfaceCodeFixProvider [] () = inherit CodeFixProvider() let queryInterfaceState appendBracketAt (pos: pos) (tokens: Tokenizer.SavedTokenInfo[]) (ast: ParsedInput) = @@ -190,7 +190,7 @@ type internal FSharpImplementInterfaceCodeFixProvider [] ( override _.RegisterCodeFixesAsync context : Task = asyncMaybe { let! parseResults, checkFileResults = - context.Document.GetFSharpParseAndCheckResultsAsync(nameof (FSharpImplementInterfaceCodeFixProvider)) + context.Document.GetFSharpParseAndCheckResultsAsync(nameof (ImplementInterfaceCodeFixProvider)) |> liftAsync let cancellationToken = context.CancellationToken @@ -198,7 +198,7 @@ type internal FSharpImplementInterfaceCodeFixProvider [] ( let textLine = sourceText.Lines.GetLineFromPosition context.Span.Start let! _, _, parsingOptions, _ = - context.Document.GetFSharpCompilationOptionsAsync(nameof (FSharpImplementInterfaceCodeFixProvider)) + context.Document.GetFSharpCompilationOptionsAsync(nameof (ImplementInterfaceCodeFixProvider)) |> liftAsync let defines = CompilerEnvironment.GetConditionalDefinesForEditing parsingOptions diff --git a/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs b/vsintegration/src/FSharp.Editor/CodeFixes/MakeDeclarationMutable.fs similarity index 93% rename from vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/MakeDeclarationMutable.fs index e230e719c9..d3ad0ff821 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/MakeDeclarationMutable.fs @@ -13,7 +13,7 @@ open FSharp.Compiler.EditorServices open FSharp.Compiler.Text [] -type internal FSharpMakeDeclarationMutableFixProvider [] () = +type internal MakeDeclarationMutableFixProvider [] () = inherit CodeFixProvider() static let title = SR.MakeDeclarationMutable() @@ -33,7 +33,7 @@ type internal FSharpMakeDeclarationMutableFixProvider [] ( SymbolLookupKind.Greedy, false, false, - nameof (FSharpMakeDeclarationMutableFixProvider) + nameof (MakeDeclarationMutableFixProvider) ) let! sourceText = document.GetTextAsync() |> liftTaskAsync @@ -42,7 +42,7 @@ type internal FSharpMakeDeclarationMutableFixProvider [] ( let fcsTextLineNumber = Line.fromZ textLinePos.Line let! parseFileResults, checkFileResults = - document.GetFSharpParseAndCheckResultsAsync(nameof (FSharpMakeDeclarationMutableFixProvider)) + document.GetFSharpParseAndCheckResultsAsync(nameof (MakeDeclarationMutableFixProvider)) |> liftAsync let decl = diff --git a/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs b/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs similarity index 93% rename from vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs index 78fae0522f..c0c245fe06 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs @@ -10,7 +10,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes [] -type internal FSharpMakeOuterBindingRecursiveCodeFixProvider [] () = +type internal MakeOuterBindingRecursiveCodeFixProvider [] () = inherit CodeFixProvider() override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039") @@ -18,7 +18,7 @@ type internal FSharpMakeOuterBindingRecursiveCodeFixProvider [ liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/MissingReferenceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFixes/MissingReferenceCodeFixProvider.fs similarity index 100% rename from vsintegration/src/FSharp.Editor/CodeFix/MissingReferenceCodeFixProvider.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/MissingReferenceCodeFixProvider.fs diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs similarity index 93% rename from vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs index d290549fa1..ceaaa8bcbc 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs @@ -12,7 +12,7 @@ open Microsoft.CodeAnalysis.CodeActions open FSharp.Compiler.Diagnostics [] -type internal FSharpProposeUpperCaseLabelCodeFixProvider [] () = +type internal ProposeUpperCaseLabelCodeFixProvider [] () = inherit CodeFixProvider() override _.FixableDiagnosticIds = ImmutableArray.Create("FS0053") diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs b/vsintegration/src/FSharp.Editor/CodeFixes/RemoveReturnOrYield.fs similarity index 93% rename from vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/RemoveReturnOrYield.fs index 16f33212ef..728faa005a 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/RemoveReturnOrYield.fs @@ -11,7 +11,7 @@ open Microsoft.CodeAnalysis.CodeFixes open CancellableTasks [] -type internal FSharpRemoveReturnOrYieldCodeFixProvider [] () = +type internal RemoveReturnOrYieldCodeFixProvider [] () = inherit CodeFixProvider() override _.FixableDiagnosticIds = ImmutableArray.Create("FS0747", "FS0748") @@ -23,7 +23,7 @@ type internal FSharpRemoveReturnOrYieldCodeFixProvider [] cancellableTask { let! cancellationToken = CancellableTask.getCurrentCancellationToken () - let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpRemoveReturnOrYieldCodeFixProvider)) + let! parseResults = document.GetFSharpParseResultsAsync(nameof (RemoveReturnOrYieldCodeFixProvider)) let! sourceText = document.GetTextAsync(cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs b/vsintegration/src/FSharp.Editor/CodeFixes/RemoveSuperflousCaptureForUnionCaseWithNoData.fs similarity index 95% rename from vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/RemoveSuperflousCaptureForUnionCaseWithNoData.fs index d2fcfa51b1..349f4f502f 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/RemoveSuperflousCaptureForUnionCaseWithNoData.fs @@ -8,14 +8,13 @@ open System.Threading.Tasks open System.Collections.Immutable open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.CodeActions open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open FSharp.Compiler.EditorServices [] -type internal RemoveSuperflousCaptureForUnionCaseWithNoDataProvider [] () = +type internal RemoveSuperflousCaptureForUnionCaseWithNoDataCodeFixProvider [] () = inherit CodeFixProvider() diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs b/vsintegration/src/FSharp.Editor/CodeFixes/RemoveUnusedBinding.fs similarity index 93% rename from vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/RemoveUnusedBinding.fs index 9335eda000..eb55bd3f0d 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/RemoveUnusedBinding.fs @@ -9,14 +9,11 @@ open System.Threading.Tasks open System.Collections.Immutable open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.CodeActions open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes -open FSharp.Compiler.EditorServices - [] -type internal FSharpRemoveUnusedBindingCodeFixProvider [] () = +type internal RemoveUnusedBindingCodeFixProvider [] () = inherit CodeFixProvider() @@ -27,7 +24,7 @@ type internal FSharpRemoveUnusedBindingCodeFixProvider [] backgroundTask { let! sourceText = document.GetTextAsync(ct) - let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpRemoveUnusedBindingCodeFixProvider)) + let! parseResults = document.GetFSharpParseResultsAsync(nameof (RemoveUnusedBindingCodeFixProvider)) let changes = seq { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs b/vsintegration/src/FSharp.Editor/CodeFixes/RemoveUnusedOpens.fs similarity index 92% rename from vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/RemoveUnusedOpens.fs index b57f35bd61..ac1e17bc82 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/RemoveUnusedOpens.fs @@ -10,13 +10,10 @@ open System.Collections.Immutable open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes -open Microsoft.CodeAnalysis.CodeActions open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics -open FSharp.Compiler.Text - [] -type internal FSharpRemoveUnusedOpensCodeFixProvider [] () = +type internal RemoveUnusedOpensCodeFixProvider [] () = inherit CodeFixProvider() static let title = SR.RemoveUnusedOpens() diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs b/vsintegration/src/FSharp.Editor/CodeFixes/RenameParamToMatchSignature.fs similarity index 95% rename from vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/RenameParamToMatchSignature.fs index fb916fbb84..1dfc34e62b 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/RenameParamToMatchSignature.fs @@ -11,14 +11,12 @@ open System.Text.RegularExpressions open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes -open Microsoft.CodeAnalysis.CodeActions open Microsoft.VisualStudio.FSharp.Editor.SymbolHelpers -open FSharp.Compiler.Diagnostics open FSharp.Compiler.Tokenization.FSharpKeywords [] -type internal FSharpRenameParamToMatchSignature [] () = +type internal RenameParamToMatchSignatureCodeFixProvider [] () = inherit CodeFixProvider() diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs b/vsintegration/src/FSharp.Editor/CodeFixes/RenameUnusedValue.fs similarity index 96% rename from vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/RenameUnusedValue.fs index 63f8c0af54..b3afac2ae0 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/RenameUnusedValue.fs @@ -11,11 +11,7 @@ open System.Collections.Immutable open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes -open Microsoft.CodeAnalysis.CodeActions -open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics -open FSharp.Compiler -open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Symbols open FSharp.Compiler.Syntax @@ -46,7 +42,7 @@ module UnusedCodeFixHelper = async { return None } [] -type internal FSharpPrefixUnusedValueWithUnderscoreCodeFixProvider [] () = +type internal PrefixUnusedValueWithUnderscoreCodeFixProvider [] () = inherit CodeFixProvider() diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ReplaceWithSuggestion.fs similarity index 93% rename from vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/ReplaceWithSuggestion.fs index 7b82bcf3ed..d3a4c3adea 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ReplaceWithSuggestion.fs @@ -13,10 +13,9 @@ open FSharp.Compiler.Diagnostics open FSharp.Compiler.EditorServices open FSharp.Compiler.Syntax open FSharp.Compiler.Text -open FSharp.Compiler.Tokenization [] -type internal FSharpReplaceWithSuggestionCodeFixProvider [] (settings: EditorOptions) = +type internal ReplaceWithSuggestionCodeFixProvider [] (settings: EditorOptions) = inherit CodeFixProvider() override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039", "FS1129", "FS0495") @@ -28,7 +27,7 @@ type internal FSharpReplaceWithSuggestionCodeFixProvider [ let document = context.Document let! parseFileResults, checkFileResults = - document.GetFSharpParseAndCheckResultsAsync(nameof (FSharpReplaceWithSuggestionCodeFixProvider)) + document.GetFSharpParseAndCheckResultsAsync(nameof (ReplaceWithSuggestionCodeFixProvider)) |> liftAsync // This is all needed to get a declaration list diff --git a/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs b/vsintegration/src/FSharp.Editor/CodeFixes/SimplifyName.fs similarity index 93% rename from vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/SimplifyName.fs index dae2e3095a..0f31d9e68a 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/SimplifyName.fs @@ -10,13 +10,10 @@ open System.Collections.Immutable open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes -open Microsoft.CodeAnalysis.CodeActions open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics -open FSharp.Compiler.Text - [] -type internal FSharpSimplifyNameCodeFixProvider() = +type internal SimplifyNameCodeFixProvider() = inherit CodeFixProvider() override _.FixableDiagnosticIds = diff --git a/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs b/vsintegration/src/FSharp.Editor/CodeFixes/UseMutationWhenValueIsMutable.fs similarity index 93% rename from vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/UseMutationWhenValueIsMutable.fs index 591de79e44..bf2735b043 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/UseMutationWhenValueIsMutable.fs @@ -14,7 +14,7 @@ open FSharp.Compiler.Symbols open FSharp.Compiler.Text [] -type internal FSharpUseMutationWhenValueIsMutableFixProvider [] () = +type internal UseMutationWhenValueIsMutableCodeFixProvider [] () = inherit CodeFixProvider() static let title = SR.UseMutationWhenValueIsMutable() @@ -46,11 +46,11 @@ type internal FSharpUseMutationWhenValueIsMutableFixProvider [ liftAsync let! symbolUse = diff --git a/vsintegration/src/FSharp.Editor/CodeFix/UseTripleQuotedInterpolation.fs b/vsintegration/src/FSharp.Editor/CodeFixes/UseTripleQuotedInterpolation.fs similarity index 91% rename from vsintegration/src/FSharp.Editor/CodeFix/UseTripleQuotedInterpolation.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/UseTripleQuotedInterpolation.fs index 1f229f6e7a..acd4de0b84 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/UseTripleQuotedInterpolation.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/UseTripleQuotedInterpolation.fs @@ -9,7 +9,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes [] -type internal FSharpUseTripleQuotedInterpolationCodeFixProvider [] () = +type internal UseTripleQuotedInterpolationCodeFixProvider [] () = inherit CodeFixProvider() static let title = SR.UseTripleQuotedInterpolation() @@ -18,7 +18,7 @@ type internal FSharpUseTripleQuotedInterpolationCodeFixProvider [ liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs b/vsintegration/src/FSharp.Editor/CodeFixes/WrapExpressionInParentheses.fs similarity index 94% rename from vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs rename to vsintegration/src/FSharp.Editor/CodeFixes/WrapExpressionInParentheses.fs index 2c04415d2b..39a247263c 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/WrapExpressionInParentheses.fs @@ -11,7 +11,7 @@ open Microsoft.CodeAnalysis.Text open CancellableTasks [] -type internal FSharpWrapExpressionInParenthesesFixProvider() = +type internal WrapExpressionInParenthesesCodeFixProvider() = inherit CodeFixProvider() static let title = SR.WrapExpressionInParentheses() diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index a2673c2d55..d50ba5d9b6 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -101,40 +101,40 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddInstanceMemberParameterTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddInstanceMemberParameterTests.fs index 6737045f7f..b47462f7be 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddInstanceMemberParameterTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddInstanceMemberParameterTests.fs @@ -7,7 +7,7 @@ open Xunit open CodeFixTestFramework -let private codeFix = FSharpAddInstanceMemberParameterCodeFixProvider() +let private codeFix = AddInstanceMemberParameterCodeFixProvider() let private diagnostic = 0673 // This instance member needs a parameter to represent the object being invoked... [] diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddMissingRecToMutuallyRecFunctionsTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddMissingRecToMutuallyRecFunctionsTests.fs index 04e1bbd492..a3d1bc3d52 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddMissingRecToMutuallyRecFunctionsTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddMissingRecToMutuallyRecFunctionsTests.fs @@ -7,7 +7,7 @@ open Xunit open CodeFixTestFramework -let private codeFix = FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider() +let private codeFix = AddMissingRecToMutuallyRecFunctionsCodeFixProvider() let private diagnostic = 0576 // The declaration form 'let ... and ...' for non-recursive bindings is not used in F# code... // TODO: write some negative test cases here diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ChangeToUpcastTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ChangeToUpcastTests.fs index d47f320d28..e2a1522e84 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ChangeToUpcastTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ChangeToUpcastTests.fs @@ -7,7 +7,7 @@ open Xunit open CodeFixTestFramework -let private codeFix = FSharpChangeToUpcastCodeFixProvider() +let private codeFix = ChangeToUpcastCodeFixProvider() let private diagnostic = 3198 // The conversion is an upcast, not a downcast... // Test cases are taken from the original PR: diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertToAnonymousRecordTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertToAnonymousRecordTests.fs index 33207bddaa..55af0a5e04 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertToAnonymousRecordTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertToAnonymousRecordTests.fs @@ -7,7 +7,7 @@ open Xunit open CodeFixTestFramework -let private codeFix = FSharpConvertToAnonymousRecordCodeFixProvider() +let private codeFix = ConvertToAnonymousRecordCodeFixProvider() let private diagnostic = 0039 // ... is not defined... [] diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/RemoveReturnOrYieldTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/RemoveReturnOrYieldTests.fs index 83c841f198..a4eaf01e26 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/RemoveReturnOrYieldTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/RemoveReturnOrYieldTests.fs @@ -7,7 +7,7 @@ open Xunit open CodeFixTestFramework -let private codeFix = FSharpRemoveReturnOrYieldCodeFixProvider() +let private codeFix = RemoveReturnOrYieldCodeFixProvider() let private yieldDiagnostic = 0747 // This construct may only be used within list, array and sequence expressions... let private returnDiagnostic = 0748 // This construct may only be used with computation expressions... diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/FSharpWrapExpressionInParenthesesFixProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/WrapExpressionInParenthesesTests.fs similarity index 93% rename from vsintegration/tests/FSharp.Editor.Tests/CodeFixes/FSharpWrapExpressionInParenthesesFixProviderTests.fs rename to vsintegration/tests/FSharp.Editor.Tests/CodeFixes/WrapExpressionInParenthesesTests.fs index 32009583e0..bd22ca5696 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/FSharpWrapExpressionInParenthesesFixProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/WrapExpressionInParenthesesTests.fs @@ -7,7 +7,7 @@ open Xunit open CodeFixTestFramework -let private codeFix = FSharpWrapExpressionInParenthesesFixProvider() +let private codeFix = WrapExpressionInParenthesesCodeFixProvider() let private diagnostic = 0597 // ... arguments involving function or method applications should be parenthesized // Test case is taken from the original PR: diff --git a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj index f539937e07..a943a2932c 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj +++ b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj @@ -37,7 +37,7 @@ - + diff --git a/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs b/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs index 3fed33dc98..7d92ec6715 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs @@ -347,3 +347,19 @@ type RoslynTestHelpers private () = RoslynTestHelpers.CreateSolution(code, options = options) |> RoslynTestHelpers.GetSingleDocument + + static member GetFsiAndFsDocuments (fsiCode: string) (fsCode: string) = + let projectId = ProjectId.CreateNewId() + let projFilePath = "C:\\test.fsproj" + + let fsiDocInfo = + RoslynTestHelpers.CreateDocumentInfo projectId "C:\\test.fsi" fsiCode + + let fsDocInfo = RoslynTestHelpers.CreateDocumentInfo projectId "C:\\test.fs" fsCode + + let projInfo = + RoslynTestHelpers.CreateProjectInfo projectId projFilePath [ fsiDocInfo; fsDocInfo ] + + let solution = RoslynTestHelpers.CreateSolution [ projInfo ] + let project = solution.Projects |> Seq.exactlyOne + project.Documents diff --git a/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs b/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs index d252d678d2..1ea7b95f79 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs @@ -35,32 +35,6 @@ module HintTestFramework = Tooltip = tooltip } - let getFsDocument code = - // I don't know, without this lib some symbols are just not loaded - let options = - { RoslynTestHelpers.DefaultProjectOptions with - OtherOptions = [| "--targetprofile:netcore" |] - } - - RoslynTestHelpers.CreateSolution(code, options = options) - |> RoslynTestHelpers.GetSingleDocument - - let getFsiAndFsDocuments (fsiCode: string) (fsCode: string) = - let projectId = ProjectId.CreateNewId() - let projFilePath = "C:\\test.fsproj" - - let fsiDocInfo = - RoslynTestHelpers.CreateDocumentInfo projectId "C:\\test.fsi" fsiCode - - let fsDocInfo = RoslynTestHelpers.CreateDocumentInfo projectId "C:\\test.fs" fsCode - - let projInfo = - RoslynTestHelpers.CreateProjectInfo projectId projFilePath [ fsiDocInfo; fsDocInfo ] - - let solution = RoslynTestHelpers.CreateSolution [ projInfo ] - let project = solution.Projects |> Seq.exactlyOne - project.Documents - let getHints (document: Document) hintKinds = let task = cancellableTask { diff --git a/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineParameterNameHintTests.fs b/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineParameterNameHintTests.fs index c1cf532607..512d16f108 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineParameterNameHintTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineParameterNameHintTests.fs @@ -1,125 +1,126 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Editor.Tests.Hints +module FSharp.Editor.Tests.Hints.InlineParameterNameHintTests open Xunit -open HintTestFramework + +open FSharp.Editor.Tests.Helpers open FSharp.Test -module InlineParameterNameHintTests = +open HintTestFramework - [] - let ``Hint is shown for a let binding`` () = - let code = - """ +[] +let ``Hint is shown for a let binding`` () = + let code = + """ let greet friend = $"hello {friend}" let greeting = greet "darkness" """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let expected = - [ - { - Content = "friend = " - Location = (2, 22) - Tooltip = "parameter friend" - } - ] + let expected = + [ + { + Content = "friend = " + Location = (2, 22) + Tooltip = "parameter friend" + } + ] - let actual = getParameterNameHints document + let actual = getParameterNameHints document - Assert.Equal(expected, actual) + Assert.Equal(expected, actual) - [] - let ``Hints are shown for multiple function calls`` () = - let code = - """ +[] +let ``Hints are shown for multiple function calls`` () = + let code = + """ let greet friend = $"hello {friend}" let greeting1 = greet "Noel" let greeting2 = greet "Liam" """ - let document = getFsDocument code - - let expected = - [ - { - Content = "friend = " - Location = (2, 23) - Tooltip = "parameter friend" - } - { - Content = "friend = " - Location = (3, 23) - Tooltip = "parameter friend" - } - ] - - let actual = getParameterNameHints document - - Assert.Equal(expected, actual) - - [] - let ``Hints are shown for multiple parameters`` () = - let code = - """ + let document = RoslynTestHelpers.GetFsDocument code + + let expected = + [ + { + Content = "friend = " + Location = (2, 23) + Tooltip = "parameter friend" + } + { + Content = "friend = " + Location = (3, 23) + Tooltip = "parameter friend" + } + ] + + let actual = getParameterNameHints document + + Assert.Equal(expected, actual) + +[] +let ``Hints are shown for multiple parameters`` () = + let code = + """ let greet friend1 friend2 = $"hello {friend1} and {friend2}" let greeting = greet "Liam" "Noel" """ - let document = getFsDocument code - - let expected = - [ - { - Content = "friend1 = " - Location = (2, 22) - Tooltip = "parameter friend1" - } - { - Content = "friend2 = " - Location = (2, 29) - Tooltip = "parameter friend2" - } - ] - - let actual = getParameterNameHints document - - Assert.Equal(expected, actual) - - [] - let ``Hints are shown for tuple items`` () = - let code = - """ + let document = RoslynTestHelpers.GetFsDocument code + + let expected = + [ + { + Content = "friend1 = " + Location = (2, 22) + Tooltip = "parameter friend1" + } + { + Content = "friend2 = " + Location = (2, 29) + Tooltip = "parameter friend2" + } + ] + + let actual = getParameterNameHints document + + Assert.Equal(expected, actual) + +[] +let ``Hints are shown for tuple items`` () = + let code = + """ let greet (friend1, friend2) = $"hello {friend1} and {friend2}" let greeting = greet ("Liam", "Noel") """ - let document = getFsDocument code - - let expected = - [ - { - Content = "friend1 = " - Location = (2, 23) - Tooltip = "parameter friend1" - } - { - Content = "friend2 = " - Location = (2, 31) - Tooltip = "parameter friend2" - } - ] - - let actual = getParameterNameHints document - - Assert.Equal(expected, actual) - - [] - let ``Hints are shown for active patterns`` () = - let code = - """ + let document = RoslynTestHelpers.GetFsDocument code + + let expected = + [ + { + Content = "friend1 = " + Location = (2, 23) + Tooltip = "parameter friend1" + } + { + Content = "friend2 = " + Location = (2, 31) + Tooltip = "parameter friend2" + } + ] + + let actual = getParameterNameHints document + + Assert.Equal(expected, actual) + +[] +let ``Hints are shown for active patterns`` () = + let code = + """ let (|Even|Odd|) n = if n % 2 = 0 then Even else Odd @@ -133,96 +134,96 @@ let even = evenOrOdd 42 let odd = evenOrOdd 41 """ - let document = getFsDocument code - - let expected = - [ - { - Content = "number = " - Location = (10, 22) - Tooltip = "parameter number" - } - { - Content = "number = " - Location = (11, 21) - Tooltip = "parameter number" - } - ] - - let actual = getParameterNameHints document - - Assert.Equal(expected, actual) - - [] // here we don't want an empty hint before "x" - let ``Hints are not shown for nameless parameters`` () = - let code = - """ + let document = RoslynTestHelpers.GetFsDocument code + + let expected = + [ + { + Content = "number = " + Location = (10, 22) + Tooltip = "parameter number" + } + { + Content = "number = " + Location = (11, 21) + Tooltip = "parameter number" + } + ] + + let actual = getParameterNameHints document + + Assert.Equal(expected, actual) + +[] // here we don't want an empty hint before "x" +let ``Hints are not shown for nameless parameters`` () = + let code = + """ let exists predicate option = match option with | None -> false | Some x -> predicate x """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let result = getParameterNameHints document + let result = getParameterNameHints document - Assert.Empty(result) + Assert.Empty(result) - [] // here we don't want a useless (?) hint "value = " - let ``Hints are not shown for parameters of built-in operators`` () = - let code = - """ +[] // here we don't want a useless (?) hint "value = " +let ``Hints are not shown for parameters of built-in operators`` () = + let code = + """ let postTrue = not true """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let result = getParameterNameHints document + let result = getParameterNameHints document - Assert.Empty(result) + Assert.Empty(result) - [] - let ``Hints are not shown for parameters of custom operators`` () = - let code = - """ +[] +let ``Hints are not shown for parameters of custom operators`` () = + let code = + """ let (===) value1 value2 = value1 = value2 let c = "javascript" === "javascript" """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let result = getParameterNameHints document + let result = getParameterNameHints document - Assert.Empty(result) + Assert.Empty(result) - [] - let ``Hints are shown for method parameters`` () = - let code = - """ +[] +let ``Hints are shown for method parameters`` () = + let code = + """ let theAnswer = System.Console.WriteLine 42 """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let expected = - [ - { - Content = "value = " - Location = (1, 42) - Tooltip = "parameter value" - } - ] + let expected = + [ + { + Content = "value = " + Location = (1, 42) + Tooltip = "parameter value" + } + ] - let actual = getParameterNameHints document + let actual = getParameterNameHints document - Assert.Equal(expected, actual) + Assert.Equal(expected, actual) - [] - let ``Hints are shown for parameters of overloaded and curried methods`` () = - let code = - """ +[] +let ``Hints are shown for parameters of overloaded and curried methods`` () = + let code = + """ type C () = member _.Normal (alone: string) = 1 member _.Normal (what: string, what2: int) = 1 @@ -235,85 +236,85 @@ let a = c.Normal ("hmm", 2) let a = c.Normal "hmm" """ - let document = getFsDocument code - - let expected = - [ - { - Content = "curr1 = " - Location = (8, 20) - Tooltip = "parameter curr1" - } - { - Content = "curr2 = " - Location = (8, 27) - Tooltip = "parameter curr2" - } - { - Content = "x = " - Location = (8, 30) - Tooltip = "parameter x" - } - { - Content = "what = " - Location = (9, 19) - Tooltip = "parameter what" - } - { - Content = "what2 = " - Location = (9, 26) - Tooltip = "parameter what2" - } - { - Content = "alone = " - Location = (10, 18) - Tooltip = "parameter alone" - } - ] - - let actual = getParameterNameHints document - - Assert.Equal(expected, actual) - - [] - let ``Hints are shown for constructor parameters`` () = - let code = - """ + let document = RoslynTestHelpers.GetFsDocument code + + let expected = + [ + { + Content = "curr1 = " + Location = (8, 20) + Tooltip = "parameter curr1" + } + { + Content = "curr2 = " + Location = (8, 27) + Tooltip = "parameter curr2" + } + { + Content = "x = " + Location = (8, 30) + Tooltip = "parameter x" + } + { + Content = "what = " + Location = (9, 19) + Tooltip = "parameter what" + } + { + Content = "what2 = " + Location = (9, 26) + Tooltip = "parameter what2" + } + { + Content = "alone = " + Location = (10, 18) + Tooltip = "parameter alone" + } + ] + + let actual = getParameterNameHints document + + Assert.Equal(expected, actual) + +[] +let ``Hints are shown for constructor parameters`` () = + let code = + """ type C (blahFirst: int) = new (blah: int, blah2: string) = C blah let a = C (1, "") """ - let document = getFsDocument code - - let expected = - [ - { - Content = "blahFirst = " - Location = (2, 40) - Tooltip = "parameter blahFirst" - } - { - Content = "blah = " - Location = (4, 12) - Tooltip = "parameter blah" - } - { - Content = "blah2 = " - Location = (4, 15) - Tooltip = "parameter blah2" - } - ] - - let actual = getParameterNameHints document - - Assert.Equal(expected, actual) - - [] - let ``Hints are shown for discriminated union case fields with explicit names`` () = - let code = - """ + let document = RoslynTestHelpers.GetFsDocument code + + let expected = + [ + { + Content = "blahFirst = " + Location = (2, 40) + Tooltip = "parameter blahFirst" + } + { + Content = "blah = " + Location = (4, 12) + Tooltip = "parameter blah" + } + { + Content = "blah2 = " + Location = (4, 15) + Tooltip = "parameter blah2" + } + ] + + let actual = getParameterNameHints document + + Assert.Equal(expected, actual) + +[] +let ``Hints are shown for discriminated union case fields with explicit names`` () = + let code = + """ type Shape = | Square of side: int | Rectangle of width: int * height: int @@ -322,35 +323,35 @@ let a = Square 1 let b = Rectangle (1, 2) """ - let document = getFsDocument code - - let expected = - [ - { - Content = "side = " - Location = (5, 16) - Tooltip = "field side" - } - { - Content = "width = " - Location = (6, 20) - Tooltip = "field width" - } - { - Content = "height = " - Location = (6, 23) - Tooltip = "field height" - } - ] - - let actual = getParameterNameHints document - - Assert.Equal(expected, actual) - - [] - let ``Hints are not shown for discriminated union case fields with the same names as arguements`` () = - let code = - """ + let document = RoslynTestHelpers.GetFsDocument code + + let expected = + [ + { + Content = "side = " + Location = (5, 16) + Tooltip = "field side" + } + { + Content = "width = " + Location = (6, 20) + Tooltip = "field width" + } + { + Content = "height = " + Location = (6, 23) + Tooltip = "field height" + } + ] + + let actual = getParameterNameHints document + + Assert.Equal(expected, actual) + +[] +let ``Hints are not shown for discriminated union case fields with the same names as arguements`` () = + let code = + """ type Shape = | Square of side: int | Rectangle of width: int * height: int @@ -360,30 +361,30 @@ let a = Square 1 let b = Rectangle (width, 2) """ - let document = getFsDocument code - - let expected = - [ - { - Content = "side = " - Location = (6, 16) - Tooltip = "field side" - } - { - Content = "height = " - Location = (7, 27) - Tooltip = "field height" - } - ] - - let actual = getParameterNameHints document - - Assert.Equal(expected, actual) - - [] - let ``Hints for discriminated union case fields are not shown when names are generated`` () = - let code = - """ + let document = RoslynTestHelpers.GetFsDocument code + + let expected = + [ + { + Content = "side = " + Location = (6, 16) + Tooltip = "field side" + } + { + Content = "height = " + Location = (7, 27) + Tooltip = "field height" + } + ] + + let actual = getParameterNameHints document + + Assert.Equal(expected, actual) + +[] +let ``Hints for discriminated union case fields are not shown when names are generated`` () = + let code = + """ type Shape = | Triangle of side1: int * int * side3: int | Circle of int @@ -392,30 +393,30 @@ let c = Triangle (1, 2, 3) let d = Circle 1 """ - let document = getFsDocument code - - let expected = - [ - { - Content = "side1 = " - Location = (5, 19) - Tooltip = "field side1" - } - { - Content = "side3 = " - Location = (5, 25) - Tooltip = "field side3" - } - ] - - let actual = getParameterNameHints document - - Assert.Equal(expected, actual) - - [] - let ``Hints for discriminated union case fields are not shown when provided arguments don't match the expected count`` () = - let code = - """ + let document = RoslynTestHelpers.GetFsDocument code + + let expected = + [ + { + Content = "side1 = " + Location = (5, 19) + Tooltip = "field side1" + } + { + Content = "side3 = " + Location = (5, 25) + Tooltip = "field side3" + } + ] + + let actual = getParameterNameHints document + + Assert.Equal(expected, actual) + +[] +let ``Hints for discriminated union case fields are not shown when provided arguments don't match the expected count`` () = + let code = + """ type Shape = | Triangle of side1: int * side2: int * side3: int | Circle of int @@ -423,57 +424,57 @@ type Shape = let c = Triangle (1, 2) """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let actual = getParameterNameHints document + let actual = getParameterNameHints document - Assert.Empty(actual) + Assert.Empty(actual) - [] - let ``Hints for discriminated union case fields are not shown for Cons`` () = - let code = - """ +[] +let ``Hints for discriminated union case fields are not shown for Cons`` () = + let code = + """ type X = member _.Test() = 42 :: [42; 42] """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let actual = getParameterNameHints document + let actual = getParameterNameHints document - Assert.Empty(actual) + Assert.Empty(actual) - [] - let ``Hints are not shown in front of indexes`` () = - let code = - """ +[] +let ``Hints are not shown in front of indexes`` () = + let code = + """ let x = "test".Split("").[0].Split(""); """ - let document = getFsDocument code - - let expected = - [ - { - Content = "separator = " - Location = (1, 22) - Tooltip = "parameter separator" - } - { - Content = "separator = " - Location = (1, 36) - Tooltip = "parameter separator" - } - ] - - let actual = getParameterNameHints document - - Assert.Equal(expected, actual) - - [] - let ``Hints are not shown for optional parameters with specified names`` () = - let code = - """ + let document = RoslynTestHelpers.GetFsDocument code + + let expected = + [ + { + Content = "separator = " + Location = (1, 22) + Tooltip = "parameter separator" + } + { + Content = "separator = " + Location = (1, 36) + Tooltip = "parameter separator" + } + ] + + let actual = getParameterNameHints document + + Assert.Equal(expected, actual) + +[] +let ``Hints are not shown for optional parameters with specified names`` () = + let code = + """ type MyType() = member _.MyMethod(?beep: int, ?bap: int, ?boop: int) = () @@ -481,25 +482,25 @@ type MyType() = member this.Foo = this.MyMethod(3, boop = 4) """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let expected = - [ - { - Content = "beep = " - Location = (5, 37) - Tooltip = "parameter beep" - } - ] + let expected = + [ + { + Content = "beep = " + Location = (5, 37) + Tooltip = "parameter beep" + } + ] - let actual = getParameterNameHints document + let actual = getParameterNameHints document - Assert.Equal(expected, actual) + Assert.Equal(expected, actual) - [] - let ``Hints are not shown when all optional parameters are named`` () = - let code = - """ +[] +let ``Hints are not shown when all optional parameters are named`` () = + let code = + """ type MyType() = member _.MyMethod(?beep: int, ?bap : int, ?boop : int) = () @@ -507,85 +508,85 @@ type MyType() = member this.Foo = this.MyMethod(bap = 3, beep = 4) """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let actual = getParameterNameHints document + let actual = getParameterNameHints document - Assert.Empty(actual) + Assert.Empty(actual) - [] - let ``Hints are shown correctly for inner bindings`` () = - let code = - """ +[] +let ``Hints are shown correctly for inner bindings`` () = + let code = + """ let test sequences = sequences |> Seq.map (fun sequence -> sequence |> Seq.map (fun sequence' -> sequence' |> Seq.map (fun item -> item))) """ - let document = getFsDocument code - - let expected = - [ - { - Content = "mapping = " - Location = (3, 16) - Tooltip = "parameter mapping" - } - { - Content = "mapping = " - Location = (3, 53) - Tooltip = "parameter mapping" - } - { - Content = "mapping = " - Location = (3, 92) - Tooltip = "parameter mapping" - } - ] - - let actual = getParameterNameHints document - - Assert.Equal(expected, actual) - - [] - let ``Hints are shown correctly for custom operations`` () = - let code = - """ + let document = RoslynTestHelpers.GetFsDocument code + + let expected = + [ + { + Content = "mapping = " + Location = (3, 16) + Tooltip = "parameter mapping" + } + { + Content = "mapping = " + Location = (3, 53) + Tooltip = "parameter mapping" + } + { + Content = "mapping = " + Location = (3, 92) + Tooltip = "parameter mapping" + } + ] + + let actual = getParameterNameHints document + + Assert.Equal(expected, actual) + +[] +let ``Hints are shown correctly for custom operations`` () = + let code = + """ let q = query { for x in { 1 .. 10 } do select x } """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let expected = - [ - { - Content = "projection = " - Location = (1, 48) - Tooltip = "parameter projection" - } - ] + let expected = + [ + { + Content = "projection = " + Location = (1, 48) + Tooltip = "parameter projection" + } + ] - let actual = getParameterNameHints document + let actual = getParameterNameHints document - Assert.Equal(expected, actual) + Assert.Equal(expected, actual) - [] - let ``Hints are not shown for custom operations with only 1 parameter`` () = - let code = - """ +[] +let ``Hints are not shown for custom operations with only 1 parameter`` () = + let code = + """ let q = query { for _ in { 1 .. 10 } do count } """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let actual = getParameterNameHints document + let actual = getParameterNameHints document - Assert.Empty(actual) + Assert.Empty(actual) - [] - let ``Hints are not shown when parameter names coincide with variable names`` () = - let code = - """ +[] +let ``Hints are not shown when parameter names coincide with variable names`` () = + let code = + """ let getFullName name surname = $"{name} {surname}" let name = "Robert" @@ -593,25 +594,25 @@ let lastName = "Smith" let fullName = getFullName name lastName """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let expected = - [ - { - Content = "surname = " - Location = (5, 33) - Tooltip = "parameter surname" - } - ] + let expected = + [ + { + Content = "surname = " + Location = (5, 33) + Tooltip = "parameter surname" + } + ] - let actual = getParameterNameHints document + let actual = getParameterNameHints document - Assert.Equal(expected, actual) + Assert.Equal(expected, actual) - [] - let ``Hints don't break with multi-line arguments`` () = - let code = - """ +[] +let ``Hints don't break with multi-line arguments`` () = + let code = + """ None |> Option.map (fun x -> x + 5 @@ -619,56 +620,56 @@ None |> ignore """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let expected = - [ - { - Content = "mapping = " - Location = (2, 15) - Tooltip = "parameter mapping" - } - ] + let expected = + [ + { + Content = "mapping = " + Location = (2, 15) + Tooltip = "parameter mapping" + } + ] - let actual = getParameterNameHints document + let actual = getParameterNameHints document - Assert.Equal(expected, actual) + Assert.Equal(expected, actual) - [] - let ``Hints are shown correctly in type constructors mixed with functions`` () = - let code = - """ +[] +let ``Hints are shown correctly in type constructors mixed with functions`` () = + let code = + """ type X = | X of a: int list * b: string let x = X(List.map id [ 42 ], "") """ - let document = getFsDocument code - - let expected = - [ - { - Content = "a = " - Location = (3, 11) - Tooltip = "field a" - } - { - Content = "mapping = " - Location = (3, 20) - Tooltip = "parameter mapping" - } - { - Content = "list = " - Location = (3, 23) - Tooltip = "parameter list" - } - { - Content = "b = " - Location = (3, 31) - Tooltip = "field b" - } - ] - - let actual = getParameterNameHints document - - actual |> Assert.shouldBeEquivalentTo expected + let document = RoslynTestHelpers.GetFsDocument code + + let expected = + [ + { + Content = "a = " + Location = (3, 11) + Tooltip = "field a" + } + { + Content = "mapping = " + Location = (3, 20) + Tooltip = "parameter mapping" + } + { + Content = "list = " + Location = (3, 23) + Tooltip = "parameter list" + } + { + Content = "b = " + Location = (3, 31) + Tooltip = "field b" + } + ] + + let actual = getParameterNameHints document + + actual |> Assert.shouldBeEquivalentTo expected diff --git a/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineReturnTypeHintTests.fs b/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineReturnTypeHintTests.fs index 5aad963721..9e55e484f3 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineReturnTypeHintTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineReturnTypeHintTests.fs @@ -3,8 +3,11 @@ module FSharp.Editor.Tests.Hints.InlineReturnTypeHintTests open Xunit -open HintTestFramework + open FSharp.Test +open FSharp.Editor.Tests.Helpers + +open HintTestFramework [] let ``Hints are shown for let-bound function return types`` () = @@ -15,7 +18,7 @@ let func2 x = x + 1 let setConsoleOut = System.Console.SetOut """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code let result = getReturnTypeHints document @@ -49,7 +52,7 @@ type Answer = { Text: string } let getAnswer() = { Text = "42" } """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code let result = getReturnTypeHints document @@ -72,7 +75,7 @@ type Test() = member this.Func() = 3 """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code let result = getReturnTypeHints document @@ -91,7 +94,7 @@ type Test() = let ``Hints are shown for generic functions`` () = let code = "let func _a = 5" - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code let result = getReturnTypeHints document @@ -114,7 +117,7 @@ let ``Hints are shown for functions within expressions`` () = let func () = 2 """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code let result = getReturnTypeHints document @@ -133,7 +136,7 @@ let ``Hints are shown for functions within expressions`` () = let ``Hints are not shown for lambda bindings`` () = let code = "let func = fun () -> 3" - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code let result = getReturnTypeHints document @@ -145,7 +148,7 @@ let ``Hints are not shown for lambda bindings`` () = [ = [a]")>] let ``Hints are not shown when there's type annotation`` code = - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code let result = getReturnTypeHints document diff --git a/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineTypeHintTests.fs b/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineTypeHintTests.fs index f669e8bb80..01c108c746 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineTypeHintTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineTypeHintTests.fs @@ -1,281 +1,280 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Editor.Tests.Hints +module FSharp.Editor.Tests.Hints.InlineTypeHintTests open Xunit open HintTestFramework open FSharp.Test +open FSharp.Editor.Tests.Helpers -module InlineTypeHintTests = - - [] - let ``Hint is shown for a let binding`` () = - let code = - """ +[] +let ``Hint is shown for a let binding`` () = + let code = + """ type Song = { Artist: string; Title: string } let s = { Artist = "Moby"; Title = "Porcelain" } """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let expected = - [ - { - Content = ": Song" - Location = (3, 6) - Tooltip = "type Song" - } - ] + let expected = + [ + { + Content = ": Song" + Location = (3, 6) + Tooltip = "type Song" + } + ] - let actual = getTypeHints document + let actual = getTypeHints document - Assert.Equal(expected, actual) + Assert.Equal(expected, actual) - [] - let ``Hints are correct for builtin types`` () = - let code = - """ +[] +let ``Hints are correct for builtin types`` () = + let code = + """ let songName = "Happy House" """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let result = getTypeHints document + let result = getTypeHints document - let expected = - [ - { - Content = ": string" - Location = (1, 13) - Tooltip = "type string" - } - ] + let expected = + [ + { + Content = ": string" + Location = (1, 13) + Tooltip = "type string" + } + ] - Assert.Equal(expected, result) + Assert.Equal(expected, result) - [] - let ``Hint is shown for a parameter`` () = - let code = - """ +[] +let ``Hint is shown for a parameter`` () = + let code = + """ type Song = { Artist: string; Title: string } let whoSings s = s.Artist """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let expected = - [ - { - Content = ": Song" - Location = (3, 15) - Tooltip = "type Song" - } - ] + let expected = + [ + { + Content = ": Song" + Location = (3, 15) + Tooltip = "type Song" + } + ] - let actual = getTypeHints document + let actual = getTypeHints document - Assert.Equal(expected, actual) + Assert.Equal(expected, actual) - [] - let ``Hints are not shown in signature files`` () = - let fsiCode = - """ +[] +let ``Hints are not shown in signature files`` () = + let fsiCode = + """ module Test val numbers: int[] """ - let fsCode = - """ + let fsCode = + """ module Test let numbers = [|42|] """ - let fsiDocument = getFsiAndFsDocuments fsiCode fsCode |> Seq.head + let fsiDocument = RoslynTestHelpers.GetFsiAndFsDocuments fsiCode fsCode |> Seq.head - let result = getTypeHints fsiDocument + let result = getTypeHints fsiDocument - Assert.Empty(result) + Assert.Empty(result) - [] - let ``Hints are not shown for let-bound functions yet`` () = - let code = - """ +[] +let ``Hints are not shown for let-bound functions yet`` () = + let code = + """ let setConsoleOut = System.Console.SetOut """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let result = getTypeHints document + let result = getTypeHints document - Assert.Empty(result) + Assert.Empty(result) - [] - let ``Hint is not shown for a let binding when the type is manually specified`` () = - let code = - """ +[] +let ``Hint is not shown for a let binding when the type is manually specified`` () = + let code = + """ type Song = { Artist: string; Title: string } let s: Song = { Artist = "Moby"; Title = "Porcelain" } """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let result = getTypeHints document + let result = getTypeHints document - Assert.Empty(result) + Assert.Empty(result) - [] - let ``Hint is not shown for a parameter when the type is manually specified`` () = - let code = - """ +[] +let ``Hint is not shown for a parameter when the type is manually specified`` () = + let code = + """ type Song = { Artist: string; Title: string } let whoSings (s: Song) = s.Artist """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let result = getTypeHints document + let result = getTypeHints document - Assert.Empty(result) + Assert.Empty(result) - [] // here we don't want a hint after "this" - let ``Hint is not shown for type self-identifiers`` () = - let code = - """ +[] // here we don't want a hint after "this" +let ``Hint is not shown for type self-identifiers`` () = + let code = + """ type Song() = member this.GetName() = "Porcelain" """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let result = getTypeHints document + let result = getTypeHints document - Assert.Empty(result) + Assert.Empty(result) - [] // here we don't want a hint after "x" - let ``Hint is not shown for type aliases`` () = - let code = - """ +[] // here we don't want a hint after "x" +let ``Hint is not shown for type aliases`` () = + let code = + """ type Song() as x = member this.Name = "Porcelain" """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let result = getTypeHints document + let result = getTypeHints document - Assert.Empty(result) + Assert.Empty(result) - [] - let ``Hints are shown within lambdas`` () = - let code = - """ +[] +let ``Hints are shown within lambdas`` () = + let code = + """ let iamboring() = fun x -> x """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let expected = - [ - { - Content = ": 'a" - Location = (2, 10) - Tooltip = "type 'a" - } - ] + let expected = + [ + { + Content = ": 'a" + Location = (2, 10) + Tooltip = "type 'a" + } + ] - let actual = getTypeHints document + let actual = getTypeHints document - Assert.Equal(expected, actual) + Assert.Equal(expected, actual) - [] - let ``Hints are shown within lambdas with tuples`` () = - let code = - """ +[] +let ``Hints are shown within lambdas with tuples`` () = + let code = + """ let zip4 (l1: 'a list) (l2: 'b list) (l3: 'c list) (l4: 'd list) = List.zip l1 (List.zip3 l2 l3 l4) |> List.map (fun (x1, (x2, x3, x4)) -> (x1, x2, x3, x4)) """ - let document = getFsDocument code - - let expected = - [ - { - Content = ": 'a" - Location = (3, 25) - Tooltip = "type 'a" - } - { - Content = ": 'b" - Location = (3, 30) - Tooltip = "type 'b" - } - { - Content = ": 'c" - Location = (3, 34) - Tooltip = "type 'c" - } - { - Content = ": 'd" - Location = (3, 38) - Tooltip = "type 'd" - } - ] - - let actual = getTypeHints document - - actual |> Assert.shouldBeEquivalentTo expected - - [] - let ``Hints are not shown for lambda return types`` () = - let code = - """ + let document = RoslynTestHelpers.GetFsDocument code + + let expected = + [ + { + Content = ": 'a" + Location = (3, 25) + Tooltip = "type 'a" + } + { + Content = ": 'b" + Location = (3, 30) + Tooltip = "type 'b" + } + { + Content = ": 'c" + Location = (3, 34) + Tooltip = "type 'c" + } + { + Content = ": 'd" + Location = (3, 38) + Tooltip = "type 'd" + } + ] + + let actual = getTypeHints document + + actual |> Assert.shouldBeEquivalentTo expected + +[] +let ``Hints are not shown for lambda return types`` () = + let code = + """ let func = fun () -> 3 """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let result = getTypeHints document + let result = getTypeHints document - Assert.Empty(result) + Assert.Empty(result) - [] - let ``Hints are not shown for unfinished expressions`` () = - let code = - """ +[] +let ``Hints are not shown for unfinished expressions`` () = + let code = + """ let x """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let result = getTypeHints document + let result = getTypeHints document - Assert.Empty(result) + Assert.Empty(result) - [] - let ``Hints are not shown for unsolved types in _for_ expressions in collections`` () = - let code = - """ +[] +let ``Hints are not shown for unsolved types in _for_ expressions in collections`` () = + let code = + """ let _ = [ for x ] """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let result = getTypeHints document + let result = getTypeHints document - Assert.Empty(result) + Assert.Empty(result) - [] - let ``Hints are not shown for unsolved types in _for_ expressions within computational expressions`` () = - let code = - """ +[] +let ``Hints are not shown for unsolved types in _for_ expressions within computational expressions`` () = + let code = + """ do task { for x @@ -283,16 +282,16 @@ do task { } """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let result = getTypeHints document + let result = getTypeHints document - Assert.Empty(result) + Assert.Empty(result) - [] - let ``Hints are shown for IWSAM`` () = - let code = - """ +[] +let ``Hints are shown for IWSAM`` () = + let code = + """ type IAddition<'T when 'T :> IAddition<'T>> = static abstract op_Addition: 'T * 'T -> 'T @@ -302,30 +301,30 @@ type Number<'T when IAddition<'T>>(value: 'T) = static member op_Addition(a, b) = Number(a.Value + b.Value) """ - let document = getFsDocument code - - let expected = - [ - { - Content = ": Number<'T>" - Location = (7, 36) - Tooltip = "type Number`1" - } - { - Content = ": Number<'T>" - Location = (7, 39) - Tooltip = "type Number`1" - } - ] - - let actual = getTypeHints document - - actual |> Assert.shouldBeEquivalentTo expected - - [] - let ``Hints are not shown when type is specified`` () = - let code = - """ + let document = RoslynTestHelpers.GetFsDocument code + + let expected = + [ + { + Content = ": Number<'T>" + Location = (7, 36) + Tooltip = "type Number`1" + } + { + Content = ": Number<'T>" + Location = (7, 39) + Tooltip = "type Number`1" + } + ] + + let actual = getTypeHints document + + actual |> Assert.shouldBeEquivalentTo expected + +[] +let ``Hints are not shown when type is specified`` () = + let code = + """ type MyType() = member _.MyMethod(?beep: int, ?bap: int, ?boop: int) = () @@ -333,8 +332,8 @@ type MyType() = member this.Foo = this.MyMethod(bap = 3, boop = 4) """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let result = getTypeHints document + let result = getTypeHints document - Assert.Empty(result) + Assert.Empty(result) diff --git a/vsintegration/tests/FSharp.Editor.Tests/Hints/OverallHintExperienceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/Hints/OverallHintExperienceTests.fs index 195902a787..196ac5e8ed 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Hints/OverallHintExperienceTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Hints/OverallHintExperienceTests.fs @@ -1,18 +1,20 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Editor.Tests.Hints +module FSharp.Editor.Tests.Hints.OverallHintExperienceTests open Xunit -open HintTestFramework + open FSharp.Test +open FSharp.Editor.Tests.Helpers + +open HintTestFramework // just some kind of higher level testing -module OverallHintExperienceTests = - [] - let ``Baseline hints`` () = - let code = - """ +[] +let ``Baseline hints`` () = + let code = + """ type Song = { Artist: string; Title: string } let whoSings song = song.Artist @@ -32,82 +34,82 @@ let a = C 1 let cc = a.Normal "hmm" """ - let document = getFsDocument code + let document = RoslynTestHelpers.GetFsDocument code - let expected = - [ - { - Content = ": Song" - Location = (2, 18) - Tooltip = "type Song" - } - { - Content = ": string " - Location = (2, 19) - Tooltip = "type string" - } - { - Content = "song = " - Location = (4, 23) - Tooltip = "parameter song" - } - { - Content = ": string" - Location = (4, 11) - Tooltip = "type string" - } - { - Content = "side = " - Location = (10, 16) - Tooltip = "field side" - } - { - Content = ": Shape" - Location = (10, 6) - Tooltip = "type Shape" - } - { - Content = "width = " - Location = (11, 20) - Tooltip = "field width" - } - { - Content = "height = " - Location = (11, 23) - Tooltip = "field height" - } - { - Content = ": Shape" - Location = (11, 6) - Tooltip = "type Shape" - } - { - Content = ": int " - Location = (14, 36) - Tooltip = "type int" - } - { - Content = "blahFirst = " - Location = (16, 11) - Tooltip = "parameter blahFirst" - } - { - Content = ": C" - Location = (16, 6) - Tooltip = "type C" - } - { - Content = "what = " - Location = (17, 19) - Tooltip = "parameter what" - } - { - Content = ": int" - Location = (17, 7) - Tooltip = "type int" - } - ] + let expected = + [ + { + Content = ": Song" + Location = (2, 18) + Tooltip = "type Song" + } + { + Content = ": string " + Location = (2, 19) + Tooltip = "type string" + } + { + Content = "song = " + Location = (4, 23) + Tooltip = "parameter song" + } + { + Content = ": string" + Location = (4, 11) + Tooltip = "type string" + } + { + Content = "side = " + Location = (10, 16) + Tooltip = "field side" + } + { + Content = ": Shape" + Location = (10, 6) + Tooltip = "type Shape" + } + { + Content = "width = " + Location = (11, 20) + Tooltip = "field width" + } + { + Content = "height = " + Location = (11, 23) + Tooltip = "field height" + } + { + Content = ": Shape" + Location = (11, 6) + Tooltip = "type Shape" + } + { + Content = ": int " + Location = (14, 36) + Tooltip = "type int" + } + { + Content = "blahFirst = " + Location = (16, 11) + Tooltip = "parameter blahFirst" + } + { + Content = ": C" + Location = (16, 6) + Tooltip = "type C" + } + { + Content = "what = " + Location = (17, 19) + Tooltip = "parameter what" + } + { + Content = ": int" + Location = (17, 7) + Tooltip = "type int" + } + ] - let actual = getAllHints document + let actual = getAllHints document - actual |> Assert.shouldBeEquivalentTo expected + actual |> Assert.shouldBeEquivalentTo expected From d632aa4f8174e0536bbeec03afe28c3beee517d2 Mon Sep 17 00:00:00 2001 From: Janusz Wrobel Date: Mon, 19 Jun 2023 09:52:00 +0100 Subject: [PATCH 5/7] Improve performance of graph-based type-checking - don't validate filepaths unnecessarily (#15431) * Avoid checking the same filenames for every graph node. * Speedup `queryTrie` - don't memoize as that's costly, refactor the code --- .../GraphChecking/DependencyResolution.fs | 128 ++++++++++-------- .../GraphChecking/DependencyResolution.fsi | 3 +- .../TypeChecks/Graph/QueryTrieTests.fs | 2 +- 3 files changed, 72 insertions(+), 61 deletions(-) diff --git a/src/Compiler/Driver/GraphChecking/DependencyResolution.fs b/src/Compiler/Driver/GraphChecking/DependencyResolution.fs index b30d975359..4cf689963c 100644 --- a/src/Compiler/Driver/GraphChecking/DependencyResolution.fs +++ b/src/Compiler/Driver/GraphChecking/DependencyResolution.fs @@ -1,35 +1,45 @@ module internal FSharp.Compiler.GraphChecking.DependencyResolution -open FSharp.Compiler.IO open FSharp.Compiler.Syntax +open Internal.Utilities.Library -/// Find a path in the Trie. -/// This function could be cached in future if performance is an issue. -let queryTrie (trie: TrieNode) (path: LongIdentifier) : QueryTrieNodeResult = +/// Find a path from a starting TrieNode and return the end node or None +let queryTriePartial (trie: TrieNode) (path: LongIdentifier) : TrieNode option = let rec visit (currentNode: TrieNode) (path: LongIdentifier) = match path with - | [] -> failwith "path should not be empty" - | [ lastNodeFromPath ] -> - match currentNode.Children.TryGetValue(lastNodeFromPath) with - | false, _ -> QueryTrieNodeResult.NodeDoesNotExist - | true, childNode -> - if Set.isEmpty childNode.Files then - QueryTrieNodeResult.NodeDoesNotExposeData - else - QueryTrieNodeResult.NodeExposesData(childNode.Files) + // When we get through all partial identifiers, we've reached the node the full path points to. + | [] -> Some currentNode + // More segments to get through | currentPath :: restPath -> match currentNode.Children.TryGetValue(currentPath) with - | false, _ -> QueryTrieNodeResult.NodeDoesNotExist + | false, _ -> None | true, childNode -> visit childNode restPath visit trie path -let queryTrieMemoized (trie: TrieNode) : QueryTrie = - Internal.Utilities.Library.Tables.memoize (queryTrie trie) +let mapNodeToQueryResult (node: TrieNode option) : QueryTrieNodeResult = + match node with + | Some finalNode -> + if Set.isEmpty finalNode.Files then + QueryTrieNodeResult.NodeDoesNotExposeData + else + QueryTrieNodeResult.NodeExposesData(finalNode.Files) + | None -> QueryTrieNodeResult.NodeDoesNotExist + +/// Find a path in the Trie. +let queryTrie (trie: TrieNode) (path: LongIdentifier) : QueryTrieNodeResult = + queryTriePartial trie path |> mapNodeToQueryResult + +/// Same as 'queryTrie' but allows passing in a path combined from two parts, avoiding list allocation. +let queryTrieDual (trie: TrieNode) (path1: LongIdentifier) (path2: LongIdentifier) : QueryTrieNodeResult = + match queryTriePartial trie path1 with + | Some intermediateNode -> queryTriePartial intermediateNode path2 + | None -> None + |> mapNodeToQueryResult /// Process namespace declaration. -let processNamespaceDeclaration (queryTrie: QueryTrie) (path: LongIdentifier) (state: FileContentQueryState) : FileContentQueryState = - let queryResult = queryTrie path +let processNamespaceDeclaration (trie: TrieNode) (path: LongIdentifier) (state: FileContentQueryState) : FileContentQueryState = + let queryResult = queryTrie trie path match queryResult with | QueryTrieNodeResult.NodeDoesNotExist -> state @@ -38,8 +48,8 @@ let processNamespaceDeclaration (queryTrie: QueryTrie) (path: LongIdentifier) (s /// Process an "open" statement. /// The statement could link to files and/or should be tracked as an open namespace. -let processOpenPath (queryTrie: QueryTrie) (path: LongIdentifier) (state: FileContentQueryState) : FileContentQueryState = - let queryResult = queryTrie path +let processOpenPath (trie: TrieNode) (path: LongIdentifier) (state: FileContentQueryState) : FileContentQueryState = + let queryResult = queryTrie trie path match queryResult with | QueryTrieNodeResult.NodeDoesNotExist -> state @@ -47,9 +57,7 @@ let processOpenPath (queryTrie: QueryTrie) (path: LongIdentifier) (state: FileCo | QueryTrieNodeResult.NodeExposesData files -> state.AddOpenNamespace(path, files) /// Process an identifier. -let processIdentifier (queryTrie: QueryTrie) (path: LongIdentifier) (state: FileContentQueryState) : FileContentQueryState = - let queryResult = queryTrie path - +let processIdentifier (queryResult: QueryTrieNodeResult) (state: FileContentQueryState) : FileContentQueryState = match queryResult with | QueryTrieNodeResult.NodeDoesNotExist -> state | QueryTrieNodeResult.NodeDoesNotExposeData -> @@ -59,26 +67,26 @@ let processIdentifier (queryTrie: QueryTrie) (path: LongIdentifier) (state: File | QueryTrieNodeResult.NodeExposesData files -> state.AddDependencies files /// Typically used to fold FileContentEntry items over a FileContentQueryState -let rec processStateEntry (queryTrie: QueryTrie) (state: FileContentQueryState) (entry: FileContentEntry) : FileContentQueryState = +let rec processStateEntry (trie: TrieNode) (state: FileContentQueryState) (entry: FileContentEntry) : FileContentQueryState = match entry with | FileContentEntry.TopLevelNamespace (topLevelPath, content) -> let state = match topLevelPath with | [] -> state - | _ -> processNamespaceDeclaration queryTrie topLevelPath state + | _ -> processNamespaceDeclaration trie topLevelPath state - List.fold (processStateEntry queryTrie) state content + List.fold (processStateEntry trie) state content | FileContentEntry.OpenStatement path -> // An open statement can directly reference file or be a partial open statement // Both cases need to be processed. - let stateAfterFullOpenPath = processOpenPath queryTrie path state + let stateAfterFullOpenPath = processOpenPath trie path state - // Any existing open statement could be extended with the current path (if that node where to exists in the trie) + // Any existing open statement could be extended with the current path (if that node were to exists in the trie) // The extended path could add a new link (in case of a module or namespace with types) - // It might also not add anything at all (in case it the extended path is still a partial one) + // It might also not add anything at all (in case the extended path is still a partial one) (stateAfterFullOpenPath, state.OpenNamespaces) - ||> Set.fold (fun acc openNS -> processOpenPath queryTrie [ yield! openNS; yield! path ] acc) + ||> Set.fold (fun acc openNS -> processOpenPath trie [ yield! openNS; yield! path ] acc) | FileContentEntry.PrefixedIdentifier path -> match path with @@ -91,15 +99,17 @@ let rec processStateEntry (queryTrie: QueryTrie) (state: FileContentQueryState) ||> Array.fold (fun state takeParts -> let path = List.take takeParts path // process the name was if it were a FQN - let stateAfterFullIdentifier = processIdentifier queryTrie path state + let stateAfterFullIdentifier = processIdentifier (queryTrieDual trie [] path) state // Process the name in combination with the existing open namespaces (stateAfterFullIdentifier, state.OpenNamespaces) - ||> Set.fold (fun acc openNS -> processIdentifier queryTrie [ yield! openNS; yield! path ] acc)) + ||> Set.fold (fun acc openNS -> + let queryResult = queryTrieDual trie openNS path + processIdentifier queryResult acc)) | FileContentEntry.NestedModule (nestedContent = nestedContent) -> // We don't want our current state to be affect by any open statements in the nested module - let nestedState = List.fold (processStateEntry queryTrie) state nestedContent + let nestedState = List.fold (processStateEntry trie) state nestedContent // Afterward we are only interested in the found dependencies in the nested module let foundDependencies = Set.union state.FoundDependencies nestedState.FoundDependencies @@ -123,11 +133,11 @@ let rec processStateEntry (queryTrie: QueryTrie) (state: FileContentQueryState) /// This function returns an array with a potential extra dependencies that makes sure that any such namespaces can be resolved (if they exists). /// For each unused namespace `open` we return at most one file that defines that namespace. /// -let collectGhostDependencies (fileIndex: FileIndex) (trie: TrieNode) (queryTrie: QueryTrie) (result: FileContentQueryState) = +let collectGhostDependencies (fileIndex: FileIndex) (trie: TrieNode) (result: FileContentQueryState) = // For each opened namespace, if none of already resolved dependencies define it, return the top-most file that defines it. Set.toArray result.OpenedNamespaces |> Array.choose (fun path -> - match queryTrie path with + match queryTrie trie path with | QueryTrieNodeResult.NodeExposesData _ | QueryTrieNodeResult.NodeDoesNotExist -> None | QueryTrieNodeResult.NodeDoesNotExposeData -> @@ -170,7 +180,6 @@ let mkGraph (compilingFSharpCore: bool) (filePairs: FilePairMap) (files: FileInP | ParsedInput.SigFile _ -> Some f) let trie = TrieMapping.mkTrie trieInput - let queryTrie: QueryTrie = queryTrieMemoized trie let fileContents = files @@ -180,6 +189,23 @@ let mkGraph (compilingFSharpCore: bool) (filePairs: FilePairMap) (files: FileInP else FileContentMapping.mkFileContent file) + // Files in FSharp.Core have an implicit dependency on `prim-types-prelude.fsi` - add it. + let fsharpCoreImplicitDependency = + if compilingFSharpCore then + let filename = "prim-types-prelude.fsi" + + let implicitDepIdx = + files + |> Array.tryFindIndex (fun f -> System.IO.Path.GetFileName(f.FileName) = filename) + + match implicitDepIdx with + | Some idx -> Some idx + | None -> + exn $"Expected to find file '{filename}' during compilation of FSharp.Core, but it was not found." + |> raise + else + None + let findDependencies (file: FileInProject) : FileIndex array = if file.Idx = 0 then // First file cannot have any dependencies. @@ -187,7 +213,7 @@ let mkGraph (compilingFSharpCore: bool) (filePairs: FilePairMap) (files: FileInP else let fileContent = fileContents[file.Idx] - let knownFiles = [ 0 .. (file.Idx - 1) ] |> set + let knownFiles = [| 0 .. (file.Idx - 1) |] |> set // File depends on all files above it that define accessible symbols at the root level (global namespace). let filesFromRoot = trie.Files |> Set.filter (fun rootIdx -> rootIdx < file.Idx) // Start by listing root-level dependencies. @@ -197,10 +223,10 @@ let mkGraph (compilingFSharpCore: bool) (filePairs: FilePairMap) (files: FileInP let depsResult = initialDepsResult // Seq is faster than List in this case. - ||> Seq.fold (processStateEntry queryTrie) + ||> Seq.fold (processStateEntry trie) // Add missing links for cases where an unused open namespace did not create a link. - let ghostDependencies = collectGhostDependencies file.Idx trie queryTrie depsResult + let ghostDependencies = collectGhostDependencies file.Idx trie depsResult // Add a link from implementation files to their signature files. let signatureDependency = @@ -208,31 +234,17 @@ let mkGraph (compilingFSharpCore: bool) (filePairs: FilePairMap) (files: FileInP | None -> Array.empty | Some sigIdx -> Array.singleton sigIdx - // Files in FSharp.Core have an implicit dependency on `prim-types-prelude.fsi` - add it. - let fsharpCoreImplicitDependencies = - let filename = "prim-types-prelude.fsi" - - let implicitDepIdx = - files - |> Array.tryFindIndex (fun f -> FileSystemUtils.fileNameOfPath f.FileName = filename) - - [| - if compilingFSharpCore then - match implicitDepIdx with - | Some idx -> - if file.Idx > idx then - yield idx - | None -> - exn $"Expected to find file '{filename}' during compilation of FSharp.Core, but it was not found." - |> raise - |] + let fsharpCoreImplicitDependencyForThisFile = + match fsharpCoreImplicitDependency with + | Some depIdx when file.Idx > depIdx -> [| depIdx |] + | _ -> [||] let allDependencies = [| yield! depsResult.FoundDependencies yield! ghostDependencies yield! signatureDependency - yield! fsharpCoreImplicitDependencies + yield! fsharpCoreImplicitDependencyForThisFile |] |> Array.distinct diff --git a/src/Compiler/Driver/GraphChecking/DependencyResolution.fsi b/src/Compiler/Driver/GraphChecking/DependencyResolution.fsi index ef0bddec47..88d92de75a 100644 --- a/src/Compiler/Driver/GraphChecking/DependencyResolution.fsi +++ b/src/Compiler/Driver/GraphChecking/DependencyResolution.fsi @@ -7,8 +7,7 @@ val queryTrie: trie: TrieNode -> path: LongIdentifier -> QueryTrieNodeResult /// Process an open path (found in the ParsedInput) with a given FileContentQueryState. /// This code is only used directly in unit tests. -val processOpenPath: - queryTrie: QueryTrie -> path: LongIdentifier -> state: FileContentQueryState -> FileContentQueryState +val processOpenPath: trie: TrieNode -> path: LongIdentifier -> state: FileContentQueryState -> FileContentQueryState /// /// Construct an approximate* dependency graph for files within a project, based on their ASTs. diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/QueryTrieTests.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/QueryTrieTests.fs index 0da67c406f..1c06fe9dee 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/QueryTrieTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/QueryTrieTests.fs @@ -807,7 +807,7 @@ let ``ProcessOpenStatement full path match`` () = Set.empty let result = - processOpenPath (queryTrie fantomasCoreTrie) [ "Fantomas"; "Core"; "AstExtensions" ] state + processOpenPath fantomasCoreTrie [ "Fantomas"; "Core"; "AstExtensions" ] state let dep = Seq.exactlyOne result.FoundDependencies Assert.AreEqual(indexOf "AstExtensions.fsi", dep) From db4dbc1e6c497677c139807a2a89bb4b9782f2d4 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Mon, 19 Jun 2023 13:08:22 +0200 Subject: [PATCH 6/7] Parser: recover on unfinished nested modules (#15402) --- src/Compiler/SyntaxTree/LexFilter.fs | 20 +++++++----- src/Compiler/pars.fsy | 6 ++++ tests/fsharp/typecheck/sigs/neg41.bsl | 2 +- tests/fsharp/typecheck/sigs/neg42.bsl | 2 +- .../ModuleOrNamespace/Nested module 03.fs | 5 +++ .../ModuleOrNamespace/Nested module 03.fs.bsl | 20 ++++++++++++ .../ModuleOrNamespace/Nested module 04.fs | 5 +++ .../ModuleOrNamespace/Nested module 04.fs.bsl | 20 ++++++++++++ .../ModuleOrNamespace/Nested module 05.fs | 5 +++ .../ModuleOrNamespace/Nested module 05.fs.bsl | 20 ++++++++++++ .../ModuleOrNamespace/Nested module 06.fs | 5 +++ .../ModuleOrNamespace/Nested module 06.fs.bsl | 20 ++++++++++++ .../ModuleOrNamespace/Nested module 07.fs | 6 ++++ .../ModuleOrNamespace/Nested module 07.fs.bsl | 27 ++++++++++++++++ .../ModuleOrNamespace/Nested module 08.fs | 6 ++++ .../ModuleOrNamespace/Nested module 08.fs.bsl | 27 ++++++++++++++++ .../ModuleOrNamespace/Nested module 09.fs | 6 ++++ .../ModuleOrNamespace/Nested module 09.fs.bsl | 28 +++++++++++++++++ .../ModuleOrNamespace/Nested module 10.fs | 6 ++++ .../ModuleOrNamespace/Nested module 10.fs.bsl | 27 ++++++++++++++++ .../ModuleOrNamespace/Nested module 11.fs | 5 +++ .../ModuleOrNamespace/Nested module 11.fs.bsl | 19 ++++++++++++ .../ModuleOrNamespace/Nested module 12.fs | 3 ++ .../ModuleOrNamespace/Nested module 12.fs.bsl | 19 ++++++++++++ .../ModuleOrNamespace/Nested module 13.fs | 3 ++ .../ModuleOrNamespace/Nested module 13.fs.bsl | 19 ++++++++++++ .../ModuleOrNamespace/Nested module 14.fs | 3 ++ .../ModuleOrNamespace/Nested module 14.fs.bsl | 20 ++++++++++++ .../ModuleOrNamespace/Nested module 15.fs | 3 ++ .../ModuleOrNamespace/Nested module 15.fs.bsl | 19 ++++++++++++ .../ModuleOrNamespace/Nested module 16.fs | 3 ++ .../ModuleOrNamespace/Nested module 16.fs.bsl | 18 +++++++++++ .../ModuleOrNamespace/Nested module 17.fs | 3 ++ .../ModuleOrNamespace/Nested module 17.fs.bsl | 18 +++++++++++ .../IncompleteNestedModuleShouldBePresent.fs | 5 --- ...completeNestedModuleShouldBePresent.fs.bsl | 31 ------------------- ...leteNestedModuleSigShouldBePresent.fsi.bsl | 2 +- 37 files changed, 409 insertions(+), 47 deletions(-) create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 03.fs create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 03.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 04.fs create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 04.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 05.fs create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 05.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 06.fs create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 06.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 07.fs create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 07.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 08.fs create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 08.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 09.fs create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 09.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 10.fs create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 10.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 11.fs create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 11.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 12.fs create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 12.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 13.fs create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 13.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 14.fs create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 14.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 15.fs create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 15.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 16.fs create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 16.fs.bsl create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 17.fs create mode 100644 tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 17.fs.bsl delete mode 100644 tests/service/data/SyntaxTree/NestedModule/IncompleteNestedModuleShouldBePresent.fs delete mode 100644 tests/service/data/SyntaxTree/NestedModule/IncompleteNestedModuleShouldBePresent.fs.bsl diff --git a/src/Compiler/SyntaxTree/LexFilter.fs b/src/Compiler/SyntaxTree/LexFilter.fs index c90539ad3f..1c5b635498 100644 --- a/src/Compiler/SyntaxTree/LexFilter.fs +++ b/src/Compiler/SyntaxTree/LexFilter.fs @@ -49,7 +49,7 @@ type Context = | CtxtTypeDefns of Position // 'type =', not removed when we find the "=" | CtxtNamespaceHead of Position * token - | CtxtModuleHead of Position * token * LexingModuleAttributes + | CtxtModuleHead of Position * token * LexingModuleAttributes * isNested: bool | CtxtMemberHead of Position | CtxtMemberBody of Position // If bool is true then this is "whole file" @@ -68,7 +68,7 @@ type Context = member c.StartPos = match c with - | CtxtNamespaceHead (p, _) | CtxtModuleHead (p, _, _) | CtxtException p | CtxtModuleBody (p, _) | CtxtNamespaceBody p + | CtxtNamespaceHead (p, _) | CtxtModuleHead (p, _, _, _) | CtxtException p | CtxtModuleBody (p, _) | CtxtNamespaceBody p | CtxtLetDecl (_, p) | CtxtDo p | CtxtInterfaceHead p | CtxtTypeDefns p | CtxtParen (_, p) | CtxtMemberHead p | CtxtMemberBody p | CtxtWithAsLet p | CtxtWithAsAugment p @@ -1393,6 +1393,9 @@ type LexFilterImpl ( | CtxtSeqBlock(_, _, AddOneSidedBlockEnd) -> Some (ORIGHT_BLOCK_END(getLastTokenEndRange ())) + | CtxtModuleHead(isNested = true) -> + Some OBLOCKSEP + | _ -> None @@ -1598,11 +1601,11 @@ type LexFilterImpl ( // Otherwise it's a 'head' module declaration, so ignore it // Here prevToken is either 'module', 'rec', 'global' (invalid), '.', or ident, because we skip attribute tokens and access modifier tokens - | _, CtxtModuleHead (moduleTokenPos, prevToken, lexingModuleAttributes) :: rest -> + | _, CtxtModuleHead (moduleTokenPos, prevToken, lexingModuleAttributes, isNested) :: rest -> match prevToken, token with | _, GREATER_RBRACK when lexingModuleAttributes = LexingModuleAttributes && moduleTokenPos.Column < tokenStartPos.Column -> - replaceCtxt tokenTup (CtxtModuleHead (moduleTokenPos, prevToken, NotLexingModuleAttributes)) + replaceCtxt tokenTup (CtxtModuleHead (moduleTokenPos, prevToken, NotLexingModuleAttributes, isNested)) returnToken tokenLexbufState token | _ when lexingModuleAttributes = LexingModuleAttributes && moduleTokenPos.Column < tokenStartPos.Column -> @@ -1612,10 +1615,10 @@ type LexFilterImpl ( | MODULE, GLOBAL | (MODULE | REC | DOT), (REC | IDENT _) | IDENT _, DOT when moduleTokenPos.Column < tokenStartPos.Column -> - replaceCtxt tokenTup (CtxtModuleHead (moduleTokenPos, token, NotLexingModuleAttributes)) + replaceCtxt tokenTup (CtxtModuleHead (moduleTokenPos, token, NotLexingModuleAttributes, isNested)) returnToken tokenLexbufState token | MODULE, LBRACK_LESS when moduleTokenPos.Column < tokenStartPos.Column -> - replaceCtxt tokenTup (CtxtModuleHead (moduleTokenPos, prevToken, LexingModuleAttributes)) + replaceCtxt tokenTup (CtxtModuleHead (moduleTokenPos, prevToken, LexingModuleAttributes, isNested)) returnToken tokenLexbufState token | _, (EQUALS | COLON) -> if debug then dprintf "CtxtModuleHead: COLON/EQUALS, pushing CtxtModuleBody and CtxtSeqBlock\n" @@ -1643,7 +1646,7 @@ type LexFilterImpl ( // and we've encountered declarations below if debug then dprintf "CtxtModuleHead: not start of file, popping CtxtModuleHead\n" popCtxt() - reprocessWithoutBlockRule() + insertTokenFromPrevPosToCurrentPos OBLOCKSEP // Offside rule for SeqBlock. // f x @@ -1972,7 +1975,8 @@ type LexFilterImpl ( | MODULE, _ :: _ -> insertComingSoonTokens("MODULE", MODULE_COMING_SOON, MODULE_IS_HERE) if debug then dprintf "MODULE: entering CtxtModuleHead, awaiting EQUALS to go to CtxtSeqBlock (%a)\n" outputPos tokenStartPos - pushCtxt tokenTup (CtxtModuleHead (tokenStartPos, token, NotLexingModuleAttributes)) + let isNested = match offsideStack with | [ CtxtSeqBlock _ ] -> false | _ -> true + pushCtxt tokenTup (CtxtModuleHead (tokenStartPos, token, NotLexingModuleAttributes, isNested)) pool.Return tokenTup hwTokenFetch useBlockRule diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index e35eb962f6..0509f867c7 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -521,6 +521,12 @@ moduleIntro: let mModule = rhs parseState 1 mModule, $4, $5.LongIdent, $3, $2 } + | moduleKeyword opt_attributes opt_access opt_rec OBLOCKSEP + { if not (isNil $2) then + parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AttributesToRightOfModuleKeyword (rhs parseState 4) + let mModule = rhs parseState 1 + mModule, $4, [], $3, $2 } + /* The start of a namespace declaration */ namespaceIntro: diff --git a/tests/fsharp/typecheck/sigs/neg41.bsl b/tests/fsharp/typecheck/sigs/neg41.bsl index 18115064cc..122351ab76 100644 --- a/tests/fsharp/typecheck/sigs/neg41.bsl +++ b/tests/fsharp/typecheck/sigs/neg41.bsl @@ -1,2 +1,2 @@ -neg41.fs(3,1,3,5): parse error FS0010: Unexpected keyword 'type' in definition. Expected '=' or other token. +neg41.fs(2,20,3,1): parse error FS0010: Incomplete structured construct at or before this point in definition. Expected '=' or other token. diff --git a/tests/fsharp/typecheck/sigs/neg42.bsl b/tests/fsharp/typecheck/sigs/neg42.bsl index 3dec6d2329..9f2bba6bf0 100644 --- a/tests/fsharp/typecheck/sigs/neg42.bsl +++ b/tests/fsharp/typecheck/sigs/neg42.bsl @@ -1,2 +1,2 @@ -neg42.fsi(3,1,3,5): parse error FS0010: Unexpected keyword 'type' in signature file. Expected ':', '=' or other token. +neg42.fsi(2,20,3,1): parse error FS0010: Incomplete structured construct at or before this point in signature file. Expected ':', '=' or other token. diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 03.fs b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 03.fs new file mode 100644 index 0000000000..90d76105db --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 03.fs @@ -0,0 +1,5 @@ +module Module + +module A + +() diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 03.fs.bsl b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 03.fs.bsl new file mode 100644 index 0000000000..44ab73f81a --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 03.fs.bsl @@ -0,0 +1,20 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleOrNamespace/Nested module 03.fs", false, + QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [NestedModule + (SynComponentInfo + ([], None, [], [A], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false, + None, (3,0--3,8)), false, [], false, (3,0--3,8), + { ModuleKeyword = Some (3,0--3,6) + EqualsRange = None }); + Expr (Const (Unit, (5,0--5,2)), (5,0--5,2))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--5,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,9)-(5,0) parse error Incomplete structured construct at or before this point in definition. Expected '=' or other token. diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 04.fs b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 04.fs new file mode 100644 index 0000000000..2f61714c41 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 04.fs @@ -0,0 +1,5 @@ +module Module + +module + +() diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 04.fs.bsl b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 04.fs.bsl new file mode 100644 index 0000000000..4208cc9422 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 04.fs.bsl @@ -0,0 +1,20 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleOrNamespace/Nested module 04.fs", false, + QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [NestedModule + (SynComponentInfo + ([], None, [], [], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false, + None, (3,0--5,0)), false, [], false, (3,0--5,0), + { ModuleKeyword = Some (3,0--3,6) + EqualsRange = None }); + Expr (Const (Unit, (5,0--5,2)), (5,0--5,2))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--5,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,7)-(5,0) parse error Incomplete structured construct at or before this point in definition. Expected '=' or other token. diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 05.fs b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 05.fs new file mode 100644 index 0000000000..3d3602d973 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 05.fs @@ -0,0 +1,5 @@ +module Module + +module rec + +() diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 05.fs.bsl b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 05.fs.bsl new file mode 100644 index 0000000000..53b4a1f12d --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 05.fs.bsl @@ -0,0 +1,20 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleOrNamespace/Nested module 05.fs", false, + QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [NestedModule + (SynComponentInfo + ([], None, [], [], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false, + None, (3,0--5,0)), true, [], false, (3,0--5,0), + { ModuleKeyword = Some (3,0--3,6) + EqualsRange = None }); + Expr (Const (Unit, (5,0--5,2)), (5,0--5,2))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--5,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,11)-(5,0) parse error Incomplete structured construct at or before this point in definition. Expected '=' or other token. diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 06.fs b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 06.fs new file mode 100644 index 0000000000..20c697536e --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 06.fs @@ -0,0 +1,5 @@ +module Module + +module rec A + +() diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 06.fs.bsl b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 06.fs.bsl new file mode 100644 index 0000000000..d6875779d2 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 06.fs.bsl @@ -0,0 +1,20 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleOrNamespace/Nested module 06.fs", false, + QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [NestedModule + (SynComponentInfo + ([], None, [], [A], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false, + None, (3,0--3,12)), true, [], false, (3,0--3,12), + { ModuleKeyword = Some (3,0--3,6) + EqualsRange = None }); + Expr (Const (Unit, (5,0--5,2)), (5,0--5,2))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--5,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,13)-(5,0) parse error Incomplete structured construct at or before this point in definition. Expected '=' or other token. diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 07.fs b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 07.fs new file mode 100644 index 0000000000..c37b164133 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 07.fs @@ -0,0 +1,6 @@ +module Module + +module A = + module + +2 diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 07.fs.bsl b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 07.fs.bsl new file mode 100644 index 0000000000..569bdfe581 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 07.fs.bsl @@ -0,0 +1,27 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleOrNamespace/Nested module 07.fs", false, + QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [NestedModule + (SynComponentInfo + ([], None, [], [A], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false, + None, (3,0--3,8)), false, + [NestedModule + (SynComponentInfo + ([], None, [], [], + PreXmlDoc ((4,4), FSharp.Compiler.Xml.XmlDocCollector), + false, None, (4,4--6,0)), false, [], false, (4,4--6,0), + { ModuleKeyword = Some (4,4--4,10) + EqualsRange = None })], false, (3,0--6,0), + { ModuleKeyword = Some (3,0--3,6) + EqualsRange = Some (3,9--3,10) }); + Expr (Const (Int32 2, (6,0--6,1)), (6,0--6,1))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--6,1), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(6,0)-(6,1) parse error Incomplete structured construct at or before this point in definition. Expected '=' or other token. diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 08.fs b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 08.fs new file mode 100644 index 0000000000..c73c8b4320 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 08.fs @@ -0,0 +1,6 @@ +module Module + +module A = + module B + + 2 diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 08.fs.bsl b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 08.fs.bsl new file mode 100644 index 0000000000..1b6fcc1a41 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 08.fs.bsl @@ -0,0 +1,27 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleOrNamespace/Nested module 08.fs", false, + QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [NestedModule + (SynComponentInfo + ([], None, [], [A], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false, + None, (3,0--3,8)), false, + [NestedModule + (SynComponentInfo + ([], None, [], [B], + PreXmlDoc ((4,4), FSharp.Compiler.Xml.XmlDocCollector), + false, None, (4,4--4,12)), false, [], false, (4,4--4,12), + { ModuleKeyword = Some (4,4--4,10) + EqualsRange = None }); + Expr (Const (Int32 2, (6,4--6,5)), (6,4--6,5))], false, + (3,0--6,5), { ModuleKeyword = Some (3,0--3,6) + EqualsRange = Some (3,9--3,10) })], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--6,5), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(4,13)-(6,4) parse error Incomplete structured construct at or before this point in definition. Expected '=' or other token. diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 09.fs b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 09.fs new file mode 100644 index 0000000000..e5d48898ea --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 09.fs @@ -0,0 +1,6 @@ +module Module + +module A = + module B = + + 2 diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 09.fs.bsl b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 09.fs.bsl new file mode 100644 index 0000000000..ce79f7aba8 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 09.fs.bsl @@ -0,0 +1,28 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleOrNamespace/Nested module 09.fs", false, + QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [NestedModule + (SynComponentInfo + ([], None, [], [A], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false, + None, (3,0--3,8)), false, + [NestedModule + (SynComponentInfo + ([], None, [], [B], + PreXmlDoc ((4,4), FSharp.Compiler.Xml.XmlDocCollector), + false, None, (4,4--4,12)), false, [], false, (4,4--4,14), + { ModuleKeyword = Some (4,4--4,10) + EqualsRange = Some (4,13--4,14) }); + Expr (Const (Int32 2, (6,4--6,5)), (6,4--6,5))], false, + (3,0--6,5), { ModuleKeyword = Some (3,0--3,6) + EqualsRange = Some (3,9--3,10) })], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--6,5), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(6,4)-(6,5) parse error Possible incorrect indentation: this token is offside of context started at position (4:5). Try indenting this token further or using standard formatting conventions. +(6,4)-(6,5) parse error Incomplete structured construct at or before this point in definition diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 10.fs b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 10.fs new file mode 100644 index 0000000000..136af483ef --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 10.fs @@ -0,0 +1,6 @@ +module Module + +module A = + module + + 2 diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 10.fs.bsl b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 10.fs.bsl new file mode 100644 index 0000000000..4605fc9a35 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 10.fs.bsl @@ -0,0 +1,27 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleOrNamespace/Nested module 10.fs", false, + QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [NestedModule + (SynComponentInfo + ([], None, [], [A], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false, + None, (3,0--3,8)), false, + [NestedModule + (SynComponentInfo + ([], None, [], [], + PreXmlDoc ((4,4), FSharp.Compiler.Xml.XmlDocCollector), + false, None, (4,4--6,4)), false, [], false, (4,4--6,4), + { ModuleKeyword = Some (4,4--4,10) + EqualsRange = None }); + Expr (Const (Int32 2, (6,4--6,5)), (6,4--6,5))], false, + (3,0--6,5), { ModuleKeyword = Some (3,0--3,6) + EqualsRange = Some (3,9--3,10) })], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--6,5), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(4,11)-(6,4) parse error Incomplete structured construct at or before this point in definition. Expected '=' or other token. diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 11.fs b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 11.fs new file mode 100644 index 0000000000..f128433132 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 11.fs @@ -0,0 +1,5 @@ +module Module + +module + +A diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 11.fs.bsl b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 11.fs.bsl new file mode 100644 index 0000000000..181836a8b7 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 11.fs.bsl @@ -0,0 +1,19 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleOrNamespace/Nested module 11.fs", false, + QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [NestedModule + (SynComponentInfo + ([], None, [], [], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false, + None, (3,0--5,0)), false, [], false, (3,0--5,0), + { ModuleKeyword = Some (3,0--3,6) + EqualsRange = None }); Expr (Ident A, (5,0--5,1))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--5,1), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,7)-(5,0) parse error Incomplete structured construct at or before this point in definition. Expected '=' or other token. diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 12.fs b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 12.fs new file mode 100644 index 0000000000..a2fe094f38 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 12.fs @@ -0,0 +1,3 @@ +module Module + +module diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 12.fs.bsl b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 12.fs.bsl new file mode 100644 index 0000000000..eb79389bb5 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 12.fs.bsl @@ -0,0 +1,19 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleOrNamespace/Nested module 12.fs", false, + QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [NestedModule + (SynComponentInfo + ([], None, [], [], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false, + None, (3,0--4,0)), false, [], false, (3,0--4,0), + { ModuleKeyword = Some (3,0--3,6) + EqualsRange = None })], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--4,0), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(4,0)-(4,0) parse error Incomplete structured construct at or before this point in definition. Expected '=' or other token. diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 13.fs b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 13.fs new file mode 100644 index 0000000000..9d2084ebc7 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 13.fs @@ -0,0 +1,3 @@ +module Module + +module A diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 13.fs.bsl b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 13.fs.bsl new file mode 100644 index 0000000000..c57a57743c --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 13.fs.bsl @@ -0,0 +1,19 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleOrNamespace/Nested module 13.fs", false, + QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [NestedModule + (SynComponentInfo + ([], None, [], [A], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false, + None, (3,0--3,8)), false, [], false, (3,0--3,8), + { ModuleKeyword = Some (3,0--3,6) + EqualsRange = None })], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--3,8), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(4,0)-(4,0) parse error Incomplete structured construct at or before this point in definition. Expected '=' or other token. diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 14.fs b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 14.fs new file mode 100644 index 0000000000..97b8676645 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 14.fs @@ -0,0 +1,3 @@ +module Module + +module A = diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 14.fs.bsl b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 14.fs.bsl new file mode 100644 index 0000000000..8417322a57 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 14.fs.bsl @@ -0,0 +1,20 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleOrNamespace/Nested module 14.fs", false, + QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [NestedModule + (SynComponentInfo + ([], None, [], [A], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false, + None, (3,0--3,8)), false, [], false, (3,0--3,10), + { ModuleKeyword = Some (3,0--3,6) + EqualsRange = Some (3,9--3,10) })], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--3,10), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(4,0)-(4,0) parse error Possible incorrect indentation: this token is offside of context started at position (3:1). Try indenting this token further or using standard formatting conventions. +(4,0)-(4,0) parse error Incomplete structured construct at or before this point in definition diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 15.fs b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 15.fs new file mode 100644 index 0000000000..e8bc9fdc18 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 15.fs @@ -0,0 +1,3 @@ +namespace Ns + +module A = diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 15.fs.bsl b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 15.fs.bsl new file mode 100644 index 0000000000..bb23f859ec --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 15.fs.bsl @@ -0,0 +1,19 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleOrNamespace/Nested module 15.fs", false, + QualifiedNameOfFile Nested module 15, [], [], + [SynModuleOrNamespace + ([Ns], false, DeclaredNamespace, + [NestedModule + (SynComponentInfo + ([], None, [], [A], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false, + None, (3,0--3,8)), false, [], false, (3,0--3,10), + { ModuleKeyword = Some (3,0--3,6) + EqualsRange = Some (3,9--3,10) })], PreXmlDocEmpty, [], None, + (1,0--3,10), { LeadingKeyword = Namespace (1,0--1,9) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(4,0)-(4,0) parse error Possible incorrect indentation: this token is offside of context started at position (3:1). Try indenting this token further or using standard formatting conventions. +(4,0)-(4,0) parse error Incomplete structured construct at or before this point in definition diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 16.fs b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 16.fs new file mode 100644 index 0000000000..7debb3295b --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 16.fs @@ -0,0 +1,3 @@ +namespace Ns + +module A diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 16.fs.bsl b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 16.fs.bsl new file mode 100644 index 0000000000..2ce127eef5 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 16.fs.bsl @@ -0,0 +1,18 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleOrNamespace/Nested module 16.fs", false, + QualifiedNameOfFile Nested module 16, [], [], + [SynModuleOrNamespace + ([Ns], false, DeclaredNamespace, + [NestedModule + (SynComponentInfo + ([], None, [], [A], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false, + None, (3,0--3,8)), false, [], false, (3,0--3,8), + { ModuleKeyword = Some (3,0--3,6) + EqualsRange = None })], PreXmlDocEmpty, [], None, (1,0--3,8), + { LeadingKeyword = Namespace (1,0--1,9) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(4,0)-(4,0) parse error Incomplete structured construct at or before this point in definition. Expected '=' or other token. diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 17.fs b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 17.fs new file mode 100644 index 0000000000..4baf660a9b --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 17.fs @@ -0,0 +1,3 @@ +namespace Ns + +module diff --git a/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 17.fs.bsl b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 17.fs.bsl new file mode 100644 index 0000000000..4e5dd1a191 --- /dev/null +++ b/tests/service/data/SyntaxTree/ModuleOrNamespace/Nested module 17.fs.bsl @@ -0,0 +1,18 @@ +ImplFile + (ParsedImplFileInput + ("/root/ModuleOrNamespace/Nested module 17.fs", false, + QualifiedNameOfFile Nested module 17, [], [], + [SynModuleOrNamespace + ([Ns], false, DeclaredNamespace, + [NestedModule + (SynComponentInfo + ([], None, [], [], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false, + None, (3,0--4,0)), false, [], false, (3,0--4,0), + { ModuleKeyword = Some (3,0--3,6) + EqualsRange = None })], PreXmlDocEmpty, [], None, (1,0--4,0), + { LeadingKeyword = Namespace (1,0--1,9) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(4,0)-(4,0) parse error Incomplete structured construct at or before this point in definition. Expected '=' or other token. diff --git a/tests/service/data/SyntaxTree/NestedModule/IncompleteNestedModuleShouldBePresent.fs b/tests/service/data/SyntaxTree/NestedModule/IncompleteNestedModuleShouldBePresent.fs deleted file mode 100644 index 30cb557e09..0000000000 --- a/tests/service/data/SyntaxTree/NestedModule/IncompleteNestedModuleShouldBePresent.fs +++ /dev/null @@ -1,5 +0,0 @@ -module A.B - -module C - -let a = () \ No newline at end of file diff --git a/tests/service/data/SyntaxTree/NestedModule/IncompleteNestedModuleShouldBePresent.fs.bsl b/tests/service/data/SyntaxTree/NestedModule/IncompleteNestedModuleShouldBePresent.fs.bsl deleted file mode 100644 index 5ab8dedceb..0000000000 --- a/tests/service/data/SyntaxTree/NestedModule/IncompleteNestedModuleShouldBePresent.fs.bsl +++ /dev/null @@ -1,31 +0,0 @@ -ImplFile - (ParsedImplFileInput - ("/root/NestedModule/IncompleteNestedModuleShouldBePresent.fs", false, - QualifiedNameOfFile A.B, [], [], - [SynModuleOrNamespace - ([A; B], false, NamedModule, - [NestedModule - (SynComponentInfo - ([], None, [], [C], - PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false, - None, (3,0--3,8)), false, [], false, (3,0--3,8), - { ModuleKeyword = Some (3,0--3,6) - EqualsRange = None }); - Let - (false, - [SynBinding - (None, Normal, false, false, [], - PreXmlDoc ((5,0), FSharp.Compiler.Xml.XmlDocCollector), - SynValData - (None, SynValInfo ([], SynArgInfo ([], false, None)), None), - Named (SynIdent (a, None), false, None, (5,4--5,5)), None, - Const (Unit, (5,8--5,10)), (5,4--5,5), Yes (5,0--5,10), - { LeadingKeyword = Let (5,0--5,3) - InlineKeyword = None - EqualsRange = Some (5,6--5,7) })], (5,0--5,10))], - PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, - (1,0--5,10), { LeadingKeyword = Module (1,0--1,6) })], (true, true), - { ConditionalDirectives = [] - CodeComments = [] }, set [])) - -(5,0)-(5,3) parse error Unexpected keyword 'let' or 'use' in definition. Expected '=' or other token. diff --git a/tests/service/data/SyntaxTree/NestedModule/IncompleteNestedModuleSigShouldBePresent.fsi.bsl b/tests/service/data/SyntaxTree/NestedModule/IncompleteNestedModuleSigShouldBePresent.fsi.bsl index eafee5cf74..9c697d753d 100644 --- a/tests/service/data/SyntaxTree/NestedModule/IncompleteNestedModuleSigShouldBePresent.fsi.bsl +++ b/tests/service/data/SyntaxTree/NestedModule/IncompleteNestedModuleSigShouldBePresent.fsi.bsl @@ -26,4 +26,4 @@ SigFile { ConditionalDirectives = [] CodeComments = [] }, set [])) -(5,0)-(5,3) parse error Unexpected keyword 'val' in signature file. Expected ':', '=' or other token. +(3,9)-(5,0) parse error Incomplete structured construct at or before this point in signature file. Expected ':', '=' or other token. From fd051378c015490d5a8a7a3424f8fc260378bffd Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Mon, 19 Jun 2023 18:05:27 +0200 Subject: [PATCH 7/7] Print two members in signature file when the visibility differs between the getter and setter. (#15435) * Print two members in signature file when the visibility differs between the getter and setter. * Add suffix for non index getters. --- src/Compiler/Checking/NicePrint.fs | 38 +++++++++++--- .../FSharp.Compiler.ComponentTests.fsproj | 1 + .../Signatures/MemberTests.fs | 52 +++++++++++++++++++ .../setters.fsx | 3 ++ 4 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 tests/FSharp.Compiler.ComponentTests/Signatures/MemberTests.fs create mode 100644 tests/FSharp.Compiler.ComponentTests/Signatures/TestCasesForGenerationRoundTrip/setters.fsx diff --git a/src/Compiler/Checking/NicePrint.fs b/src/Compiler/Checking/NicePrint.fs index 072dc76a10..db54e06faf 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 93ff861fe6..0145bcfa60 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 0000000000..2452a90088 --- /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 0000000000..47f7601bab --- /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