@@ -13,53 +13,57 @@ module UnusedOpens =
1313{ Entity: FSharpEntity
1414 IsNestedAutoOpen: bool }
1515
16+ member this.ChildSymbols =
17+ seq { for entin this.Entity.NestedEntitiesdo
18+ yield ent:> FSharpSymbol
19+
20+ if ent.IsFSharpRecordthen
21+ for rfin ent.FSharpFieldsdo
22+ yield upcast rf
23+
24+ if ent.IsFSharpUnion&& not ( Symbol.hasAttribute< RequireQualifiedAccessAttribute> ent.Attributes) then
25+ for unionCasein ent.UnionCasesdo
26+ yield upcast unionCase
27+
28+ for fvin this.Entity.MembersFunctionsAndValuesdo
29+ yield upcast fv
30+
31+ for apCasein this.Entity.ActivePatternCasesdo
32+ yield upcast apCase
33+ } |> Seq.cache
34+
35+ type ModuleGroup =
36+ { Modules: Module list }
37+
38+ static member Create ( modul : FSharpEntity ) =
39+ let rec getModuleAndItsAutoOpens ( isNestedAutoOpen : bool ) ( modul : FSharpEntity ) =
40+ [ yield { Entity= modul; IsNestedAutoOpen= isNestedAutoOpen}
41+ for entin modul.NestedEntitiesdo
42+ if ent.IsFSharpModule&& Symbol.hasAttribute< AutoOpenAttribute> ent.Attributesthen
43+ yield ! getModuleAndItsAutoOpenstrue ent]
44+ { Modules= getModuleAndItsAutoOpensfalse modul}
45+
1646/// Represents single open statement.
1747type OpenStatement =
18- { /// All modules which this open declaration effectively opens, includingall auto open ones, recursively .
19- Modules: Module list
48+ { /// All modules which this open declaration effectively opens,_not_ including auto open ones.
49+ Modules: ModuleGroup list
2050/// Range of open statement itself.
2151 Range: range
2252/// Scope on which this open declaration is applied.
23- AppliedScope: range
24- /// If it's prefixed with the special "global" namespace.
25- IsGlobal: bool }
26-
27- member this.AllChildSymbols =
28- seq { for modulin this.Modules|> List.map( fun x -> x.Entity) do
29- for entin modul.NestedEntitiesdo
30- yield ent:> FSharpSymbol
31-
32- if ent.IsFSharpRecordthen
33- for rfin ent.FSharpFieldsdo
34- yield upcast rf
35-
36- if ent.IsFSharpUnion&& not ( Symbol.hasAttribute< RequireQualifiedAccessAttribute> ent.Attributes) then
37- for unionCasein ent.UnionCasesdo
38- yield upcast unionCase
39-
40- for fvin modul.MembersFunctionsAndValuesdo
41- yield upcast fv
42-
43- for apCasein modul.ActivePatternCasesdo
44- yield upcast apCase
45- } |> Seq.cache
46-
47- let rec getModuleAndItsAutoOpens ( isNestedAutoOpen : bool ) ( modul : FSharpEntity ) =
48- [ yield { Entity= modul; IsNestedAutoOpen= isNestedAutoOpen}
49- for entin modul.NestedEntitiesdo
50- if ent.IsFSharpModule&& Symbol.hasAttribute< AutoOpenAttribute> ent.Attributesthen
51- yield ! getModuleAndItsAutoOpenstrue ent]
53+ AppliedScope: range }
5254
5355let getOpenStatements ( openDeclarations : FSharpOpenDeclaration list ) : OpenStatement list =
5456 openDeclarations
5557|> List.filter( fun x -> not x.IsOwnNamespace)
5658|> List.choose( fun openDecl ->
5759match openDecl.LongId, openDecl.Rangewith
5860| firstId:: _, Some range->
59- Some{ Modules= openDecl.Modules|> List.collect( getModuleAndItsAutoOpensfalse )
60- Range= range
61- AppliedScope= openDecl.AppliedScope
62- IsGlobal= firstId.idText= MangledGlobalName}
61+ if firstId.idText= MangledGlobalNamethen
62+ None
63+ else
64+ Some{ Modules= openDecl.Modules|> List.map ModuleGroup.Create
65+ Range= range
66+ AppliedScope= openDecl.AppliedScope}
6367| _ -> None)
6468
6569let filterSymbolUses ( getSourceLineStr : int -> string ) ( symbolUses : FSharpSymbolUse []) : FSharpSymbolUse [] =
@@ -76,44 +80,45 @@ module UnusedOpens =
7680// it's `open System` which really brings it into scope.
7781 partialName.QualifyingIdents= [])
7882
83+ type UsedModule =
84+ { Module: FSharpEntity
85+ AppliedScope: range }
86+
7987let getUnusedOpens ( checkFileResults : FSharpCheckFileResults , getSourceLineStr : int -> string ) : Async < range list > =
8088
8189let filterOpenStatements ( openStatements : OpenStatement list ) ( symbolUses : FSharpSymbolUse []) : OpenStatement list =
82- let rec filterInner acc ( openStatements : OpenStatement list ) ( seenOpenStatements : OpenStatement list ) =
83-
84- let isUsed ( openStatement : OpenStatement ) =
85- if openStatement.IsGlobalthen true
86- else
87- let usedSomewhere =
88- symbolUses
89- |> Array.exists( fun symbolUse ->
90- let inScope = rangeContainsRange openStatement.AppliedScope symbolUse.RangeAlternate
91- if not inScopethen false
92- else
93- openStatement.AllChildSymbols
94- |> Seq.exists( fun x -> x.IsEffectivelySameAs symbolUse.Symbol))
90+
91+ let rec filterInner acc ( openStatements : OpenStatement list ) ( usedModules : UsedModule list ) =
92+
93+ let getUsedModules ( openStatement : OpenStatement ) =
94+ let notAlreadyUsedModuleGroups =
95+ openStatement.Modules
96+ |> List.filter( fun x ->
97+ not ( usedModules
98+ |> List.exists( fun used ->
99+ rangeContainsRange used.AppliedScope openStatement.AppliedScope&&
100+ x.Modules|> List.exists( fun x -> not x.IsNestedAutoOpen&& used.Module.IsEffectivelySameAs x.Entity))))
95101
96- if not usedSomewherethen false
97- else
98- let alreadySeen =
99- seenOpenStatements
100- |> List.exists( fun seenNs ->
101- // if such open statement has already been marked as used in this or outer module, we skip it
102- // (that is, do not mark as used so far)
103- rangeContainsRange seenNs.AppliedScope openStatement.AppliedScope&&
104- openStatement.Modules
105- |> List.exists( fun x ->
106- // do not check if any of auto open nested modules has already been seen,
107- // current open statement should be seen itself or as an auto open module of its outer module.
108- not x.IsNestedAutoOpen&&
109- seenNs.Modules|> List.exists( fun s -> s.Entity.IsEffectivelySameAs x.Entity)))
110- not alreadySeen
111-
102+ match notAlreadyUsedModuleGroupswith
103+ | [] -> []
104+ | _ ->
105+ let symbolUsesInScope = symbolUses|> Array.filter( fun symbolUse -> rangeContainsRange openStatement.AppliedScope symbolUse.RangeAlternate)
106+ notAlreadyUsedModuleGroups
107+ |> List.filter( fun modulGroup ->
108+ modulGroup.Modules
109+ |> List.exists( fun modul ->
110+ symbolUsesInScope
111+ |> Array.exists( fun symbolUse ->
112+ modul.ChildSymbols
113+ |> Seq.exists( fun x -> x.IsEffectivelySameAs symbolUse.Symbol))))
114+ |> List.collect( fun mg ->
115+ mg.Modules|> List.map( fun x -> { Module= x.Entity; AppliedScope= openStatement.AppliedScope}))
116+
112117match openStatementswith
113- | os:: xswhen not ( isUsed os) ->
114- filterInner( os:: acc) xs( os:: seenOpenStatements)
115118| os:: xs->
116- filterInner acc xs( os:: seenOpenStatements)
119+ match getUsedModules oswith
120+ | [] -> filterInner( os:: acc) xs usedModules
121+ | um-> filterInner acc xs( um@ usedModules)
117122| [] -> List.rev acc
118123
119124 filterInner[] openStatements[]