@@ -8,11 +8,11 @@ open FSharpLint.Framework.Ast
88open FSharpLint.Framework .Rules
99open FSharpLint.Framework .ExpressionUtilities
1010
11- let private validateLambdaCannotBeReplacedWithComposition _ lambda range =
12- let canBeReplacedWithFunctionComposition expression =
11+ let private validateLambdaCannotBeReplacedWithComposition fileContents _ lambda range =
12+ let tryReplaceWithFunctionComposition expression =
1313 let getLastElement = List.rev >> List.head
1414
15- let rec lambdaArgumentIsLastApplicationInFunctionCalls expression ( lambdaArgument : Ident ) numFunctionCalls =
15+ let rec lambdaArgumentIsLastApplicationInFunctionCalls expression ( lambdaArgument : Ident ) ( calledFunctionIdents : List < string >) =
1616 let rec appliedValuesAreConstants appliedValues =
1717 match appliedValues with
1818 | ( SynExpr.Const(_)| SynExpr.Null(_)):: rest -> appliedValuesAreConstants rest
@@ -22,34 +22,56 @@ let private validateLambdaCannotBeReplacedWithComposition _ lambda range =
2222 match AstNode.Expression expression with
2323 | FuncApp( exprs, _) ->
2424 match List.map removeParens exprs with
25- | ( SynExpr.Ident (_) | SynExpr.LongIdent ( _)):: appliedValues
25+ | ( ExpressionUtilities.Identifier ( idents , _)):: appliedValues
2626 when appliedValuesAreConstants appliedValues ->
27-
27+
28+ let funcName = String.Join( " ." , idents)
29+ let funcStringParts =
30+ Seq.append
31+ ( Seq.singleton funcName)
32+ ( appliedValues
33+ |> Seq.take ( appliedValues.Length - 1 )
34+ |> Seq.choose ( fun value -> ExpressionUtilities.tryFindTextOfRange value.Range fileContents))
35+ let funcString = String.Join( " " , funcStringParts)
36+
2837 match getLastElement appliedValues with
29- | SynExpr.Ident( lastArgument) when numFunctionCalls > 1 ->
30- lastArgument.idText = lambdaArgument.idText
38+ | SynExpr.Ident( lastArgument) when calledFunctionIdents.Length > 1 ->
39+ if lastArgument.idText = lambdaArgument.idText then
40+ funcString :: calledFunctionIdents
41+ else
42+ []
3143 | SynExpr.App(_, false , _, _, _) as nextFunction ->
32- lambdaArgumentIsLastApplicationInFunctionCalls nextFunction lambdaArgument ( numFunctionCalls + 1 )
33- | _ -> false
34- | _ -> false
35- | _ -> false
44+ lambdaArgumentIsLastApplicationInFunctionCalls
45+ nextFunction
46+ lambdaArgument
47+ ( funcString :: calledFunctionIdents)
48+ | _ -> []
49+ | _ -> []
50+ | _ -> []
3651
3752 match lambda.Arguments with
3853 | [ singleParameter] ->
39- Helper.FunctionReimplementation.getLambdaParamIdent singleParameter
40- |> Option.exists ( fun paramIdent -> lambdaArgumentIsLastApplicationInFunctionCalls expression paramIdent 1 )
41- | _ -> false
54+ match Helper.FunctionReimplementation.getLambdaParamIdent singleParameter with
55+ | Some paramIdent ->
56+ match lambdaArgumentIsLastApplicationInFunctionCalls expression paramIdent [] with
57+ | [] -> None
58+ | funcStrings -> Some funcStrings
59+ | None -> None
60+ | _ -> None
4261
43- if canBeReplacedWithFunctionComposition lambda.Body then
62+ match tryReplaceWithFunctionComposition lambda.Body with
63+ | None -> Array.empty
64+ | Some funcStrings ->
65+ let suggestedFix =
66+ lazy (
67+ Some { FromRange = range; FromText = fileContents; ToText = String.Join( " >> " , funcStrings) })
4468 { Range = range
4569 Message = Resources.GetString( " RulesCanBeReplacedWithComposition" )
46- SuggestedFix = None
70+ SuggestedFix = Some suggestedFix
4771 TypeChecks = [] } |> Array.singleton
48- else
49- Array.empty
5072
5173let runner ( args : AstNodeRuleParams ) =
52- Helper.FunctionReimplementation.checkLambda args validateLambdaCannotBeReplacedWithComposition
74+ Helper.FunctionReimplementation.checkLambda args ( validateLambdaCannotBeReplacedWithComposition args.FileContent )
5375
5476let rule =
5577 { Name = " CanBeReplacedWithComposition"
0 commit comments