Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.VisualStudio/18.vNext.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Find All References for external DLL symbols now only searches projects that reference the specific assembly. ([Issue #10227](https://github.com/dotnet/fsharp/issues/10227), [PR #19252](https://github.com/dotnet/fsharp/pull/19252))
* Improve static compilation of state machines. ([PR #19297](https://github.com/dotnet/fsharp/pull/19297))
* Make Alt+F1 (momentary toggle) work for inlay hints. ([PR #19421](https://github.com/dotnet/fsharp/pull/19421))
* Fix doubled F# diagnostics in tooltips. ([Issue #16360](https://github.com/dotnet/fsharp/issues/16360))

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ type internal FSharpDocumentDiagnosticAnalyzer [<ImportingConstructor>] () =

let! parseResults = document.GetFSharpParseResultsAsync("GetDiagnostics")

// Old logic, rollback once https://github.com/dotnet/fsharp/issues/15972 is fixed (likely on Roslyn side, since we're returning diagnostics, but they're not getting to VS).
(*
match diagnosticType with
| DiagnosticsType.Syntax ->
for diagnostic in parseResults.Diagnostics do
Expand All @@ -93,23 +91,6 @@ type internal FSharpDocumentDiagnosticAnalyzer [<ImportingConstructor>] () =
errors.Add(diagnostic) |> ignore

errors.ExceptWith(parseResults.Diagnostics)
*)

// TODO: see comment above, this is a workaround for issue we have in current VS/Roslyn
match diagnosticType with
| DiagnosticsType.Syntax ->
for diagnostic in parseResults.Diagnostics do
errors.Add(diagnostic) |> ignore

// We always add syntactic, and do not exclude them when semantic is requested
| DiagnosticsType.Semantic ->
for diagnostic in parseResults.Diagnostics do
errors.Add(diagnostic) |> ignore

let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync("GetDiagnostics")

for diagnostic in checkResults.Diagnostics do
errors.Add(diagnostic) |> ignore

let! unnecessaryParentheses =
match diagnosticType with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@ type DocumentDiagnosticAnalyzerTests() =

task.Result

member private _.getSyntaxAndSemantic(fileContents: string) =
let task =
cancellableTask {
let document =
RoslynTestHelpers.CreateSolution(fileContents)
|> RoslynTestHelpers.GetSingleDocument

let! syntactic = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Syntax)
let! semantic = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Semantic)
return Seq.toArray syntactic, Seq.toArray semantic
}
|> CancellableTask.start CancellationToken.None

task.Result

member private this.VerifyNoErrors(fileContents: string, ?additionalFlags: string[]) =
let errors = this.getDiagnostics (fileContents, ?additionalFlags = additionalFlags)

Expand Down Expand Up @@ -91,14 +106,13 @@ type DocumentDiagnosticAnalyzerTests() =
member private this.VerifyDiagnosticBetweenMarkers_HACK_PLEASE_REFER_TO_COMMENT_INSIDE
(fileContents: string, expectedMessage: string, expectedSeverity: DiagnosticSeverity)
=
// TODO: once workaround (https://github.com/dotnet/fsharp/pull/15982) will not be needed, this should be reverted back to normal method (see PR)
let errors =
this.getDiagnostics fileContents
|> Seq.filter (fun e -> e.Severity = expectedSeverity)
|> Seq.toArray

errors.Length
|> Assert.shouldBeEqualWith 2 "There should be two errors generated"
|> Assert.shouldBeEqualWith 1 "There should be exactly one error generated"

let actualError = errors.[0]
Assert.Equal(expectedSeverity, actualError.Severity)
Expand All @@ -117,7 +131,6 @@ type DocumentDiagnosticAnalyzerTests() =
|> Assert.shouldBeEqualWith expectedEnd "Error end positions should match"

member private this.VerifyErrorBetweenMarkers_HACK_PLEASE_REFER_TO_COMMENT_INSIDE(fileContents: string, expectedMessage: string) =
// TODO: once workaround (https://github.com/dotnet/fsharp/pull/15982) will not be needed, this should be reverted back to normal method (see PR)
this.VerifyDiagnosticBetweenMarkers_HACK_PLEASE_REFER_TO_COMMENT_INSIDE(fileContents, expectedMessage, DiagnosticSeverity.Error)

member private this.VerifyErrorAtMarker_HACK_PLEASE_REFER_TO_COMMENT_INSIDE
Expand All @@ -129,7 +142,7 @@ type DocumentDiagnosticAnalyzerTests() =
|> Seq.toArray

errors.Length
|> Assert.shouldBeEqualWith 2 "There should be exactly two errors generated"
|> Assert.shouldBeEqualWith 1 "There should be exactly one error generated"

let actualError = errors.[0]

Expand Down Expand Up @@ -523,3 +536,63 @@ printf "%d" x
""",
additionalFlags = [| "--times" |]
)

[<Fact>]
member this.``Parse diagnostic not duplicated in semantic results``() =
let source = "let x = // incomplete expression - parse error\n"
let syntaxDiags, semanticDiags = this.getSyntaxAndSemantic source

let duplicates =
semanticDiags
|> Array.filter (fun (d: Diagnostic) ->
syntaxDiags
|> Array.exists (fun (s: Diagnostic) -> s.Id = d.Id && s.Location.SourceSpan = d.Location.SourceSpan))

Assert.Empty(duplicates)

[<Fact>]
member this.``Type error appears only in semantic results``() =
let source = "let x: int = \"hello\"\n"
let _syntaxDiags, semanticDiags = this.getSyntaxAndSemantic source
Assert.Contains(semanticDiags, fun (d: Diagnostic) -> d.Id = "FS0001")

[<Fact>]
member this.``Parse errors still reported in syntax pass``() =
let source = "let x =\n"
let syntaxDiags, _ = this.getSyntaxAndSemantic source
Assert.NotEmpty(syntaxDiags)

[<Fact>]
member this.``Clean code has no diagnostics``() =
let source = "let x = 42\nlet y = x + 1\n"
let syntaxDiags, semanticDiags = this.getSyntaxAndSemantic source
Assert.Empty(syntaxDiags)
Assert.Empty(semanticDiags)

[<Fact>]
member this.``Multiple parse errors not duplicated``() =
let source = "let x =\nlet y =\n"
let syntaxDiags, semanticDiags = this.getSyntaxAndSemantic source
let allDiags = Array.append syntaxDiags semanticDiags

let uniqueCount =
allDiags
|> Array.distinctBy (fun (d: Diagnostic) -> d.Id, d.Location.SourceSpan)
|> Array.length

Assert.Equal(allDiags.Length, uniqueCount)

[<Fact>]
member this.``Warning not duplicated across passes``() =
let source = "let x = 42\n"
let syntaxDiags, semanticDiags = this.getSyntaxAndSemantic source

let allWarnings =
Array.append syntaxDiags semanticDiags
|> Array.filter (fun (d: Diagnostic) -> d.Severity = DiagnosticSeverity.Warning)

let uniqueWarnings =
allWarnings
|> Array.distinctBy (fun (d: Diagnostic) -> d.Id, d.Location.SourceSpan)

Assert.Equal(allWarnings.Length, uniqueWarnings.Length)
Loading