From 596de0161b44ae2c493643e2e0e995e99449fd36 Mon Sep 17 00:00:00 2001 From: Retheesh Erathu Date: Wed, 17 Dec 2025 19:40:26 +0100 Subject: [PATCH 1/8] Add new rule FavourSingleton Fixes https://github.com/fsprojects/FSharpLint/issues/526 Co-authored-by: Parham Saremi --- docs/content/how-tos/rule-configuration.md | 1 + docs/content/how-tos/rules/FL0089.md | 29 ++++++ .../Application/Configuration.fs | 5 +- src/FSharpLint.Core/FSharpLint.Core.fsproj | 1 + .../Rules/Conventions/FavourSingleton.fs | 38 ++++++++ src/FSharpLint.Core/Rules/Identifiers.fs | 1 + src/FSharpLint.Core/Text.resx | 3 + src/FSharpLint.Core/fsharplint.json | 1 + .../FSharpLint.Core.Tests.fsproj | 1 + .../Rules/Conventions/FavourSingleton.fs | 93 +++++++++++++++++++ 10 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 docs/content/how-tos/rules/FL0089.md create mode 100644 src/FSharpLint.Core/Rules/Conventions/FavourSingleton.fs create mode 100644 tests/FSharpLint.Core.Tests/Rules/Conventions/FavourSingleton.fs diff --git a/docs/content/how-tos/rule-configuration.md b/docs/content/how-tos/rule-configuration.md index 421beeabe..b0af74e66 100644 --- a/docs/content/how-tos/rule-configuration.md +++ b/docs/content/how-tos/rule-configuration.md @@ -129,3 +129,4 @@ The following rules can be specified for linting. - [FavourAsKeyword (FL0086)](rules/FL0086.html) - [InterpolatedStringWithNoSubstitution (FL0087)](rules/FL0087.html) - [IndexerAccessorStyleConsistency (FL0088)](rules/FL0088.html) +- [FavourSingleton (FL0089)](rules/FL0089.html) diff --git a/docs/content/how-tos/rules/FL0089.md b/docs/content/how-tos/rules/FL0089.md new file mode 100644 index 000000000..10b44b9f0 --- /dev/null +++ b/docs/content/how-tos/rules/FL0089.md @@ -0,0 +1,29 @@ +--- +title: FL0089 +category: how-to +hide_menu: true +--- + +# FavourSingleton (FL0089) + +*Introduced in `0.26.10`* + +## Cause + +Rule to detect usage of lists or arrays with only one item. + +## Rationale + +List.singleton/Array.singleton is more readable and explicit than [ foo]/[| foo |]. + +## How To Fix + +Replace all occurrences of single member lists/arrays with a call to List.singleton/Array.singleton. + +## Rule Settings + + { + "favourSingleton": { + "enabled": false + } + } diff --git a/src/FSharpLint.Core/Application/Configuration.fs b/src/FSharpLint.Core/Application/Configuration.fs index 1d4b62a49..44cc28a77 100644 --- a/src/FSharpLint.Core/Application/Configuration.fs +++ b/src/FSharpLint.Core/Application/Configuration.fs @@ -513,7 +513,8 @@ type Configuration = SuggestUseAutoProperty:EnabledConfig option EnsureTailCallDiagnosticsInRecursiveFunctions:EnabledConfig option FavourAsKeyword:EnabledConfig option - InterpolatedStringWithNoSubstitution:EnabledConfig option } + InterpolatedStringWithNoSubstitution:EnabledConfig option + FavourSingleton:EnabledConfig option } with static member Zero = { Global = None @@ -610,6 +611,7 @@ with EnsureTailCallDiagnosticsInRecursiveFunctions = None FavourAsKeyword = None InterpolatedStringWithNoSubstitution = None + FavourSingleton = None } // fsharplint:enable RecordFieldNames @@ -807,6 +809,7 @@ let flattenConfig (config:Configuration) = config.EnsureTailCallDiagnosticsInRecursiveFunctions |> Option.bind (constructRuleIfEnabled EnsureTailCallDiagnosticsInRecursiveFunctions.rule) config.FavourAsKeyword |> Option.bind (constructRuleIfEnabled FavourAsKeyword.rule) config.InterpolatedStringWithNoSubstitution |> Option.bind (constructRuleIfEnabled InterpolatedStringWithNoSubstitution.rule) + config.FavourSingleton |> Option.bind (constructRuleIfEnabled FavourSingleton.rule) |] findDeprecation config deprecatedAllRules allRules diff --git a/src/FSharpLint.Core/FSharpLint.Core.fsproj b/src/FSharpLint.Core/FSharpLint.Core.fsproj index 3188a59e7..86bb8d7e8 100644 --- a/src/FSharpLint.Core/FSharpLint.Core.fsproj +++ b/src/FSharpLint.Core/FSharpLint.Core.fsproj @@ -69,6 +69,7 @@ + diff --git a/src/FSharpLint.Core/Rules/Conventions/FavourSingleton.fs b/src/FSharpLint.Core/Rules/Conventions/FavourSingleton.fs new file mode 100644 index 000000000..6b8bbfc1d --- /dev/null +++ b/src/FSharpLint.Core/Rules/Conventions/FavourSingleton.fs @@ -0,0 +1,38 @@ +module FSharpLint.Rules.FavourSingleton + +open FSharpLint.Framework +open FSharpLint.Framework.Suggestion +open FSharp.Compiler.Syntax +open FSharp.Compiler.Text +open FSharpLint.Framework.Ast +open FSharpLint.Framework.Rules +open System + +let runner args = + match args.AstNode with + | AstNode.Binding(SynBinding(_, _, _, _, _, _, _, _, _, expression, _, _)) -> + match expression with + | SynExpr.ArrayOrListOfSeqExpr(_, SynExpr.CompExpr(_, _, expr,range), _) -> + match expr with + | SynExpr.Const(_, range) -> + { Range = range + Message = String.Format(Resources.GetString "RulesFavourSingleton") + SuggestedFix = None + TypeChecks = List.Empty } + |> Array.singleton + | SynExpr.Ident _ -> + { Range = range + Message = String.Format(Resources.GetString "RulesFavourSingleton") + SuggestedFix = None + TypeChecks = List.Empty } + |> Array.singleton + | _ -> Array.empty + | _ -> Array.empty + | _ -> Array.empty +let rule = + { Name = "FavourSingleton" + Identifier = Identifiers.FavourSingleton + RuleConfig = + { AstNodeRuleConfig.Runner = runner + Cleanup = ignore } } + |> AstNodeRule diff --git a/src/FSharpLint.Core/Rules/Identifiers.fs b/src/FSharpLint.Core/Rules/Identifiers.fs index 7b2456824..910e968d6 100644 --- a/src/FSharpLint.Core/Rules/Identifiers.fs +++ b/src/FSharpLint.Core/Rules/Identifiers.fs @@ -93,3 +93,4 @@ let EnsureTailCallDiagnosticsInRecursiveFunctions = identifier 85 let FavourAsKeyword = identifier 86 let InterpolatedStringWithNoSubstitution = identifier 87 let IndexerAccessorStyleConsistency = identifier 88 +let FavourSingleton = identifier 89 diff --git a/src/FSharpLint.Core/Text.resx b/src/FSharpLint.Core/Text.resx index 97f8f804a..92b713b3b 100644 --- a/src/FSharpLint.Core/Text.resx +++ b/src/FSharpLint.Core/Text.resx @@ -387,4 +387,7 @@ Consider switching the indexer accessor to {0} style. + + Consider using List.singleton/Array.singleton instead of single member list/array. + diff --git a/src/FSharpLint.Core/fsharplint.json b/src/FSharpLint.Core/fsharplint.json index 4e37a666a..76372f1c6 100644 --- a/src/FSharpLint.Core/fsharplint.json +++ b/src/FSharpLint.Core/fsharplint.json @@ -282,6 +282,7 @@ } }, "favourIgnoreOverLetWild": { "enabled": true }, + "favourSingleton": { "enabled": false }, "wildcardNamedWithAsPattern": { "enabled": true }, "uselessBinding": { "enabled": true }, "tupleOfWildcards": { "enabled": true }, diff --git a/tests/FSharpLint.Core.Tests/FSharpLint.Core.Tests.fsproj b/tests/FSharpLint.Core.Tests/FSharpLint.Core.Tests.fsproj index 319680687..b376875d1 100644 --- a/tests/FSharpLint.Core.Tests/FSharpLint.Core.Tests.fsproj +++ b/tests/FSharpLint.Core.Tests/FSharpLint.Core.Tests.fsproj @@ -48,6 +48,7 @@ + diff --git a/tests/FSharpLint.Core.Tests/Rules/Conventions/FavourSingleton.fs b/tests/FSharpLint.Core.Tests/Rules/Conventions/FavourSingleton.fs new file mode 100644 index 000000000..3bc71344a --- /dev/null +++ b/tests/FSharpLint.Core.Tests/Rules/Conventions/FavourSingleton.fs @@ -0,0 +1,93 @@ +module FSharpLint.Core.Tests.Rules.Conventions.FavourSingleton + +open NUnit.Framework +open FSharpLint.Rules +open System + +[] +type TestConventionsFavourSingleton() = + inherit TestAstNodeRuleBase.TestAstNodeRuleBase(FavourSingleton.rule) + + [] + member this.ListWithManyItemsShouldNotProduceError() = + this.Parse """ +let foo = [ 10; 20 ]""" + + this.AssertNoWarnings() + + [] + member this.ListWithASingleConstantShouldProduceError() = + this.Parse """ +let foo = [ 10 ]""" + + Assert.IsTrue this.ErrorsExist + Assert.IsTrue(this.ErrorExistsAt(2, 12)) + + [] + member this.ListWithASingleIdentShouldProduceError() = + this.Parse """ +let bar = true +let foo = [ bar ]""" + + Assert.IsTrue this.ErrorsExist + Assert.IsTrue(this.ErrorExistsAt(3, 12)) + + [] + member this.ListWithMultipleIdentsShouldNotProduceError() = + this.Parse """ +let bar = true +let foo = [ bar; false; true ]""" + + this.AssertNoWarnings() + + [] + member this.ListWithManyItemsShouldNotProduceError_Arrays() = + this.Parse """ +let foo = [| 10; 20 |]""" + + Assert.IsTrue this.NoErrorsExist + + [] + member this.ListWithASingleConstantShouldProduceError_Arrays() = + this.Parse """ +let foo = [| 10 |]""" + + Assert.IsTrue this.ErrorsExist + Assert.IsTrue(this.ErrorExistsAt(2, 13)) + + [] + member this.ListWithASingleIdentShouldProduceError_Arrays() = + this.Parse """ +let bar = true +let foo = [| bar |]""" + + Assert.IsTrue this.ErrorsExist + Assert.IsTrue(this.ErrorExistsAt(3, 13)) + + [] + member this.ListWithMultipleIdentsShouldNotProduceError_Arrays() = + this.Parse """ +let bar = true +let foo = [| bar; false; true |]""" + + this.AssertNoWarnings() + + [] + member this.SingletonListWithMatchCaseShouldNotProduceError() = + this.Parse """ +let foo = List.empty +match foo with +| [x] -> printf x +| _ -> printf "baz" """ + + this.AssertNoWarnings() + + [] + member this.SingletonArrayWithMatchCaseShouldNotProduceError() = + this.Parse """ +let foo = Array.empty +match foo with +| [| x |] -> printf x +| _ -> printf "baz" """ + + this.AssertNoWarnings() From 9bca4cc866d5dd8aba8cb9b933f8c762fa6aa9dc Mon Sep 17 00:00:00 2001 From: "Andres G. Aragoneses" Date: Wed, 17 Dec 2025 21:38:35 +0100 Subject: [PATCH 2/8] FavourSingleton: fix build & tests after rebase And respect tiny DRY issue. --- .../Rules/Conventions/FavourSingleton.fs | 27 +++++++++---------- .../Rules/Conventions/FavourSingleton.fs | 7 ++--- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/FSharpLint.Core/Rules/Conventions/FavourSingleton.fs b/src/FSharpLint.Core/Rules/Conventions/FavourSingleton.fs index 6b8bbfc1d..fc003ef57 100644 --- a/src/FSharpLint.Core/Rules/Conventions/FavourSingleton.fs +++ b/src/FSharpLint.Core/Rules/Conventions/FavourSingleton.fs @@ -9,24 +9,23 @@ open FSharpLint.Framework.Rules open System let runner args = + let generateViolation range = + let msg = Resources.GetString "RulesFavourSingleton" + { Range = range + Message = msg + SuggestedFix = None + TypeChecks = List.Empty } + |> Array.singleton match args.AstNode with - | AstNode.Binding(SynBinding(_, _, _, _, _, _, _, _, _, expression, _, _)) -> + | AstNode.Binding(SynBinding(_, _, _, _, _, _, _, _, _, expression, _, _, _)) -> match expression with - | SynExpr.ArrayOrListOfSeqExpr(_, SynExpr.CompExpr(_, _, expr,range), _) -> - match expr with + | SynExpr.ArrayOrListComputed(_isArray, innerExpr, range) -> + match innerExpr with | SynExpr.Const(_, range) -> - { Range = range - Message = String.Format(Resources.GetString "RulesFavourSingleton") - SuggestedFix = None - TypeChecks = List.Empty } - |> Array.singleton + generateViolation range | SynExpr.Ident _ -> - { Range = range - Message = String.Format(Resources.GetString "RulesFavourSingleton") - SuggestedFix = None - TypeChecks = List.Empty } - |> Array.singleton - | _ -> Array.empty + generateViolation range + | _ -> Array.empty | _ -> Array.empty | _ -> Array.empty let rule = diff --git a/tests/FSharpLint.Core.Tests/Rules/Conventions/FavourSingleton.fs b/tests/FSharpLint.Core.Tests/Rules/Conventions/FavourSingleton.fs index 3bc71344a..25e36c00b 100644 --- a/tests/FSharpLint.Core.Tests/Rules/Conventions/FavourSingleton.fs +++ b/tests/FSharpLint.Core.Tests/Rules/Conventions/FavourSingleton.fs @@ -1,8 +1,9 @@ module FSharpLint.Core.Tests.Rules.Conventions.FavourSingleton +open System open NUnit.Framework open FSharpLint.Rules -open System +open FSharpLint.Core.Tests [] type TestConventionsFavourSingleton() = @@ -30,7 +31,7 @@ let bar = true let foo = [ bar ]""" Assert.IsTrue this.ErrorsExist - Assert.IsTrue(this.ErrorExistsAt(3, 12)) + Assert.IsTrue(this.ErrorExistsAt(3, 10)) [] member this.ListWithMultipleIdentsShouldNotProduceError() = @@ -62,7 +63,7 @@ let bar = true let foo = [| bar |]""" Assert.IsTrue this.ErrorsExist - Assert.IsTrue(this.ErrorExistsAt(3, 13)) + Assert.IsTrue(this.ErrorExistsAt(3, 10)) [] member this.ListWithMultipleIdentsShouldNotProduceError_Arrays() = From 5c95835fbd40074353d8118d71b400eea59638e3 Mon Sep 17 00:00:00 2001 From: "Andres G. Aragoneses" Date: Wed, 17 Dec 2025 23:05:48 +0100 Subject: [PATCH 3/8] Fix SelfCheck CI step --- src/FSharpLint.Core/Framework/ParseFile.fs | 2 +- .../Rules/Conventions/FavourSingleton.fs | 22 +++++++++---------- .../Rules/Conventions/FavourSingleton.fs | 8 +++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/FSharpLint.Core/Framework/ParseFile.fs b/src/FSharpLint.Core/Framework/ParseFile.fs index 6a5edcb56..b8e844401 100644 --- a/src/FSharpLint.Core/Framework/ParseFile.fs +++ b/src/FSharpLint.Core/Framework/ParseFile.fs @@ -57,7 +57,7 @@ module ParseFile = let getProjectOptionsFromScript (checker:FSharpChecker) file (source:string) = async { let sourceText = SourceText.ofString source let assumeDotNetFramework = false - let otherOpts = [| "--targetprofile:netstandard" |] + let otherOpts = Array.singleton "--targetprofile:netstandard" let! options, _diagnostics = checker.GetProjectOptionsFromScript(file, sourceText, assumeDotNetFramework = assumeDotNetFramework, useSdkRefs = not assumeDotNetFramework, otherFlags = otherOpts) diff --git a/src/FSharpLint.Core/Rules/Conventions/FavourSingleton.fs b/src/FSharpLint.Core/Rules/Conventions/FavourSingleton.fs index fc003ef57..1279c2efb 100644 --- a/src/FSharpLint.Core/Rules/Conventions/FavourSingleton.fs +++ b/src/FSharpLint.Core/Rules/Conventions/FavourSingleton.fs @@ -11,11 +11,11 @@ open System let runner args = let generateViolation range = let msg = Resources.GetString "RulesFavourSingleton" - { Range = range - Message = msg - SuggestedFix = None - TypeChecks = List.Empty } - |> Array.singleton + Array.singleton + { Range = range + Message = msg + SuggestedFix = None + TypeChecks = List.Empty } match args.AstNode with | AstNode.Binding(SynBinding(_, _, _, _, _, _, _, _, _, expression, _, _, _)) -> match expression with @@ -29,9 +29,9 @@ let runner args = | _ -> Array.empty | _ -> Array.empty let rule = - { Name = "FavourSingleton" - Identifier = Identifiers.FavourSingleton - RuleConfig = - { AstNodeRuleConfig.Runner = runner - Cleanup = ignore } } - |> AstNodeRule + AstNodeRule + { Name = "FavourSingleton" + Identifier = Identifiers.FavourSingleton + RuleConfig = + { AstNodeRuleConfig.Runner = runner + Cleanup = ignore } } diff --git a/tests/FSharpLint.Core.Tests/Rules/Conventions/FavourSingleton.fs b/tests/FSharpLint.Core.Tests/Rules/Conventions/FavourSingleton.fs index 25e36c00b..3b4a9be85 100644 --- a/tests/FSharpLint.Core.Tests/Rules/Conventions/FavourSingleton.fs +++ b/tests/FSharpLint.Core.Tests/Rules/Conventions/FavourSingleton.fs @@ -42,14 +42,14 @@ let foo = [ bar; false; true ]""" this.AssertNoWarnings() [] - member this.ListWithManyItemsShouldNotProduceError_Arrays() = + member this.ArrayWithManyItemsShouldNotProduceError() = this.Parse """ let foo = [| 10; 20 |]""" Assert.IsTrue this.NoErrorsExist [] - member this.ListWithASingleConstantShouldProduceError_Arrays() = + member this.ArrayWithASingleConstantShouldProduceError() = this.Parse """ let foo = [| 10 |]""" @@ -57,7 +57,7 @@ let foo = [| 10 |]""" Assert.IsTrue(this.ErrorExistsAt(2, 13)) [] - member this.ListWithASingleIdentShouldProduceError_Arrays() = + member this.ArrayWithASingleIdentShouldProduceError() = this.Parse """ let bar = true let foo = [| bar |]""" @@ -66,7 +66,7 @@ let foo = [| bar |]""" Assert.IsTrue(this.ErrorExistsAt(3, 10)) [] - member this.ListWithMultipleIdentsShouldNotProduceError_Arrays() = + member this.ArrayWithMultipleIdentsShouldNotProduceError() = this.Parse """ let bar = true let foo = [| bar; false; true |]""" From 07aa04a2178f9a77f698aa6b73ade7f21e5e6662 Mon Sep 17 00:00:00 2001 From: "Andres G. Aragoneses" Date: Wed, 17 Dec 2025 23:36:40 +0100 Subject: [PATCH 4/8] FavourSingleton: improve docs --- docs/content/how-tos/rules/FL0089.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/content/how-tos/rules/FL0089.md b/docs/content/how-tos/rules/FL0089.md index 10b44b9f0..054319575 100644 --- a/docs/content/how-tos/rules/FL0089.md +++ b/docs/content/how-tos/rules/FL0089.md @@ -14,7 +14,10 @@ Rule to detect usage of lists or arrays with only one item. ## Rationale -List.singleton/Array.singleton is more readable and explicit than [ foo]/[| foo |]. +`List.singleton foo`/`Array.singleton foo` is more readable and explicit than `[foo]`/`[|foo|]`. +Especially if we take in account that F# newbies may not yet be familiar with the difference between +inline array vs list instantiation; and square brackets with only one element could potentially be +confused with an indexing accessor. ## How To Fix From 89d731dfe1d03ae8e629f13ea0f6f0ac06f8ee5d Mon Sep 17 00:00:00 2001 From: "Andres G. Aragoneses" Date: Wed, 24 Dec 2025 01:38:59 +0100 Subject: [PATCH 5/8] tests/Hints: add test for recently added hint Added in d703c557659638dc9d3207fef07c94e246829423 . --- .../Rules/Hints/HintMatcher.fs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/FSharpLint.Core.Tests/Rules/Hints/HintMatcher.fs b/tests/FSharpLint.Core.Tests/Rules/Hints/HintMatcher.fs index 311bd5c4b..1ebe7dac6 100644 --- a/tests/FSharpLint.Core.Tests/Rules/Hints/HintMatcher.fs +++ b/tests/FSharpLint.Core.Tests/Rules/Hints/HintMatcher.fs @@ -953,4 +953,15 @@ let x y = this.SetConfig(["List.map f (List.map g x) ===> List.map (g >> f) x"]) this.Parse(source) - Assert.AreEqual(expected, this.ApplyQuickFix source) \ No newline at end of file + Assert.AreEqual(expected, this.ApplyQuickFix source) + + [] + member this.``List append of singleton item can be replaced with :: operator``() = + let source = """[head] @ tail""" + + let expected = """head :: tail""" + + this.SetConfig(["[x] @ y ===> x::y"]) + this.Parse(source) + Assert.AreEqual(expected, this.ApplyQuickFix source) + From 6fa06f123e90c5c5fb9d8c382447ca374655ff97 Mon Sep 17 00:00:00 2001 From: "Andres G. Aragoneses" Date: Wed, 24 Dec 2025 02:00:13 +0100 Subject: [PATCH 6/8] tests/Hints: demonstrate issue with recently added hint Added in d703c557659638dc9d3207fef07c94e246829423 . --- tests/FSharpLint.Core.Tests/Rules/Hints/HintMatcher.fs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/FSharpLint.Core.Tests/Rules/Hints/HintMatcher.fs b/tests/FSharpLint.Core.Tests/Rules/Hints/HintMatcher.fs index 1ebe7dac6..9514b5e4e 100644 --- a/tests/FSharpLint.Core.Tests/Rules/Hints/HintMatcher.fs +++ b/tests/FSharpLint.Core.Tests/Rules/Hints/HintMatcher.fs @@ -965,3 +965,13 @@ let x y = this.Parse(source) Assert.AreEqual(expected, this.ApplyQuickFix source) + [] + member this.``Demonstrate issue with recently added hint``() = + let source = """[a;b] @ tail""" + + this.SetConfig(["[x] @ y ===> x::y"]) + this.Parse(source) + + // invalid! + Assert.AreNotEqual("a;b :: tail", this.ApplyQuickFix source) + From 76ac4f00222bb7955dcc2479cbf89624c5eeba1a Mon Sep 17 00:00:00 2001 From: "Andres G. Aragoneses" Date: Wed, 24 Dec 2025 02:14:44 +0100 Subject: [PATCH 7/8] Hints: morph hint to avoid issue Workaround recently added hint in a related way to FL0089, in order to avoid issue found in: 6fa06f123e90c5c5fb9d8c382447ca374655ff97 --- src/FSharpLint.Core/fsharplint.json | 2 +- .../Rules/Hints/HintMatcher.fs | 15 ++------------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/FSharpLint.Core/fsharplint.json b/src/FSharpLint.Core/fsharplint.json index 76372f1c6..320fbd6e1 100644 --- a/src/FSharpLint.Core/fsharplint.json +++ b/src/FSharpLint.Core/fsharplint.json @@ -451,7 +451,7 @@ "pattern: x::[] ===> [x]", "x @ [] ===> x", - "[x] @ y ===> x::y", + "(List.singleton x) @ y ===> x :: y", "List.isEmpty [] ===> true", "Array.isEmpty [||] ===> true", diff --git a/tests/FSharpLint.Core.Tests/Rules/Hints/HintMatcher.fs b/tests/FSharpLint.Core.Tests/Rules/Hints/HintMatcher.fs index 9514b5e4e..05a5c9fe9 100644 --- a/tests/FSharpLint.Core.Tests/Rules/Hints/HintMatcher.fs +++ b/tests/FSharpLint.Core.Tests/Rules/Hints/HintMatcher.fs @@ -957,21 +957,10 @@ let x y = [] member this.``List append of singleton item can be replaced with :: operator``() = - let source = """[head] @ tail""" + let source = """(List.singleton head) @ tail""" let expected = """head :: tail""" - this.SetConfig(["[x] @ y ===> x::y"]) + this.SetConfig(["(List.singleton x) @ y ===> x :: y"]) this.Parse(source) Assert.AreEqual(expected, this.ApplyQuickFix source) - - [] - member this.``Demonstrate issue with recently added hint``() = - let source = """[a;b] @ tail""" - - this.SetConfig(["[x] @ y ===> x::y"]) - this.Parse(source) - - // invalid! - Assert.AreNotEqual("a;b :: tail", this.ApplyQuickFix source) - From 5ba242628994bfe21814503dbfc4ea4ea8fa1cfd Mon Sep 17 00:00:00 2001 From: "Andres G. Aragoneses" Date: Wed, 24 Dec 2025 02:22:13 +0100 Subject: [PATCH 8/8] fsharplint.json: move FL0089 config to end of file Because it's the last rule to be added, it's better to locate it at the end of the file, for easier update. --- src/FSharpLint.Core/fsharplint.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharpLint.Core/fsharplint.json b/src/FSharpLint.Core/fsharplint.json index 320fbd6e1..6077efb11 100644 --- a/src/FSharpLint.Core/fsharplint.json +++ b/src/FSharpLint.Core/fsharplint.json @@ -282,7 +282,6 @@ } }, "favourIgnoreOverLetWild": { "enabled": true }, - "favourSingleton": { "enabled": false }, "wildcardNamedWithAsPattern": { "enabled": true }, "uselessBinding": { "enabled": true }, "tupleOfWildcards": { "enabled": true }, @@ -341,6 +340,7 @@ "style": "OCaml" } }, + "favourSingleton": { "enabled": false }, "hints": { "add": [ "not (a = b) ===> a <> b",