Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit bc7402c

Browse files
authoredMay 24, 2025··
Merge pull request #3165 from dawedawe/fix_3126
Preserve backticked idents in Active Pattern idents
2 parents e411e39 + 8d73193 commit bc7402c

File tree

3 files changed

+100
-36
lines changed

3 files changed

+100
-36
lines changed
 

‎CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
### Fixed
1010

11+
* Preserve backticks in active pattern idents. [#3126](https://github.com/fsprojects/fantomas/issues/3126)
1112
* New lines are added after comment in measure type. [#3145](https://github.com/fsprojects/fantomas/issues/3145)
1213
* Idempotency problem with comments in applications on lambda expressions. [#3128](https://github.com/fsprojects/fantomas/issues/3128)
1314

‎src/Fantomas.Core.Tests/ActivePatternTests.fs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,44 @@ match expr with
9595
match expr with
9696
| SpecificCall <@@ ( ** ) @@> (_, _, [ s1; s2 ]) -> ()
9797
"""
98+
99+
[<Test>]
100+
let ``active pattern with backticks in ident, 3126`` () =
101+
formatSourceString
102+
"""
103+
let (|``Custom Prefix``|_|) (p: string) (s: string) =
104+
if s.StartsWith(p) then
105+
Some(s.Substring(p.Length))
106+
else
107+
None
108+
"""
109+
config
110+
|> prepend newline
111+
|> should
112+
equal
113+
"""
114+
let (|``Custom Prefix``|_|) (p: string) (s: string) =
115+
if s.StartsWith(p) then
116+
Some(s.Substring(p.Length))
117+
else
118+
None
119+
"""
120+
121+
[<Test>]
122+
let ``multiple backticked idents in active pattern are preserved`` () =
123+
formatSourceString
124+
"""
125+
let (|``Is Even``|``Is Odd``|) (p: int) =
126+
if p % 2 = 0 then
127+
``Is Even``
128+
else
129+
``Is Odd``
130+
"""
131+
config
132+
|> prepend newline
133+
|> should
134+
equal
135+
"""
136+
let (|``Is Even``|``Is Odd``|) (p: int) =
137+
if p % 2 = 0 then ``Is Even`` else ``Is Odd``
138+
"""

‎src/Fantomas.Core/ASTTransformer.fs

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,27 @@ let mkIdent (ident: Ident) =
3434

3535
stn text ident.idRange
3636

37-
let mkSynIdent (SynIdent(ident, trivia)) =
37+
let mkSynIdent (creationAide: CreationAide) (SynIdent(ident, trivia)) =
3838
match trivia with
3939
| None -> mkIdent ident
4040
| Some(IdentTrivia.OriginalNotation text) -> stn text ident.idRange
4141
| Some(IdentTrivia.OriginalNotationWithParen(_, text, _)) -> stn $"(%s{text})" ident.idRange
42-
| Some(IdentTrivia.HasParenthesis _) -> stn $"(%s{ident.idText})" ident.idRange
42+
| Some(IdentTrivia.HasParenthesis _) ->
43+
let width = ident.idRange.EndColumn - ident.idRange.StartColumn
44+
45+
let text =
46+
if ident.idText.Length < width then
47+
// preserve backticks inside the idText, e.g. the idText could be "|``Is Even``|``Is Odd``|"
48+
creationAide.TextFromSource (fun () -> $"(%s{ident.idText})") ident.idRange
49+
else
50+
ident.idText
4351

44-
let mkSynLongIdent (sli: SynLongIdent) =
52+
stn $"(%s{text})" ident.idRange
53+
54+
let mkSynLongIdent (creationAide: CreationAide) (sli: SynLongIdent) =
4555
match sli.IdentsWithTrivia with
4656
| [] -> IdentListNode.Empty
47-
| [ single ] -> IdentListNode([ IdentifierOrDot.Ident(mkSynIdent single) ], sli.Range)
57+
| [ single ] -> IdentListNode([ IdentifierOrDot.Ident(mkSynIdent creationAide single) ], sli.Range)
4858
| head :: tail ->
4959
assert (tail.Length = sli.Dots.Length)
5060

@@ -53,9 +63,9 @@ let mkSynLongIdent (sli: SynLongIdent) =
5363
||> List.zip
5464
|> List.collect (fun (dot, ident) ->
5565
[ IdentifierOrDot.KnownDot(stn "." dot)
56-
IdentifierOrDot.Ident(mkSynIdent ident) ])
66+
IdentifierOrDot.Ident(mkSynIdent creationAide ident) ])
5767

58-
IdentListNode(IdentifierOrDot.Ident(mkSynIdent head) :: rest, sli.Range)
68+
IdentListNode(IdentifierOrDot.Ident(mkSynIdent creationAide head) :: rest, sli.Range)
5969

6070
let mkLongIdent (longIdent: LongIdent) : IdentListNode =
6171
match longIdent with
@@ -116,7 +126,7 @@ let mkParsedHashDirective (creationAide: CreationAide) (ParsedHashDirective(iden
116126
let text = creationAide.TextFromSource (fun () -> $"%A{value}") range
117127
stn text range |> Choice1Of2
118128
| ParsedHashDirectiveArgument.Ident(value = ident) -> mkIdent ident |> Choice1Of2
119-
| ParsedHashDirectiveArgument.LongIdent(value = lid) -> mkSynLongIdent lid |> Choice2Of2)
129+
| ParsedHashDirectiveArgument.LongIdent(value = lid) -> mkSynLongIdent creationAide lid |> Choice2Of2)
120130

121131
ParsedHashDirectiveNode(ident, args, range)
122132

@@ -209,7 +219,7 @@ let mkAttribute (creationAide: CreationAide) (a: SynAttribute) =
209219
| UnitExpr _ -> None
210220
| e -> mkExpr creationAide e |> Some
211221

212-
AttributeNode(mkSynLongIdent a.TypeName, expr, Option.map mkIdent a.Target, a.Range)
222+
AttributeNode(mkSynLongIdent creationAide a.TypeName, expr, Option.map mkIdent a.Target, a.Range)
213223

214224
let mkAttributeList (creationAide: CreationAide) (al: SynAttributeList) : AttributeListNode =
215225
let attributes = List.map (mkAttribute creationAide) al.Attributes
@@ -1021,7 +1031,10 @@ let mkExpr (creationAide: CreationAide) (e: SynExpr) : Expr =
10211031
|> List.choose (function
10221032
| SynExprRecordField((fieldName, _), Some mEq, Some expr, _) ->
10231033
let m = unionRanges fieldName.Range expr.Range
1024-
Some(RecordFieldNode(mkSynLongIdent fieldName, stn "=" mEq, mkExpr creationAide expr, m))
1034+
1035+
Some(
1036+
RecordFieldNode(mkSynLongIdent creationAide fieldName, stn "=" mEq, mkExpr creationAide expr, m)
1037+
)
10251038
| _ -> None)
10261039

10271040
match baseInfo, copyInfo with
@@ -1049,7 +1062,7 @@ let mkExpr (creationAide: CreationAide) (e: SynExpr) : Expr =
10491062
|> List.choose (function
10501063
| sli, Some mEq, e ->
10511064
let m = unionRanges sli.Range e.Range
1052-
let longIdent = mkSynLongIdent sli
1065+
let longIdent = mkSynLongIdent creationAide sli
10531066

10541067
Some(RecordFieldNode(longIdent, stn "=" mEq, mkExpr creationAide e, m))
10551068
| _ -> None)
@@ -1069,7 +1082,7 @@ let mkExpr (creationAide: CreationAide) (e: SynExpr) : Expr =
10691082
|> List.choose (function
10701083
| sli, Some mEq, e ->
10711084
let m = unionRanges sli.Range e.Range
1072-
let longIdent = mkSynLongIdent sli
1085+
let longIdent = mkSynLongIdent creationAide sli
10731086
Some(RecordFieldNode(longIdent, stn "=" mEq, mkExpr creationAide e, m))
10741087
| _ -> None)
10751088

@@ -1294,7 +1307,7 @@ let mkExpr (creationAide: CreationAide) (e: SynExpr) : Expr =
12941307
ExprChain(chainLinks, exprRange) |> Expr.Chain
12951308

12961309
| AppSingleParenArg(SynExpr.LongIdent(longDotId = longDotId), px) ->
1297-
ExprAppLongIdentAndSingleParenArgNode(mkSynLongIdent longDotId, mkExpr creationAide px, exprRange)
1310+
ExprAppLongIdentAndSingleParenArgNode(mkSynLongIdent creationAide longDotId, mkExpr creationAide px, exprRange)
12981311
|> Expr.AppLongIdentAndSingleParenArg
12991312
| AppSingleParenArg(e, px) ->
13001313
ExprAppSingleParenArgNode(mkExpr creationAide e, mkExpr creationAide px, exprRange)
@@ -1479,9 +1492,10 @@ let mkExpr (creationAide: CreationAide) (e: SynExpr) : Expr =
14791492

14801493
| SynExpr.Ident ident -> mkIdent ident |> Expr.Ident
14811494
| SynExpr.LongIdent(isOpt, synLongIdent, _, m) ->
1482-
ExprOptVarNode(isOpt, mkSynLongIdent synLongIdent, m) |> Expr.OptVar
1495+
ExprOptVarNode(isOpt, mkSynLongIdent creationAide synLongIdent, m)
1496+
|> Expr.OptVar
14831497
| SynExpr.LongIdentSet(synLongIdent, e, _) ->
1484-
ExprLongIdentSetNode(mkSynLongIdent synLongIdent, mkExpr creationAide e, exprRange)
1498+
ExprLongIdentSetNode(mkSynLongIdent creationAide synLongIdent, mkExpr creationAide e, exprRange)
14851499
|> Expr.LongIdentSet
14861500
| SynExpr.DotIndexedGet(objectExpr, indexArgs, _, _) ->
14871501
ExprDotIndexedGetNode(mkExpr creationAide objectExpr, mkExpr creationAide indexArgs, exprRange)
@@ -1497,7 +1511,7 @@ let mkExpr (creationAide: CreationAide) (e: SynExpr) : Expr =
14971511
|> Expr.DotIndexedSet
14981512
| SynExpr.NamedIndexedPropertySet(synLongIdent, e1, e2, _) ->
14991513
ExprNamedIndexedPropertySetNode(
1500-
mkSynLongIdent synLongIdent,
1514+
mkSynLongIdent creationAide synLongIdent,
15011515
mkExpr creationAide e1,
15021516
mkExpr creationAide e2,
15031517
exprRange
@@ -1506,7 +1520,7 @@ let mkExpr (creationAide: CreationAide) (e: SynExpr) : Expr =
15061520
| SynExpr.DotNamedIndexedPropertySet(e, synLongIdent, e1, e2, _) ->
15071521
ExprDotNamedIndexedPropertySetNode(
15081522
mkExpr creationAide e,
1509-
mkSynLongIdent synLongIdent,
1523+
mkSynLongIdent creationAide synLongIdent,
15101524
mkExpr creationAide e1,
15111525
mkExpr creationAide e2,
15121526
exprRange
@@ -1721,7 +1735,8 @@ let mkPat (creationAide: CreationAide) (p: SynPat) =
17211735
PatNamedParenStarIdentNode(mkSynAccess ao, stn "(" lpr, stn op ident.idRange, stn ")" rpr, patternRange)
17221736
|> Pattern.NamedParenStarIdent
17231737
| SynPat.Named(accessibility = ao; ident = ident) ->
1724-
PatNamedNode(mkSynAccess ao, mkSynIdent ident, patternRange) |> Pattern.Named
1738+
PatNamedNode(mkSynAccess ao, mkSynIdent creationAide ident, patternRange)
1739+
|> Pattern.Named
17251740
| SynPat.As(p1, p2, _) ->
17261741
PatLeftMiddleRight(mkPat creationAide p1, Choice2Of2 "as", mkPat creationAide p2, patternRange)
17271742
|> Pattern.As
@@ -1753,14 +1768,21 @@ let mkPat (creationAide: CreationAide) (p: SynPat) =
17531768
unionRanges ident.idRange pat.Range
17541769
)))
17551770

1756-
PatNamePatPairsNode(mkSynLongIdent synLongIdent, typarDecls, stn "(" lpr, pairs, stn ")" rpr, patternRange)
1771+
PatNamePatPairsNode(
1772+
mkSynLongIdent creationAide synLongIdent,
1773+
typarDecls,
1774+
stn "(" lpr,
1775+
pairs,
1776+
stn ")" rpr,
1777+
patternRange
1778+
)
17571779
|> Pattern.NamePatPairs
17581780
| SynPat.LongIdent(synLongIdent, _, vtdo, SynArgPats.Pats pats, ao, _) ->
17591781
let typarDecls = mkSynValTyparDecls creationAide vtdo
17601782

17611783
PatLongIdentNode(
17621784
mkSynAccess ao,
1763-
mkSynLongIdent synLongIdent,
1785+
mkSynLongIdent creationAide synLongIdent,
17641786
typarDecls,
17651787
List.map (mkPat creationAide) pats,
17661788
patternRange
@@ -1825,13 +1847,13 @@ let mkBinding
18251847
match sli.IdentsWithTrivia with
18261848
| [ prefix; OperatorWithStar operatorNode ] ->
18271849
IdentListNode(
1828-
[ IdentifierOrDot.Ident(mkSynIdent prefix)
1850+
[ IdentifierOrDot.Ident(mkSynIdent creationAide prefix)
18291851
IdentifierOrDot.UnknownDot
18301852
operatorNode ],
18311853
sli.Range
18321854
)
18331855
| [ OperatorWithStar operatorNode ] -> IdentListNode([ operatorNode ], sli.Range)
1834-
| _ -> mkSynLongIdent sli
1856+
| _ -> mkSynLongIdent creationAide sli
18351857

18361858
let ao, functionName, genericParameters, parameters =
18371859
match pat with
@@ -1844,7 +1866,7 @@ let mkBinding
18441866
let name =
18451867
match si with
18461868
| OperatorWithStar operatorNode -> operatorNode
1847-
| _ -> IdentifierOrDot.Ident(mkSynIdent si)
1869+
| _ -> IdentifierOrDot.Ident(mkSynIdent creationAide si)
18481870

18491871
let m =
18501872
let (SynIdent(ident, _)) = si
@@ -1958,7 +1980,7 @@ let mkExternBinding
19581980
id = [ ArrayText suffix ]))
19591981
isPostfix = true
19601982
typeArgs = [ SynType.App(typeName = SynType.LongIdent argLid; isPostfix = false; typeArgs = []) ]) ->
1961-
let lid = mkSynLongIdent argLid
1983+
let lid = mkSynLongIdent creationAide argLid
19621984

19631985
let lidPieces =
19641986
lid.Content
@@ -1991,7 +2013,7 @@ let mkExternBinding
19912013
| SynPat.LongIdent(
19922014
longDotId = longDotId
19932015
argPats = SynArgPats.Pats [ SynPat.Tuple(_, ps, _, StartEndRange 1 (mOpen, _, mClose)) ]) ->
1994-
mkSynLongIdent longDotId, stn "(" mOpen, List.map mkExternPat ps, stn ")" mClose
2016+
mkSynLongIdent creationAide longDotId, stn "(" mOpen, List.map mkExternPat ps, stn ")" mClose
19952017
| _ -> failwith "expecting a SynPat.LongIdent for extern binding"
19962018

19972019
ExternBindingNode(
@@ -2276,22 +2298,22 @@ let mkType (creationAide: CreationAide) (t: SynType) : Type =
22762298
| SynType.LongIdentApp(t, lid, Some mLt, args, _, Some mGt, _) ->
22772299
TypeAppPrefixNode(
22782300
mkType creationAide t,
2279-
Some(mkSynLongIdent lid),
2301+
Some(mkSynLongIdent creationAide lid),
22802302
stn "<" mLt,
22812303
List.map (mkType creationAide) args,
22822304
stn ">" mGt,
22832305
typeRange
22842306
)
22852307
|> Type.AppPrefix
22862308
| SynType.LongIdentApp(t, lid, None, [], _, None, _) ->
2287-
TypeLongIdentAppNode(mkType creationAide t, mkSynLongIdent lid, typeRange)
2309+
TypeLongIdentAppNode(mkType creationAide t, mkSynLongIdent creationAide lid, typeRange)
22882310
|> Type.LongIdentApp
22892311
| SynType.WithGlobalConstraints(SynType.Var _, [ SynTypeConstraint.WhereTyparSubtypeOfType _ as tc ], _) ->
22902312
mkSynTypeConstraint creationAide tc |> Type.WithSubTypeConstraint
22912313
| SynType.WithGlobalConstraints(t, tcs, _) ->
22922314
TypeWithGlobalConstraintsNode(mkType creationAide t, List.map (mkSynTypeConstraint creationAide) tcs, typeRange)
22932315
|> Type.WithGlobalConstraints
2294-
| SynType.LongIdent lid -> Type.LongIdent(mkSynLongIdent lid)
2316+
| SynType.LongIdent lid -> Type.LongIdent(mkSynLongIdent creationAide lid)
22952317
| SynType.AnonRecd(isStruct, fields, StartEndRange 2 (_, r, mClosing)) ->
22962318
let structNode, openingNode =
22972319
if isStruct then
@@ -2361,7 +2383,7 @@ let rec (|OpenL|_|) =
23612383
let mkOpenNodeForImpl (creationAide: CreationAide) (target, range) : Open =
23622384
match target with
23632385
| SynOpenDeclTarget.ModuleOrNamespace(longId, _) ->
2364-
OpenModuleOrNamespaceNode(mkSynLongIdent longId, range)
2386+
OpenModuleOrNamespaceNode(mkSynLongIdent creationAide longId, range)
23652387
|> Open.ModuleOrNamespace
23662388
| SynOpenDeclTarget.Type(typeName, _) -> OpenTargetNode(mkType creationAide typeName, range) |> Open.Target
23672389

@@ -2456,7 +2478,7 @@ let mkSynUnionCase
24562478
mkXmlDoc xmlDoc,
24572479
mkAttributes creationAide attributes,
24582480
Option.map (stn "|") trivia.BarRange,
2459-
mkSynIdent ident,
2481+
mkSynIdent creationAide ident,
24602482
fields,
24612483
fullRange
24622484
)
@@ -2570,7 +2592,7 @@ let mkTypeDefn
25702592
mkXmlDoc xmlDoc,
25712593
Option.map (stn "|") trivia.BarRange,
25722594
mkAttributes creationAide attributes,
2573-
mkSynIdent ident,
2595+
mkSynIdent creationAide ident,
25742596
stn "=" trivia.EqualsRange,
25752597
mkExpr creationAide valueExpr,
25762598
range
@@ -2930,7 +2952,7 @@ let mkMemberDefn (creationAide: CreationAide) (md: SynMemberDefn) =
29302952
mkXmlDoc px,
29312953
mkAttributes creationAide ats,
29322954
mkSynLeadingKeyword trivia.LeadingKeyword,
2933-
mkSynIdent ident,
2955+
mkSynIdent creationAide ident,
29342956
mkSynValTyparDecls creationAide (Some tds),
29352957
mkType creationAide t,
29362958
mkWithGetSet trivia.WithKeyword abstractSlotTrivia.GetSetKeywords visGet visSet,
@@ -3003,7 +3025,7 @@ let mkMemberDefn (creationAide: CreationAide) (md: SynMemberDefn) =
30033025
mkSynLeadingKeyword lk,
30043026
Option.map (stn "inline") inlineKw,
30053027
mkSynAccess accessibility,
3006-
mkSynLongIdent memberName,
3028+
mkSynLongIdent creationAide memberName,
30073029
stn "with" withKeyword,
30083030
firstBinding,
30093031
Some(stn "and" andKeyword),
@@ -3054,7 +3076,7 @@ let mkMemberDefn (creationAide: CreationAide) (md: SynMemberDefn) =
30543076
mkSynLeadingKeyword lk,
30553077
Option.map (stn "inline") inlineKw,
30563078
mkSynAccess visMember,
3057-
mkSynLongIdent memberName,
3079+
mkSynLongIdent creationAide memberName,
30583080
stn "with" withKeyword,
30593081
bindingNode,
30603082
None,
@@ -3072,7 +3094,7 @@ let mkMemberDefn (creationAide: CreationAide) (md: SynMemberDefn) =
30723094
mkSynLeadingKeyword lk,
30733095
Option.map (stn "inline") inlineKw,
30743096
mkSynAccess visMember,
3075-
mkSynLongIdent memberName,
3097+
mkSynLongIdent creationAide memberName,
30763098
stn "with" withKeyword,
30773099
bindingNode,
30783100
None,
@@ -3099,7 +3121,7 @@ let mkVal
30993121
Option.map (stn "inline") trivia.InlineKeyword,
31003122
isMutable,
31013123
mkSynAccess ao,
3102-
mkSynIdent synIdent,
3124+
mkSynIdent creationAide synIdent,
31033125
mkSynValTyparDecls creationAide (Some vtd),
31043126
mkType creationAide t,
31053127
Option.map (stn "=") trivia.EqualsRange,
@@ -3356,7 +3378,7 @@ let mkTypeDefnSig (creationAide: CreationAide) (SynTypeDefnSig(typeInfo, typeRep
33563378
mkXmlDoc xmlDoc,
33573379
Option.map (stn "|") trivia.BarRange,
33583380
mkAttributes creationAide attributes,
3359-
mkSynIdent ident,
3381+
mkSynIdent creationAide ident,
33603382
stn "=" trivia.EqualsRange,
33613383
mkExpr creationAide valueExpr,
33623384
range

0 commit comments

Comments
 (0)
Please sign in to comment.