Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"rollForward": false
},
"fsharp-analyzers": {
"version": "0.39.0",
"version": "0.39.1",
"commands": [
"fsharp-analyzers"
],
Expand Down
2 changes: 1 addition & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:10.0.103
FROM mcr.microsoft.com/dotnet/sdk:10.0.400

# Avoid warnings by switching to noninteractive
ENV DEBIAN_FRONTEND=noninteractive
Expand Down
9 changes: 9 additions & 0 deletions .devcontainer/devcontainer-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {
"version": "1.1.2",
"resolved": "ghcr.io/devcontainers/features/github-cli@sha256:7c409bf6316ffd85f04fd92fba85a744282639e603417dd4796409866bdb6805",
"integrity": "sha256:7c409bf6316ffd85f04fd92fba85a744282639e603417dd4796409866bdb6805"
}
}
}
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ fsharp_multi_line_lambda_closing_newline=true
fsharp_bar_before_discriminated_union_declaration=true
fsharp_experimental_keep_indent_in_branch=true

[{build.fsx,scripts/**/*.fsx}]
fsharp_experimental_keep_indent_in_branch=true

[build.fsx]
fsharp_keep_max_number_of_blank_lines=1
fsharp_blank_lines_around_nested_multiline_expressions=false
Expand Down
15 changes: 10 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ dotnet fsi build.fsx -- -p AnalyzeChanged

This analyzes the files the working tree changed, and nothing else. A project is loaded when it
owns a changed `.fs` or `.fsi`, and is then analyzed for those files alone. A changed `.fsproj`
asks for the whole project, because what it compiles is no longer what it compiled before.
asks for the whole project, because what it compiles is no longer what it compiled before. A
changed `.fsx` is reported on through the `Scripts` target.

