@@ -20,8 +20,10 @@ open Symbols
2020module internal UnusedOpens =
2121/// Represents single open statement.
2222type private OpenStatement =
23- { /// Open namespace or module effective names.
24- Names: Set < string >
23+ { /// Full namespace or module identifier as it's presented in source code.
24+ LiteralIdent: string
25+ /// All possible namespace or module identifiers, including the literal one.
26+ AllPossibleIdents: Set < string >
2527/// Range of open statement itself.
2628 Range: range
2729/// Enclosing module or namespace range (that is, the scope on in which this open statement is visible).
@@ -31,9 +33,11 @@ module internal UnusedOpens =
3133[ for declin declsdo
3234match declwith
3335| SynModuleDecl.Open( LongIdentWithDots.LongIdentWithDots( id= longId), range) ->
36+ let literalIdent = longId|> List.map( fun l -> l.idText) |> String.concat" ."
3437yield
35- { Names=
36- set[ yield ( longId|> List.map( fun l -> l.idText) |> String.concat" ." )
38+ { LiteralIdent= literalIdent
39+ AllPossibleIdents=
40+ set[ yield literalIdent
3741// `open N.M` can open N.M module from parent module as well, if it's non empty
3842if not ( List.isEmpty parent) then
3943yield ( parent@ longId|> List.map( fun l -> l.idText) |> String.concat" ." ) ]
@@ -127,33 +131,53 @@ module internal UnusedOpens =
127131
128132return
129133[ for namein fullNamesdo
130- yield getPartNamespace symbolUse name
134+ let partNamespace = getPartNamespace symbolUse name
135+ yield partNamespace
131136yield ! entityNamespace declaringEntity]
132137} |> Option.toList|> List.concat|> List.choose id
133138
134139let namespacesInUse : NamespaceUse list =
135- symbolUses
136- |> Array.filter( fun ( s : FSharpSymbolUse ) -> not s.IsFromDefinition)
140+ let importantSymbolUses =
141+ symbolUses
142+ |> Array.filter( fun ( symbolUse : FSharpSymbolUse ) ->
143+ not symbolUse.IsFromDefinition&&
144+ match symbolUse.Symbolwith
145+ | :? FSharpEntityas e-> not e.IsNamespace
146+ | _ -> true
147+ )
148+
149+ importantSymbolUses
137150|> Array.toList
138- |> List.collect( fun x ->
139- getPossibleNamespaces x
140- |> List.distinct
141- |> List.map( fun ns -> { Ident= ns; Location= x.RangeAlternate}))
151+ |> List.collect( fun su ->
152+ let lineStr = sourceText.Lines.[ Line.toZ su.RangeAlternate.StartLine]. ToString()
153+ let partialName = QuickParse.GetPartialLongNameEx( lineStr, su.RangeAlternate.EndColumn- 1 )
154+ let qualifier = partialName.QualifyingIdents|> String.concat" ."
155+ getPossibleNamespaces su
156+ |> List.distinct
157+ |> List.choose( fun ns ->
158+ if qualifier= " " then Some ns
159+ elif ns= qualifierthen None
160+ elif ns.EndsWith qualifierthen Some ns.[..( ns.Length- qualifier.Length) - 2 ]
161+ else None)
162+ |> List.map( fun ns ->
163+ { Ident= ns
164+ Location= su.RangeAlternate}))
142165
143166let filter list : OpenStatement list =
144167let rec filterInner acc ( list : OpenStatement list ) ( seenOpenStatements : OpenStatement list ) =
145168
146169let notUsed ( os : OpenStatement ) =
147- let notUsedAnywhere = not ( namespacesInUse|> List.exists( fun nsu -> rangeContainsRange os.ModuleRange nsu.Location&& os.Names|> Set.contains nsu.Ident))
170+ let notUsedAnywhere =
171+ not ( namespacesInUse|> List.exists( fun nsu ->
172+ rangeContainsRange os.ModuleRange nsu.Location&& os.AllPossibleIdents|> Set.contains nsu.Ident))
148173if notUsedAnywherethen true
149174else
150175let alreadySeen =
151176 seenOpenStatements
152177|> List.exists( fun seenNs ->
153178// if such open statement has already been marked as used in this or outer module, we skip it
154179// (that is, do not mark as used so far)
155- ( seenNs.ModuleRange= os.ModuleRange|| rangeContainsRange seenNs.ModuleRange os.ModuleRange) &&
156- not ( os.Names|> Set.intersect seenNs.Names|> Set.isEmpty))
180+ rangeContainsRange seenNs.ModuleRange os.ModuleRange&& os.LiteralIdent= seenNs.LiteralIdent)
157181 alreadySeen
158182
159183match listwith