Skip to content

Commit 7d1af9c

Browse files
committed
Revert "Fix Find All References for Active Pattern Cases (#1436)"
This reverts commit 2c2e497. Wasn't a sufficient solution.
1 parent 2c2e497 commit 7d1af9c

File tree

6 files changed

+48
-695
lines changed

6 files changed

+48
-695
lines changed

src/FsAutoComplete.Core/Commands.fs

Lines changed: 8 additions & 272 deletions
Original file line numberDiff line numberDiff line change
@@ -92,192 +92,6 @@ module Commands =
9292
let fantomasLogger = LogProvider.getLoggerByName "Fantomas"
9393
let commandsLogger = LogProvider.getLoggerByName "Commands"
9494

95-
/// Extracts the case name(s) from an active pattern name string.
96-
/// For partial patterns like "|CaseName|_|" returns "CaseName"
97-
/// For full patterns like "|Even|Odd|" returns the first case name
98-
let extractActivePatternCaseName (name: string) : string =
99-
let parts = name.Split('|') |> Array.filter (fun s -> s <> "" && s <> "_")
100-
if parts.Length > 0 then parts.[0] else name
101-
102-
/// Extracts all case names from an active pattern display name.
103-
/// For "(|ParseInt|_|)" returns ["ParseInt"]
104-
/// For "(|Even|Odd|)" returns ["Even"; "Odd"]
105-
let extractActivePatternCaseNames (displayName: string) : string list =
106-
displayName.TrimStart('|', '(').TrimEnd('|', '_', ')').Split('|')
107-
|> Array.filter (fun s -> not (String.IsNullOrWhiteSpace(s)))
108-
|> Array.toList
109-
110-
/// Finds usages of partial active pattern cases by walking the AST.
111-
///
112-
/// Returns syntactic ranges from the AST where the specified case names appear in pattern matches.
113-
/// For qualified identifiers (e.g., `MyModule.ParseInt`), returns the full qualified range to match FCS behavior.
114-
/// Recursively traverses all pattern forms, including nested and composite patterns (tuples, lists, or/and/as patterns, etc.).
115-
///
116-
/// Note: Only syntactic (AST) ranges are returned; semantic information is not considered.
117-
let findPartialActivePatternCaseUsages (caseNames: string list) (parseResults: FSharpParseFileResults) : range list =
118-
let rec walkPat (pat: SynPat) =
119-
seq {
120-
match pat with
121-
| SynPat.LongIdent(longDotId = synLongIdent; argPats = args) ->
122-
// Check if this is a potential case usage
123-
match synLongIdent with
124-
| SynLongIdent(id = idents) ->
125-
match idents with
126-
| [] -> ()
127-
| [ singleIdent ] when List.contains singleIdent.idText caseNames ->
128-
// Single identifier that matches a case name
129-
yield singleIdent.idRange
130-
| multipleIdents ->
131-
// Qualified identifier (e.g., MyModule.ParseInt)
132-
let lastIdent = List.last multipleIdents
133-
134-
if List.contains lastIdent.idText caseNames then
135-
// Return the full qualified range to match what FCS returns
136-
yield synLongIdent.Range
137-
138-
// Recursively check arguments
139-
match args with
140-
| SynArgPats.Pats pats -> yield! List.collect (walkPat >> Seq.toList) pats
141-
| SynArgPats.NamePatPairs(pats = pairs; range = _) ->
142-
yield! List.collect (fun (NamePatPairField(pat = pat)) -> walkPat pat |> Seq.toList) pairs
143-
144-
| SynPat.Paren(pat, _) -> yield! walkPat pat
145-
| SynPat.Tuple(elementPats = pats) -> yield! List.collect (walkPat >> Seq.toList) pats
146-
| SynPat.ArrayOrList(_, pats, _) -> yield! List.collect (walkPat >> Seq.toList) pats
147-
| SynPat.Ands(pats, _) -> yield! List.collect (walkPat >> Seq.toList) pats
148-
| SynPat.Or(lhsPat = pat1; rhsPat = pat2) ->
149-
yield! walkPat pat1
150-
yield! walkPat pat2
151-
| SynPat.As(lpat, rpat, _) ->
152-
yield! walkPat lpat
153-
yield! walkPat rpat
154-
| SynPat.Typed(pat, _, _) -> yield! walkPat pat
155-
| SynPat.Attrib(pat, _, _) -> yield! walkPat pat
156-
| SynPat.ListCons(lpat, rpat, _, _) ->
157-
yield! walkPat lpat
158-
yield! walkPat rpat
159-
| _ -> ()
160-
}
161-
162-
let rec walkExpr (expr: SynExpr) =
163-
let walkMatchClauses clauses =
164-
seq {
165-
for clause in clauses do
166-
match clause with
167-
| SynMatchClause(pat = pat; resultExpr = resultExpr) ->
168-
yield! walkPat pat
169-
yield! walkExpr resultExpr
170-
}
171-
172-
seq {
173-
match expr with
174-
| SynExpr.Match(expr = matchExpr; clauses = clauses) ->
175-
yield! walkExpr matchExpr
176-
yield! walkMatchClauses clauses
177-
| SynExpr.MatchBang(expr = matchExpr; clauses = clauses) ->
178-
yield! walkExpr matchExpr
179-
yield! walkMatchClauses clauses
180-
| SynExpr.TryWith(tryExpr = tryExpr; withCases = clauses) ->
181-
yield! walkExpr tryExpr
182-
yield! walkMatchClauses clauses
183-
| SynExpr.MatchLambda(matchClauses = clauses) -> yield! walkMatchClauses clauses
184-
| SynExpr.Lambda(args = args; body = body) ->
185-
match args with
186-
| SynSimplePats.SimplePats(pats = pats) ->
187-
for spat in pats do
188-
match spat with
189-
| SynSimplePat.Typed(pat, _, _)
190-
| SynSimplePat.Attrib(pat, _, _) ->
191-
match pat with
192-
| SynSimplePat.Id(ident, _, _, _, _, _) when List.contains ident.idText caseNames -> yield ident.idRange
193-
| _ -> ()
194-
| SynSimplePat.Id(ident, _, _, _, _, _) when List.contains ident.idText caseNames -> yield ident.idRange
195-
| _ -> ()
196-
197-
yield! walkExpr body
198-
// Continue walking nested expressions
199-
| SynExpr.App(funcExpr = e1; argExpr = e2) ->
200-
yield! walkExpr e1
201-
yield! walkExpr e2
202-
| SynExpr.LetOrUse(bindings = bindings; body = body) ->
203-
// Walk the binding expressions
204-
for binding in bindings do
205-
match binding with
206-
| SynBinding(expr = expr) -> yield! walkExpr expr
207-
208-
// Walk the body
209-
yield! walkExpr body
210-
| SynExpr.Sequential(expr1 = e1; expr2 = e2) ->
211-
yield! walkExpr e1
212-
yield! walkExpr e2
213-
| SynExpr.IfThenElse(ifExpr = e1; thenExpr = e2; elseExpr = e3opt) ->
214-
yield! walkExpr e1
215-
yield! walkExpr e2
216-
217-
match e3opt with
218-
| Some e3 -> yield! walkExpr e3
219-
| None -> ()
220-
| SynExpr.Paren(expr, _, _, _) -> yield! walkExpr expr
221-
| SynExpr.Typed(expr = expr) -> yield! walkExpr expr
222-
// Computation expressions (seq, async, task, etc.)
223-
| SynExpr.ComputationExpr(expr = expr) -> yield! walkExpr expr
224-
// Array or list expressions
225-
| SynExpr.ArrayOrList(exprs = exprs) -> yield! List.collect (walkExpr >> Seq.toList) exprs
226-
| SynExpr.ArrayOrListComputed(expr = expr) -> yield! walkExpr expr
227-
// For loops
228-
| SynExpr.For(doBody = body) -> yield! walkExpr body
229-
| SynExpr.ForEach(enumExpr = enumExpr; bodyExpr = body) ->
230-
yield! walkExpr enumExpr
231-
yield! walkExpr body
232-
// While loop
233-
| SynExpr.While(whileExpr = whileExpr; doExpr = doExpr) ->
234-
yield! walkExpr whileExpr
235-
yield! walkExpr doExpr
236-
// Tuples
237-
| SynExpr.Tuple(exprs = exprs) -> yield! List.collect (walkExpr >> Seq.toList) exprs
238-
// Record expressions
239-
| SynExpr.Record(copyInfo = copyInfo; recordFields = fields) ->
240-
match copyInfo with
241-
| Some(expr, _) -> yield! walkExpr expr
242-
| None -> ()
243-
244-
for (SynExprRecordField(expr = exprOpt)) in fields do
245-
match exprOpt with
246-
| Some expr -> yield! walkExpr expr
247-
| None -> ()
248-
| _ -> ()
249-
}
250-
251-
let rec walkDecl (decl: SynModuleDecl) =
252-
seq {
253-
match decl with
254-
| SynModuleDecl.Let(bindings = bindings) ->
255-
for binding in bindings do
256-
match binding with
257-
| SynBinding(expr = expr) -> yield! walkExpr expr
258-
| SynModuleDecl.Expr(expr, _) -> yield! walkExpr expr
259-
| SynModuleDecl.NestedModule(decls = decls) -> yield! List.collect (walkDecl >> Seq.toList) decls
260-
| SynModuleDecl.Types(typeDefs, _) ->
261-
for typeDef in typeDefs do
262-
match typeDef with
263-
| SynTypeDefn(members = members) ->
264-
for memb in members do
265-
match memb with
266-
| SynMemberDefn.Member(SynBinding(expr = expr), _) -> yield! walkExpr expr
267-
| SynMemberDefn.LetBindings(bindings, _, _, _) ->
268-
for SynBinding(expr = expr) in bindings do
269-
yield! walkExpr expr
270-
| _ -> ()
271-
| _ -> ()
272-
}
273-
274-
match parseResults.ParseTree with
275-
| ParsedInput.ImplFile(ParsedImplFileInput(contents = modules)) ->
276-
modules
277-
|> List.collect (fun (SynModuleOrNamespace(decls = decls)) -> decls |> List.collect (walkDecl >> Seq.toList))
278-
|> List.distinctBy (fun r -> r.Start, r.End)
279-
| _ -> []
280-
28195
let addFile (fsprojPath: string) fileVirtPath =
28296
async {
28397
try
@@ -936,25 +750,7 @@ module Commands =
936750
asyncResult {
937751
let symbol = symbolUse.Symbol
938752

939-
let symbolNameCore =
940-
match symbol with
941-
| :? FSharpActivePatternCase as apc ->
942-
// For active pattern cases, extract just the case name without bars
943-
// apc.Name may include bars like "|LetterOrDigit|_|" for partial patterns
944-
if apc.Name.StartsWith("|") then
945-
extractActivePatternCaseName apc.Name
946-
else
947-
apc.Name
948-
| :? FSharpMemberOrFunctionOrValue as mfv when mfv.IsActivePattern ->
949-
// For active pattern functions, extract just the case name for partial patterns
950-
// For full patterns like "(|Even|Odd|)", use DisplayNameCore as-is
951-
let displayName = symbol.DisplayNameCore
952-
953-
if displayName.Contains("|_|") then
954-
extractActivePatternCaseName displayName
955-
else
956-
displayName
957-
| _ -> symbol.DisplayNameCore
753+
let symbolNameCore = symbol.DisplayNameCore
958754

959755
let tryAdjustRanges (text: IFSACSourceText, ranges: seq<Range>) =
960756
let ranges = ranges |> Seq.map (fun range -> range.NormalizeDriveLetterCasing())
@@ -983,73 +779,13 @@ module Commands =
983779
let! ct = Async.CancellationToken
984780
let symbolUses = tyRes.GetCheckResults.GetUsesOfSymbolInFile(symbol, ct)
985781

986-
let (symbolUses: _ seq), additionalRangesForPartialPatterns =
987-
let baseFiltered: _ seq =
988-
if includeDeclarations then
989-
symbolUses
990-
else
991-
symbolUses |> Seq.filter (fun u -> not u.IsFromDefinition)
992-
993-
// For Active Pattern Cases, FCS returns all cases in the pattern, not just the specific one
994-
// We need to filter to only the symbol that matches our query
995-
// BUT: if querying from the Active Pattern declaration itself (FSharpMemberOrFunctionOrValue),
996-
// we also want to find the case usages
997-
match symbolUse.Symbol with
998-
| :? FSharpActivePatternCase as apc ->
999-
// Querying from a specific case - filter to just that case
1000-
let filtered =
1001-
baseFiltered
1002-
|> Seq.filter (fun u ->
1003-
match u.Symbol with
1004-
| :? FSharpActivePatternCase as foundApc -> foundApc.Name = apc.Name
1005-
| _ -> false)
1006-
1007-
// For partial active patterns in .fsx files, FCS may not return case usages
1008-
// We walk the AST and deduplicate with what FCS found
1009-
let isPartialPattern = apc.Group.IsTotal |> not
1010-
1011-
let caseUsageRanges =
1012-
if isPartialPattern then
1013-
findPartialActivePatternCaseUsages [ apc.Name ] tyRes.GetParseResults
1014-
else
1015-
// Complete patterns - FCS handles these correctly
1016-
[]
1017-
1018-
filtered, caseUsageRanges
1019-
| :? FSharpMemberOrFunctionOrValue as mfv when
1020-
mfv.IsActivePattern
1021-
|| (mfv.DisplayName.StartsWith("(|") && mfv.DisplayName.EndsWith("|)"))
1022-
->
1023-
// Querying from the active pattern function declaration
1024-
// For partial active patterns like (|ParseInt|_|), include case usages in match expressions
1025-
// For complete active patterns like (|Even|Odd|), only return the function declaration
1026-
// Note: IsActivePattern is true for direct definitions, but let-bound values need DisplayName check
1027-
1028-
let patternDisplayName = mfv.DisplayName
1029-
// DisplayName includes parens: "(|ParseInt|_|)" so we need to check for "|_|)" not just "|_|"
1030-
let isPartialActivePattern = patternDisplayName.Contains("|_|")
1031-
1032-
if not isPartialActivePattern then
1033-
// For complete patterns, only return the pattern function declaration
1034-
baseFiltered, []
1035-
else
1036-
// For partial patterns, find case usages by walking the AST
1037-
let caseNames = extractActivePatternCaseNames patternDisplayName
1038-
1039-
let caseUsageRanges =
1040-
findPartialActivePatternCaseUsages caseNames tyRes.GetParseResults
1041-
1042-
// For partial patterns, FCS doesn't include case usages in pattern matches
1043-
// We found them by walking the AST, so we return them as additional ranges
1044-
baseFiltered, caseUsageRanges
1045-
| _ -> baseFiltered, []
1046-
1047-
let ranges =
1048-
let baseRanges = symbolUses |> Seq.map (fun u -> u.Range)
1049-
// Add any additional ranges we found from walking the AST for partial active patterns
1050-
// Deduplicate based on range start and end positions
1051-
Seq.append baseRanges (Seq.ofList additionalRangesForPartialPatterns)
1052-
|> Seq.distinctBy (fun r -> r.Start, r.End)
782+
let symbolUses: _ seq =
783+
if includeDeclarations then
784+
symbolUses
785+
else
786+
symbolUses |> Seq.filter (fun u -> not u.IsFromDefinition)
787+
788+
let ranges = symbolUses |> Seq.map (fun u -> u.Range)
1053789
// Note: tryAdjustRanges is designed to only be able to fail iff `errorOnFailureToFixRange` is `true`
1054790
let! ranges = tryAdjustRanges (text, ranges)
1055791
let ranges = dict [ (text.FileName, Seq.toArray ranges) ]

0 commit comments

Comments
 (0)