Skip to content

Commit 995a182

Browse files
Parens: miscellaneous fixes (#16262)
* Miscellaneous parenthesization fixes
1 parent c7d1ab9 commit 995a182

File tree

2 files changed

+229
-38
lines changed

2 files changed

+229
-38
lines changed

src/Compiler/Service/ServiceAnalysis.fs

Lines changed: 123 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,8 @@ module UnusedDeclarations =
456456
module UnnecessaryParentheses =
457457
open System
458458

459+
let (|Ident|) (ident: Ident) = ident.idText
460+
459461
/// Represents an expression's precedence, or,
460462
/// for a few few types of expression whose exact
461463
/// kind can be significant, the expression's exact kind.
@@ -873,11 +875,27 @@ module UnnecessaryParentheses =
873875
| SynExpr.DotIndexedGet(objectExpr = SynExpr.Paren(expr = Is inner)) -> ValueSome(Dot, Left)
874876
| _ -> ValueNone
875877

878+
/// Matches a SynExpr.App nested in a sequence of dot-gets.
879+
///
880+
/// x.M.N().O
881+
[<return: Struct>]
882+
let (|NestedApp|_|) expr =
883+
let rec loop =
884+
function
885+
| SynExpr.DotGet (expr = expr)
886+
| SynExpr.DotIndexedGet (objectExpr = expr) -> loop expr
887+
| SynExpr.App _ -> ValueSome NestedApp
888+
| _ -> ValueNone
889+
890+
loop expr
891+
876892
/// Returns the given expression's precedence, if applicable.
877893
[<return: Struct>]
878894
let (|InnerBinaryExpr|_|) expr : Precedence voption =
879895
match expr with
880896
| SynExpr.Tuple(isStruct = false) -> ValueSome Comma
897+
| SynExpr.DotGet(expr = NestedApp)
898+
| SynExpr.DotIndexedGet(objectExpr = NestedApp) -> ValueSome Apply
881899
| SynExpr.DotGet _
882900
| SynExpr.DotIndexedGet _ -> ValueSome Dot
883901
| PrefixApp prec -> ValueSome prec
@@ -936,6 +954,13 @@ module UnnecessaryParentheses =
936954
| SynExpr.IfThenElse _ as expr -> Some expr
937955
| _ -> None)
938956

957+
/// Matches a dangling sequential expression.
958+
[<return: Struct>]
959+
let (|Sequential|_|) =
960+
dangling (function
961+
| SynExpr.Sequential _ as expr -> Some expr
962+
| _ -> None)
963+
939964
/// Matches a dangling try-with or try-finally construct.
940965
[<return: Struct>]
941966
let (|Try|_|) =
@@ -1192,6 +1217,19 @@ module UnnecessaryParentheses =
11921217
SyntaxNode.SynExpr (SynExpr.App _) :: SyntaxNode.SynExpr (SynExpr.App(argExpr = SynExpr.ArrayOrListComputed(isArray = false))) :: _ ->
11931218
ValueNone
11941219

1220+
// Parens must stay around binary equals expressions in argument
1221+
// position lest they be interpreted as named argument assignments:
1222+
//
1223+
// o.M((x = y))
1224+
// o.N((x = y), z)
1225+
| SynExpr.Paren(expr = SynExpr.Paren(expr = InfixApp (Eq, _))),
1226+
SyntaxNode.SynExpr (SynExpr.App(funcExpr = SynExpr.LongIdent _)) :: _
1227+
| SynExpr.Paren(expr = InfixApp (Eq, _)),
1228+
SyntaxNode.SynExpr (SynExpr.Paren _) :: SyntaxNode.SynExpr (SynExpr.App(funcExpr = SynExpr.LongIdent _)) :: _
1229+
| SynExpr.Paren(expr = InfixApp (Eq, _)),
1230+
SyntaxNode.SynExpr (SynExpr.Tuple(isStruct = false)) :: SyntaxNode.SynExpr (SynExpr.Paren _) :: SyntaxNode.SynExpr (SynExpr.App(funcExpr = SynExpr.LongIdent _)) :: _ ->
1231+
ValueNone
1232+
11951233
// The :: operator is parsed differently from other symbolic infix operators,
11961234
// so we need to give it special treatment.
11971235

@@ -1249,6 +1287,8 @@ module UnnecessaryParentheses =
12491287
match outer, inner with
12501288
| ConfusableWithTypeApp, _ -> ValueNone
12511289

