11module FSharpLint.Rules.DisallowShadowing
22
33open System
4-
4+ open FSharp. Compiler . Syntax
55open FSharpLint.Framework
66open FSharpLint.Framework .Suggestion
7- open FSharp.Compiler .Syntax
8- open FSharp.Compiler .Text
9- open FSharp.Compiler .CodeAnalysis
107open FSharpLint.Framework .Ast
118open 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+
13120let 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
17132let rule =
18133 { Name = " DisallowShadowing"
19134 Identifier = Identifiers.DisallowShadowing
20135 RuleConfig = { AstNodeRuleConfig.Runner = runner; Cleanup = ignore } }
21- |> AstNodeRule
136+ |> AstNodeRule
0 commit comments