From fdb83e44520295d5b8e5c699cedad90b450f5b41 Mon Sep 17 00:00:00 2001 From: Richard Webb Date: Wed, 11 Feb 2026 21:16:56 +0000 Subject: [PATCH] Pass an FSharpProjectOptions down into the lint machinery when available Rather than reading it from FSharpCheckProjectResults on every use, which appears to have a substantional performance cost. The project options is stored as a Lazy because it's expensive to calculate and only used by a few rules, so the work can be avoided altogether if those rules aren't being run. refs #770 --- src/FSharpLint.Core/Application/Lint.fs | 6 ++++++ src/FSharpLint.Core/Application/Lint.fsi | 1 + src/FSharpLint.Core/Framework/Rules.fs | 1 + .../Rules/Conventions/Naming/AsynchronousFunctionNames.fs | 4 ++-- .../Conventions/Naming/SimpleAsyncComplementaryHelpers.fs | 4 ++-- .../Rules/Smells/NoAsyncRunSynchronouslyInLibrary.fs | 6 +++--- tests/FSharpLint.Core.Tests/Rules/TestAstNodeRule.fs | 1 + tests/FSharpLint.Core.Tests/Rules/TestHintMatcherBase.fs | 1 + tests/FSharpLint.Core.Tests/Rules/TestIndentationRule.fs | 1 + tests/FSharpLint.Core.Tests/Rules/TestLineRule.fs | 1 + .../FSharpLint.Core.Tests/Rules/TestNoTabCharactersRule.fs | 1 + 11 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/FSharpLint.Core/Application/Lint.fs b/src/FSharpLint.Core/Application/Lint.fs index 4e0c7db22..ac91adea9 100644 --- a/src/FSharpLint.Core/Application/Lint.fs +++ b/src/FSharpLint.Core/Application/Lint.fs @@ -125,6 +125,7 @@ module Lint = GlobalConfig: Rules.GlobalRuleConfig TypeCheckResults: FSharpCheckFileResults option ProjectCheckResults: FSharpCheckProjectResults option + ProjectOptions: Lazy FilePath: string FileContent: string Lines: string[] @@ -149,6 +150,7 @@ module Lint = Lines = config.Lines CheckInfo = config.TypeCheckResults ProjectCheckInfo = config.ProjectCheckResults + ProjectOptions = config.ProjectOptions GlobalConfig = config.GlobalConfig } // Build state for rules with context. @@ -263,6 +265,10 @@ module Lint = GlobalConfig = enabledRules.GlobalConfig TypeCheckResults = fileInfo.TypeCheckResults ProjectCheckResults = fileInfo.ProjectCheckResults + ProjectOptions = lazy( + fileInfo.ProjectCheckResults + |> Option.map _.ProjectContext.ProjectOptions + ) FilePath = fileInfo.File FileContent = fileInfo.Text Lines = lines diff --git a/src/FSharpLint.Core/Application/Lint.fsi b/src/FSharpLint.Core/Application/Lint.fsi index 7653223d6..7128eadb7 100644 --- a/src/FSharpLint.Core/Application/Lint.fsi +++ b/src/FSharpLint.Core/Application/Lint.fsi @@ -129,6 +129,7 @@ module Lint = GlobalConfig: Rules.GlobalRuleConfig TypeCheckResults: FSharpCheckFileResults option ProjectCheckResults: FSharpCheckProjectResults option + ProjectOptions: Lazy FilePath: string FileContent: string Lines: string[] diff --git a/src/FSharpLint.Core/Framework/Rules.fs b/src/FSharpLint.Core/Framework/Rules.fs index d53629800..413b6c932 100644 --- a/src/FSharpLint.Core/Framework/Rules.fs +++ b/src/FSharpLint.Core/Framework/Rules.fs @@ -31,6 +31,7 @@ type AstNodeRuleParams = Lines:string [] CheckInfo:FSharpCheckFileResults option ProjectCheckInfo:FSharpCheckProjectResults option + ProjectOptions: Lazy GlobalConfig:GlobalRuleConfig } type LineRuleParams = diff --git a/src/FSharpLint.Core/Rules/Conventions/Naming/AsynchronousFunctionNames.fs b/src/FSharpLint.Core/Rules/Conventions/Naming/AsynchronousFunctionNames.fs index e329ece0c..e95c008ea 100644 --- a/src/FSharpLint.Core/Rules/Conventions/Naming/AsynchronousFunctionNames.fs +++ b/src/FSharpLint.Core/Rules/Conventions/Naming/AsynchronousFunctionNames.fs @@ -32,8 +32,8 @@ let runner (config: Config) (args: AstNodeRuleParams) = | _ -> config.Mode = AllAPIs let likelyhoodOfBeingInLibrary = - match args.ProjectCheckInfo with - | Some projectInfo -> howLikelyProjectIsLibrary projectInfo.ProjectContext.ProjectOptions.ProjectFileName + match args.ProjectOptions.Value with + | Some projectOptions -> howLikelyProjectIsLibrary projectOptions.ProjectFileName | None -> Unlikely if config.Mode = OnlyPublicAPIsInLibraries && likelyhoodOfBeingInLibrary <> Likely then diff --git a/src/FSharpLint.Core/Rules/Conventions/Naming/SimpleAsyncComplementaryHelpers.fs b/src/FSharpLint.Core/Rules/Conventions/Naming/SimpleAsyncComplementaryHelpers.fs index ee590aaa7..27aa57f5a 100644 --- a/src/FSharpLint.Core/Rules/Conventions/Naming/SimpleAsyncComplementaryHelpers.fs +++ b/src/FSharpLint.Core/Rules/Conventions/Naming/SimpleAsyncComplementaryHelpers.fs @@ -205,8 +205,8 @@ let runner (config: Config) (args: AstNodeRuleParams) = Array.append (checkFuncs asyncFuncs taskFuncs) (checkFuncs taskFuncs asyncFuncs) let likelyhoodOfBeingInLibrary = - match args.ProjectCheckInfo with - | Some projectInfo -> howLikelyProjectIsLibrary projectInfo.ProjectContext.ProjectOptions.ProjectFileName + match args.ProjectOptions.Value with + | Some projectOptions -> howLikelyProjectIsLibrary projectOptions.ProjectFileName | None -> Unlikely if config.Mode = OnlyPublicAPIsInLibraries && likelyhoodOfBeingInLibrary <> Likely then diff --git a/src/FSharpLint.Core/Rules/Smells/NoAsyncRunSynchronouslyInLibrary.fs b/src/FSharpLint.Core/Rules/Smells/NoAsyncRunSynchronouslyInLibrary.fs index f74304f96..2eab65600 100644 --- a/src/FSharpLint.Core/Rules/Smells/NoAsyncRunSynchronouslyInLibrary.fs +++ b/src/FSharpLint.Core/Rules/Smells/NoAsyncRunSynchronouslyInLibrary.fs @@ -95,9 +95,9 @@ let checkIfInLibrary (args: AstNodeRuleParams) (range: range) : array - let projectFile = System.IO.FileInfo checkProjectResults.ProjectContext.ProjectOptions.ProjectFileName + match (args.CheckInfo, args.ProjectOptions.Value) with + | Some checkFileResults, Some projectOptions -> + let projectFile = System.IO.FileInfo projectOptions.ProjectFileName match howLikelyProjectIsLibrary projectFile.Name with | Likely -> false | Unlikely -> true diff --git a/tests/FSharpLint.Core.Tests/Rules/TestAstNodeRule.fs b/tests/FSharpLint.Core.Tests/Rules/TestAstNodeRule.fs index aa4162fba..ee77ae33b 100644 --- a/tests/FSharpLint.Core.Tests/Rules/TestAstNodeRule.fs +++ b/tests/FSharpLint.Core.Tests/Rules/TestAstNodeRule.fs @@ -43,6 +43,7 @@ type TestAstNodeRuleBase (rule:Rule) = GlobalConfig = resolvedGlobalConfig TypeCheckResults = checkResult ProjectCheckResults = None + ProjectOptions = Lazy<_>(None) FilePath = (Option.defaultValue String.Empty maybeFileName) FileContent = input Lines = (input.Split("\n")) diff --git a/tests/FSharpLint.Core.Tests/Rules/TestHintMatcherBase.fs b/tests/FSharpLint.Core.Tests/Rules/TestHintMatcherBase.fs index 842ec077d..fd52c56f4 100644 --- a/tests/FSharpLint.Core.Tests/Rules/TestHintMatcherBase.fs +++ b/tests/FSharpLint.Core.Tests/Rules/TestHintMatcherBase.fs @@ -65,6 +65,7 @@ type TestHintMatcherBase () = GlobalConfig = resolvedGlobalConfig TypeCheckResults = checkResult ProjectCheckResults = None + ProjectOptions = Lazy<_>() FilePath = (Option.defaultValue String.Empty maybeFileName) FileContent = input Lines = (input.Split("\n")) diff --git a/tests/FSharpLint.Core.Tests/Rules/TestIndentationRule.fs b/tests/FSharpLint.Core.Tests/Rules/TestIndentationRule.fs index f52b54b6b..20c94fac0 100644 --- a/tests/FSharpLint.Core.Tests/Rules/TestIndentationRule.fs +++ b/tests/FSharpLint.Core.Tests/Rules/TestIndentationRule.fs @@ -38,6 +38,7 @@ type TestIndentationRuleBase (rule:Rule) = GlobalConfig = resolvedGlobalConfig TypeCheckResults = None ProjectCheckResults = None + ProjectOptions = Lazy<_>(None) FilePath = resolvedFileName FileContent = input Lines = lines diff --git a/tests/FSharpLint.Core.Tests/Rules/TestLineRule.fs b/tests/FSharpLint.Core.Tests/Rules/TestLineRule.fs index 078d025fa..c53e93377 100644 --- a/tests/FSharpLint.Core.Tests/Rules/TestLineRule.fs +++ b/tests/FSharpLint.Core.Tests/Rules/TestLineRule.fs @@ -38,6 +38,7 @@ type TestLineRuleBase (rule:Rule) = GlobalConfig = resolvedGlobalConfig TypeCheckResults = None ProjectCheckResults = None + ProjectOptions = Lazy<_>(None) FilePath = resolvedFileName FileContent = input Lines = lines diff --git a/tests/FSharpLint.Core.Tests/Rules/TestNoTabCharactersRule.fs b/tests/FSharpLint.Core.Tests/Rules/TestNoTabCharactersRule.fs index 40d9c1652..5e534bb22 100644 --- a/tests/FSharpLint.Core.Tests/Rules/TestNoTabCharactersRule.fs +++ b/tests/FSharpLint.Core.Tests/Rules/TestNoTabCharactersRule.fs @@ -38,6 +38,7 @@ type TestNoTabCharactersRuleBase (rule:Rule) = GlobalConfig = resolvedGlobalConfig TypeCheckResults = None ProjectCheckResults = None + ProjectOptions = Lazy<_>() FilePath = resolvedFileName FileContent = input Lines = lines