-
Notifications
You must be signed in to change notification settings - Fork 73
/
Copy pathRedundantNewKeyword.fs
59 lines (50 loc) · 2.56 KB
/
RedundantNewKeyword.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
module FSharpLint.Rules.RedundantNewKeyword
open FSharpLint.Framework
open FSharpLint.Framework.Suggestion
open FSharp.Compiler.Symbols
open FSharp.Compiler.Syntax
open FSharp.Compiler.CodeAnalysis
open FSharpLint.Framework.Ast
open FSharpLint.Framework.Rules
let private implementsIDisposable (fsharpType:FSharpType) =
if fsharpType.HasTypeDefinition then
match fsharpType.TypeDefinition.TryFullName with
| Some(fullName) -> fullName = typeof<System.IDisposable>.FullName
| None -> false
else
false
let private doesNotImplementIDisposable (checkFile:FSharpCheckFileResults) (ident:LongIdentWithDots) = fun () ->
let names = ident.Lid |> List.map (fun x -> x.idText)
let symbol = checkFile.GetSymbolUseAtLocation(ident.Range.StartLine, ident.Range.EndColumn, "", names)
match symbol with
| Some(symbol) when (symbol.Symbol :? FSharpMemberOrFunctionOrValue) ->
let ctor = symbol.Symbol :?> FSharpMemberOrFunctionOrValue
ctor.DeclaringEntity
|> Option.exists (fun ctorForType ->
Seq.forall (implementsIDisposable >> not) ctorForType.AllInterfaces)
| Some symbol when (symbol.Symbol :? FSharpEntity) ->
let ctor = symbol.Symbol :?> FSharpEntity
Seq.forall (implementsIDisposable >> not) ctor.AllInterfaces
| Some _ -> false
| None -> true
let private generateFix (text:string) range = lazy(
ExpressionUtilities.tryFindTextOfRange range text
|> Option.map (fun fromText ->
let withoutLeadingWhitespace = fromText.TrimStart()
let newKeywordRemoved = withoutLeadingWhitespace.Substring(3).TrimStart()
{ FromText = fromText; FromRange = range; ToText = newKeywordRemoved }))
let runner args =
match args.AstNode, args.CheckInfo with
| AstNode.Expression (SynExpr.Upcast (SynExpr.New (_, SynType.LongIdent (identifier), _, range), _, _)), Some checkInfo
| AstNode.Expression (SynExpr.New(_, SynType.LongIdent(identifier), _, range)), Some checkInfo
| AstNode.Expression (SynExpr.New(_, SynType.App(SynType.LongIdent(identifier), _, _, _, _, _, _), _, range)), Some checkInfo ->
{ Range = range
Message = Resources.GetString("RulesRedundantNewKeyword")
SuggestedFix = Some (generateFix args.FileContent range)
TypeChecks = [ doesNotImplementIDisposable checkInfo identifier ] } |> Array.singleton
| _ -> Array.empty
let rule =
{ Name = "RedundantNewKeyword"
Identifier = Identifiers.RedundantNewKeyword
RuleConfig = { AstNodeRuleConfig.Runner = runner; Cleanup = ignore } }
|> AstNodeRule