33namespace Microsoft.FSharp.Compiler.SourceCodeServices
44
55open Microsoft.FSharp .Compiler
6- open Microsoft.FSharp .Compiler .NameResolution
76open Microsoft.FSharp .Compiler .Ast
87open Microsoft.FSharp .Compiler .Range
98
109module UnusedOpens =
1110open Microsoft.FSharp .Compiler .PrettyNaming
12- open Microsoft.FSharp .Compiler .AbstractIL .Internal .Library
1311
1412/// Represents single open statement.
1513type OpenStatement =
@@ -35,7 +33,7 @@ module UnusedOpens =
3533|> List.collect( fun ( modul , fullName ) ->
3634[ yield fullName
3735if modul.HasFSharpModuleSuffixthen
38- yield fullName.[.. fullName.Length- 7 ] // "Module" length plus zeroindexign correction
36+ yield fullName.[.. fullName.Length- 7 ] // "Module" length plus zeroindex correction
3937])
4038|> Set.ofList
4139 Modules= modules
@@ -96,7 +94,7 @@ module UnusedOpens =
9694if lengthDiff<= 0 || lengthDiff> fullName.Length- 1 then None
9795else Some fullName.[ 0 .. lengthDiff]
9896
99- let getPossibleNamespaces ( getSourceLineStr : int -> string ) ( symbolUse : FSharpSymbolUse ) : string list =
97+ let getPossibleNamespaces ( getSourceLineStr : int -> string ) ( symbolUse : FSharpSymbolUse ) : Set < string > =
10098let isQualified = symbolIsFullyQualified getSourceLineStr symbolUse
10199
102100( match symbolUsewith
@@ -118,144 +116,34 @@ module UnusedOpens =
118116 Some([ p.FullName], Some p.Type.TypeDefinition)
119117| _ -> None)
120118|> Option.map( fun ( fullNames , declaringEntity ) ->
121- [ for namein fullNamesdo
122- let partNamespace = getPartNamespace symbolUse name
123- yield partNamespace
124- yield ! entityNamespace declaringEntity])
125- |> Option.toList
126- |> List.concat
127- |> List.choose id
119+ [| for namein fullNamesdo
120+ let partNamespace = getPartNamespace symbolUse name
121+ yield partNamespace
122+ yield ! entityNamespace declaringEntity|])
123+ |> Option.toArray
124+ |> Array.concat
125+ |> Array.choose id
126+ |> set
128127
129128type SymbolUseWithFullNames =
130129{ SymbolUse: FSharpSymbolUse
131130 FullNames: string [][] }
132131
133132type SymbolUse =
134133{ SymbolUse: FSharpSymbolUse
135- RequiredPrefix : string }
134+ PossibleNamespaces : Set < string > }
136135
137136let getSymbolUses ( getSourceLineStr : int -> string ) ( symbolUses : FSharpSymbolUse []) : SymbolUse [] =
138- let importantSymbolUses =
139- symbolUses
140- |> Array.filter( fun ( symbolUse : FSharpSymbolUse ) ->
141- not symbolUse.IsFromDefinition
142- //&& match symbolUse.Symbol with
143- //| :? FSharpEntity as e -> not e.IsNamespace
144- //| _ -> true
145- )
146-
147- let symbolUsesWithFullNames : SymbolUseWithFullNames [] =
148- importantSymbolUses
149- |> Array.map( fun symbolUse ->
150- let fullNames : string [][] =
151- match symbolUse.Symbolwith
152- | Symbol.MemberFunctionOrValue funcwhen func.IsExtensionMember->
153- if func.IsPropertythen
154- let fullNames =
155- [|
156- if func.HasGetterMethodthen
157- yield try func.GetterMethod.EnclosingEntity|> Option.map( fun x -> x.FullName) with _ -> None
158- if func.HasSetterMethodthen
159- yield try func.SetterMethod.EnclosingEntity|> Option.map( fun x -> x.FullName) with _ -> None
160- |]
161- |> Array.choose id
162- match fullNameswith
163- | [||] -> None
164- | _ -> Some fullNames
165- else
166- match func.EnclosingEntitywith
167- // C# extension method
168- | Some( Symbol.FSharpEntity Symbol.Class) ->
169- let fullName = symbolUse.Symbol.FullName.Split'.'
170- if fullName.Length> 2 then
171- (* For C# extension methods FCS returns full name including the class name, like:
172- Namespace.StaticClass.ExtensionMethod
173- So, in order to properly detect that "open Namespace" actually opens ExtensionMethod,
174- we remove "StaticClass" part. This makes C# extension methods looks identically
175- with F# extension members.
176- *)
177- let fullNameWithoutClassName =
178- Array.append fullName.[ 0 .. fullName.Length- 3 ] fullName.[ fullName.Length- 1 ..]
179- Some[| fullNameWithoutClassName|> String.concat" ." |]
180- else None
181- | _ -> None
182- // Operators
183- | Symbol.MemberFunctionOrValue func->
184- match funcwith
185- | Symbol.Constructor_ ->
186- // full name of a constructor looks like "UnusedSymbolClassifierTests.PrivateClass.( .ctor )"
187- // to make well formed full name parts we cut "( .ctor )" from the tail.
188- let fullName = func.FullName
189- let ctorSuffix = " .( .ctor )"
190- let fullName =
191- if fullName.EndsWith ctorSuffixthen
192- fullName.[ 0 .. fullName.Length- ctorSuffix.Length- 1 ]
193- else fullName
194- Some[| fullName|]
195- | _ ->
196- Some[| yield func.FullName
197- match func.TryGetFullCompiledOperatorNameIdents() with
198- | Some idents-> yield String.concat" ." idents
199- | None-> ()
200- |]
201- | Symbol.FSharpEntity e->
202- match ewith
203- | e, Symbol.Attribute, _ ->
204- e.TryGetFullName()
205- |> Option.map( fun fullName ->
206- [| fullName; fullName.Substring( 0 , fullName.Length- " Attribute" .Length) |])
207- | e, _, _ ->
208- e.TryGetFullName() |> Option.map( fun fullName -> [| fullName|])
209- //| SymbolUse.RecordField _
210- | Symbol.UnionCase_ as symbol->
211- Some[| let fullName = symbol.FullName
212- yield fullName
213- let idents = fullName.Split'.'
214- // Union cases/Record fields can be accessible without mentioning the enclosing type.
215- // So we add a FullName without having the type part.
216- if idents.Length> 1 then
217- yield Array.append idents.[ 0 .. idents.Length- 3 ] idents.[ idents.Length- 1 ..] |> String.concat" ."
218- |]
219- | _ -> None
220- |> Option.defaultValue[| symbolUse.Symbol.FullName|]
221- |> Array.map( fun fullName -> fullName.Split'.' )
222-
223- { SymbolUse= symbolUse
224- FullNames= fullNames})
225-
226- //let outerSymbolUses =
227- // symbolUsesWithFullNames
228- // |> Seq.sortBy (fun x -> -x.SymbolUse.RangeAlternate.EndColumn)
229- // |> Seq.fold (fun (prev, acc) next ->
230- // match prev with
231- // | Some prev ->
232- // if prev.FullNames
233- // |> Array.exists (fun prevFullName ->
234- // next.FullNames
235- // |> Array.exists (fun nextFullName ->
236- // nextFullName.Length < prevFullName.Length
237- // && prevFullName |> Microsoft.FSharp.Compiler.AbstractIL.Internal.Library.Array.startsWith nextFullName)) then
238- // Some prev, acc
239- // else Some next, next :: acc
240- // | None -> Some next, next :: acc)
241- // (None, [])
242- // |> snd
243- // |> List.map (fun x -> x.SymbolUse)
244- // |> List.rev
245-
246- symbolUsesWithFullNames
247- |> Array.collect( fun su ->
248- let lineStr = getSourceLineStr su.SymbolUse.RangeAlternate.StartLine
249- let partialName = QuickParse.GetPartialLongNameEx( lineStr, su.SymbolUse.RangeAlternate.EndColumn- 1 )
250- let suffix = partialName.QualifyingIdents@ [ partialName.PartialIdent] |> List.toArray
251- su.FullNames
252- |> Array.choose( fun fullName ->
253- if fullName= suffixthen None
254- elif fullName|> Array.endsWith suffixthen Some( su.SymbolUse, fullName.[..( fullName.Length- suffix.Length) - 1 ])
255- else None)
256- |> Array.map( fun ( su , prefix ) ->
257- { SymbolUse= su
258- RequiredPrefix= prefix|> String.concat" ." }))
137+ symbolUses
138+ |> Array.filter( fun ( symbolUse : FSharpSymbolUse ) ->
139+ not symbolUse.IsFromDefinition
140+ //&& match symbolUse.Symbol with
141+ //| :? FSharpEntity as e -> not e.IsNamespace
142+ //| _ -> true
143+ )
144+ |> Array.map( fun su ->
145+ { SymbolUse= su
146+ PossibleNamespaces= getPossibleNamespaces getSourceLineStr su})
259147
260148let getUnusedOpens ( checkFileResults : FSharpCheckFileResults , getSourceLineStr : int -> string ) : Async < range list > =
261149
@@ -270,7 +158,7 @@ module UnusedOpens =
270158|> Array.exists( fun symbolUse ->
271159let inScope = rangeContainsRange openStatement.AppliedScope symbolUse.SymbolUse.RangeAlternate
272160if not inScopethen false
273- elif not ( openStatement.Idents|> Set.contains symbolUse.RequiredPrefix ) then false
161+ elif openStatement.Idents|> Set.intersect symbolUse.PossibleNamespaces |> Set.isEmpty then false
274162else
275163 openStatement.Modules
276164|> List.exists( fun m ->
@@ -305,7 +193,6 @@ module UnusedOpens =
305193async {
306194let! fsharpSymbolUses = checkFileResults.GetAllUsesOfAllSymbolsInFile()
307195let symbolUses = getSymbolUses getSourceLineStr fsharpSymbolUses
308- //let namespacesInUse = getNamespacesInUse getSourceLineStr symbolUses
309196let openStatements = getOpenStatements checkFileResults.OpenDeclarations
310197return filter openStatements symbolUses|> List.map( fun os -> os.Range)
311198}