diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md index d406d513e96..4cf0b78629c 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md @@ -14,6 +14,7 @@ * Fix nullness warning for overrides of generic code with nullable type instance ([Issue #17988](https://github.com/dotnet/fsharp/issues/17988), [PR #18337](https://github.com/dotnet/fsharp/pull/18337)) * Unsafe downcast from `obj` to generic `T` no longer requires `not null` constraint on `T`([Issue #18275](https://github.com/dotnet/fsharp/issues/18275), [PR #18343](https://github.com/dotnet/fsharp/pull/18343)) * Fix for missing parse diagnostics in TransparentCompiler.ParseAndCheckProject ([PR #18366](https://github.com/dotnet/fsharp/pull/18366)) +* Miscellanous parentheses analyzer fixes. ([PR #18350](https://github.com/dotnet/fsharp/pull/18350)) ### Added * Added missing type constraints in FCS. ([PR #18241](https://github.com/dotnet/fsharp/pull/18241)) diff --git a/src/Compiler/Service/SynExpr.fs b/src/Compiler/Service/SynExpr.fs index e9605582514..5981dd5b286 100644 --- a/src/Compiler/Service/SynExpr.fs +++ b/src/Compiler/Service/SynExpr.fs @@ -343,6 +343,7 @@ module SynExpr = | PrefixApp prec -> ValueSome(prec, Non) | InfixApp(prec, side) -> ValueSome(prec, side) | SynExpr.App(argExpr = SynExpr.ComputationExpr _) -> ValueSome(UnaryPrefix, Left) + | SynExpr.App(argExpr = SynExpr.Paren(expr = SynExpr.App _ & Is inner)) -> ValueSome(Apply, Right) | SynExpr.App(funcExpr = SynExpr.Paren(expr = SynExpr.App _)) -> ValueSome(Apply, Left) | SynExpr.App(flag = ExprAtomicFlag.Atomic) -> ValueSome(Dot, Non) | SynExpr.App _ -> ValueSome(Apply, Non) @@ -396,6 +397,7 @@ module SynExpr = | SynExpr.DotIndexedSet _ | SynExpr.DotNamedIndexedPropertySet _ | SynExpr.DotSet _ -> ValueSome Set + | SynExpr.TypeTest _ -> ValueSome TypeTest | _ -> ValueNone module Dangling = @@ -540,7 +542,7 @@ module SynExpr = if i >= 0 && i < offsidesCol then let slice = line.AsSpan(i, min (offsidesCol - i) (line.Length - i)) - let j = slice.IndexOfAnyExcept("*/%-+:^@><=!|0$.?".AsSpan()) + let j = slice.IndexOfAnyExcept("*/%-+:^@><=!|$.?".AsSpan()) let lo = i + (if j >= 0 && slice[j] = ' ' then j else 0) @@ -845,22 +847,6 @@ module SynExpr = // precedence than regular function application. | _, SyntaxNode.SynExpr(SynExpr.App _) :: SyntaxNode.SynExpr(PrefixApp High) :: _ -> true - // Parens are never required around suffixed or infixed numeric literals, e.g., - // - // (1l).ToString() - // (1uy).ToString() - // (0b1).ToString() - // (1e10).ToString() - // (1.0).ToString() - | DotSafeNumericLiteral, _ -> false - - // Parens are required around bare decimal ints or doubles ending - // in dots when being dotted into, e.g., - // - // (1).ToString() - // (1.).ToString() - | SynExpr.Const(constant = SynConst.Int32 _ | SynConst.Double _), SyntaxNode.SynExpr(SynExpr.DotGet _) :: _ -> true - // Parens are required around join conditions: // // join … on (… = …) @@ -907,6 +893,22 @@ module SynExpr = -> true + // Parens are never required around suffixed or infixed numeric literals, e.g., + // + // (1l).ToString() + // (1uy).ToString() + // (0b1).ToString() + // (1e10).ToString() + // (1.0).ToString() + | DotSafeNumericLiteral, _ -> false + + // Parens are required around bare decimal ints or doubles ending + // in dots when being dotted into, e.g., + // + // (1).ToString() + // (1.).ToString() + | SynExpr.Const(constant = SynConst.Int32 _ | SynConst.Double _), SyntaxNode.SynExpr(SynExpr.DotGet _) :: _ -> true + // The :: operator is parsed differently from other symbolic infix operators, // so we need to give it special treatment. @@ -952,6 +954,7 @@ module SynExpr = let rec loop = function | [] -> false + | SynMatchClause(whenExpr = Some(SynExpr.Paren(expr = Dangling.ArrowSensitive _ & Is inner))) :: _ -> true | SynMatchClause(trivia = trivia) :: clauses -> trivia.BarRange |> Option.exists (problematic matchOrTryRange) || trivia.ArrowRange |> Option.exists (problematic matchOrTryRange) @@ -1148,6 +1151,7 @@ module SynExpr = | _, MulDivMod(Mod, _) | _, AddSub(Sub, _) -> true | Relational _, Relational _ -> true + | Apply, Apply -> true | _ -> false | c -> c > 0 diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/RemoveUnnecessaryParenthesesTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/RemoveUnnecessaryParenthesesTests.fs index b9b95bc44ac..224849a59e2 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/RemoveUnnecessaryParenthesesTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/RemoveUnnecessaryParenthesesTests.fs @@ -398,7 +398,7 @@ let _ = ", " match () with - | () when box x :? int + | () when (box x :? int) -> () | _ -> () " @@ -1830,6 +1830,8 @@ in x f ((+) x y) z " + "(Gen.map f << Gen.map g) (Gen.constant x)", "(Gen.map f << Gen.map g) (Gen.constant x)" + // TypeApp "id (id)", "id id" @@ -1890,6 +1892,7 @@ in x """(id "x").Length""", """(id "x").Length""" """(3L.ToString("x")).Length""", """(3L.ToString "x").Length""" "~~TypedResults.Ok(maybe.Value)", "~~TypedResults.Ok(maybe.Value)" + "bg.lighten(0.2).hexa ()", "bg.lighten(0.2).hexa ()" // DotLambda "[{| A = x |}] |> List.map (_.A)", "[{| A = x |}] |> List.map _.A"