Skip to content

Commit d04a873

Browse files
authored
Merge PR fsprojects#841 from webwarrior-ws/simple-async-complementary-helpers-2-squashed
Added SimpleAsyncComplementaryHelpers rule. Contributes to fsprojects#517
2 parents f910d7b + 0d149cf commit d04a873

File tree

15 files changed

+591
-49
lines changed

15 files changed

+591
-49
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
title: FL0097
3+
category: how-to
4+
hide_menu: true
5+
---
6+
7+
# SimpleAsyncComplementaryHelpers (FL0097)
8+
9+
*Introduced in `0.26.12`*
10+
11+
## Cause
12+
13+
There is a function that returns `Async<'T>` but no corresponding function that returns `Task<'T>` or vice versa.
14+
15+
## Rationale
16+
17+
Having both variants of asynchronous function (`Async` and `Task`) gives better interoperability, because C# uses `Task` and F# mainly uses `Async`.
18+
19+
By convention, `Async` prefix or suffix in function name means that the function is asynchronous.
20+
21+
Prefix is used for functions that return `Async` type, e.g.:
22+
```
23+
let AsyncFoo(): Async<int> =
24+
async { return 1 }
25+
```
26+
27+
Suffix is used for functions that return `System.Threading.Task` type, e.g.:
28+
```
29+
open System.Threading
30+
31+
let FooAsync(): Task<int> =
32+
task { return 1 }
33+
```
34+
35+
## How To Fix
36+
37+
For each function `AsyncFoo(): Async<'T>` create corresponding function `FooAsync(): Task<'T>` that just calls `Async.StartAsTask(AsyncFoo())`.
38+
For each function `FooAsync(): Task<'T>` create corresponding function `AsyncFoo(): Async<'T>` that just calls `async { return Async.AwaitTask (FooAsync()) }`.
39+
40+
## Rule Settings
41+
42+
{
43+
"simpleAsyncComplementaryHelpers": {
44+
"enabled": false,
45+
"config": {
46+
"mode": "OnlyPublicAPIsInLibraries"
47+
}
48+
}
49+
}

src/FSharpLint.Core/Application/Configuration.fs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,8 @@ type Configuration =
566566
DiscourageStringInterpolationWithStringFormat:EnabledConfig option
567567
FavourNamedMembers:EnabledConfig option
568568
SynchronousFunctionNames:EnabledConfig option
569-
AsynchronousFunctionNames:EnabledConfig option }
569+
AsynchronousFunctionNames:EnabledConfig option
570+
SimpleAsyncComplementaryHelpers:RuleConfig<SimpleAsyncComplementaryHelpers.Config> option }
570571
with
571572
// Method Zero is too big but can't be split into parts because it returns a record
572573
// and it requires all fields to be set.
@@ -678,6 +679,7 @@ with
678679
FavourNamedMembers = None
679680
SynchronousFunctionNames = None
680681
AsynchronousFunctionNames = None
682+
SimpleAsyncComplementaryHelpers = None
681683
}
682684

683685
// fsharplint:enable MaxLinesInMember
@@ -902,6 +904,7 @@ let flattenConfig (config:Configuration) =
902904
config.FavourNamedMembers |> Option.bind (constructRuleIfEnabled FavourNamedMembers.rule)
903905
config.SynchronousFunctionNames |> Option.bind (constructRuleIfEnabled SynchronousFunctionNames.rule)
904906
config.AsynchronousFunctionNames |> Option.bind (constructRuleIfEnabled AsynchronousFunctionNames.rule)
907+
config.SimpleAsyncComplementaryHelpers |> Option.bind (constructRuleWithConfig SimpleAsyncComplementaryHelpers.rule)
905908
|]
906909

907910
let allEnabledRules = Array.choose id allPossibleRules

src/FSharpLint.Core/Application/Lint.fs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,9 @@ module Lint =
484484
return FailedToLoadFile projectFilePath |> LintResult.Failure
485485
}
486486

