FSharp.Analyzers.SDK 0.13.0-beta007

This is a prerelease version of FSharp.Analyzers.SDK.
There is a newer version of this package available.
See the version list below for details.
dotnet add package FSharp.Analyzers.SDK --version 0.13.0-beta007                
NuGet\Install-Package FSharp.Analyzers.SDK -Version 0.13.0-beta007                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FSharp.Analyzers.SDK" Version="0.13.0-beta007" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add FSharp.Analyzers.SDK --version 0.13.0-beta007                
#r "nuget: FSharp.Analyzers.SDK, 0.13.0-beta007"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install FSharp.Analyzers.SDK as a Cake Addin
#addin nuget:?package=FSharp.Analyzers.SDK&version=0.13.0-beta007&prerelease

// Install FSharp.Analyzers.SDK as a Cake Tool
#tool nuget:?package=FSharp.Analyzers.SDK&version=0.13.0-beta007&prerelease                

FSharp.Analyzers.SDK

Library used for building custom analyzers for FSAC / F# editors.

F# analyzers are live, real-time, project based plugins that enables to diagnose source code and surface custom errors, warnings and code fixes into editor. Read more about analyzers here - https://medium.com/lambda-factory/introducing-f-analyzers-772487889429

How to build

  1. Install the .NET SDK version specified in global.json
  2. dotnet tool restore
  3. Open and build in your favorite IDE, or use dotnet build

How to run sample

  1. dotnet build -c Release
dotnet run --project src\FSharp.Analyzers.Cli\FSharp.Analyzers.Cli.fsproj -- --project ./samples/OptionAnalyzer/OptionAnalyzer.fsproj --analyzers-path ./samples/OptionAnalyzer/bin/Release --verbose

You can also set up a run configuration of FSharp.Analyzers.Cli in your favorite IDE using similar arguments. This also allows you to debug FSharp.Analyzers.Cli.

Writing Analyzers

Analyzers that are consumed by this SDK and from Ionide are simply .NET core class libraries. These class libraries expose a value of type Analyzer which is effectively a function that has input of type Context and returns a list of Message records:

module BadCodeAnalyzer

open FSharp.Analyzers.SDK

[<Analyzer>]
let badCodeAnalyzer : Analyzer =
  fun (context: Context) ->
    // inspect context to determine the error/warning messages
    [   ]

Notice how we expose the function BadCodeAnalyzer.badCodeAnalyzer with an attribute [<Analyzer>] that allows the SDK to detect the function. The input Context is a record that contains information about a single F# file such as the typed AST, the AST, the file content, the file name and more. The SDK runs this function against all files of a project during editing. The output messages that come out of the function are eventually used by Ionide to highlight the inspected code as a warning or error depending on the Severity level of each message.

Analyzers can also be named which allows for better logging if something went wrong while using the SDK from Ionide:

[<Analyzer "BadCodeAnalyzer">]
let badCodeAnalyzer : Analyzer =
  fun (context: Context) ->
    // inspect context to determine the error/warning messages
    [   ]

Analyzer Requirements

Analyzers are .NET core class libraries and they are distributed as such. However, since the SDK relies on dynamically loading the analyzers during runtime, there are some requirements to get them to work properly:

  • The analyzer class library has to target the net6.0 framework
  • The analyzer has to reference the latest FSharp.Analyzers.SDK (at least the version used by FsAutoComplete which is subsequently used by Ionide)

Packaging and Distribution

Since analyzers are just .NET core libraries, you can distribute them to the nuget registry just like you would with a normal .NET package. Simply run dotnet pack --configuration Release against the analyzer project to get a nuget package and publish it with

dotnet nuget push {NugetPackageFullPath} -s nuget.org -k {NugetApiKey}

However, the story is different and slightly more complicated when your analyzer package has third-party dependencies also coming from nuget. Since the SDK dynamically loads the package assemblies (.dll files), the assemblies of the dependencies has be there next to the main assembly of the analyzer. Using dotnet pack will not include these dependencies into the output Nuget package. More specifically, the ./lib/net6.0 directory of the nuget package must have all the required assemblies, also those from third-party packages. In order to package the analyzer properly with all the assemblies, you need to take the output you get from running:

dotnet publish --configuration Release --framework net6.0

against the analyzer project and put every file from that output into the ./lib/net6.0 directory of the nuget package. This requires some manual work by unzipping the nuget package first (because it is just an archive), modifying the directories then zipping the package again. It can be done using a FAKE build target to automate the work:

// make ZipFile available
#r "System.IO.Compression.FileSystem.dll"

let releaseNotes = ReleaseNotes.load "RELEASE_NOTES.md"

