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
20 changes: 12 additions & 8 deletions src/FSharpLint.Core/Application/Lint.fs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ module Lint =
GlobalConfig: Rules.GlobalRuleConfig
TypeCheckResults: FSharpCheckFileResults option
ProjectCheckResults: FSharpCheckProjectResults option
ProjectOptions: Lazy<FSharpProjectOptions option>
ProjectOptions: ParseFile.LinterProjectOptions option
FilePath: string
FileContent: string
Lines: string[]
Expand Down Expand Up @@ -265,10 +265,7 @@ module Lint =
GlobalConfig = enabledRules.GlobalConfig
TypeCheckResults = fileInfo.TypeCheckResults
ProjectCheckResults = fileInfo.ProjectCheckResults
ProjectOptions = lazy(
fileInfo.ProjectCheckResults
|> Option.map _.ProjectContext.ProjectOptions
)
ProjectOptions = fileInfo.ProjectOptions
FilePath = fileInfo.File
FileContent = fileInfo.Text
Lines = lines
Expand Down Expand Up @@ -399,6 +396,8 @@ module Lint =
TypeCheckResults:FSharpCheckFileResults option
/// Optional results of project-wide type info (allows for a more accurate lint).
ProjectCheckResults:FSharpCheckProjectResults option
/// Optional project options. Allows rules to operate on project options.
ProjectOptions: ParseFile.LinterProjectOptions option
}

/// Gets a FSharpLint Configuration based on the provided ConfigurationParam.
Expand Down Expand Up @@ -581,6 +580,7 @@ module Lint =
ParseFile.Ast = parsedFileInfo.Ast
ParseFile.TypeCheckResults = parsedFileInfo.TypeCheckResults
ParseFile.ProjectCheckResults = parsedFileInfo.ProjectCheckResults
ParseFile.ProjectOptions = parsedFileInfo.ProjectOptions
ParseFile.File = "<inline source>" }

lint lintInformation parsedFileInfo
Expand All @@ -600,7 +600,8 @@ module Lint =
{ Source = parseFileInformation.Text
Ast = parseFileInformation.Ast
TypeCheckResults = parseFileInformation.TypeCheckResults
ProjectCheckResults = None }
ProjectCheckResults = None
ProjectOptions = None }

return lintParsedSource optionalParams parsedFileInfo
| ParseFile.Failed failure -> return LintResult.Failure(FailedToParseFile failure)
Expand Down Expand Up @@ -635,6 +636,7 @@ module Lint =
ParseFile.Ast = parsedFileInfo.Ast
ParseFile.TypeCheckResults = parsedFileInfo.TypeCheckResults
ParseFile.ProjectCheckResults = parsedFileInfo.ProjectCheckResults
ParseFile.ProjectOptions = parsedFileInfo.ProjectOptions
ParseFile.File = filePath }

lint lintInformation parsedFileInfo
Expand All @@ -653,7 +655,8 @@ module Lint =
{ Source = astFileParseInfo.Text
Ast = astFileParseInfo.Ast
TypeCheckResults = astFileParseInfo.TypeCheckResults
ProjectCheckResults = astFileParseInfo.ProjectCheckResults }
ProjectCheckResults = astFileParseInfo.ProjectCheckResults
ProjectOptions = astFileParseInfo.ProjectOptions }

return lintParsedFile optionalParams parsedFileInfo filePath
| ParseFile.Failed failure -> return LintResult.Failure(FailedToParseFile failure)
Expand Down Expand Up @@ -684,7 +687,8 @@ module Lint =
{ Source = astFileParseInfo.Text
Ast = astFileParseInfo.Ast
TypeCheckResults = astFileParseInfo.TypeCheckResults
ProjectCheckResults = astFileParseInfo.ProjectCheckResults }
ProjectCheckResults = astFileParseInfo.ProjectCheckResults
ProjectOptions = astFileParseInfo.ProjectOptions }
return lintParsedFile optionalParams parsedFileInfo filePath
| ParseFile.Failed failure ->
return LintResult.Failure (FailedToParseFile failure)
Expand Down
5 changes: 4 additions & 1 deletion src/FSharpLint.Core/Application/Lint.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ module Lint =

/// Optional results of project-wide type info (allows for a more accurate lint).
ProjectCheckResults:FSharpCheckProjectResults option

