forked from fsprojects/FSharpLint
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes fsprojects#517
- Loading branch information
Showing
9 changed files
with
146 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
--- | ||
title: FL0078 | ||
category: how-to | ||
hide_menu: true | ||
--- | ||
|
||
# FriendlyAsyncOverload (FL0078) | ||
|
||
*Introduced in `0.20.3`* | ||
|
||
## Cause | ||
|
||
Rule to suggest adding C#-friendly async overloads. | ||
|
||
## Rationale | ||
|
||
Exposing public F#-async APIs in a C#-friendly manner for better C# interoperability. | ||
|
||
## How To Fix | ||
|
||
Add an `Async` version of the API that returns a `Task<'T>` | ||
|
||
## Rule Settings | ||
|
||
{ | ||
"friendlyAsyncOverload": { | ||
"enabled": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
src/FSharpLint.Core/Rules/Conventions/FriendlyAsyncOverload.fs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
module FSharpLint.Rules.FriendlyAsyncOverload | ||
|
||
open FSharpLint.Framework | ||
open FSharpLint.Framework.Suggestion | ||
open FSharp.Compiler.Syntax | ||
open FSharp.Compiler.Text | ||
open FSharpLint.Framework.Ast | ||
open FSharpLint.Framework.Rules | ||
open System | ||
|
||
type NodeDetails = { Ident: string; Range: range } | ||
|
||
let rec private getIdentFromSynPat = | ||
function | ||
| SynPat.LongIdent (longDotId = longDotId) -> | ||
longDotId | ||
|> ExpressionUtilities.longIdentWithDotsToString | ||
|> Some | ||
| SynPat.Typed (pat, _, _) -> getIdentFromSynPat pat | ||
| _ -> None | ||
|
||
let runner (args: AstNodeRuleParams) = | ||
let hasAsync (syntaxArray: AbstractSyntaxArray.Node []) nodeIndex fnIdent = | ||
let rec hasAsync index = | ||
if index >= syntaxArray.Length then | ||
None | ||
else | ||
let node = syntaxArray.[index].Actual | ||
match node with | ||
| AstNode.Binding (SynBinding (_, _, _, _, _attributes, _, _, pattern, _, _, range, _)) -> | ||
match getIdentFromSynPat pattern with | ||
| Some ident when ident = fnIdent + "Async" -> | ||
Some( | ||
{ Ident = fnIdent | ||
Range = range } | ||
) | ||
| _ -> hasAsync (index + 1) | ||
| _ -> hasAsync (index + 1) | ||
|
||
hasAsync nodeIndex | ||
|
||
match args.AstNode with | ||
| AstNode.Binding (SynBinding (_, _, _, _, _, _, _, pattern, synInfo, _, range, _)) -> | ||
match synInfo with | ||
| Some (SynBindingReturnInfo ((SynType.App(SynType.LongIdent(LongIdentWithDots(ident,_)),_,_,_,_,_,_)),_,_)) -> | ||
match ident with | ||
| head::_ when head.idText = "Async" -> | ||
let idents = getIdentFromSynPat pattern | ||
match idents with | ||
| Some ident when ident.EndsWith("Async") = false -> | ||
match hasAsync args.SyntaxArray args.NodeIndex ident with | ||
| Some nod -> Array.empty | ||
| None -> | ||
{ Range = range | ||
Message = String.Format(Resources.GetString "RulesFriendlyAsyncOverload", ident) | ||
SuggestedFix = None | ||
TypeChecks = [] } | ||
|> Array.singleton | ||
| _ -> Array.empty | ||
| _ -> Array.empty | ||
| _ -> Array.empty | ||
| _ -> Array.empty | ||
|
||
|
||
let rule = | ||
{ Name = "FriendlyAsyncOverload" | ||
Identifier = Identifiers.FriendlyAsyncOverload | ||
RuleConfig = | ||
{ AstNodeRuleConfig.Runner = runner | ||
Cleanup = ignore } } | ||
|> AstNodeRule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
tests/FSharpLint.Core.Tests/Rules/Conventions/FriendlyAsyncOverload.fs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
module FSharpLint.Core.Tests.Rules.Conventions.FriendlyAsyncOverload | ||
|
||
open NUnit.Framework | ||
open FSharpLint.Rules | ||
|
||
[<TestFixture>] | ||
type TestConventionsFriendlyAsyncOverload() = | ||
inherit TestAstNodeRuleBase.TestAstNodeRuleBase(FriendlyAsyncOverload.rule) | ||
|
||
[<Test>] | ||
member this.``async function must suggest friendly implementation``() = | ||
this.Parse(""" | ||
module Foo = | ||
let Bar(): Async<unit> = | ||
Async.Sleep 5""") | ||
|
||
Assert.IsTrue(this.ErrorExistsAt(3, 8)) | ||
|
||
[<Test>] | ||
member this.``async function with friendly implementation must not have errors``() = | ||
this.Parse(""" | ||
module Foo = | ||
let Bar(): Async<unit> = | ||
Async.Sleep 5 | ||
let BarAsync(): Task<unit> = | ||
Bar() |> Async.StartAsTask""") | ||
|
||
this.AssertNoWarnings() | ||
|
||
[<Test>] | ||
member this.``non async function must not create warnings``() = | ||
this.Parse(""" | ||
module Foo = | ||
let Bar() = | ||
()""") | ||
|
||
this.AssertNoWarnings() |