487+
let lintProjectAsync (optionalParams:OptionalLintParameters) (projectFilePath:string) (toolsPath:Ionide.ProjInfo.Types.ToolsPath) =
488+
Async.StartAsTask(asyncLintProject optionalParams projectFilePath toolsPath)
489+
487490
[<Obsolete "Use asyncLintProject instead, otherwise this synchronous version might cause thread blocking issues; this API will be removed in the future.">]
488491
let lintProject optionalParams projectFilePath toolsPath =
489492
asyncLintProject optionalParams projectFilePath toolsPath |> Async.RunSynchronously
@@ -544,6 +547,9 @@ module Lint =
544547
return FailedToLoadFile solutionFilePath |> LintResult.Failure
545548
}
546549

550+
let lintSolutionAsync (optionalParams:OptionalLintParameters) (solutionFilePath:string) (toolsPath:Ionide.ProjInfo.Types.ToolsPath) =
551+
Async.StartAsTask(asyncLintSolution optionalParams solutionFilePath toolsPath)
552+
547553
[<Obsolete "Use asyncLintSolution instead, otherwise this synchronous version might cause thread blocking issues; this API will be removed in the future.">]
548554
let lintSolution optionalParams solutionFilePath toolsPath =
549555
asyncLintSolution optionalParams solutionFilePath toolsPath |> Async.RunSynchronously
@@ -594,6 +600,9 @@ module Lint =
594600
| ParseFile.Failed failure -> return LintResult.Failure(FailedToParseFile failure)
595601
}
596602

603+
let lintSourceAsync optionalParams source =
604+
Async.StartAsTask(asyncLintSource optionalParams source)
605+
597606
[<Obsolete "Use asyncLintSource instead, otherwise this synchronous version might cause thread blocking issues; this API will be removed in the future.">]
598607
let lintSource optionalParams source =
599608
asyncLintSource optionalParams source |> Async.RunSynchronously
@@ -646,6 +655,9 @@ module Lint =
646655
return FailedToLoadFile filePath |> LintResult.Failure
647656
}
648657

658+
let lintFileAsync optionalParams filePath =
659+
Async.StartAsTask(asyncLintFile optionalParams filePath)
660+
649661
[<Obsolete "Use asyncLintFile instead, otherwise this synchronous version might cause thread blocking issues; this API will be removed in the future.">]
650662
let lintFile optionalParams filePath =
651663
asyncLintFile optionalParams filePath |> Async.RunSynchronously
@@ -692,6 +704,9 @@ module Lint =
692704
return LintResult.Failure (RunTimeConfigError err)
693705
}
694706

707+
let lintFilesAsync optionalParams filePaths =
708+
Async.StartAsTask(asyncLintFiles optionalParams filePaths)
709+
695710
[<Obsolete "Use asyncLintFiles instead, otherwise this synchronous version might cause thread blocking issues; this API will be removed in the future.">]
696711
let lintFiles optionalParams filePaths =
697712
asyncLintFiles optionalParams filePaths |> Async.RunSynchronously

src/FSharpLint.Core/Application/Lint.fsi

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module ConfigurationManagement =
1515
module Lint =
1616

1717
open System.Threading
18+
open System.Threading.Tasks
1819
open FSharpLint.Framework
1920
open FSharpLint.Framework.Configuration
2021
open FSharpLint.Framework.Rules
@@ -156,19 +157,28 @@ module Lint =
156157
/// Lints an entire F# solution by linting all projects specified in the `.sln`, `slnx` or `.slnf` file.
157158
val asyncLintSolution : optionalParams:OptionalLintParameters -> solutionFilePath:string -> toolsPath:Ionide.ProjInfo.Types.ToolsPath -> Async<LintResult>
158159

160+
/// Lints an entire F# solution by linting all projects specified in the `.sln`, `slnx` or `.slnf` file.
161+
val lintSolutionAsync : optionalParams:OptionalLintParameters -> solutionFilePath:string -> toolsPath:Ionide.ProjInfo.Types.ToolsPath -> Task<LintResult>
162+
159163
/// [Obsolete] Lints an entire F# solution by linting all projects specified in the `.sln`, `slnx` or `.slnf` file.
160164
val lintSolution : optionalParams:OptionalLintParameters -> solutionFilePath:string -> toolsPath:Ionide.ProjInfo.Types.ToolsPath -> LintResult
161165