/// Optional project options. Allows rules to operate on project options.
ProjectOptions: ParseFile.LinterProjectOptions option
}

type BuildFailure = | InvalidProjectFileMessage of string
Expand Down Expand Up @@ -129,7 +132,7 @@ module Lint =
GlobalConfig: Rules.GlobalRuleConfig
TypeCheckResults: FSharpCheckFileResults option
ProjectCheckResults: FSharpCheckProjectResults option
ProjectOptions: Lazy<FSharpProjectOptions option>
ProjectOptions: ParseFile.LinterProjectOptions option
FilePath: string
FileContent: string
Lines: string[]
Expand Down
2 changes: 1 addition & 1 deletion src/FSharpLint.Core/FSharpLint.Core.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
<Compile Include="Framework\Ast.fs" />
<Compile Include="Framework\AstInfo.fs" />
<Compile Include="Framework\AbstractSyntaxArray.fs" />
<Compile Include="Framework\ParseFile.fs" />
<Compile Include="Framework\Rules.fs" />
<Compile Include="Framework\Resources.fs" />
<Compile Include="Framework\ParseFile.fs" />
<Compile Include="Framework\Suppression.fs" />
<!-- Rules -->
<Compile Include="Rules\Identifiers.fs" />
Expand Down
58 changes: 58 additions & 0 deletions src/FSharpLint.Core/Framework/ParseFile.fs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
namespace FSharpLint.Framework

#nowarn "FS0057" // 'FSharpProjectSnapshot' is considered experimental. Note: Could suppress this more locally if building with the .NET 10 compiler

/// Provides functionality to parse F# files using `FSharp.Compiler.Service`.
module ParseFile =

Expand All @@ -11,6 +13,58 @@ module ParseFile =
open FSharp.Compiler.Text
open Utilities

/// Options related to the project being linted.
/// Based on https://github.com/ionide/FSharp.Analyzers.SDK/blob/f323144f0a4db51be564a3187838f2328f0e9182/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.fsi#L66
[<NoEquality; NoComparison>]
type LinterProjectOptions =

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this should live in this file or elsewhere, just put it here for somewhere high up the file order.
If ifdefed out part is the full set of properties present in the analyzers sdk, but only the file name is used here at present - the other parts could be deleted to minimize the change, or left in case they might be useful later.

| ProjectOptions of options: FSharpProjectOptions
| ProjectSnapshot of snapshot: FSharpProjectSnapshot

member this.ProjectFileName =
match this with
| ProjectOptions(options) -> options.ProjectFileName
| ProjectSnapshot(snapshot) -> snapshot.ProjectFileName

#if false

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Numpsy why is this wrapped in #if false? put the explanation in a comment please

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what the

ifdefed out part is the full set of properties present in the analyzers sdk, but only the file name is used here at present - the other parts could be deleted to minimize the change, or left in case they might be useful later.

comment above was about - It could keep the full set of functions available in FsAutoComplete/AnalyzersSDK, or trim it down to just what's presently being used (the others may or may not be useful later)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I meant that it has to be a code comment, not a PR comment. PR comments are not visible even with git blame.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, the question should be to confirm whether you want to keep the ifdefed out parts with comments, or just remove them altogether

member x.ProjectId =

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Numpsy don't use x please, but the convention

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Names just cloned in directly from https://github.com/ionide/FSharp.Analyzers.SDK/blob/f323144f0a4db51be564a3187838f2328f0e9182/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.fs#L261, will change them if you want to keep the ifdefed parts rather than just deleting them

match x with
| BackgroundCompilerOptions(options) -> options.ProjectId
| TransparentCompilerOptions(snapshot) -> snapshot.ProjectId

member x.SourceFiles =
match x with
| BackgroundCompilerOptions(options) ->
options.SourceFiles
|> Array.toList
| TransparentCompilerOptions(snapshot) ->
snapshot.SourceFiles
|> List.map (fun f -> f.FileName)
|> List.map System.IO.Path.GetFullPath

member x.ReferencedProjectsPath =
match x with
| BackgroundCompilerOptions(options) ->
options.ReferencedProjects
|> Array.choose (fun p -> p.ProjectFilePath)
|> Array.toList
| TransparentCompilerOptions(snapshot) ->
snapshot.ReferencedProjects
|> List.choose (fun p -> p.ProjectFilePath)

