Skip to content

Commit aeadd7f

Browse files
authored
Merge PR #652 from Mersho/fix-650
New rule UnneededRecKeyword. Fixes #650
2 parents 6636fb2 + 1c3547a commit aeadd7f

File tree

11 files changed

+136
-2
lines changed

11 files changed

+136
-2
lines changed

docs/content/how-tos/rule-configuration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,4 @@ The following rules can be specified for linting.
123123
- [UnnestedFunctionNames (FL0080)](rules/FL0080.html)
124124
- [NestedFunctionNames (FL0081)](rules/FL0081.html)
125125
- [UsedUnderscorePrefixedElements (FL0082)](rules/FL0082.html)
126+
- [UnneededRecKeyword (FL0083)](rules/FL0083.html)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
title: FL0083
3+
category: how-to
4+
hide_menu: true
5+
---
6+
7+
# UnneededRecKeyword (FL0083)
8+
9+
*Introduced in `0.23.8`*
10+
11+
## Cause
12+
13+
Recursive function (function marked with a "rec" keyword) does not invoke itself.
14+
15+
## Rationale
16+
17+
Using "rec" keyword on a function that is not recursive is unnecessary.
18+
19+
## How To Fix
20+
21+
Update the function to invoke itself or remove "rec" keyword in case it doesn't need to invoke itself recursively.
22+
23+
## Rule Settings
24+
25+
{
26+
"unneededRecKeyword": {
27+
"enabled": true
28+
}
29+
}

src/FSharpLint.Core/Application/Configuration.fs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ type ConventionsConfig =
310310
redundantNewKeyword:EnabledConfig option
311311
favourStaticEmptyFields:EnabledConfig option
312312
asyncExceptionWithoutReturn:EnabledConfig option
313+
unneededRecKeyword:EnabledConfig option
313314
nestedStatements:RuleConfig<NestedStatements.Config> option
314315
cyclomaticComplexity:RuleConfig<CyclomaticComplexity.Config> option
315316
reimplementsFunction:EnabledConfig option
@@ -333,6 +334,7 @@ with
333334
this.favourReRaise |> Option.bind (constructRuleIfEnabled FavourReRaise.rule) |> Option.toArray
334335
this.favourStaticEmptyFields |> Option.bind (constructRuleIfEnabled FavourStaticEmptyFields.rule) |> Option.toArray
335336
this.asyncExceptionWithoutReturn |> Option.bind (constructRuleIfEnabled AsyncExceptionWithoutReturn.rule) |> Option.toArray
337+
this.unneededRecKeyword |> Option.bind (constructRuleIfEnabled UnneededRecKeyword.rule) |> Option.toArray
336338
this.nestedStatements |> Option.bind (constructRuleWithConfig NestedStatements.rule) |> Option.toArray
337339
this.favourConsistentThis |> Option.bind (constructRuleWithConfig FavourConsistentThis.rule) |> Option.toArray
338340
this.cyclomaticComplexity |> Option.bind (constructRuleWithConfig CyclomaticComplexity.rule) |> Option.toArray
@@ -406,6 +408,7 @@ type Configuration =
406408
FavourReRaise:EnabledConfig option
407409
FavourStaticEmptyFields:EnabledConfig option
408410
AsyncExceptionWithoutReturn:EnabledConfig option
411+
UnneededRecKeyword:EnabledConfig option
409412
NestedStatements:RuleConfig<NestedStatements.Config> option
410413
FavourConsistentThis:RuleConfig<FavourConsistentThis.Config> option
411414
CyclomaticComplexity:RuleConfig<CyclomaticComplexity.Config> option
@@ -494,6 +497,7 @@ with
494497
FavourReRaise = None
495498
FavourStaticEmptyFields = None
496499
AsyncExceptionWithoutReturn = None
500+
UnneededRecKeyword = None
497501
NestedStatements = None
498502
FavourConsistentThis = None
499503
CyclomaticComplexity = None
@@ -645,6 +649,7 @@ let flattenConfig (config:Configuration) =
645649
config.FavourReRaise |> Option.bind (constructRuleIfEnabled FavourReRaise.rule)
646650
config.FavourStaticEmptyFields |> Option.bind (constructRuleIfEnabled FavourStaticEmptyFields.rule)
647651
config.AsyncExceptionWithoutReturn |> Option.bind (constructRuleIfEnabled AsyncExceptionWithoutReturn.rule)
652+
config.UnneededRecKeyword |> Option.bind (constructRuleIfEnabled UnneededRecKeyword.rule)
648653
config.NestedStatements |> Option.bind (constructRuleWithConfig NestedStatements.rule)
649654
config.FavourConsistentThis |> Option.bind (constructRuleWithConfig FavourConsistentThis.rule)
650655
config.CyclomaticComplexity |> Option.bind (constructRuleWithConfig CyclomaticComplexity.rule)

