Skip to content

Commit

Permalink
CanBeReplacedWithComposition: implement suggestion
Browse files Browse the repository at this point in the history
Implement suggested fix for CanBeReplacedWithComposition rule.
  • Loading branch information
webwarrior-ws committed Dec 20, 2023
1 parent 09f4305 commit f02a42e
Showing 1 changed file with 41 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ open FSharp.Compiler.Syntax
open FSharpLint.Framework.Ast
open FSharpLint.Framework.Rules

let private validateLambdaCannotBeReplacedWithComposition _ lambda range =
let canBeReplacedWithFunctionComposition expression =
let private validateLambdaCannotBeReplacedWithComposition fileContents _ lambda range =
let tryReplaceWithFunctionComposition expression =
let getLastElement = List.rev >> List.head

let rec lambdaArgumentIsLastApplicationInFunctionCalls expression (lambdaArgument:Ident) numFunctionCalls =
let rec lambdaArgumentIsLastApplicationInFunctionCalls expression (lambdaArgument:Ident) (calledFunctionIdents: List<string>) =
let rec appliedValuesAreConstants appliedValues =
match appliedValues with
| (SynExpr.Const(_)| SynExpr.Null(_))::rest -> appliedValuesAreConstants rest
Expand All @@ -21,34 +21,56 @@ let private validateLambdaCannotBeReplacedWithComposition _ lambda range =
match AstNode.Expression expression with
| FuncApp(exprs, _) ->
match List.map removeParens exprs with
| (SynExpr.Ident(_) | SynExpr.LongIdent(_))::appliedValues
| (ExpressionUtilities.Identifier(idents, _))::appliedValues
when appliedValuesAreConstants appliedValues ->


let funcName = String.Join(".", idents)
let funcStringParts =
Seq.append
(Seq.singleton funcName)
(appliedValues
|> Seq.take (appliedValues.Length - 1)
|> Seq.choose (fun value -> ExpressionUtilities.tryFindTextOfRange value.Range fileContents))
let funcString = String.Join(" ", funcStringParts)

match getLastElement appliedValues with
| SynExpr.Ident(lastArgument) when numFunctionCalls > 1 ->
lastArgument.idText = lambdaArgument.idText
| SynExpr.Ident(lastArgument) when calledFunctionIdents.Length > 1 ->
if lastArgument.idText = lambdaArgument.idText then
funcString :: calledFunctionIdents
else
[]
| SynExpr.App(_, false, _, _, _) as nextFunction ->
lambdaArgumentIsLastApplicationInFunctionCalls nextFunction lambdaArgument (numFunctionCalls + 1)
| _ -> false
| _ -> false
| _ -> false
lambdaArgumentIsLastApplicationInFunctionCalls
nextFunction
lambdaArgument
(funcString :: calledFunctionIdents)
| _ -> []
| _ -> []
| _ -> []

match lambda.Arguments with
| [singleParameter] ->
Helper.FunctionReimplementation.getLambdaParamIdent singleParameter
|> Option.exists (fun paramIdent -> lambdaArgumentIsLastApplicationInFunctionCalls expression paramIdent 1)
| _ -> false
match Helper.FunctionReimplementation.getLambdaParamIdent singleParameter with
| Some paramIdent ->
match lambdaArgumentIsLastApplicationInFunctionCalls expression paramIdent [] with
| [] -> None
| funcStrings -> Some funcStrings
| None -> None
| _ -> None

if canBeReplacedWithFunctionComposition lambda.Body then
match tryReplaceWithFunctionComposition lambda.Body with
| None -> Array.empty
| Some funcStrings ->
let suggestedFix =
lazy(
Some { FromRange = range; FromText = fileContents; ToText = String.Join(" >> ", funcStrings) })
{ Range = range
Message = Resources.GetString("RulesCanBeReplacedWithComposition")
SuggestedFix = None
SuggestedFix = Some suggestedFix
TypeChecks = [] } |> Array.singleton
else
Array.empty

let runner (args:AstNodeRuleParams) =
Helper.FunctionReimplementation.checkLambda args validateLambdaCannotBeReplacedWithComposition
Helper.FunctionReimplementation.checkLambda args (validateLambdaCannotBeReplacedWithComposition args.FileContent)

let rule =
{ Name = "CanBeReplacedWithComposition"
Expand Down

1 comment on commit f02a42e

@knocte
Copy link

@knocte knocte commented on f02a42e Dec 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.