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 src/FSharpLint.Core/FSharpLint.Core.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@
<Compile Include="Rules\Conventions\FunctionReimplementation\FunctionReimplementationHelper.fs" />
<Compile Include="Rules\Conventions\FunctionReimplementation\ReimplementsFunction.fs" />
<Compile Include="Rules\Conventions\FunctionReimplementation\CanBeReplacedWithComposition.fs" />
<Compile Include="Rules\Conventions\Naming\NamingHelper.fs" />
<Compile Include="Rules\Conventions\Naming\SynchronousFunctionNames.fs" />
<Compile Include="Rules\Conventions\Naming\AsynchronousFunctionNames.fs" />
<Compile Include="Rules\Conventions\Naming\NamingHelper.fs" />
<Compile Include="Rules\Conventions\Naming\InterfaceNames.fs" />
<Compile Include="Rules\Conventions\Naming\ExceptionNames.fs" />
<Compile Include="Rules\Conventions\Naming\TypeNames.fs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ open FSharpLint.Framework
open FSharpLint.Framework.Suggestion
open FSharpLint.Framework.Ast
open FSharpLint.Framework.Rules
open Helper.Naming.Asynchronous
open FSharp.Compiler.Syntax

let runner (args: AstNodeRuleParams) =
Expand All @@ -18,7 +19,8 @@ let runner (args: AstNodeRuleParams) =
}

match args.AstNode with
| AstNode.Binding (SynBinding (_, _, _, _, _, _, _, SynPat.LongIdent(funcIdent, _, _, _, (None | Some(SynAccess.Public _)), identRange), returnInfo, _, _, _, _)) ->
| AstNode.Binding (SynBinding (_, _, _, _, attributes, _, _, SynPat.LongIdent(funcIdent, _, _, _, (None | Some(SynAccess.Public _)), identRange), returnInfo, _, _, _, _))
when not <| Helper.Naming.isAttribute "Obsolete" attributes ->
let parents = args.GetParents args.NodeIndex
let hasEnclosingFunctionOrMethod =
parents
Expand All @@ -32,21 +34,21 @@ let runner (args: AstNodeRuleParams) =
Array.empty
else
match returnInfo with
| Some SynchronousFunctionNames.ReturnsAsync ->
| Some ReturnsAsync ->
match funcIdent with
| SynchronousFunctionNames.HasAsyncPrefix _ ->
| HasAsyncPrefix _ ->
Array.empty
| SynchronousFunctionNames.HasAsyncSuffix name
| SynchronousFunctionNames.HasNoAsyncPrefixOrSuffix name ->
let nameWithAsync = SynchronousFunctionNames.asyncSuffixOrPrefix + name
| HasAsyncSuffix name
| HasNoAsyncPrefixOrSuffix name ->
let nameWithAsync = asyncSuffixOrPrefix + name
emitWarning identRange nameWithAsync "Async"
| Some SynchronousFunctionNames.ReturnsTask ->
| Some ReturnsTask ->
match funcIdent with
| SynchronousFunctionNames.HasAsyncSuffix _ ->
| HasAsyncSuffix _ ->
Array.empty
| SynchronousFunctionNames.HasAsyncPrefix name
| SynchronousFunctionNames.HasNoAsyncPrefixOrSuffix name ->
let nameWithAsync = name + SynchronousFunctionNames.asyncSuffixOrPrefix
| HasAsyncPrefix name
| HasNoAsyncPrefixOrSuffix name ->
let nameWithAsync = name + asyncSuffixOrPrefix
emitWarning identRange nameWithAsync "Task"
| None ->
// TODO: get type using typed tree in args.CheckInfo
Expand Down
24 changes: 24 additions & 0 deletions src/FSharpLint.Core/Rules/Conventions/Naming/NamingHelper.fs
Original file line number Diff line number Diff line change
Expand Up @@ -464,3 +464,27 @@ let getFunctionIdents (pattern:SynPat) =
| Some ident -> Array.singleton (ident, ident.idText, None)
| None -> Array.empty
| _ -> Array.empty

module Asynchronous =
let asyncSuffixOrPrefix = "Async"

let (|HasAsyncPrefix|HasAsyncSuffix|HasNoAsyncPrefixOrSuffix|) (pattern: SynLongIdent) =
match List.tryLast pattern.LongIdent with
| Some name ->
if name.idText.StartsWith(asyncSuffixOrPrefix, StringComparison.InvariantCultureIgnoreCase) then
HasAsyncPrefix name.idText
elif name.idText.EndsWith asyncSuffixOrPrefix then
HasAsyncSuffix name.idText
else
HasNoAsyncPrefixOrSuffix name.idText
| _ -> HasNoAsyncPrefixOrSuffix String.Empty

