Skip to content

Commit 53a00e2

Browse files
authored
Merge pull request #876 from nojaf/use-analyzers
Use analyzers
2 parents 468fefe + eb10d02 commit 53a00e2

40 files changed

+329
-220
lines changed

.config/dotnet-tools.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
"commands": [
88
"fantomas"
99
]
10+
},
11+
"fsharp-analyzers": {
12+
"version": "0.20.2",
13+
"commands": [
14+
"fsharp-analyzers"
15+
]
1016
}
1117
}
1218
}

.github/workflows/pull-requests.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ on:
55
branches:
66
- main
77

8+
permissions:
9+
id-token: write
10+
security-events: write
11+
812
jobs:
913
build:
1014

@@ -20,3 +24,14 @@ jobs:
2024
uses: actions/setup-dotnet@v3
2125
- name: Run CI
2226
run: dotnet fsi build.fsx
27+
28+
- name: Analyze Solution
29+
if: matrix.os == 'ubuntu-latest'
30+
run: dotnet msbuild /t:AnalyzeSolution
31+
continue-on-error: true
32+
33+
- name: Upload SARIF file
34+
uses: github/codeql-action/upload-sarif@v2
35+
if: matrix.os == 'ubuntu-latest'
36+
with:
37+
sarif_file: ./reports

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,7 @@ tests/FSharp.Literate.Tests/output1/
4747
.vscode/
4848
.DS_Store
4949
tests/FSharp.Literate.Tests/output2/
50-
tests/FSharp.Literate.Tests/previous-next-output/
50+
tests/FSharp.Literate.Tests/previous-next-output/
51+
52+
# Analyzer
53+
reports/

Directory.Packages.props

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@
1919
<PackageVersion Include="Suave" Version="2.6.2" />
2020
<PackageVersion Include="System.Memory" Version="4.5.5" />
2121
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
22-
<PackageVersion Include="NUnit" Version="3.13.3" />
22+
<PackageVersion Include="NUnit" Version="3.14.0" />
2323
<PackageVersion Include="FsUnit" Version="5.6.0" />
2424
<PackageVersion Include="FSharp.Data" Version="6.3.0" />
2525
<PackageVersion Include="NUnit3TestAdapter" Version="4.5.0" />
26-
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
27-
<PackageVersion Include="Ionide.KeepAChangelog.Tasks" Version="0.1.8" />
26+
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
27+
<PackageVersion Include="Ionide.KeepAChangelog.Tasks" Version="0.1.8" />
28+
<PackageVersion Include="FSharp.Analyzers.Build" Version="0.2.0" />
29+
<PackageVersion Include="G-Research.FSharp.Analyzers" Version="0.3.1" />
30+
<PackageVersion Include="Ionide.Analyzers" Version="0.4.0" />
2831
</ItemGroup>
2932
</Project>

Directory.Solution.targets

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Project>
2+
<ItemGroup>
3+
<ProjectsToAnalyze Include="src/**/*.fsproj" />
4+
</ItemGroup>
5+
6+
<Target Name="AnalyzeSolution">
7+
<MSBuild Projects="@(ProjectsToAnalyze)" Targets="AnalyzeFSharpProject" />
8+
</Target>
9+
</Project>

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ Once built, you can run the command-line tool to self-build the docs for this di
1818
src\fsdocs-tool\bin\Debug\net6.0\fsdocs.exe watch
1919
src\fsdocs-tool\bin\Debug\net6.0\fsdocs.exe build --clean
2020

21+
### Pipelines
22+
23+
Run
24+
dotnet fsi build.fsx -- --help
25+
26+
to see what other pipelines can be run from `build.fsx`.
27+
28+
dotnet fsi build.fsx -- -p Verify
29+
30+
Will perform the linting, unit tests and analyzer check.
31+
This is useful to run locally before submitting your PR.
2132

2233
## Maintainer(s)
2334

build.fsx

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#r "nuget: Fun.Build, 0.3.8"
1+
#r "nuget: Fun.Build, 1.0.4"
22
#r "nuget: Fake.IO.FileSystem, 6.0.0"
33
#r "nuget: Ionide.KeepAChangelog, 0.1.8"
44

@@ -38,12 +38,21 @@ let releaseNugetVersion, _, _ =
3838

3939
let solutionFile = "FSharp.Formatting.sln"
4040

