|
2 | 2 |
|
3 | 3 | open System |
4 | 4 | open FSharp.Compiler.Syntax |
| 5 | +open FSharp.Compiler.CodeAnalysis |
| 6 | +open FSharp.Compiler.Text |
5 | 7 | open FSharpLint.Framework.Ast |
6 | 8 | open FSharpLint.Framework.Rules |
7 | 9 | open FSharpLint.Framework |
8 | 10 | open FSharpLint.Framework.Suggestion |
9 | 11 |
|
10 | | -let runner (args: AstNodeRuleParams) = |
11 | | - match args.AstNode, args.CheckInfo with |
12 | | - | AstNode.ModuleDeclaration (SynModuleDecl.Let (isRecursive, bindings, letRange)), Some checkInfo when isRecursive -> |
13 | | - match bindings with |
14 | | - | SynBinding (_, _, _, _, _, _, _, SynPat.LongIdent (SynLongIdent([ident], _, _), _, _, _, _, range), _, _, _, _, _) :: _ -> |
15 | | - let symbolUses = checkInfo.GetAllUsesOfAllSymbolsInFile() |
16 | | - let funcName = ident.idText |
| 12 | +type internal RecursiveFunctionInfo = |
| 13 | + { |
| 14 | + Identifier: Ident |
| 15 | + Range: range |
| 16 | + Body: SynExpr |
| 17 | + Attributes: SynAttributes |
| 18 | + } |
17 | 19 |
|
18 | | - let functionCalls = |
19 | | - symbolUses |
20 | | - |> Seq.filter (fun usage -> |
21 | | - usage.Symbol.DisplayName = funcName |
22 | | - && usage.Range.StartLine >= letRange.StartLine |
23 | | - && usage.Range.EndLine <= letRange.EndLine) |
| 20 | +let internal (|RecursiveFunction|_|) (astNode: AstNode) = |
| 21 | + match astNode with |
| 22 | + | AstNode.ModuleDeclaration (SynModuleDecl.Let (true, bindings, _)) -> |
| 23 | + match bindings with |
| 24 | + | SynBinding (_, _, _, _, attributes, _, _, SynPat.LongIdent (SynLongIdent([ident], _, _), _, _, _, _, range), _, body, _, _, _) :: _ -> |
| 25 | + Some { Identifier = ident; Range = range; Body = body; Attributes = attributes } |
| 26 | + | _ -> None |
| 27 | + | _ -> None |
24 | 28 |
|
| 29 | +let internal functionCallsItself (checkInfo: FSharpCheckFileResults) (func: RecursiveFunctionInfo) = |
| 30 | + let funcName = func.Identifier.idText |
| 31 | + checkInfo.GetAllUsesOfAllSymbolsInFile() |
| 32 | + |> Seq.exists (fun usage -> |
| 33 | + usage.Symbol.DisplayName = funcName |
| 34 | + && ExpressionUtilities.rangeContainsOtherRange func.Body.Range usage.Range) |
25 | 35 |
|
26 | | - if (functionCalls |> Seq.length) <= 1 then |
27 | | - { Range = range |
28 | | - Message = |
29 | | - String.Format( |
30 | | - Resources.GetString "RulesUnneededRecKeyword", |
31 | | - funcName |
32 | | - ) |
33 | | - SuggestedFix = None |
34 | | - TypeChecks = list.Empty } |
35 | | - |> Array.singleton |
36 | | - else |
37 | | - Array.empty |
38 | | - | _ -> Array.empty |
| 36 | +let private emitWarning (func: RecursiveFunctionInfo) = |
| 37 | + { Range = func.Range |
| 38 | + Message = |
| 39 | + String.Format( |
| 40 | + Resources.GetString "RulesUnneededRecKeyword", |
| 41 | + func.Identifier.idText |
| 42 | + ) |
| 43 | + SuggestedFix = None |
| 44 | + TypeChecks = list.Empty } |
39 | 45 |
|
| 46 | +let runner (args: AstNodeRuleParams) = |
| 47 | + match args.AstNode, args.CheckInfo with |
| 48 | + | RecursiveFunction(func), Some checkInfo when not (functionCallsItself checkInfo func) -> |
| 49 | + emitWarning func |> Array.singleton |
40 | 50 | | _ -> Array.empty |
41 | 51 |
|
42 | 52 | let rule = |
|
0 commit comments