member x.LoadTime =
match x with
| BackgroundCompilerOptions(options) -> options.LoadTime
| TransparentCompilerOptions(snapshot) -> snapshot.LoadTime

member x.OtherOptions =
match x with
| BackgroundCompilerOptions(options) ->
options.OtherOptions
|> Array.toList
| TransparentCompilerOptions(snapshot) -> snapshot.OtherOptions

#endif

/// Information for a file to be linted that is given to the analysers.
[<NoEquality; NoComparison>]
type FileParseInfo = {
Expand All @@ -26,6 +80,9 @@ module ParseFile =
/// Optional results of project-wide type info (allows for a more accurate lint).
ProjectCheckResults:FSharpCheckProjectResults option

/// Optional project options. Allows rules to operate on project options.
ProjectOptions: LinterProjectOptions option

/// Path to the file.
File:string
}
Expand Down Expand Up @@ -53,6 +110,7 @@ module ParseFile =
Ast = parseResults.ParseTree
TypeCheckResults = Some(typeCheckResults)
ProjectCheckResults = None
ProjectOptions = Some (ProjectOptions options)
File = file
}
| FSharpCheckFileAnswer.Aborted -> return Failed(AbortedTypeCheck)
Expand Down
2 changes: 1 addition & 1 deletion src/FSharpLint.Core/Framework/Rules.fs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type AstNodeRuleParams =
Lines:string []
CheckInfo:FSharpCheckFileResults option
ProjectCheckInfo:FSharpCheckProjectResults option
ProjectOptions: Lazy<FSharpProjectOptions option>
ProjectOptions: ParseFile.LinterProjectOptions option
GlobalConfig:GlobalRuleConfig }

type LineRuleParams =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ let runner (config: Config) (args: AstNodeRuleParams) =
| _ -> config.Mode = AllAPIs

let likelyhoodOfBeingInLibrary =
match args.ProjectOptions.Value with
match args.ProjectOptions with
| Some projectOptions -> howLikelyProjectIsLibrary projectOptions.ProjectFileName
| None -> Unlikely

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ let runner (config: Config) (args: AstNodeRuleParams) =
Array.append (checkFuncs asyncFuncs taskFuncs) (checkFuncs taskFuncs asyncFuncs)

let likelyhoodOfBeingInLibrary =
match args.ProjectOptions.Value with
match args.ProjectOptions with
| Some projectOptions -> howLikelyProjectIsLibrary projectOptions.ProjectFileName
| None -> Unlikely

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ let checkIfInLibrary (args: AstNodeRuleParams) (range: range) : array<WarningDet
||
isInObsoleteMethodOrFunction (args.GetParents args.NodeIndex)
||
match (args.CheckInfo, args.ProjectOptions.Value) with
match (args.CheckInfo, args.ProjectOptions) with
| Some checkFileResults, Some projectOptions ->
let projectFile = System.IO.FileInfo projectOptions.ProjectFileName
match howLikelyProjectIsLibrary projectFile.Name with
Expand Down
2 changes: 1 addition & 1 deletion tests/FSharpLint.Benchmarks/Benchmark.fs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type Benchmark () =
let (fileInfo, _lines) =
let text = File.ReadAllText sourceFile
let tree = generateAst text sourceFile
({ Ast = tree; Source = text; TypeCheckResults = None; ProjectCheckResults = None }, String.toLines text |> Array.toList)
({ Ast = tree; Source = text; TypeCheckResults = None; ProjectCheckResults = None; ProjectOptions = None }, String.toLines text |> Array.toList)