1290+
| SynExpr.IfThenElse _, Dangling.Sequential _ -> ValueNone
1291+
12521292
| SynExpr.IfThenElse (trivia = trivia), Dangling.IfThen ifThenElse when
12531293
problematic ifThenElse.Range trivia.ThenKeyword
12541294
|| trivia.ElseKeyword |> Option.exists (problematic ifThenElse.Range)
@@ -1258,15 +1298,44 @@ module UnnecessaryParentheses =
12581298
| SynExpr.TryFinally (trivia = trivia), Dangling.Try tryExpr when problematic tryExpr.Range trivia.FinallyKeyword ->
12591299
ValueNone
12601300

1261-
| (SynExpr.Match (clauses = clauses) | SynExpr.MatchLambda (matchClauses = clauses) | SynExpr.MatchBang (clauses = clauses)),
1262-
Dangling.Match matchOrTry when anyProblematic matchOrTry.Range clauses -> ValueNone
1301+
| SynExpr.Match (clauses = clauses; trivia = { WithKeyword = withKeyword }), Dangling.Match matchOrTry when
1302+
problematic matchOrTry.Range withKeyword
1303+
|| anyProblematic matchOrTry.Range clauses
1304+
->
1305+
ValueNone
1306+
1307+
| SynExpr.MatchBang (clauses = clauses; trivia = { WithKeyword = withKeyword }), Dangling.Match matchOrTry when
1308+
problematic matchOrTry.Range withKeyword
1309+
|| anyProblematic matchOrTry.Range clauses
1310+
->
1311+
ValueNone
1312+
1313+
| SynExpr.MatchLambda (matchClauses = clauses), Dangling.Match matchOrTry when anyProblematic matchOrTry.Range clauses ->
1314+
ValueNone
12631315

12641316
| SynExpr.TryWith (withCases = clauses; trivia = trivia), Dangling.Match matchOrTry when
1265-
anyProblematic matchOrTry.Range clauses
1266-
|| problematic matchOrTry.Range trivia.WithKeyword
1317+
problematic matchOrTry.Range trivia.WithKeyword
1318+
|| anyProblematic matchOrTry.Range clauses
12671319
->
12681320
ValueNone
12691321

1322+
| SynExpr.Sequential(expr1 = SynExpr.Paren(expr = Is inner)), Dangling.Problematic _ -> ValueNone
1323+
1324+
| SynExpr.Paren _, SynExpr.Typed _
1325+
| SynExpr.Quote _, SynExpr.Typed _
1326+
| SynExpr.AnonRecd _, SynExpr.Typed _
1327+
| SynExpr.Record _, SynExpr.Typed _
1328+
| SynExpr.While(doExpr = SynExpr.Paren(expr = Is inner)), SynExpr.Typed _
1329+
| SynExpr.WhileBang(doExpr = SynExpr.Paren(expr = Is inner)), SynExpr.Typed _
1330+
| SynExpr.For(doBody = Is inner), SynExpr.Typed _
1331+
| SynExpr.ForEach(bodyExpr = Is inner), SynExpr.Typed _
1332+
| SynExpr.Match _, SynExpr.Typed _
1333+
| SynExpr.Do _, SynExpr.Typed _
1334+
| SynExpr.LetOrUse(body = Is inner), SynExpr.Typed _
1335+
| SynExpr.TryWith _, SynExpr.Typed _
1336+
| SynExpr.TryFinally _, SynExpr.Typed _ -> ValueSome range
1337+
| _, SynExpr.Typed _ -> ValueNone
1338+
12701339
| OuterBinaryExpr inner (outerPrecedence, side), InnerBinaryExpr innerPrecedence ->
12711340
let ambiguous =
12721341
match Precedence.compare outerPrecedence innerPrecedence with
@@ -1295,31 +1364,6 @@ module UnnecessaryParentheses =
12951364
| OuterBinaryExpr inner (_, Right), (SynExpr.Sequential _ | SynExpr.LetOrUse(trivia = { InKeyword = None })) -> ValueNone
12961365
| OuterBinaryExpr inner (_, Right), inner -> if dangling inner then ValueNone else ValueSome range
12971366

