Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
dotNET Platform
fsharp
提交
ac000a0a
F
fsharp
项目概览
dotNET Platform
/
fsharp
12 个月 前同步成功
通知
0
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
F
fsharp
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
ac000a0a
编写于
4月 26, 2017
作者:
V
Vasily Kirichenko
提交者:
Kevin Ransom (msft)
4月 25, 2017
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
rewrite UnusedDeclarationsAnalyzer logic (#2914)
上级
466f911b
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
63 addition
and
58 deletion
+63
-58
vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs
...c/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs
+63
-58
未找到文件。
vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs
浏览文件 @
ac000a0a
...
...
@@ -29,68 +29,74 @@ type internal UnusedDeclarationsAnalyzer() =
isEnabledByDefault
=
true
,
customTags
=
DiagnosticCustomTags
.
Unnecessary
)
let
symbolUseComparer
=
{
new
IEqualityComparer
<
FSharpSymbolUse
>
with
member
__.
Equals
(
x
,
y
)
=
if
x
.
IsFromDefinition
&&
y
.
IsFromDefinition
then
x
.
RangeAlternate
=
y
.
RangeAlternate
else
x
.
Symbol
.
DeclarationLocation
=
y
.
Symbol
.
DeclarationLocation
||
x
.
Symbol
.
IsEffectivelySameAs
y
.
Symbol
member
__.
GetHashCode
x
=
x
.
Symbol
.
DeclarationLocation
|>
Option
.
orElseWith
(
fun
_
->
x
.
Symbol
.
ImplementationLocation
)
|>
hash
}
let
isPotentiallyUnusedDeclaration
(
symbol
:
FSharpSymbol
)
:
bool
=
match
symbol
with
// Determining that a record, DU or module is used anywhere requires inspecting all their enclosed entities (fields, cases and func / vals)
// for usages, which is too expensive to do. Hence we never gray them out.
|
:?
FSharpEntity
as
e
when
e
.
IsFSharpRecord
||
e
.
IsFSharpUnion
||
e
.
IsInterface
||
e
.
IsFSharpModule
||
e
.
IsClass
->
false
// FCS returns inconsistent results for override members; we're skipping these symbols.
|
:?
FSharpMemberOrFunctionOrValue
as
f
when
f
.
IsOverrideOrExplicitInterfaceImplementation
||
f
.
IsBaseValue
||
f
.
IsConstructor
->
false
// Usage of DU case parameters does not give any meaningful feedback; we never gray them out.
|
:?
FSharpParameter
->
false
|
_
->
true
let
countSymbolsUses
(
symbolsUses
:
FSharpSymbolUse
[]
)
=
let
result
=
Dictionary
<
FSharpSymbolUse
,
int
>(
symbolUseComparer
)
for
symbolUse
in
symbolsUses
do
match
result
.
TryGetValue
symbolUse
with
|
true
,
count
->
result
.[
symbolUse
]
<-
count
+
1
|
_
->
result
.[
symbolUse
]
<-
1
result
let
getUnusedDeclarationRanges
(
symbolsUses
:
FSharpSymbolUse
[]
)
(
isScript
:
bool
)
=
let
definitions
=
symbolsUses
|>
Array
.
filter
(
fun
su
->
su
.
IsFromDefinition
&&
su
.
Symbol
.
DeclarationLocation
.
IsSome
&&
(
isScript
||
su
.
IsPrivateToFile
)
&&
not
(
su
.
Symbol
.
DisplayName
.
StartsWith
"_"
)
&&
isPotentiallyUnusedDeclaration
su
.
Symbol
)
let
usages
=
let
usages
=
symbolsUses
|>
Array
.
filter
(
fun
su
->
not
su
.
IsFromDefinition
)
|>
Array
.
choose
(
fun
su
->
su
.
Symbol
.
DeclarationLocation
)
HashSet
(
usages
)
let
getSingleDeclarations
(
symbolsUses
:
FSharpSymbolUse
[]
)
(
isScript
:
bool
)
=
let
symbolUsesCounts
=
countSymbolsUses
symbolsUses
let
declarations
=
symbolUsesCounts
|>
Seq
.
map
(
fun
(
KeyValue
(
su
,
count
))
->
su
,
count
)
|>
Seq
.
groupBy
(
fun
(
su
,
_)
->
su
.
RangeAlternate
)
|>
Seq
.
collect
(
fun
(_,
symbolUsesWithSameRange
)
->
let
singleDeclarationSymbolUses
=
symbolUsesWithSameRange
|>
Seq
.
choose
(
fun
(
symbolUse
,
count
)
->
match
symbolUse
.
Symbol
with
// Determining that a record, DU or module is used anywhere requires inspecting all their enclosed entities (fields, cases and func / vals)
// for usages, which is too expensive to do. Hence we never gray them out.
|
:?
FSharpEntity
as
e
when
e
.
IsFSharpRecord
||
e
.
IsFSharpUnion
||
e
.
IsInterface
||
e
.
IsFSharpModule
||
e
.
IsClass
->
None
// FCS returns inconsistent results for override members; we're skipping these symbols.
|
:?
FSharpMemberOrFunctionOrValue
as
f
when
f
.
IsOverrideOrExplicitInterfaceImplementation
||
//f.IsConstructorThisValue ||
f
.
IsBaseValue
||
f
.
IsConstructor
->
None
// Usage of DU case parameters does not give any meaningful feedback; we never gray them out.
|
:?
FSharpParameter
when
symbolUse
.
IsFromDefinition
->
None
|
_
when
count
=
1
&&
symbolUse
.
IsFromDefinition
&&
(
isScript
||
symbolUse
.
IsPrivateToFile
)
->
Some
symbolUse
|
_
->
None
)
|>
Seq
.
toList
let
unusedRanges
=
definitions
|>
Array
.
map
(
fun
defSu
->
defSu
,
usages
.
Contains
defSu
.
Symbol
.
DeclarationLocation
.
Value
)
|>
Array
.
groupBy
(
fun
(
defSu
,
_)
->
defSu
.
RangeAlternate
)
|>
Array
.
filter
(
fun
(_,
defSus
)
->
defSus
|>
Array
.
forall
(
fun
(_,
isUsed
)
->
not
isUsed
))
|>
Array
.
map
(
fun
(
m
,
_)
->
m
)
// Only if *all* `SymbolUse`s are single declarations, return them. If there is at least one that should not be marked as unused, return nothing.
if
Seq
.
length
symbolUsesWithSameRange
=
List
.
length
singleDeclarationSymbolUses
then
singleDeclarationSymbolUses
else
[]
)
|>
Seq
.
toList
//#if DEBUG
//let formatRange (x: Microsoft.FSharp.Compiler.Range.range) = sprintf "(%d, %d) - (%d, %d)" x.StartLine x.StartColumn x.EndLine x.EndColumn
HashSet
(
declarations
,
symbolUseComparer
)
//symbolsUses
//|> Array.map (fun su -> sprintf "%s, %s, is definition = %b, Symbol (def range = %A)"
// (formatRange su.RangeAlternate) su.Symbol.DisplayName su.IsFromDefinition
// (su.Symbol.DeclarationLocation |> Option.map formatRange))
//|> Logging.Logging.logInfof "SymbolUses:\n%+A"
//
//definitions
//|> Seq.map (fun su -> sprintf "su range = %s, symbol range = %A, symbol name = %s"
// (formatRange su.RangeAlternate) (su.Symbol.DeclarationLocation |> Option.map formatRange) su.Symbol.DisplayName)
//|> Logging.Logging.logInfof "Definitions:\n%A"
//
//usages
//|> Seq.map formatRange
//|> Seq.toArray
//|> Logging.Logging.logInfof "Used ranges:\n%A"
//
//unusedRanges
//|> Array.map formatRange
//|> Logging.Logging.logInfof "Unused ranges: %A"
//#endif
unusedRanges
override
__.
SupportedDiagnostics
=
ImmutableArray
.
Create
Descriptor
override
this
.
AnalyzeSyntaxAsync
(_,
_)
=
Task
.
FromResult
ImmutableArray
<
Diagnostic
>.
Empty
override
__
.
AnalyzeSyntaxAsync
(_,
_)
=
Task
.
FromResult
ImmutableArray
<
Diagnostic
>.
Empty
override
this
.
AnalyzeSemanticsAsync
(
document
,
cancellationToken
)
=
override
__
.
AnalyzeSemanticsAsync
(
document
,
cancellationToken
)
=
asyncMaybe
{
match
getProjectInfoManager
(
document
).
TryGetOptionsForEditingDocumentOrProject
(
document
)
with
|
Some
options
->
...
...
@@ -98,11 +104,10 @@ type internal UnusedDeclarationsAnalyzer() =
let
checker
=
getChecker
document
let
!
_,
_,
checkResults
=
checker
.
ParseAndCheckDocument
(
document
,
options
,
sourceText
=
sourceText
,
allowStaleResults
=
true
)
let
!
allSymbolUsesInFile
=
checkResults
.
GetAllUsesOfAllSymbolsInFile
()
|>
liftAsync
let
unused
Declarations
=
getSingleDeclaration
s
allSymbolUsesInFile
(
isScriptFile
document
.
FilePath
)
let
unused
Ranges
=
getUnusedDeclarationRange
s
allSymbolUsesInFile
(
isScriptFile
document
.
FilePath
)
return
unusedDeclarations
|>
Seq
.
filter
(
fun
symbolUse
->
not
(
symbolUse
.
Symbol
.
DisplayName
.
StartsWith
"_"
))
|>
Seq
.
map
(
fun
symbolUse
->
Diagnostic
.
Create
(
Descriptor
,
RoslynHelpers
.
RangeToLocation
(
symbolUse
.
RangeAlternate
,
sourceText
,
document
.
FilePath
)))
unusedRanges
|>
Seq
.
map
(
fun
m
->
Diagnostic
.
Create
(
Descriptor
,
RoslynHelpers
.
RangeToLocation
(
m
,
sourceText
,
document
.
FilePath
)))
|>
Seq
.
toImmutableArray
|
None
->
return
ImmutableArray
.
Empty
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录