Skip to content

Commit 1d37b03

Browse files
committed
FavourNestedFunctions: implement rule
Implemented FavourNestedFunctions rule. Added rule text message to Text.resx. Fixes #638
1 parent 4909981 commit 1d37b03

File tree

2 files changed

+59
-1
lines changed

2 files changed

+59
-1
lines changed

src/FSharpLint.Core/Rules/Conventions/FavourNestedFunctions.fs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,63 @@ open FSharpLint.Framework.Rules
77
open FSharpLint.Framework
88
open FSharpLint.Framework.Suggestion
99

10+
let private (|FunctionDeclaration|_|) (declaration: SynModuleDecl) =
11+
match declaration with
12+
| SynModuleDecl.Let(_, [ SynBinding(_, _, _, _, _, _, _, headPat, _, expr, _, _) ], _) ->
13+
match headPat with
14+
| SynPat.LongIdent(LongIdentWithDots([ident], _), _, _, _, accessibility, _) ->
15+
Some(ident, expr, accessibility)
16+
| _ -> None
17+
| _ -> None
18+
1019
let runner (args: AstNodeRuleParams) =
11-
failwith "Not yet implemeted"
20+
match args.AstNode with
21+
| AstNode.ModuleOrNamespace(SynModuleOrNamespace(_, _, _kind, declarations, _, _, _, _)) ->
22+
let privateFunctionIdentifiers =
23+
declarations
24+
|> Seq.choose
25+
(fun declaration ->
26+
match declaration with
27+
| FunctionDeclaration(ident, _body, Some(SynAccess.Private)) ->
28+
Some ident
29+
| _ -> None)
30+
|> Seq.toArray
31+
32+
match args.CheckInfo with
33+
| Some checkInfo when privateFunctionIdentifiers.Length > 0 ->
34+
let otherFunctionBodies =
35+
declarations
36+
|> List.choose
37+
(fun declaration ->
38+
match declaration with
39+
| FunctionDeclaration(ident, body, _)
40+
when not(Array.exists (fun (each: Ident) -> each.idText = ident.idText) privateFunctionIdentifiers) ->
41+
Some body
42+
| _ -> None)
43+
44+
privateFunctionIdentifiers
45+
|> Array.choose
46+
(fun currFunctionIdentifier ->
47+
match ExpressionUtilities.getSymbolFromIdent args.CheckInfo (SynExpr.Ident currFunctionIdentifier) with
48+
| Some symbolUse ->
49+
let numberOfOtherFunctionsCurrFunctionIsUsedIn =
50+
otherFunctionBodies
51+
|> Seq.filter (fun funcBody ->
52+
checkInfo.GetUsesOfSymbolInFile symbolUse.Symbol
53+
|> Array.exists (fun usage -> ExpressionUtilities.rangeContainsOtherRange funcBody.Range usage.Range))
54+
|> Seq.length
55+
if numberOfOtherFunctionsCurrFunctionIsUsedIn = 1 then
56+
Some {
57+
Range = currFunctionIdentifier.idRange
58+
WarningDetails.Message = Resources.GetString "RulesFavourNestedFunctions"
59+
SuggestedFix = None
60+
TypeChecks = List.Empty
61+
}
62+
else
63+
None
64+
| None -> None)
65+
| _ -> Array.empty
66+
| _ -> Array.empty
1267

1368
let rule =
1469
{ Name = "FavourNestedFunctions"

src/FSharpLint.Core/Text.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,4 +372,7 @@
372372
<data name="RulesFavourAsKeyword" xml:space="preserve">
373373
<value>Prefer using the 'as' pattern to match a constant and bind it to a variable.</value>
374374
</data>
375+
<data name="RulesFavourNestedFunctions" xml:space="preserve">
376+
<value>Prefer using local functions over private module-level functions</value>
377+
</data>
375378
</root>

0 commit comments

Comments
 (0)