Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,5 @@ tests/FSharpLint.FunctionalTest.TestedProject/FSharpLintMSBuildTaskTest/

DesignTimeBuild/
.vs/
.ionide/
.ionide/
.vscode/
1 change: 1 addition & 0 deletions docs/content/how-tos/rule-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,4 @@ The following rules can be specified for linting.
- [FavourConsistentThis (FL0074)](rules/FL0074.html)
- [AvoidTooShortNames (FL0075)](rules/FL0075.html)
- [FavourStaticEmptyFields (FL0076)](rules/FL0076.html)
- [IndexerAccessorStyleConsistency (FL0077)](rules/FL0077.html)
32 changes: 32 additions & 0 deletions docs/content/how-tos/rules/FL0077.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: FL0077
category: how-to
hide_menu: true
---

# IndexerAccessorStyleConsistency (FL0077)

*Introduced in `0.21.3`*

## Cause

Use of OCaml style indexer accessors instead of CSharp or viceversa.

## Rationale

F# 6.0 introduces a new style for indexer accessor, similar to the one in C#. But it's convenient for our codebase to be consistent in which accessor style to use.

## How To Fix

If the config style is OCaml use `someArray.[1]` and if the default style is CSharp use `someArray[1]`.

## Rule Settings

{
"favourStaticEmptyFields": {
"enabled": false,
"config": {
"style": "OCaml"
}
}
}
7 changes: 6 additions & 1 deletion src/FSharpLint.Core/Application/Configuration.fs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ with
type ConventionsConfig =
{ recursiveAsyncFunction:EnabledConfig option
avoidTooShortNames:EnabledConfig option
indexerAccessorStyleConsistency: RuleConfig<IndexerAccessorStyleConsistency.Config> option
redundantNewKeyword:EnabledConfig option
favourStaticEmptyFields:EnabledConfig option
nestedStatements:RuleConfig<NestedStatements.Config> option
Expand All @@ -324,7 +325,8 @@ with
member this.Flatten() =
[|
this.recursiveAsyncFunction |> Option.bind (constructRuleIfEnabled RecursiveAsyncFunction.rule) |> Option.toArray
this.avoidTooShortNames |> Option.bind (constructRuleIfEnabled AvoidTooShortNames.rule) |> Option.toArray
this.avoidTooShortNames |> Option.bind (constructRuleIfEnabled AvoidTooShortNames.rule) |> Option.toArray
this.indexerAccessorStyleConsistency |> Option.bind (constructRuleWithConfig IndexerAccessorStyleConsistency.rule) |> Option.toArray
this.redundantNewKeyword |> Option.bind (constructRuleIfEnabled RedundantNewKeyword.rule) |> Option.toArray
this.favourReRaise |> Option.bind (constructRuleIfEnabled FavourReRaise.rule) |> Option.toArray
this.favourStaticEmptyFields |> Option.bind (constructRuleIfEnabled FavourStaticEmptyFields.rule) |> Option.toArray
Expand Down Expand Up @@ -394,6 +396,7 @@ type Configuration =
PatternMatchExpressionIndentation:EnabledConfig option
RecursiveAsyncFunction:EnabledConfig option
AvoidTooShortNames:EnabledConfig option
IndexerAccessorStyleConsistency:RuleConfig<IndexerAccessorStyleConsistency.Config> option
RedundantNewKeyword:EnabledConfig option
FavourReRaise:EnabledConfig option
FavourStaticEmptyFields:EnabledConfig option
Expand Down Expand Up @@ -478,6 +481,7 @@ with
PatternMatchExpressionIndentation = None
RecursiveAsyncFunction = None
AvoidTooShortNames = None
IndexerAccessorStyleConsistency = None
RedundantNewKeyword = None
FavourReRaise = None
FavourStaticEmptyFields = None
Expand Down Expand Up @@ -625,6 +629,7 @@ let flattenConfig (config:Configuration) =
config.PatternMatchExpressionIndentation |> Option.bind (constructRuleIfEnabled PatternMatchExpressionIndentation.rule)
config.RecursiveAsyncFunction |> Option.bind (constructRuleIfEnabled RecursiveAsyncFunction.rule)
config.AvoidTooShortNames |> Option.bind (constructRuleIfEnabled AvoidTooShortNames.rule)
config.IndexerAccessorStyleConsistency |> Option.bind (constructRuleWithConfig IndexerAccessorStyleConsistency.rule)
config.RedundantNewKeyword |> Option.bind (constructRuleIfEnabled RedundantNewKeyword.rule)
config.FavourReRaise |> Option.bind (constructRuleIfEnabled FavourReRaise.rule)
config.FavourStaticEmptyFields |> Option.bind (constructRuleIfEnabled FavourStaticEmptyFields.rule)
Expand Down
1 change: 1 addition & 0 deletions src/FSharpLint.Core/FSharpLint.Core.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
<Compile Include="Rules\Conventions\Binding\UselessBinding.fs" />
<Compile Include="Rules\Conventions\Binding\TupleOfWildcards.fs" />
<Compile Include="Rules\Conventions\AvoidTooShortNames.fs" />
<Compile Include="Rules\Conventions\IndexerAccessorStyleConsistency.fs" />
<Compile Include="Rules\Typography\Indentation.fs" />
<Compile Include="Rules\Typography\MaxCharactersOnLine.fs" />
<Compile Include="Rules\Typography\TrailingWhitespaceOnLine.fs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module FSharpLint.Rules.IndexerAccessorStyleConsistency

open FSharp.Compiler.Syntax
open FSharpLint.Framework.Ast
open FSharpLint.Framework.AstInfo
open FSharpLint.Framework.Rules
open FSharpLint.Framework.Suggestion
open FSharpLint.Framework
open System

[<RequireQualifiedAccess>]
type Config = {
Style: string
}

let generateOutput (range: FSharp.Compiler.Text.Range) =
{ Range = range
Message = Resources.GetString "RulesIndexerAccessorStyleConsistency"
SuggestedFix = None
TypeChecks = List.Empty } |> Array.singleton

let runner (config: Config) (args: AstNodeRuleParams) =
let styleType = config.Style
if String.Equals (styleType, "ocaml", StringComparison.InvariantCultureIgnoreCase) then
match args.AstNode with
| AstNode.Binding binding ->
match binding with
| SynBinding (_, _, _, _, _, _, _, SynPat.Named _, _
, SynExpr.App (ExprAtomicFlag.Atomic, _, SynExpr.Ident _, SynExpr.ArrayOrListOfSeqExpr (_, expr, range), _), _, _) ->
generateOutput range
| _ ->
Array.empty
| _ ->
Array.empty
elif String.Equals (styleType, "csharp", StringComparison.InvariantCultureIgnoreCase) then
match args.AstNode with
| AstNode.Binding binding ->
match binding with
| SynBinding (_, _, _, _, _, _, _, SynPat.Named _, _
, SynExpr.DotIndexedGet (_, _, _, range), _, _) ->
generateOutput range
| _ ->
Array.empty
| _ ->
Array.empty
else
failwithf "Unknown style type %s" styleType

let rule config =
{ Name = "IndexerAccessorStyleConsistency"
Identifier = Identifiers.IndexerAccessorStyleConsistency
RuleConfig = { AstNodeRuleConfig.Runner = runner config; Cleanup = ignore } }
|> AstNodeRule
1 change: 1 addition & 0 deletions src/FSharpLint.Core/Rules/Identifiers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,4 @@ let FavourReRaise = identifier 73
let FavourConsistentThis = identifier 74
let AvoidTooShortNames = identifier 75
let FavourStaticEmptyFields = identifier 76
let IndexerAccessorStyleConsistency = identifier 77
3 changes: 3 additions & 0 deletions src/FSharpLint.Core/Text.resx
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@
<data name="RulesAvoidTooShortNamesError" xml:space="preserve">
<value>Consider using a longer name, as it is currently too short.</value>
</data>
<data name="RulesIndexerAccessorStyleConsistency" xml:space="preserve">
<value>Consider switching the indexer accessor from OCaml style to CSharp style or viceversa.</value>
</data>
<data name="RulesNamingConventionsPrefixError" xml:space="preserve">
<value>Consider changing `{0}` to be prefixed with `{1}`.</value>
</data>
Expand Down
6 changes: 6 additions & 0 deletions src/FSharpLint.Core/fsharplint.json
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,12 @@
}
},
"avoidTooShortNames": { "enabled": false },
"indexerAccessorStyleConsistency": {
"enabled": false,
"config": {
"style": "OCaml"
}
},
"indentation": {
"enabled": false
},
Expand Down
1 change: 1 addition & 0 deletions tests/FSharpLint.Core.Tests/FSharpLint.Core.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<Compile Include="Rules\Conventions\FavourReRaise.fs" />
<Compile Include="Rules\Conventions\FavourConsistentThis.fs" />
<Compile Include="Rules\Conventions\AvoidTooShortNames.fs" />
<Compile Include="Rules\Conventions\IndexerAccessorStyleConsistency.fs" />
<Compile Include="Rules\Conventions\Naming\NamingHelpers.fs" />
<Compile Include="Rules\Conventions\Naming\InterfaceNames.fs" />
<Compile Include="Rules\Conventions\Naming\ExceptionNames.fs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
module FSharpLint.Core.Tests.Rules.Conventions.IndexerAccessorStyleConsistency

