Skip to content

Commit

Permalink
Validate formatted code after unit tests. Fixes fsprojects#842.
Browse files Browse the repository at this point in the history
  • Loading branch information
nojaf committed Aug 28, 2020
1 parent 31b3e6c commit 851ee73
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 17 deletions.
16 changes: 7 additions & 9 deletions src/Fantomas.Tests/ControlStructureTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,9 @@ let x =
|> prepend newline
|> should equal """
let x =
if try
true
with Failure _ -> false
then
if (try
true
with Failure _ -> false) then
()
else
()
Expand All @@ -239,11 +238,10 @@ let y =
|> prepend newline
|> should equal """
let y =
if try
true
finally
false
then
if (try
true
finally
false) then
()
else
()
Expand Down
45 changes: 42 additions & 3 deletions src/Fantomas.Tests/TestHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,54 @@ let newline = "\n"

let sharedChecker = lazy(FSharpChecker.Create())

let private safeToIgnoreWarnings =
[ "This construct is deprecated: it is only for use in the F# library"
"Identifiers containing '@' are reserved for use in F# code generation" ]

let private isValidAndHasNoWarnings fileName source parsingOptions =
let allDefineOptions =
TokenParser.getOptimizedDefinesSets source
@ (TokenParser.getDefines source |> List.map List.singleton)
@ [[]]
|> List.distinct

allDefineOptions
|> List.map (fun conditionalCompilationDefines ->
async {
let parsingOptionsWithDefines = { parsingOptions with ConditionalCompilationDefines = conditionalCompilationDefines } // IsInteractive = false
// Run the first phase (untyped parsing) of the compiler
let sourceText = FSharp.Compiler.Text.SourceText.ofString source
let! untypedRes = sharedChecker.Value.ParseFile(fileName, sourceText, parsingOptionsWithDefines)

let errors =
untypedRes.Errors
|> Array.filter (fun e -> not (List.contains e.Message safeToIgnoreWarnings))
// FSharpErrorInfo contains both Errors and Warnings
// https://fsharp.github.io/FSharp.Compiler.Service/reference/fsharp-compiler-sourcecodeservices-fsharperrorinfo.html
return Array.isEmpty errors
}
)
|> Async.Parallel
|> Async.map (fun results -> Seq.fold (&&) true results)

let formatSourceString isFsiFile (s : string) config =
// On Linux/Mac this will exercise different line endings
let s = s.Replace("\r\n", Environment.NewLine)
let fileName = if isFsiFile then "/src.fsi" else "/src.fsx"
let parsingOptions = FakeHelpers.createParsingOptionsFromFile fileName

async {
let! formatted =
CodeFormatter.FormatDocumentAsync(fileName, SourceOrigin.SourceString s, config, parsingOptions, sharedChecker.Value)

let! isValid = isValidAndHasNoWarnings fileName formatted parsingOptions
if not isValid then
failwithf "The formatted result is not valid F# code or contains warnings\n%s" formatted

return formatted.Replace("\r\n", "\n")
}

CodeFormatter.FormatDocumentAsync(fileName, SourceOrigin.SourceString s, config,
FakeHelpers.createParsingOptionsFromFile fileName, sharedChecker.Value)
|> Async.RunSynchronously
|> fun s -> s.Replace("\r\n", "\n")

let formatSourceStringWithDefines defines (s : string) config =
// On Linux/Mac this will exercise different line endings
Expand Down
8 changes: 4 additions & 4 deletions src/Fantomas/CodePrinter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1965,12 +1965,12 @@ and genExpr astContext synExpr =
// x
// bool expr x should be indented
+> ifElse hasCommentAfterIfKeyword (indent +> sepNln) sepNone
+> genExpr astContext e1
+> ifElse hasCommentAfterBoolExpr sepNln sepSpace
+> (match e1 with
| SynExpr.TryWith _
| SynExpr.TryFinally _ -> atCurrentColumn (sepNln +> genThen synExpr.Range)
| _ -> genThen synExpr.Range)
| SynExpr.TryFinally _ -> sepOpenT +> genExpr astContext e1 +> sepCloseT
| _ -> genExpr astContext e1)
+> ifElse hasCommentAfterBoolExpr sepNln sepSpace
+> genThen synExpr.Range
// f.ex if x then // meh
// 0
// 0 should be indented
Expand Down
6 changes: 5 additions & 1 deletion src/Fantomas/Utils.fs
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,8 @@ module Map =
| None -> defaultValue

let tryFindOrEmptyList (key:'t) (map: Map<'t, 'g list>) =
tryFindOrDefault [] key map
tryFindOrDefault [] key map

module Async =
let map f computation =
async.Bind(computation, f >> async.Return)

0 comments on commit 851ee73

Please sign in to comment.