1298-
| SynExpr.Typed _, SynExpr.Typed _
1299-
| SynExpr.WhileBang(whileExpr = SynExpr.Paren(expr = Is inner)), SynExpr.Typed _
1300-
| SynExpr.While(whileExpr = SynExpr.Paren(expr = Is inner)), SynExpr.Typed _
1301-
| SynExpr.For(identBody = Is inner), SynExpr.Typed _
1302-
| SynExpr.For(toBody = Is inner), SynExpr.Typed _
1303-
| SynExpr.ForEach(enumExpr = Is inner), SynExpr.Typed _
1304-
| SynExpr.ArrayOrList _, SynExpr.Typed _
1305-
| SynExpr.ArrayOrListComputed _, SynExpr.Typed _
1306-
| SynExpr.IndexRange _, SynExpr.Typed _
1307-
| SynExpr.IndexFromEnd _, SynExpr.Typed _
1308-
| SynExpr.ComputationExpr _, SynExpr.Typed _
1309-
| SynExpr.Lambda _, SynExpr.Typed _
1310-
| SynExpr.Assert _, SynExpr.Typed _
1311-
| SynExpr.App _, SynExpr.Typed _
1312-
| SynExpr.Lazy _, SynExpr.Typed _
1313-
| SynExpr.LongIdentSet _, SynExpr.Typed _
1314-
| SynExpr.DotSet _, SynExpr.Typed _
1315-
| SynExpr.Set _, SynExpr.Typed _
1316-
| SynExpr.DotIndexedSet _, SynExpr.Typed _
1317-
| SynExpr.NamedIndexedPropertySet _, SynExpr.Typed _
1318-
| SynExpr.Upcast _, SynExpr.Typed _
1319-
| SynExpr.Downcast _, SynExpr.Typed _
1320-
| SynExpr.AddressOf _, SynExpr.Typed _
1321-
| SynExpr.JoinIn _, SynExpr.Typed _ -> ValueNone
1322-
13231367
// new T(expr)
13241368
| SynExpr.New _, AtomicExprAfterType -> ValueSome range
13251369
| SynExpr.New _, _ -> ValueNone
@@ -1331,7 +1375,6 @@ module UnnecessaryParentheses =
13311375
| _, SynExpr.Paren _
13321376
| _, SynExpr.Quote _
13331377
| _, SynExpr.Const _
1334-
| _, SynExpr.Typed _
13351378
| _, SynExpr.Tuple(isStruct = true)
13361379
| _, SynExpr.AnonRecd _
13371380
| _, SynExpr.ArrayOrList _
@@ -1378,29 +1421,51 @@ module UnnecessaryParentheses =
13781421
| _ -> ValueNone
13791422

13801423
module SynPat =
1424+
[<return: Struct>]
1425+
let (|AnyTyped|_|) pats =
1426+
if
1427+
pats
1428+
|> List.exists (function
1429+
| SynPat.Typed _ -> true
1430+
| _ -> false)
1431+
then
1432+
ValueSome AnyTyped
1433+
else
1434+
ValueNone
1435+
13811436
/// If the given pattern is a parenthesized pattern and the parentheses
13821437
/// are unnecessary in the given context, returns the unnecessary parentheses' range.
13831438
let unnecessaryParentheses pat path =
1439+
let (|Last|) = List.last
1440+
13841441
match pat, path with
13851442
// Parens are needed in:
13861443
//
13871444
// let (Pattern …) = …
1445+
// let (x: …, y…) = …
1446+
// let (x: …), (y: …) = …
13881447
// let! (x: …) = …
13891448
// and! (x: …) = …
13901449
// use! (x: …) = …
13911450
// _.member M(x: …) = …
13921451
// match … with (x: …) -> …
1452+
// match … with (x, y: …) -> …
13931453
// function (x: …) -> …
13941454
// fun (x, y, …) -> …
13951455
// fun (x: …) -> …
13961456
// fun (Pattern …) -> …
1397-
| SynPat.Paren _, SyntaxNode.SynExpr (SynExpr.LetOrUseBang(pat = SynPat.Paren(pat = SynPat.Typed _))) :: _
1398-
| SynPat.Paren _, SyntaxNode.SynMatchClause (SynMatchClause(pat = SynPat.Paren(pat = SynPat.Typed _))) :: _
1399-
| SynPat.Paren(pat = SynPat.LongIdent _), SyntaxNode.SynBinding _ :: _
1400-
| SynPat.Paren(pat = SynPat.LongIdent _), SyntaxNode.SynExpr (SynExpr.Lambda _) :: _
1401-
| SynPat.Paren _, SyntaxNode.SynExpr (SynExpr.Lambda(args = SynSimplePats.SimplePats(pats = _ :: _ :: _))) :: _
1402-
| SynPat.Paren _, SyntaxNode.SynExpr (SynExpr.Lambda(args = SynSimplePats.SimplePats(pats = [ SynSimplePat.Typed _ ]))) :: _ ->
1403-
ValueNone
1457+
| SynPat.Paren (SynPat.Typed _, _), SyntaxNode.SynExpr (SynExpr.LetOrUseBang _) :: _
1458+
| SynPat.Paren (SynPat.Typed _, _), SyntaxNode.SynPat (SynPat.Tuple _) :: SyntaxNode.SynExpr (SynExpr.LetOrUseBang _) :: _
1459+
| SynPat.Paren (SynPat.Tuple (isStruct = false; elementPats = AnyTyped), _), SyntaxNode.SynExpr (SynExpr.LetOrUseBang _) :: _
1460+
| SynPat.Paren (SynPat.Typed _, _), SyntaxNode.SynMatchClause _ :: _
1461+
| SynPat.Paren (SynPat.Typed _, _), SyntaxNode.SynPat (SynPat.Tuple _) :: SyntaxNode.SynMatchClause _ :: _
1462+
| SynPat.Paren (SynPat.Tuple (isStruct = false; elementPats = Last (SynPat.Typed _)), _), SyntaxNode.SynMatchClause _ :: _
1463+
| SynPat.Paren (SynPat.Typed _, _), SyntaxNode.SynPat (SynPat.Tuple _) :: SyntaxNode.SynBinding _ :: _
1464+
| SynPat.Paren (SynPat.Tuple (isStruct = false; elementPats = AnyTyped), _), SyntaxNode.SynBinding _ :: _
1465+
| SynPat.Paren (SynPat.LongIdent _, _), SyntaxNode.SynBinding _ :: _
1466+
| SynPat.Paren (SynPat.LongIdent _, _), SyntaxNode.SynExpr (SynExpr.Lambda _) :: _
1467+
| SynPat.Paren (SynPat.Tuple(isStruct = false), _), SyntaxNode.SynExpr (SynExpr.Lambda(parsedData = Some _)) :: _
1468+
| SynPat.Paren (SynPat.Typed _, _), SyntaxNode.SynExpr (SynExpr.Lambda(parsedData = Some _)) :: _ -> ValueNone
14041469