Scoping it to the changed files is what makes this quick: analyzing one file of
`Fantomas.Core.Tests` takes seconds where the whole project takes minutes.
Expand All @@ -91,10 +92,14 @@ to act on a run. What comes out is the thing to fix.
dotnet fsi build.fsx -- -p Analyze
```

This analyzes every file of every project. The test projects are the largest of the solution and
decide how long that takes: the smallest projects report within seconds, `Fantomas.Core.Tests`
takes a couple of minutes. Run it before opening a pull request, and while working use
`AnalyzeChanged`, which cannot see a finding your change causes in a file you did not edit.
This analyzes every file of every project, and the scripts. The test projects are the largest of the
solution and decide how long that takes: the smallest projects report within seconds, the scripts in
about four, `Fantomas.Core.Tests` takes a couple of minutes. Run it before opening a pull request,
and while working use `AnalyzeChanged`, which cannot see a finding your change causes in a file you
did not edit.

The scripts are a weaker check than the projects, for reasons that are all downstream of the typed
tree a script gets. [analyzers/AGENTS.md](analyzers/AGENTS.md) has what that costs and why.

Both pipelines analyze each project in its own process, so findings are printed per project as
that project finishes rather than all at the end.
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [8.0.0-beta-003] - 2026-09-12

### Changed

- Updated the packages the command line tool is built against: `editorconfig` to `0.18.0`, `Serilog` to `4.4.0`, `Spectre.Console` to `0.57.2` and `System.IO.Abstractions` to `22.2.0`. Only one of these is visible in what Fantomas does: `editorconfig` `0.18.0` scopes its cache of the `.editorconfig` files it has read to the parser that reads them, where it used to share one statically. Fantomas keeps a single parser for the process, so a file is still read once and no more. `Fantomas.Core` asks for the same `FSharp.Core` it always did, so nothing referencing it has to move. [#3468](https://github.com/fsprojects/fantomas/pull/3468)

## [8.0.0-beta-002] - 2026-09-08

### Fixed
Expand Down
32 changes: 16 additions & 16 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,41 @@
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="FSharp.Core" Version="10.0.100"/>
<PackageVersion Include="System.Collections.Immutable" Version="10.0.6" />
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="10.0.6" />
<PackageVersion Include="FSharp.Core" Version="10.1.203"/>
<PackageVersion Include="System.Collections.Immutable" Version="10.0.12" />
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="10.0.12" />
<PackageVersion Include="System.Memory" Version="4.6.3" />
<PackageVersion Include="System.Runtime" Version="4.3.1" />
<PackageVersion Include="FsLexYacc" Version="11.4.1" />
<PackageVersion Include="FsLexYacc" Version="12.0.0" />

<!-- Hosts the vendored FSharpEmbedResourceText MSBuild task, see Fantomas.FCS.BuildTasks. -->
<PackageVersion Include="Microsoft.Build.Framework" Version="18.0.2" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="18.0.2" />

<PackageVersion Include="Ionide.KeepAChangelog.Tasks" Version="0.3.3"/>
<PackageVersion Include="DotNet.ReproducibleBuilds" Version="2.0.2"/>
<PackageVersion Include="DotNet.ReproducibleBuilds" Version="2.0.5"/>

<PackageVersion Include="G-Research.FSharp.Analyzers" Version="0.25.0" />
<PackageVersion Include="Ionide.Analyzers" Version="0.19.0" />

<PackageVersion Include="StreamJsonRpc" Version="2.25.29" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="SemanticVersioning" Version="3.0.0" />
<PackageVersion Include="Serilog" Version="4.3.1"/>
<PackageVersion Include="Serilog" Version="4.4.0"/>
<PackageVersion Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageVersion Include="editorconfig" Version="0.15.0" />
<PackageVersion Include="editorconfig" Version="0.18.0" />
<PackageVersion Include="Ignore" Version="0.2.1" />
<PackageVersion Include="System.IO.Abstractions" Version="22.1.0" />
<PackageVersion Include="Spectre.Console" Version="0.55.0" />
<PackageVersion Include="System.IO.Abstractions" Version="22.2.0" />
<PackageVersion Include="Spectre.Console" Version="0.57.2" />
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />

<PackageVersion Include="CliWrap" Version="3.10.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.4.0"/>
<PackageVersion Include="NUnit" Version="4.5.1"/>
<PackageVersion Include="NUnit3TestAdapter" Version="6.2.0"/>
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.1.1" />
<PackageVersion Include="FsCheck" Version="3.3.2" />
<PackageVersion Include="CliWrap" Version="3.10.5" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.10.0"/>
<PackageVersion Include="NUnit" Version="4.6.1"/>
<PackageVersion Include="NUnit3TestAdapter" Version="6.3.0"/>
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.2.0" />
<PackageVersion Include="FsCheck" Version="3.4.0" />
<PackageVersion Include="FsUnit" Version="7.1.1" />
<PackageVersion Include="AltCover" Version="9.0.102" />
<PackageVersion Include="AltCover" Version="9.0.145" />
</ItemGroup>
</Project>
56 changes: 56 additions & 0 deletions analyzers/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,62 @@ since SDK 0.38.0, because the tool cannot load an assembly built for another run
the solution happens to target the same, but the two are pinned for different reasons and only the
analyzer project moves when the tool does.

## The scripts

`build.fsx` and the scripts beside it are source too, and both `Analyze` pipelines run over them as a
target of their own, `Scripts`, alongside the projects. `--script` is given the scripts that compile
on their own, which between them `#load` every other one, so a rule reaches `BuildCommon.fsx`
through `build.fsx` and `shared.fsx` through the diagnostic scripts.