162166
/// Lints an entire F# project by retrieving the files from a given
163167
/// path to the `.fsproj` file.
164168
val asyncLintProject : optionalParams:OptionalLintParameters -> projectFilePath:string -> toolsPath:Ionide.ProjInfo.Types.ToolsPath -> Async<LintResult>
165169

170+
/// Lints an entire F# project by retrieving the files from a given path to the `.fsproj` file.
171+
val lintProjectAsync : optionalParams:OptionalLintParameters -> projectFilePath:string -> toolsPath:Ionide.ProjInfo.Types.ToolsPath -> Task<LintResult>
172+
166173
/// [Obsolete] Lints an entire F# project by retrieving the files from a given path to the `.fsproj` file.
167174
val lintProject : optionalParams:OptionalLintParameters -> projectFilePath:string -> toolsPath:Ionide.ProjInfo.Types.ToolsPath -> LintResult
168175

169176
/// Lints F# source code async.
170177
val asyncLintSource : optionalParams:OptionalLintParameters -> source:string -> Async<LintResult>
171178

179+
/// Lints F# source code async.
180+
val lintSourceAsync : optionalParams:OptionalLintParameters -> source:string -> Task<LintResult>
181+
172182
/// [Obsolete] Lints F# source code.
173183
val lintSource : optionalParams:OptionalLintParameters -> source:string -> LintResult
174184

@@ -179,12 +189,18 @@ module Lint =
179189
/// Lints an F# file from a given path to the `.fs` file.
180190
val asyncLintFile : optionalParams:OptionalLintParameters -> filePath:string -> Async<LintResult>
181191

192+
/// Lints an F# file from a given path to the `.fs` file.
193+
val lintFileAsync : optionalParams:OptionalLintParameters -> filePath:string -> Task<LintResult>
194+
182195
/// [Obsolete] Lints an F# file from a given path to the `.fs` file.
183196
val lintFile : optionalParams:OptionalLintParameters -> filePath:string -> LintResult
184197

185198
/// Lints multiple F# files from given file paths.
186199
val asyncLintFiles : optionalParams:OptionalLintParameters -> filePaths:string seq -> Async<LintResult>
187200

201+
/// Lints multiple F# files from given file paths.
202+
val lintFilesAsync : optionalParams:OptionalLintParameters -> filePaths:string seq -> Task<LintResult>
203+
188204
/// [Obsolete] Lints multiple F# files from given file paths.
189205
val lintFiles : optionalParams:OptionalLintParameters -> filePaths:string seq -> LintResult
190206

src/FSharpLint.Core/FSharpLint.Core.fsproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
<Compile Include="Framework\Suppression.fs" />
3232
<!-- Rules -->
3333
<Compile Include="Rules\Identifiers.fs" />
34+
<Compile Include="Rules\NamingHelper.fs" />
35+
<Compile Include="Rules\Utilities.fs" />
3436
<Compile Include="Rules\Formatting\TupleFormatting\TupleFormattingHelper.fs" />
3537
<Compile Include="Rules\Formatting\TupleFormatting\TupleCommaSpacing.fs" />
3638
<Compile Include="Rules\Formatting\TupleFormatting\TupleIndentation.fs" />
@@ -80,9 +82,9 @@
8082
<Compile Include="Rules\Conventions\FunctionReimplementation\FunctionReimplementationHelper.fs" />
8183
<Compile Include="Rules\Conventions\FunctionReimplementation\ReimplementsFunction.fs" />
8284
<Compile Include="Rules\Conventions\FunctionReimplementation\CanBeReplacedWithComposition.fs" />
83-
<Compile Include="Rules\Conventions\Naming\NamingHelper.fs" />
8485
<Compile Include="Rules\Conventions\Naming\SynchronousFunctionNames.fs" />
8586
<Compile Include="Rules\Conventions\Naming\AsynchronousFunctionNames.fs" />
87+
<Compile Include="Rules\Conventions\Naming\SimpleAsyncComplementaryHelpers.fs" />
8688
<Compile Include="Rules\Conventions\Naming\InterfaceNames.fs" />
8789
<Compile Include="Rules\Conventions\Naming\ExceptionNames.fs" />
8890
<Compile Include="Rules\Conventions\Naming\TypeNames.fs" />

0 commit comments

Comments
 (0)