14051470
// () is parsed as this in certain cases…
14061471
//
@@ -1415,6 +1480,24 @@ module UnnecessaryParentheses =
14151480
| SynPat.Paren (SynPat.Const (SynConst.Unit, _), _), SyntaxNode.SynExpr (SynExpr.LetOrUseBang _) :: _
14161481
| SynPat.Paren (SynPat.Const (SynConst.Unit, _), _), SyntaxNode.SynMatchClause _ :: _ -> ValueNone
14171482

1483+
// (()) is required when overriding a generic member
1484+
// where unit is the generic type argument:
1485+
//
1486+
// type C<'T> = abstract M : 'T -> unit
1487+
// let _ = { new C<unit> with override _.M (()) = () }
1488+
| SynPat.Paren (SynPat.Paren (SynPat.Const (SynConst.Unit, _), _), _),
1489+
SyntaxNode.SynPat (SynPat.LongIdent _) :: SyntaxNode.SynBinding _ :: _
1490+
| SynPat.Paren (SynPat.Const (SynConst.Unit, _), _),
1491+
SyntaxNode.SynPat (SynPat.Paren _) :: SyntaxNode.SynPat (SynPat.LongIdent _) :: SyntaxNode.SynBinding _ :: _ -> ValueNone
1492+
1493+
// Parens are required for the first of multiple additional constructors.
1494+
// We simply require them always.
1495+
//
1496+
// type T … =
1497+
// new (x) = …
1498+
// new (x, y) = …
1499+
| SynPat.Paren _, SyntaxNode.SynPat (SynPat.LongIdent(longDotId = SynLongIdent(id = [ Ident "new" ]))) :: _ -> ValueNone
1500+
14181501
// Parens are otherwise never needed in these cases:
14191502
//
14201503
// let (x: …) = …
@@ -1437,7 +1520,9 @@ module UnnecessaryParentheses =
14371520
| SynPat.Paren (inner, range), SyntaxNode.SynPat outer :: _ ->
14381521
match outer, inner with
14391522
// (x :: xs) :: ys
1440-
| SynPat.ListCons(lhsPat = SynPat.Paren(pat = Is inner)), SynPat.ListCons _ -> ValueNone
1523+
// (x, xs) :: ys
1524+
| SynPat.ListCons(lhsPat = SynPat.Paren(pat = Is inner)), SynPat.ListCons _
1525+
| SynPat.ListCons(lhsPat = SynPat.Paren(pat = Is inner)), SynPat.Tuple(isStruct = false) -> ValueNone
14411526

14421527
// A as (B | C)
14431528
// A as (B & C)

0 commit comments

Comments
 (0)