let (|ReturnsTask|ReturnsAsync|ReturnsNonAsync|) (returnInfo: SynBindingReturnInfo) =
match returnInfo with
| SynBindingReturnInfo(SynType.LongIdent(SynLongIdent(typeIdent, _, _)), _, _, _)
| SynBindingReturnInfo(SynType.App(SynType.LongIdent(SynLongIdent(typeIdent, _, _)), _, _, _, _, _, _), _, _, _) ->
match List.tryLast typeIdent with
| Some ident when ident.idText = "Async" -> ReturnsAsync
| Some ident when ident.idText = "Task" -> ReturnsTask
| _ -> ReturnsNonAsync
| _ -> ReturnsNonAsync
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,12 @@ open FSharpLint.Framework
open FSharpLint.Framework.Suggestion
open FSharpLint.Framework.Ast
open FSharpLint.Framework.Rules
open Helper.Naming.Asynchronous
open FSharp.Compiler.Syntax
open FSharp.Compiler.Symbols

let asyncSuffixOrPrefix = "Async"

let (|HasAsyncPrefix|HasAsyncSuffix|HasNoAsyncPrefixOrSuffix|) (pattern: SynLongIdent) =
match List.tryLast pattern.LongIdent with
| Some name ->
if name.idText.StartsWith(asyncSuffixOrPrefix, StringComparison.InvariantCultureIgnoreCase) then
HasAsyncPrefix name.idText
elif name.idText.EndsWith asyncSuffixOrPrefix then
HasAsyncSuffix name.idText
else
HasNoAsyncPrefixOrSuffix name.idText
| _ -> HasNoAsyncPrefixOrSuffix String.Empty

let (|ReturnsTask|ReturnsAsync|ReturnsNonAsync|) (returnInfo: SynBindingReturnInfo) =
match returnInfo with
| SynBindingReturnInfo(SynType.LongIdent(SynLongIdent(typeIdent, _, _)), _, _, _)
| SynBindingReturnInfo(SynType.App(SynType.LongIdent(SynLongIdent(typeIdent, _, _)), _, _, _, _, _, _), _, _, _) ->
match List.tryLast typeIdent with
| Some ident when ident.idText = "Async" -> ReturnsAsync
| Some ident when ident.idText = "Task" -> ReturnsTask
| _ -> ReturnsNonAsync
| _ -> ReturnsNonAsync

let runner (args: AstNodeRuleParams) =
let emitWarning range (newFunctionName: string) =
Array.singleton
Expand All @@ -42,7 +22,8 @@ let runner (args: AstNodeRuleParams) =
}

match args.AstNode with
| AstNode.Binding (SynBinding (_, _, _, _, _, _, _, SynPat.LongIdent(funcIdent, _, _, _, _, identRange), returnInfo, _, _, _, _)) ->
| AstNode.Binding (SynBinding (_, _, _, _, attributes, _, _, SynPat.LongIdent(funcIdent, _, _, _, _, identRange), returnInfo, _, _, _, _))
when not <| Helper.Naming.isAttribute "Obsolete" attributes ->
match returnInfo with
| Some ReturnsNonAsync ->
match funcIdent with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,41 @@ type Foo() =
"""

Assert.IsTrue this.NoErrorsExist

[<Test>]
member this.``Functions returning Async or Task with [<Obsolete>] attribute should not give violations``() =
this.Parse """
module Foo =
[<Obsolete>]
let Foo(): Async<int> =
async { return 1 }

[<Obsolete>]
let Bar(): Task<int> =
null

[<Obsolete>]
let Baz(): Task =
null
"""

Assert.IsTrue this.NoErrorsExist

[<Test>]
member this.``Methods returning Async or Task with [<Obsolete>] attribute should not give violations``() =
this.Parse """
type Foo() =
[<Obsolete>]
member this.Foo(): Async<int> =
async { return 1 }

[<Obsolete>]
member this.Bar(): Task<int> =
null

[<Obsolete>]
member this.Baz(): Task =
null
"""

Assert.IsTrue this.NoErrorsExist
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,41 @@ type Foo() =
"""

Assert.IsTrue this.NoErrorsExist

[<Test>]
member this.``Non-asynchronous functions marked with [<Obsolete>] attribute should not give violations``() =
this.Parse """
module Foo =
[<Obsolete>]
let AsyncBar(): int =
1

[<Obsolete>]
let BarAsync(): int =
1

[<Obsolete>]
let private AsyncBar(): int =
1
"""

Assert.IsTrue this.NoErrorsExist

[<Test>]
member this.``Non-asynchronous methods marked with [<Obsolete>] attribute should not give violations``() =
this.Parse """
type Foo() =
[<Obsolete>]
member this.AsyncBar(): int =
1

[<Obsolete>]
member this.BarAsync(): int =
1

[<Obsolete>]
member private this.AsyncBar(): int =
1
"""

Assert.IsTrue this.NoErrorsExist
Loading