[<Benchmark>]
member this.LintParsedFile () =
Expand Down
2 changes: 1 addition & 1 deletion tests/FSharpLint.Core.Tests/Rules/TestAstNodeRule.fs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type TestAstNodeRuleBase (rule:Rule) =
GlobalConfig = resolvedGlobalConfig
TypeCheckResults = checkResult
ProjectCheckResults = None
ProjectOptions = Lazy<_>(None)
ProjectOptions = None
FilePath = (Option.defaultValue String.Empty maybeFileName)
FileContent = input
Lines = (input.Split("\n"))
Expand Down
2 changes: 1 addition & 1 deletion tests/FSharpLint.Core.Tests/Rules/TestHintMatcherBase.fs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ type TestHintMatcherBase () =
GlobalConfig = resolvedGlobalConfig
TypeCheckResults = checkResult
ProjectCheckResults = None
ProjectOptions = Lazy<_>()
ProjectOptions = None
FilePath = (Option.defaultValue String.Empty maybeFileName)
FileContent = input
Lines = (input.Split("\n"))
Expand Down
2 changes: 1 addition & 1 deletion tests/FSharpLint.Core.Tests/Rules/TestIndentationRule.fs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type TestIndentationRuleBase (rule:Rule) =
GlobalConfig = resolvedGlobalConfig
TypeCheckResults = None
ProjectCheckResults = None
ProjectOptions = Lazy<_>(None)
ProjectOptions = None
FilePath = resolvedFileName
FileContent = input
Lines = lines
Expand Down
2 changes: 1 addition & 1 deletion tests/FSharpLint.Core.Tests/Rules/TestLineRule.fs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type TestLineRuleBase (rule:Rule) =
GlobalConfig = resolvedGlobalConfig
TypeCheckResults = None
ProjectCheckResults = None
ProjectOptions = Lazy<_>(None)
ProjectOptions = None
FilePath = resolvedFileName
FileContent = input
Lines = lines
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type TestNoTabCharactersRuleBase (rule:Rule) =
GlobalConfig = resolvedGlobalConfig
TypeCheckResults = None
ProjectCheckResults = None
ProjectOptions = Lazy<_>()
ProjectOptions = None
FilePath = resolvedFileName
FileContent = input
Lines = lines
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="LibAsyncNames.fs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Foo

let Bar(): Async<int> =
async { return 1 }
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
Expand All @@ -8,6 +8,7 @@
<ItemGroup>
<Compile Include="TestConsoleApplication.fs" />
<Compile Include="TestApi.fs" />
<Compile Include="TransparentCompiler.fs" />
<Compile Include="Attributes.fs" />
</ItemGroup>

Expand Down
22 changes: 21 additions & 1 deletion tests/FSharpLint.FunctionalTest/TestApi.fs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ module TestApi =

let sourceFile = basePath </> "tests" </> "TypeChecker.fs"

// Test project used for transparent/background compiler project options tests
let asyncTestProjectPath = basePath </> "tests" </> "FSharpLint.FunctionalTest.TestedProject" </> "LibAsync"
let asyncTestProjectFile = asyncTestProjectPath </> "LibAsync.fsproj"

[<TestFixture(Category = "Acceptance Tests")>]
type TestApi() =
let generateAst source =
Expand All @@ -38,7 +42,7 @@ module TestApi =
member _.``Performance of linting an existing file``() =
let text = File.ReadAllText sourceFile
let tree = generateAst text
let fileInfo = { Ast = tree; Source = text; TypeCheckResults = None; ProjectCheckResults = None }
let fileInfo = { Ast = tree; Source = text; TypeCheckResults = None; ProjectCheckResults = None; ProjectOptions = None }

let stopwatch = Stopwatch.StartNew()
let times = ResizeArray()
Expand All @@ -59,6 +63,22 @@ module TestApi =
Assert.Less(result, 250)
fprintf TestContext.Out "Average runtime of linter on parsed file: %d (milliseconds)." result

// Test linting the async-name test project with the default linting functions, which use the background compiler
// This should tokenize "LibAsync.fsproj" tokenizes to ["Lib"; "Async"; ".fsproj"] -> Likely a library.
[<Test>]
member _.``Lint async naming test project with background compiler``() =

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this test here to test the project options being set up correctly inside asyncLintProject

task {
let! result = asyncLintProject OptionalLintParameters.Default asyncTestProjectFile toolsPath

match result with
| LintResult.Success warnings ->
Assert.AreEqual(1, warnings.Length)
Assert.AreEqual(FSharpLint.Rules.Identifiers.AsynchronousFunctionNames, warnings.[0].RuleIdentifier)
StringAssert.Contains("This function returns Async. Consider renaming it to AsyncBar.", warnings.[0].Details.Message)
| LintResult.Failure err ->
Assert.Fail(string err)
}

[<Test>]
member _.``Lint project via absolute path``() =
let projectPath = basePath </> "tests" </> "FSharpLint.FunctionalTest.TestedProject" </> "FSharpLint.FunctionalTest.TestedProject.NetCore"
Expand Down
Loading
Loading