It is a weaker check than a project gets, and the difference is worth knowing before acting on what
it says or fails to say. The typed tree the analyzers receive for a script is missing every top level
bare expression, which in this repository is every `pipeline { }` of `build.fsx` and the command line
handling at the foot of each diagnostic script. See
[FSharp.Analyzers.SDK#332](https://github.com/ionide/FSharp.Analyzers.SDK/issues/332).

Three things follow from that, and all three are handled in `BuildAnalyzers.fsx` rather than here:

- **Only the analyzers this repository owns run over the scripts.** `Ionide.Analyzers` and
`G-Research.FSharp.Analyzers` walk the typed tree, and walking one with error recovery nodes in it
throws out of `FSharpExprConvert` and takes the whole run down. Both packages do it, on every
script that has a top level `match`.
- **`FANTOMAS-OPENS-001` is excluded.** It asks the compiler which opens the file resolves nothing
through, and on a script it answers about that same incomplete tree: the opens `build.fsx` uses
only inside a `pipeline { }` read as unused. It also brings the run down on any script that
references `Fantomas.FCS`, because resolving the members of `System.ReadOnlySpan` needs an
assembly no script references.
- **`--include-files` holds the report to scripts.** A script compilation includes whatever it
loads, and `shared.fsx` loads `EditorConfig.fs` and `Suggestion.fs` out of `src/Fantomas`. Those
are analyzed properly as part of their own project; reporting on them here would say something
else about them, because a script loads the `.fs` alone and the signature file that keeps
`FANTOMAS-ANNOTATE-001` and `FANTOMAS-XMLDOC-001` quiet about them is no part of the compilation.

So a clean `Scripts` run covers less ground than a clean project run, and a rule that says nothing
about a script has not necessarily looked at it.

All three are answered upstream, by two changes that together give a script the same references
`dotnet fsi` gives it:

- [#333](https://github.com/ionide/FSharp.Analyzers.SDK/pull/333) resolves a script against the SDK
reference assemblies rather than the .NET Framework ones, so `FSharp.Core` loads.
- [#334](https://github.com/ionide/FSharp.Analyzers.SDK/issues/334) is the `fsi` object.
`GetProjectOptionsFromScript` did not reference `FSharp.Compiler.Interactive.Settings`, so every
script here that reads `fsi.CommandLineArgs` failed to type check on that one name and error
recovery took the expressions around it with it.

Built against both, every script of this repository type checks with no errors, both crashes go, and
the fourteen opens the scripts falsely reported drop to none while `FANTOMAS-OPENS-001` still catches
a real unused one in a script. Once that is released and `.config/dotnet-tools.json` is bumped, all
three bullets above can go.

The packaged analyzers then have plenty to say about the scripts, `GRA-INTERPOLATED-001` alone
accounts for 94 findings, so letting them in is a decision rather than a formality.

The `.editorconfig` turns `fsharp_experimental_keep_indent_in_branch` on for `build.fsx` and
`scripts/**/*.fsx` as well as for `src` and `analyzers`, so acting on a `FANTOMAS-KEEPINDENT-001`
finding in a script is not something the formatter undoes. Turning it on changed no formatting: the
setting holds a body already written that way and never de-indents one itself.

## Two ways a rule silently does nothing

Both of these produce a clean run rather than an error, so check that a new rule actually fires
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="FSharp.Core" Version="10.1.400" />
<PackageReference Include="FSharp.Analyzers.SDK.Testing" Version="0.39.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.4.0" />
<PackageReference Include="NUnit" Version="4.5.1" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
<PackageReference Include="FSharp.Analyzers.SDK.Testing" Version="0.39.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.0" />
<PackageReference Include="NUnit" Version="4.6.1" />
<PackageReference Include="NUnit3TestAdapter" Version="6.3.0" />
</ItemGroup>
</Project>
4 changes: 2 additions & 2 deletions analyzers/Fantomas.Analyzers/Fantomas.Analyzers.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<IsPackable>false</IsPackable>
<!--
FSharp.Analyzers.SDK depends on FSharp.Core at exactly 10.1.400, where the repository pins
10.0.100 centrally. Under central package management that is NU1109, a hard error, so this
10.1.401 centrally. Under central package management that is NU1109, a hard error, so this
project opts out and carries its versions inline. Keeping them here also puts the SDK version
next to the analyzers rather than in the product's package list.
-->
Expand Down Expand Up @@ -48,7 +48,7 @@
</ItemGroup>
<ItemGroup>
<!-- Must track the fsharp-analyzers version pinned in .config/dotnet-tools.json. -->
<PackageReference Include="FSharp.Analyzers.SDK" Version="0.39.0" />
<PackageReference Include="FSharp.Analyzers.SDK" Version="0.39.1" />
<PackageReference Include="FSharp.Core" Version="10.1.400" />
</ItemGroup>
</Project>
Loading