|
| 1 | +module FSharpLint.Rules.FavourStaticEmptyFields |
| 2 | + |
| 3 | +open FSharpLint.Framework |
| 4 | +open FSharpLint.Framework.Suggestion |
| 5 | +open FSharp.Compiler.Syntax |
| 6 | +open FSharp.Compiler.Text |
| 7 | +open FSharpLint.Framework.Ast |
| 8 | +open FSharpLint.Framework.Rules |
| 9 | + |
| 10 | +type private EmptyLiteralType = |
| 11 | + | EmptyStringLiteral |
| 12 | + | EmptyListLiteral |
| 13 | + | EmptyArrayLiteral |
| 14 | + |
| 15 | +let private getEmptyLiteralType (str: string): EmptyLiteralType = |
| 16 | + if str.Length = 0 then |
| 17 | + EmptyLiteralType.EmptyStringLiteral |
| 18 | + elif str = "[]" then |
| 19 | + EmptyLiteralType.EmptyListLiteral |
| 20 | + else |
| 21 | + EmptyLiteralType.EmptyArrayLiteral |
| 22 | + |
| 23 | +let private getStaticEmptyErrorMessage (range:FSharp.Compiler.Text.Range) (emptyLiteralType: EmptyLiteralType) = |
| 24 | + let errorMessageKey = |
| 25 | + match emptyLiteralType with |
| 26 | + | EmptyStringLiteral -> "RulesFavourStaticEmptyFieldsForString" |
| 27 | + | EmptyListLiteral -> "RulesFavourStaticEmptyFieldsForList" |
| 28 | + | EmptyArrayLiteral -> "RulesFavourStaticEmptyFieldsForArray" |
| 29 | + |
| 30 | + let formatError errorName = |
| 31 | + Resources.GetString errorName |
| 32 | + |
| 33 | + errorMessageKey |> formatError |> Array.singleton |
| 34 | + |
| 35 | +let private generateError (range:FSharp.Compiler.Text.Range) (idText:string) (emptyLiteralType: EmptyLiteralType) = |
| 36 | + if idText = "" || idText = "[]" then |
| 37 | + getStaticEmptyErrorMessage range emptyLiteralType |
| 38 | + |> Array.map (fun message -> |
| 39 | + { Range = range |
| 40 | + Message = message |
| 41 | + SuggestedFix = None |
| 42 | + TypeChecks = List.Empty }) |
| 43 | + else |
| 44 | + Array.empty |
| 45 | + |
| 46 | +let private runner (args: AstNodeRuleParams) = |
| 47 | + match args.AstNode with |
| 48 | + | AstNode.Expression expr -> |
| 49 | + match expr with |
| 50 | + | SynExpr.App (_, _, SynExpr.Ident failwithId, expression, range) -> |
| 51 | + match expression with |
| 52 | + | SynExpr.Const (SynConst.String (id, _, _), _) when id = "" -> |
| 53 | + (range, id, None, getEmptyLiteralType id) |
| 54 | + |> Array.singleton |
| 55 | + |> Array.collect (fun (range, idText, typeCheck, emptyLiteralType) -> |
| 56 | + let suggestions = generateError range idText emptyLiteralType |
| 57 | + suggestions |> Array.map (fun suggestion -> { suggestion with TypeChecks = Option.toList typeCheck })) |
| 58 | + | _ -> Array.empty |
| 59 | + | SynExpr.ArrayOrList (_, id, range) when "[]" = sprintf "%A" id || "[||]" = sprintf "%A" id -> |
| 60 | + (range, sprintf "%A" id, None, getEmptyLiteralType (sprintf "%A" id)) |
| 61 | + |> Array.singleton |
| 62 | + |> Array.collect (fun (range, idText, typeCheck, emptyLiteralType) -> |
| 63 | + let suggestions = generateError range idText emptyLiteralType |
| 64 | + suggestions |> Array.map (fun suggestion -> { suggestion with TypeChecks = Option.toList typeCheck })) |
| 65 | + | SynExpr.Const (SynConst.String (id, _, range), _) when id = "" -> |
| 66 | + (range, id, None, getEmptyLiteralType id) |
| 67 | + |> Array.singleton |
| 68 | + |> Array.collect (fun (range, idText, typeCheck, emptyLiteralType) -> |
| 69 | + let suggestions = generateError range idText emptyLiteralType |
| 70 | + suggestions |> Array.map (fun suggestion -> { suggestion with TypeChecks = Option.toList typeCheck })) |
| 71 | + | _ -> Array.empty |
| 72 | + | Binding(SynBinding(_, _, _, _, _, _, _, _, _, expression, _, _)) -> |
| 73 | + match expression with |
| 74 | + | SynExpr.Const (SynConst.String (id, _, range), _) when id = "" -> |
| 75 | + (range, id, None, getEmptyLiteralType id) |
| 76 | + |> Array.singleton |
| 77 | + |> Array.collect (fun (range, idText, typeCheck, emptyLiteralType) -> |
| 78 | + let suggestions = generateError range idText emptyLiteralType |
| 79 | + suggestions |> Array.map (fun suggestion -> { suggestion with TypeChecks = Option.toList typeCheck })) |
| 80 | + | SynExpr.ArrayOrList (_, id, range) when "[]" = sprintf "%A" id || "[||]" = sprintf "%A" id -> |
| 81 | + (range, sprintf "%A" id, None, getEmptyLiteralType (sprintf "%A" id)) |
| 82 | + |> Array.singleton |
| 83 | + |> Array.collect (fun (range, idText, typeCheck, emptyLiteralType) -> |
| 84 | + let suggestions = generateError range idText emptyLiteralType |
| 85 | + suggestions |> Array.map (fun suggestion -> { suggestion with TypeChecks = Option.toList typeCheck })) |
| 86 | + | _ -> Array.empty |
| 87 | + | _ -> Array.empty |
| 88 | + |
| 89 | + |
| 90 | +let rule = |
| 91 | + { Name = "FavourStaticEmptyFields" |
| 92 | + Identifier = Identifiers.FavourStaticEmptyFields |
| 93 | + RuleConfig = |
| 94 | + { AstNodeRuleConfig.Runner = runner |
| 95 | + Cleanup = ignore } } |
| 96 | + |> AstNodeRule |
0 commit comments