open NUnit.Framework
open FSharpLint.Framework.Rules
open FSharpLint.Rules.IndexerAccessorStyleConsistency
open FSharpLint.Rules

[<TestFixture>]
type TestConventionsIndexerAccessorStyleConsistencyCSharp() =
inherit TestAstNodeRuleBase.TestAstNodeRuleBase(IndexerAccessorStyleConsistency.rule { Style = "CSharp" })

[<Test>]
member this.IndexerAccessorStyleConsistencyOCamlStyleWhenUsingCSharp() =
this.Parse """
module Program

let someArray = [| "foo" ; "bar" |]
let bar = someArray.[1]
System.Console.WriteLine bar"""

Assert.IsTrue this.ErrorsExist

[<Test>]
member this.IndexerAccessorStyleConsistencyCSharpStyleWhenUsingCSharp() =
this.Parse """
module Program

let someArray = [| "foo" ; "bar" |]
let bar = someArray[1]
System.Console.WriteLine bar"""

Assert.IsTrue this.NoErrorsExist

[<TestFixture>]
type TestConventionsIndexerAccessorStyleConsistencyOCaml() =
inherit TestAstNodeRuleBase.TestAstNodeRuleBase(IndexerAccessorStyleConsistency.rule { Style = "OCaml" })

[<Test>]
member this.IndexerAccessorStyleConsistencyCSharpStyleWhenUsingOCaml() =
this.Parse """
module Program

let someArray = [| "foo" ; "bar" |]
let bar = someArray[1]
System.Console.WriteLine bar"""

Assert.IsTrue this.ErrorsExist

[<Test>]
member this.IndexerAccessorStyleConsistencyOCamlStyleWhenUsingOCaml() =
this.Parse """
module Program

let someArray = [| "foo" ; "bar" |]
let bar = someArray.[1]
System.Console.WriteLine bar"""

Assert.IsTrue this.NoErrorsExist