src/FSharpLint.Core/FSharpLint.Core.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
<Compile Include="Rules\Formatting\TypedItemSpacing.fs" />
4343
<Compile Include="Rules\Formatting\TypePrefixing.fs" />
4444
<Compile Include="Rules\Formatting\UnionDefinitionIndentation.fs" />
45+
<Compile Include="Rules\Conventions\UnneededRecKeyword.fs" />
4546
<Compile Include="Rules\Conventions\AsyncExceptionWithoutReturn.fs" />
4647
<Compile Include="Rules\Conventions\FavourStaticEmptyFields.fs" />
4748
<Compile Include="Rules\Conventions\RecursiveAsyncFunction.fs" />

src/FSharpLint.Core/Rules/Conventions/Naming/NamingHelper.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ let rec identFromSimplePat = function
369369
| SynSimplePat.Typed(p, _, _) -> identFromSimplePat p
370370
| SynSimplePat.Attrib(_) -> None
371371

372-
let rec isNested args nodeIndex =
372+
let isNested args nodeIndex =
373373
let parent = args.SyntaxArray.[nodeIndex].ParentIndex
374374
let actual = args.SyntaxArray.[parent].Actual
375375

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
module FSharpLint.Rules.UnneededRecKeyword
2+
3+
open System
4+
open FSharp.Compiler.Syntax
5+
open FSharpLint.Framework.Ast
6+
open FSharpLint.Framework.Rules
7+
open FSharpLint.Framework
8+
open FSharpLint.Framework.Suggestion
9+
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 (LongIdentWithDots([ident], _), _, _, _, _, range), _, _, _, _) :: _ ->
15+
let symbolUses = checkInfo.GetAllUsesOfAllSymbolsInFile()
16+
let funcName = ident.idText
17+
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)
24+
25+
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
39+
40+
| _ -> Array.empty
41+
42+
let rule =
43+
{ Name = "UnneededRecKeyword"
44+
Identifier = Identifiers.UnneededRecKeyword
45+
RuleConfig =
46+
{ AstNodeRuleConfig.Runner = runner
47+
Cleanup = ignore } }
48+
|> AstNodeRule

src/FSharpLint.Core/Rules/Identifiers.fs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,5 @@ let AsyncExceptionWithoutReturn = identifier 78
8686
let SuggestUseAutoProperty = identifier 79
8787
let UnnestedFunctionNames = identifier 80
8888
let NestedFunctionNames = identifier 81
89-
let UsedUnderscorePrefixedElements = identifier 82
89+
let UsedUnderscorePrefixedElements = identifier 82
90+
let UnneededRecKeyword = identifier 83

src/FSharpLint.Core/Text.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,4 +357,7 @@
357357
<data name="RulesSuggestUseAutoProperty" xml:space="preserve">
358358
<value>Consider using auto-properties via the 'val' keyword.</value>
359359
</data>
360+
<data name="RulesUnneededRecKeyword" xml:space="preserve">
361+
<value>The '{0}' function has a "rec" keyword, but it is not really recursive, consider removing it.</value>
362+
</data>
360363
</root>

src/FSharpLint.Core/fsharplint.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@
292292
"suggestUseAutoProperty": { "enabled": false },
293293
"avoidTooShortNames": { "enabled": false },
294294
"asyncExceptionWithoutReturn": { "enabled": false },
295+
"unneededRecKeyword": { "enabled": true },
295296
"indentation": {
296297
"enabled": false
297298
},

tests/FSharpLint.Core.Tests/FSharpLint.Core.Tests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<Compile Include="Rules\Formatting\TypedItemSpacing.fs" />
2727
<Compile Include="Rules\Formatting\UnionDefinitionIndentation.fs" />
2828
<Compile Include="Rules\Formatting\TypePrefixing.fs" />
29+
<Compile Include="Rules\Conventions\UnneededRecKeyword.fs" />
2930
<Compile Include="Rules\Conventions\AsyncExceptionWithoutReturn.fs" />
3031
<Compile Include="Rules\Conventions\FavourStaticEmptyFields.fs" />
3132
<Compile Include="Rules\Conventions\RecursiveAsyncFunction.fs" />

0 commit comments

Comments
 (0)