Target.create "PackAnalyzer" (fun _ ->
    let analyzerProject = "src" </> "BadCodeAnalyzer"
    let args =
        [
            "pack"
            "--configuration Release"
            sprintf "/p:PackageVersion=%s" releaseNotes.NugetVersion
            sprintf "/p:PackageReleaseNotes=\"%s\"" (String.concat "\n" releaseNotes.Notes)
            sprintf "--output %s" (__SOURCE_DIRECTORY__ </> "dist")
        ]

    // create initial nuget package
    let exitCode = Shell.Exec("dotnet", String.concat " " args, analyzerProject)
    if exitCode <> 0 then
        failwith "dotnet pack failed"
    else
        match Shell.Exec("dotnet", "publish --configuration Release --framework net6.0", analyzerProject) with
        | 0 ->
            let nupkg =
                System.IO.Directory.GetFiles(__SOURCE_DIRECTORY__ </> "dist")
                |> Seq.head
                |> IO.Path.GetFullPath

            let nugetParent = DirectoryInfo(nupkg).Parent.FullName
            let nugetFileName = IO.Path.GetFileNameWithoutExtension(nupkg)

            let publishPath = analyzerProject </> "bin" </> "Release" </> "net6.0" </> "publish"
            // Unzip the nuget
            ZipFile.ExtractToDirectory(nupkg, nugetParent </> nugetFileName)
            // delete the initial nuget package
            File.Delete nupkg
            // remove stuff from ./lib/net6.0
            Shell.deleteDir (nugetParent </> nugetFileName </> "lib" </> "net6.0")
            // move the output of publish folder into the ./lib/net6.0 directory
            Shell.copyDir (nugetParent </> nugetFileName </> "lib" </> "net6.0") publishPath (fun _ -> true)
            // re-create the nuget package
            ZipFile.CreateFromDirectory(nugetParent </> nugetFileName, nupkg)
            // delete intermediate directory
            Shell.deleteDir(nugetParent </> nugetFileName)
        | _ ->
            failwith "dotnet publish failed"
)

How to contribute

Imposter syndrome disclaimer: I want your help. No really, I do.

There might be a little voice inside that tells you you're not ready; that you need to do one more tutorial, or learn another framework, or write a few more blog posts before you can help me with this project.

I assure you, that's not the case.

This project has some clear Contribution Guidelines and expectations that you can read here.

The contribution guidelines outline the process that you'll need to follow to get a patch merged. By making expectations and process explicit, I hope it will make it easier for you to contribute.

And you don't just have to write code. You can help out by writing documentation, tests, or even by giving feedback about this work. (And yes, that includes giving feedback about the contribution guidelines.)

Thank you for contributing!

The project is hosted on GitHub where you can report issues, fork the project and submit pull requests.

The library is available under MIT license, which allows modification and redistribution for both commercial and non-commercial purposes.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (7)

Showing the top 5 NuGet packages that depend on FSharp.Analyzers.SDK:

Package Downloads
NpgsqlFSharpAnalyzer

Advanced embedded static analysis and type-checking for SQL code from F#

BinaryDefense.FSharp.Analyzers.Hashing

Looks for insecure hashing functions

AzureTackleAnalyzer

AzureTackleAnalyzer

FSharp.CosmosDb.Analyzer

An F# analyzer that will help you work with CosmosDB.

FSharp.Analyzers.SDK.Testing

Test facilities for the FSharp.Analyzers.SDK

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.28.0 1,037 11/19/2024
0.27.0 2,273 8/17/2024
0.26.1 133 8/5/2024
0.26.0 3,232 5/15/2024
0.25.0 2,790 2/14/2024
0.24.0 1,217 1/30/2024
0.23.0 1,060 1/5/2024
0.22.0 399 12/19/2023
0.21.0 320 11/23/2023
0.20.2 246 11/15/2023
0.20.1 195 11/14/2023
0.20.0 178 11/13/2023
0.19.0 336 11/7/2023
0.18.0 254 11/3/2023
0.17.1 153 10/30/2023
0.17.0 154 10/26/2023
0.16.0 1,344 10/16/2023
0.15.0 229 10/10/2023
0.14.1 980 9/26/2023
0.14.0 241 9/21/2023
0.13.0 206 9/7/2023
0.13.0-beta007 133 9/7/2023
0.12.0 640 5/20/2023
0.11.0 24,124 1/12/2022
0.10.1 2,456 6/23/2021
0.10.0 416 6/22/2021
0.9.0 1,694 5/28/2021
0.8.0 16,526 2/11/2021
0.7.0 2,006 1/20/2021
0.6.0 2,223 12/20/2020
0.5.0 10,412 7/11/2020
0.4.1 4,395 4/10/2020
0.4.0 9,530 3/8/2020
0.3.1 1,137 2/28/2020
0.3.0 4,295 2/17/2020
0.2.0 1,270 12/24/2019
0.1.0 567 12/17/2019
0.0.10 573 12/16/2019
0.0.9 609 11/21/2019
0.0.8 1,284 10/1/2019
0.0.7 683 8/28/2019
0.0.6 967 7/1/2019
0.0.5 1,319 5/27/2019
0.0.4 1,392 3/29/2019
0.0.3 1,002 2/26/2019
0.0.2 816 2/8/2019
0.0.1 2,026 9/14/2018

### Added

* * [Add Parse and Check Results to Context](https://github.com/ionide/FSharp.Analyzers.SDK/pull/73) (thanks @dawedawe!)

### Changed

* * [Enforce SDK version](https://github.com/ionide/FSharp.Analyzers.SDK/pull/72) (thanks @nojar!)
* * [Update FCS to 43.7.400](https://github.com/ionide/FSharp.Analyzers.SDK/pull/74) (thanks @TheAngryByrd!)