Skip to content

Commit 12a7edc

Browse files
authored
Factor out comment-searching code, unify DisposedBeforeAsyncRunAnalyzer magic string (#94)
1 parent 8645188 commit 12a7edc

7 files changed

Lines changed: 48 additions & 25 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## 0.18.0 - 2025-08-27
4+
5+
### Changed
6+
* The magic string for the DisposedBeforeAsyncRunAnalyzer has changed: it is now "disposed before returned workflow runs". [#94](https://github.com/G-Research/fsharp-analyzers/pull/94)
7+
38
## 0.17.0 - 2025-07-12
49

510
### Changed

docs/analyzers/DisposedBeforeAsyncRunAnalyzer.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ let f () =
3131
}
3232
```
3333

34-
If the `use` before the `async` is really your intent, you can disable the warning with a line comment on top of the `use` statement containing `disposed before returned async runs` or `disposed before returned task runs`.
34+
If the `use` before the `async` is really your intent, you can disable the warning with a line comment on top of the `use` statement containing `disposed before returned workflow runs`.
3535
```fsharp
3636
let f () =
37-
// Note: disposed before returned async runs
37+
// Note: disposed before returned workflow runs
3838
use t = new DisposableThing()
3939
async {
4040
return "hi"

src/FSharp.Analyzers/Comments.fs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module FSharp.Analyzers.Comments
2+
3+
open System
4+
open FSharp.Compiler.SyntaxTrivia
5+
open FSharp.Compiler.Text
6+
7+
/// A standard pattern within an analyzer is to look for a specific comment preceding a problematic line,
8+
/// indicating "suppress the analyzer on this line".
9+
/// This function performs that common check.
10+
let isSwitchedOffPerComment
11+
(magicComment : string)
12+
(comments : CommentTrivia list)
13+
(sourceText : ISourceText)
14+
(analyzerTriggeredOn : Range)
15+
: bool
16+
=
17+
comments
18+
|> List.exists (fun c ->
19+
match c with
20+
| CommentTrivia.LineComment r ->
21+
if r.StartLine <> analyzerTriggeredOn.StartLine - 1 then
22+
false
23+
else
24+
let lineOfComment = sourceText.GetLineString (r.StartLine - 1) // 0-based
25+
26+
lineOfComment.Contains (magicComment, StringComparison.OrdinalIgnoreCase)
27+
| _ -> false
28+
)

src/FSharp.Analyzers/DisposedBeforeAsyncRunAnalyzer.fs

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module GR.FSharp.Analyzers.DisposedBeforeAsyncRunAnalyzer
22

33
open System
4+
open FSharp.Analyzers.Comments
45
open FSharp.Analyzers.SDK
56
open FSharp.Analyzers.SDK.ASTCollecting
67
open FSharp.Compiler.CodeAnalysis
@@ -65,32 +66,14 @@ let pathContainsComputationExpr (path : SyntaxVisitorPath) =
6566
)
6667

6768
[<Literal>]
68-
let SwitchOffAsyncComment = "disposed before returned async runs"
69-
70-
[<Literal>]
71-
let SwitchOffTaskComment = "disposed before returned task runs"
69+
let SwitchOffComment = "disposed before returned workflow runs"
7270

7371
let collectUses (sourceText : ISourceText) (ast : ParsedInput) (checkFileResults : FSharpCheckFileResults) =
7472
let comments =
7573
match ast with
7674
| ParsedInput.ImplFile parsedImplFileInput -> parsedImplFileInput.Trivia.CodeComments
7775
| _ -> []
7876

79-
let isSwitchedOffPerComment (range : Range) =
80-
comments
81-
|> List.exists (fun c ->
82-
match c with
83-
| CommentTrivia.LineComment r ->
84-
if r.StartLine <> range.StartLine - 1 then
85-
false
86-
else
87-
let lineOfComment = sourceText.GetLineString (r.StartLine - 1) // 0-based
88-
89-
lineOfComment.Contains (SwitchOffAsyncComment, StringComparison.OrdinalIgnoreCase)
90-
|| lineOfComment.Contains (SwitchOffTaskComment, StringComparison.OrdinalIgnoreCase)
91-
| _ -> false
92-
)
93-
9477
let uses = ResizeArray<range * string> ()
9578

9679
// Note: not tailrecursive
@@ -117,7 +100,13 @@ let collectUses (sourceText : ISourceText) (ast : ParsedInput) (checkFileResults
117100
match synExpr with
118101
| SynExpr.LetOrUse (isUse = true ; bindings = [ binding ] ; body = body) ->
119102
if
120-
not (isSwitchedOffPerComment binding.RangeOfBindingWithoutRhs)
103+
not (
104+
isSwitchedOffPerComment
105+
SwitchOffComment
106+
comments
107+
sourceText
108+
binding.RangeOfBindingWithoutRhs
109+
)
121110
&& hasAsyncOrTaskInBody body
122111
then
123112
match pathContainsAsyncOrTaskReturningFunc checkFileResults sourceText path with

src/FSharp.Analyzers/FSharp.Analyzers.fsproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
</PropertyGroup>
1616
<ItemGroup>
1717
<Compile Include="Util.fs" />
18+
<Compile Include="Comments.fs" />
1819
<Compile Include="StringAnalyzer.fsi" />
1920
<Compile Include="StringAnalyzer.fs" />
2021
<Compile Include="JsonSerializerOptionsAnalyzer.fs" />
@@ -43,4 +44,4 @@
4344
<TfmSpecificPackageFile Include="$(OutputPath)\$(AssemblyName).dll" PackagePath="analyzers/dotnet/fs" />
4445
</ItemGroup>
4546
</Target>
46-
</Project>
47+
</Project>

tests/FSharp.Analyzers.Tests/data/disposedBeforeAsyncRun/negative/CommentOnUseBeforeAsync.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ type DisposableThing () =
77
member _.Dispose() = ()
88

99
let asyncReturningFunc () =
10-
// Note: disposed before returned async runs
10+
// Note: disposed before returned workflow runs
1111
use t = new DisposableThing()
1212
async {
1313
return "hi"

tests/FSharp.Analyzers.Tests/data/disposedBeforeAsyncRun/negative/CommentOnUseBeforeTask.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ type DisposableThing () =
77
member _.Dispose() = ()
88

99
let taskReturningFunc () =
10-
// Note: disposed before returned task runs
10+
// Note: disposed before returned workflow runs
1111
use t = new DisposableThing()
1212
task {
1313
return "hi"

0 commit comments

Comments
 (0)