Skip to content

Commit f349def

Browse files
committed
DisallowShadowing: implemented rule
Implemented DisallowShadowing rule.
1 parent fd82f14 commit f349def

File tree

1 file changed

+122
-7
lines changed

1 file changed

+122
-7
lines changed
Lines changed: 122 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,136 @@
11
module FSharpLint.Rules.DisallowShadowing
22

33
open System
4-
4+
open FSharp.Compiler.Syntax
55
open FSharpLint.Framework
66
open FSharpLint.Framework.Suggestion
7-
open FSharp.Compiler.Syntax
8-
open FSharp.Compiler.Text
9-
open FSharp.Compiler.CodeAnalysis
107
open FSharpLint.Framework.Ast
118
open FSharpLint.Framework.Rules
129

10+
let rec private extractIdentifiersFromSimplePats (simplePats: SynSimplePats) : List<Ident> =
11+
let rec extractIdentifier (pattern: SynSimplePat) =
12+
match pattern with
13+
| SynSimplePat.Id(ident, _, _, _, _, _) ->
14+
ident
15+
| SynSimplePat.Attrib(pat, _, _)
16+
| SynSimplePat.Typed(pat, _, _) ->
17+
extractIdentifier pat
18+
19+
match simplePats with
20+
| SynSimplePats.SimplePats(patterns, _) ->
21+
patterns |> List.map extractIdentifier
22+
| SynSimplePats.Typed(pats, _, _) ->
23+
extractIdentifiersFromSimplePats pats
24+
25+
26+
let private checkIdentifier (args: AstNodeRuleParams) (identifier: Ident) : array<WarningDetails> =
27+
let name = identifier.idText
28+
match args.CheckInfo with
29+
| Some checkResults ->
30+
let allUsages = checkResults.GetAllUsesOfAllSymbolsInFile()
31+
let definitionsWithSameName =
32+
allUsages
33+
|> Seq.filter (fun usage -> usage.IsFromDefinition && usage.Symbol.DisplayName = name)
34+
35+
let definitionsBeforeCurrent =
36+
definitionsWithSameName
37+
|> Seq.filter
38+
(fun usage ->
39+
(usage.Range.StartLine, usage.Range.StartColumn)
40+
< (identifier.idRange.StartLine, identifier.idRange.StartColumn) )
41+
|> Seq.toArray
42+
43+
let rangeIncludedsDefinitions range =
44+
definitionsBeforeCurrent
45+
|> Array.exists (fun usage -> ExpressionUtilities.rangeContainsOtherRange range usage.Range)
46+
47+
let processBinding binding =
48+
match binding with
49+
| SynBinding(_, _, _, _, _, _, _, _, _, _, range, _, _) ->
50+
rangeIncludedsDefinitions range
51+
52+
let processArgs (args: SynSimplePats) =
53+
args
54+
|> extractIdentifiersFromSimplePats
55+
|> List.exists (fun ident -> rangeIncludedsDefinitions ident.idRange)
56+
57+
let rec processExpression (expression: SynExpr) =
58+
match expression with
59+
| SynExpr.LetOrUse(_, _, bindings, _, _, _) ->
60+
bindings |> List.exists processBinding
61+
| SynExpr.Sequential(_, _, expr1, expr2, _) ->
62+
processExpression expr1 || processExpression expr2
63+
| SynExpr.Lambda(_, _, args, body, _, _, _) ->
64+
processExpression body || processArgs args
65+
| _ -> false
66+
67+
let rec processPattern (pattern: SynPat) =
68+
match pattern with
69+
| SynPat.Named(SynIdent(ident, _), _, _, _) -> rangeIncludedsDefinitions ident.idRange
70+
| SynPat.Ands(pats, _) -> pats |> List.exists processPattern
71+
| SynPat.Or(lhs, rhs, _, _) -> processPattern lhs || processPattern rhs
72+
| SynPat.ArrayOrList(_, pats, _) -> pats |> List.exists processPattern
73+
| SynPat.As(_lhs, rhs, _) -> processPattern rhs
74+
| SynPat.OptionalVal(ident, _) -> rangeIncludedsDefinitions ident.idRange
75+
| SynPat.Paren(pat, _) -> processPattern pat
76+
| SynPat.Record(fieldPats, _) -> fieldPats |> List.exists (fun (_, _, pat) -> processPattern pat)
77+
| SynPat.Tuple(_, pats, _) -> pats |> List.exists processPattern
78+
| SynPat.Typed(pat, _, _) -> processPattern pat
79+
| _ -> false
80+
81+
let processModuleDeclaration (moduleDecl: SynModuleDecl) =
82+
match moduleDecl with
83+
| SynModuleDecl.Let(_, bindings, _) ->
84+
bindings
85+
|> List.exists processBinding
86+
| _ -> false
87+
88+
let processAstNode (node: AstNode) =
89+
match node with
90+
| ModuleOrNamespace(SynModuleOrNamespace(_, _, _, declarations, _, _, _, _, _)) ->
91+
declarations |> List.exists processModuleDeclaration
92+
| Expression(expression) ->
93+
processExpression (ExpressionUtilities.removeParens expression)
94+
| Lambda(lambda, _) ->
95+
processExpression lambda.Body
96+
|| (lambda.Arguments |> List.exists processArgs)
97+
| Match(SynMatchClause(pattern, _, _, _, _, _)) ->
98+
processPattern pattern
99+
| MemberDefinition(SynMemberDefn.Member(memberDefn, _)) ->
100+
processBinding memberDefn
101+
| TypeDefinition(SynTypeDefn(_, _, _, Some(SynMemberDefn.ImplicitCtor(_, _, ctorArgs, _, _, _)), _, _)) ->
102+
processArgs ctorArgs
103+
| _ -> false
104+
105+
let parents = args.GetParents args.NodeIndex
106+
let isShadowing =
107+
parents |> List.exists processAstNode
108+
109+
if isShadowing then
110+
Array.singleton {
111+
Range = identifier.idRange
112+
Message = "RulesDisallowShadowing"
113+
SuggestedFix = None
114+
TypeChecks = List.Empty }
115+
else
116+
Array.empty
117+
118+
| None -> Array.empty
119+
13120
let runner (args: AstNodeRuleParams) =
14-
15-
failwith "Not yet implemented"
121+
match args.AstNode with
122+
| Pattern(SynPat.Named(SynIdent(ident, _), _, _, _)) ->
123+
checkIdentifier args ident
124+
| Lambda(lambda, _) ->
125+
lambda.Arguments
126+
|> List.collect extractIdentifiersFromSimplePats
127+
|> List.toArray
128+
|> Array.collect (fun each -> checkIdentifier args each)
129+
| _ ->
130+
Array.empty
16131

17132
let rule =
18133
{ Name = "DisallowShadowing"
19134
Identifier = Identifiers.DisallowShadowing
20135
RuleConfig = { AstNodeRuleConfig.Runner = runner; Cleanup = ignore } }
21-
|> AstNodeRule
136+
|> AstNodeRule

0 commit comments

Comments
 (0)