41-
pipeline "CI" {
41+
let lintStage =
4242
stage "Lint" {
4343
run "dotnet tool restore"
4444
run $"dotnet fantomas {__SOURCE_FILE__} src tests docs --check"
4545
}
4646

47+
let testStage =
48+
stage "Tests" {
49+
run
50+
$"dotnet test {solutionFile} --configuration {configuration} --no-build --blame --logger trx --framework net7.0 --results-directory TestResults"
51+
}
52+
53+
pipeline "CI" {
54+
lintStage
55+
4756
stage "Clean" {
4857
run (fun _ ->
4958
!!artifactsDir ++ "temp" |> Shell.cleanDirs
@@ -58,10 +67,7 @@ pipeline "CI" {
5867

5968
stage "NuGet" { run $"dotnet pack {solutionFile} --output \"{artifactsDir}\" --configuration {configuration}" }
6069

61-
stage "Tests" {
62-
run
63-
$"dotnet test {solutionFile} --configuration {configuration} --no-build --blame --logger trx --framework net7.0 --results-directory TestResults"
64-
}
70+
testStage
6571

6672
stage "GenerateDocs" {
6773
run (fun _ ->
@@ -80,4 +86,11 @@ pipeline "CI" {
8086
runIfOnlySpecified false
8187
}
8288

89+
pipeline "Verify" {
90+
lintStage
91+
testStage
92+
stage "Analyzers" { run "dotnet msbuild /t:AnalyzeSolution" }
93+
runIfOnlySpecified true
94+
}
95+
8396
tryPrintPipelineCommandHelp ()

src/Common/Collections.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ module internal List =
103103

104104
/// Represents a tree with nodes containing values an a list of children
105105
///
106-
type internal Tree<'T> = Node of 'T * list<Tree<'T>>
106+
type internal Tree<'T> = Node of self: 'T * children: Tree<'T> list
107107

108108
module internal Tree =
109109
/// Takes all elements at the specified level and turns them into nodes

src/Common/StringParsing.fs

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ module String =
2323

2424
/// Matches when a string starts with the specified sub-string
2525
let (|StartsWith|_|) (start: string) (text: string) =
26-
if text.StartsWith(start) then
26+
if text.StartsWith(start, StringComparison.Ordinal) then
2727
Some(text.Substring(start.Length))
2828
else
2929
None
3030

3131
/// Matches when a string starts with the specified sub-string
3232
/// The matched string is trimmed from all whitespace.
3333
let (|StartsWithTrim|_|) (start: string) (text: string) =
34-
if text.StartsWith(start) then
34+
if text.StartsWith(start, StringComparison.Ordinal) then
3535
Some(text.Substring(start.Length).Trim())
3636
else
3737
None
@@ -40,8 +40,8 @@ module String =
4040
/// with a given value (and returns the rest of it)
4141
let (|StartsAndEndsWith|_|) (starts: string, ends: string) (s: string) =
4242
if
43-
s.StartsWith(starts)
44-
&& s.EndsWith(ends)
43+
s.StartsWith(starts, StringComparison.Ordinal)
44+
&& s.EndsWith(ends, StringComparison.Ordinal)
4545
&& s.Length >= starts.Length + ends.Length
4646
then
4747
Some(s.Substring(starts.Length, s.Length - starts.Length - ends.Length))
@@ -60,8 +60,8 @@ module String =
6060
/// For example "[aa]bc" is wrapped in [ and ] pair. Returns the wrapped
6161
/// text together with the rest.
6262
let (|StartsWithWrapped|_|) (starts: string, ends: string) (text: string) =
63-
if text.StartsWith(starts) then
64-
let id = text.IndexOf(ends, starts.Length)
63+
if text.StartsWith(starts, StringComparison.Ordinal) then
64+
let id = text.IndexOf(ends, starts.Length, StringComparison.Ordinal)
6565

6666
if id >= 0 then
6767
let wrapped = text.Substring(starts.Length, id - starts.Length)
@@ -79,7 +79,7 @@ module String =
7979
let rec tryEol eolList =
8080
match eolList with
8181
| h: string :: t ->
82-
match text.IndexOf(h) with
82+
match text.IndexOf(h, StringComparison.Ordinal) with
8383
| i when i < 0 -> tryEol t
8484
| i -> text.Substring(i + h.Length)
8585
| _ -> text
@@ -174,15 +174,15 @@ module StringPosition =
174174
StartColumn = n.StartColumn + text.Length - trimmed.Length })
175175

176176
/// Matches when a string starts with any of the specified sub-strings
177-
let (|StartsWithAny|_|) (starts: seq<string>) (text: string, _n: MarkdownRange) =
178-
if starts |> Seq.exists (text.StartsWith) then
177+
let (|StartsWithAny|_|) (starts: string seq) (text: string, _n: MarkdownRange) =
178+
if starts |> Seq.exists (fun s -> text.StartsWith(s, StringComparison.Ordinal)) then
179179
Some()
180180
else
181181
None
182182

183183
/// Matches when a string starts with the specified sub-string
184184
let (|StartsWith|_|) (start: string) (text: string, n: MarkdownRange) =
185-
if text.StartsWith(start) then
185+
if text.StartsWith(start, StringComparison.Ordinal) then
186186
Some(
187187
text.Substring(start.Length),
188188
{ n with
@@ -194,7 +194,7 @@ module StringPosition =
194194
/// Matches when a string starts with the specified sub-string
195195
/// The matched string is trimmed from all whitespace.
196196
let (|StartsWithTrim|_|) (start: string) (text: string, n: MarkdownRange) =
197-
if text.StartsWith(start) then
197+
if text.StartsWith(start, StringComparison.Ordinal) then
198198
Some(
199199
text.Substring(start.Length).Trim(),
200200
{ n with
@@ -207,7 +207,7 @@ module StringPosition =
207207
/// The matched string is trimmed from all whitespace.
208208
let (|StartsWithNTimesTrimIgnoreStartWhitespace|_|) (start: string) (text: string, _n: MarkdownRange) =
209209
if text.Contains(start) then
210-
let beforeStart = text.Substring(0, text.IndexOf(start))
210+
let beforeStart = text.Substring(0, text.IndexOf(start, StringComparison.Ordinal))
211211

212212
if String.IsNullOrWhiteSpace(beforeStart) then
213213
let startAndRest = text.Substring(beforeStart.Length)
@@ -232,8 +232,8 @@ module StringPosition =
232232
/// with a given value (and returns the rest of it)
233233
let (|StartsAndEndsWith|_|) (starts: string, ends: string) (s: string, n: MarkdownRange) =
234234
if
235-
s.StartsWith(starts)
236-
&& s.EndsWith(ends)
235+
s.StartsWith(starts, StringComparison.Ordinal)
236+
&& s.EndsWith(ends, StringComparison.Ordinal)
237237
&& s.Length >= starts.Length + ends.Length
238238
then
239239
Some(
@@ -276,8 +276,8 @@ module StringPosition =
276276
/// For example "[aa]bc" is wrapped in [ and ] pair. Returns the wrapped
277277
/// text together with the rest.
278278
let (|StartsWithWrapped|_|) (starts: string, ends: string) (text: string, n: MarkdownRange) =
279-
if text.StartsWith(starts) then
280-
let id = text.IndexOf(ends, starts.Length)
279+
if text.StartsWith(starts, StringComparison.Ordinal) then
280+
let id = text.IndexOf(ends, starts.Length, StringComparison.Ordinal)
281281

282282
if id >= 0 then
283283
let wrapped = text.Substring(starts.Length, id - starts.Length)
@@ -299,7 +299,7 @@ module StringPosition =
299299
/// complete repetitions of a specified sub-string.
300300
let (|EqualsRepeated|_|) (repeated, _n: MarkdownRange) =
301301
function
302-
| StartsWithRepeated repeated (_n, ("", _)) -> Some()
302+
| StartsWithRepeated repeated (_n, (v, _)) when (String.IsNullOrWhiteSpace v) -> Some()
303303
| _ -> None
304304

305305
module List =
@@ -363,7 +363,8 @@ module Lines =
363363
let (|TakeStartingWithOrBlank|_|) (start: string) (input: string list) =
364364
match
365365
input
366-
|> List.partitionWhile (fun s -> String.IsNullOrWhiteSpace s || s.StartsWith(start))
366+
|> List.partitionWhile (fun s ->
367+
String.IsNullOrWhiteSpace s || s.StartsWith(start, StringComparison.Ordinal))
367368
with
368369
| matching, rest when matching <> [] -> Some(matching, rest)
369370
| _ -> None
@@ -405,7 +406,7 @@ module Lines =
405406
|> List.map (fun (StringPosition.TrimStart s) -> s)
406407
// Now remove all additional spaces at the end, but keep two spaces if existent
407408
|> List.map (fun (s, n) ->
408-
let endsWithTwoSpaces = s.EndsWith(" ")
409+
let endsWithTwoSpaces = s.EndsWith(" ", StringComparison.Ordinal)
409410

410411
let trimmed = s.TrimEnd([| ' ' |]) + if endsWithTwoSpaces then " " else ""
411412

src/Directory.Build.props

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project>
2+
<ItemGroup>
3+
<PackageReference Include="FSharp.Analyzers.Build">
4+
<PrivateAssets>all</PrivateAssets>
5+
<IncludeAssets>build</IncludeAssets>
6+
</PackageReference>
7+
<PackageReference Include="G-Research.FSharp.Analyzers">
8+
<PrivateAssets>all</PrivateAssets>
9+
<IncludeAssets>analyzers</IncludeAssets>
10+
</PackageReference>
11+
<PackageReference Include="Ionide.Analyzers">
12+
<PrivateAssets>all</PrivateAssets>
13+
<IncludeAssets>analyzers</IncludeAssets>
14+
</PackageReference>
15+
</ItemGroup>
16+
</Project>

0 commit comments

Comments
 (0)