Skip to content

Commit 48f963f

Browse files
authored
Add new rule FavourConsistentThis (#518)
Fixes #511
1 parent a1b4b30 commit 48f963f

File tree

9 files changed

+144
-1
lines changed

9 files changed

+144
-1
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,5 @@ The following rules can be specified for linting.
113113
- [FavourTypedIgnore (FL0070)](rules/FL0070.html)
114114
- [CyclomaticComplexity (FL0071)](rules/FL0071.html)
115115
- [FailwithBadUsage (FL0072)](rules/FL0072.html)
116-
- [FavourReRaise (FL0073)](rules/FL0073.html)
116+
- [FavourReRaise (FL0073)](rules/FL0073.html)
117+
- [FavourConsistentThis (FL0074)](rules/FL0074.html)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
title: FL0074
3+
category: how-to
4+
hide_menu: true
5+
---
6+
7+
# FavourConsistentThis (FL0074)
8+
9+
*Introduced in `0.21.1`*
10+
11+
## Cause
12+
13+
Rule to detect inconsistent symbol used for the instance identifier (e.g. `this`).
14+
15+
## Rationale
16+
17+
Using the same symbol for the instance identifier (e.g. `this`, or `self`, or `instance`) aids readability.
18+
19+
## How To Fix
20+
21+
Replace all occurrences of the instance symbol used with the one configured in the rule settings (e.g. if using both `self` and `this` in the same codebase, decide which one to keep, and then only use one of them, not both).
22+
23+
## Rule Settings
24+
25+
{
26+
"favourConsistentThis": {
27+
"enabled": false,
28+
"config": {
29+
"symbol": "this"
30+
}
31+
}
32+
}
33+

src/FSharpLint.Core/FSharpLint.Core.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
<Compile Include="Rules\Conventions\NoPartialFunctions.fs" />
4949
<Compile Include="Rules\Conventions\CyclomaticComplexity.fs" />
5050
<Compile Include="Rules\Conventions\FavourReRaise.fs" />
51+
<Compile Include="Rules\Conventions\FavourConsistentThis.fs" />
5152
<Compile Include="Rules\Conventions\RaiseWithTooManyArguments\RaiseWithTooManyArgumentsHelper.fs" />
5253
<Compile Include="Rules\Conventions\RaiseWithTooManyArguments\FailwithWithSingleArgument.fs" />
5354
<Compile Include="Rules\Conventions\RaiseWithTooManyArguments\RaiseWithSingleArgument.fs" />
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
module FSharpLint.Rules.FavourConsistentThis
2+
3+
open FSharpLint.Framework
4+
open FSharpLint.Framework.Suggestion
5+
open FSharp.Compiler.Syntax
6+
open FSharp.Compiler.Text
7+
open FSharpLint.Framework.Ast
8+
open FSharpLint.Framework.Rules
9+
open System
10+
11+
/// Configuration of the favour consistent 'this' (FL0074) rule.
12+
[<RequireQualifiedAccess>]
13+
type Config = { Symbol: string }
14+
15+
let runner (config: Config) args =
16+
let symbol = config.Symbol
17+
match args.AstNode with
18+
| AstNode.Binding(SynBinding(_, _, _, _, _, _, _, pattern, _, _, range, _)) ->
19+
match pattern with
20+
| SynPat.LongIdent(LongIdentWithDots(identifiers, _),_, _, _, _, _) ->
21+
match identifiers with
22+
| head::_ when head.idText <> config.Symbol ->
23+
let error =
24+
{ Range = range
25+
Message = String.Format(Resources.GetString "RulesFavourConsistentThis", config.Symbol)
26+
SuggestedFix = None
27+
TypeChecks = List.Empty }
28+
|> Array.singleton
29+
error
30+
| _ -> Array.empty
31+
| _ -> Array.empty
32+
| _ -> Array.empty
33+
34+
let rule config =
35+
{ Name = "FavourConsistentThis"
36+
Identifier = Identifiers.FavourConsistentThis
37+
RuleConfig = { AstNodeRuleConfig.Runner = runner config; Cleanup = ignore } }
38+
|> AstNodeRule

src/FSharpLint.Core/Rules/Identifiers.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,4 @@ let FavourTypedIgnore = identifier 70
7878
let CyclomaticComplexity = identifier 71
7979
let FailwithBadUsage = identifier 72
8080
let FavourReRaise = identifier 73
81+
let FavourConsistentThis = identifier 74

src/FSharpLint.Core/Text.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,4 +330,7 @@
330330
<data name="RulesFavourReRaise" xml:space="preserve">
331331
<value>Rather use reraise() instead of using raise with the same captured exception.</value>
332332
</data>
333+
<data name="RulesFavourConsistentThis" xml:space="preserve">
334+
<value>Prefer using '{0}' consistently.</value>
335+
</data>
333336
</root>

src/FSharpLint.Core/fsharplint.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,12 @@
266266
"tupleOfWildcards": { "enabled": true },
267267
"favourTypedIgnore": { "enabled": false },
268268
"favourReRaise": { "enabled": true },
269+
"favourConsistentThis": {
270+
"enabled": false,
271+
"config": {
272+
"symbol": "this"
273+
}
274+
},
269275
"indentation": {
270276
"enabled": false
271277
},

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
<Compile Include="Rules\Conventions\SourceLength.fs" />
3737
<Compile Include="Rules\Conventions\NoPartialFunctions.fs" />
3838
<Compile Include="Rules\Conventions\FavourReRaise.fs" />
39+
<Compile Include="Rules\Conventions\FavourConsistentThis.fs" />
3940
<Compile Include="Rules\Conventions\Naming\NamingHelpers.fs" />
4041
<Compile Include="Rules\Conventions\Naming\InterfaceNames.fs" />
4142
<Compile Include="Rules\Conventions\Naming\ExceptionNames.fs" />
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
module FSharpLint.Core.Tests.Rules.Conventions.FavourConsistentThis
2+
3+
open NUnit.Framework
4+
open FSharpLint.Rules
5+
open System
6+
7+
[<TestFixture>]
8+
type TestConventionsFavourConsistentThis() =
9+
inherit TestAstNodeRuleBase.TestAstNodeRuleBase(FavourConsistentThis.rule { Symbol = "this" })
10+
11+
[<Test>]
12+
member this.ConsistentThisShouldNotProduceError() =
13+
this.Parse """
14+
type Foo =
15+
{ Bar : Baz }
16+
member this.FooBar =
17+
()
18+
member this.FooBarBaz x =
19+
failwith "foobarbaz" """
20+
21+
Assert.IsTrue this.NoErrorsExist
22+
23+
[<Test>]
24+
member this.InConsistentSelfShouldProduceError() =
25+
this.Parse """
26+
type Foo =
27+
{ Bar : Baz }
28+
member self.FooBar =
29+
()
30+
member this.FooBarBaz x =
31+
failwith "foobarbaz" """
32+
33+
Assert.IsTrue this.ErrorsExist
34+
Assert.IsTrue(this.ErrorExistsAt(4, 11))
35+
36+
[<Test>]
37+
member this.InConsistentSelfShouldProduceError_2() =
38+
this.Parse """
39+
type Foo =
40+
{ Bar : Baz }
41+
member this.FooBar =
42+
()
43+
member self.FooBarBaz x =
44+
failwith "foobarbaz" """
45+
46+
Assert.IsTrue this.ErrorsExist
47+
Assert.IsTrue(this.ErrorExistsAt(6, 11))
48+
49+
[<Test>]
50+
member this.InConsistentSelfShouldProduceError_3() =
51+
this.Parse """
52+
type CustomerName(firstName, middleInitial, lastName) =
53+
member this.FirstName = firstName
54+
member self.MiddleInitial = middleInitial
55+
member this.LastName = lastName """
56+
57+
Assert.IsTrue this.ErrorsExist
58+
Assert.IsTrue(this.ErrorExistsAt(4, 11))
59+

0 commit comments

Comments
 (0)