Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
aedb97b9
R
roslyn
项目概览
lwm1986
/
roslyn
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
roslyn
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
aedb97b9
编写于
2月 27, 2017
作者:
C
CyrusNajmabadi
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add support for adding usings to resolve a 'Deconstruct' extension method.
上级
d1e3d73d
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
171 addition
and
57 deletion
+171
-57
src/EditorFeatures/CSharpTest/Diagnostics/AddUsing/AddUsingTests_ExtensionMethods.cs
...st/Diagnostics/AddUsing/AddUsingTests_ExtensionMethods.cs
+42
-0
src/Features/CSharp/Portable/AddImport/CSharpAddImportCodeFixProvider.cs
...harp/Portable/AddImport/CSharpAddImportCodeFixProvider.cs
+16
-1
src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs
...re/Portable/AddImport/AbstractAddImportCodeFixProvider.cs
+45
-38
src/Features/Core/Portable/AddImport/SearchScope.cs
src/Features/Core/Portable/AddImport/SearchScope.cs
+5
-5
src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs
...Features/Core/Portable/AddImport/SymbolReferenceFinder.cs
+53
-12
src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportCodeFixProvider.vb
...Portable/AddImport/VisualBasicAddImportCodeFixProvider.vb
+10
-1
未找到文件。
src/EditorFeatures/CSharpTest/Diagnostics/AddUsing/AddUsingTests_ExtensionMethods.cs
浏览文件 @
aedb97b9
...
...
@@ -1067,5 +1067,47 @@ public T F<T>(T x)
}"
;
await
TestAsync
(
initialText
,
expectedText
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsAddImport
)]
public
async
Task
TestDeconstructExtension
()
{
await
TestAsync
(
@"
class Program
{
void M(Program p)
{
var (x, y) = [|p|];
}
}
namespace N
{
static class E
{
public static void Deconstruct(this Program p, out int x, out int y) { }
}
}"
,
@"
using N;
class Program
{
void M(Program p)
{
var (x, y) = [|p|];
}
}
namespace N
{
static class E
{
public static void Deconstruct(this Program p, out int x, out int y) { }
}
}"
,
parseOptions
:
null
,
withScriptOption
:
false
);
}
}
}
\ No newline at end of file
src/Features/CSharp/Portable/AddImport/CSharpAddImportCodeFixProvider.cs
浏览文件 @
aedb97b9
...
...
@@ -117,6 +117,11 @@ internal static class AddImportDiagnosticIds
/// </summary>
public
const
string
CS7036
=
nameof
(
CS7036
);
/// <summary>
/// o Deconstruct instance or extension method was found for type 'X', with N out parameters
/// </summary>
public
const
string
CS8129
=
nameof
(
CS8129
);
public
static
ImmutableArray
<
string
>
FixableTypeIds
=
ImmutableArray
.
Create
(
CS0103
,
...
...
@@ -127,7 +132,8 @@ internal static class AddImportDiagnosticIds
CS0307
,
CS0616
,
CS1580
,
CS1581
);
CS1581
,
CS8129
);
public
static
ImmutableArray
<
string
>
FixableDiagnosticIds
=
FixableTypeIds
.
Concat
(
ImmutableArray
.
Create
(
...
...
@@ -260,6 +266,9 @@ protected override bool CanAddImport(SyntaxNode node, CancellationToken cancella
return
true
;
}
protected
override
bool
CanAddImportForDeconstruct
(
Diagnostic
diagnostic
,
SyntaxNode
node
)
=>
diagnostic
.
Id
==
CS8129
;
protected
override
bool
CanAddImportForNamespace
(
Diagnostic
diagnostic
,
SyntaxNode
node
,
out
SimpleNameSyntax
nameNode
)
{
nameNode
=
null
;
...
...
@@ -346,6 +355,12 @@ private static SimpleNameSyntax GetLeftMostSimpleName(QualifiedNameSyntax qn)
return
semanticModel
.
GetUsingNamespacesInScope
(
node
);
}
protected
override
ITypeSymbol
GetDeconstructInfo
(
SemanticModel
semanticModel
,
SyntaxNode
node
,
CancellationToken
cancellationToken
)
{
return
semanticModel
.
GetTypeInfo
(
node
).
Type
;
}
protected
override
ITypeSymbol
GetQueryClauseInfo
(
SemanticModel
semanticModel
,
SyntaxNode
node
,
...
...
src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs
浏览文件 @
aedb97b9
...
...
@@ -40,10 +40,12 @@ internal abstract partial class AbstractAddImportCodeFixProvider<TSimpleNameSynt
protected
abstract
bool
CanAddImport
(
SyntaxNode
node
,
CancellationToken
cancellationToken
);
protected
abstract
bool
CanAddImportForMethod
(
Diagnostic
diagnostic
,
ISyntaxFactsService
syntaxFacts
,
SyntaxNode
node
,
out
TSimpleNameSyntax
nameNode
);
protected
abstract
bool
CanAddImportForNamespace
(
Diagnostic
diagnostic
,
SyntaxNode
node
,
out
TSimpleNameSyntax
nameNode
);
protected
abstract
bool
CanAddImportForDeconstruct
(
Diagnostic
diagnostic
,
SyntaxNode
node
);
protected
abstract
bool
CanAddImportForQuery
(
Diagnostic
diagnostic
,
SyntaxNode
node
);
protected
abstract
bool
CanAddImportForType
(
Diagnostic
diagnostic
,
SyntaxNode
node
,
out
TSimpleNameSyntax
nameNode
);
protected
abstract
ISet
<
INamespaceSymbol
>
GetImportNamespacesInScope
(
SemanticModel
semanticModel
,
SyntaxNode
node
,
CancellationToken
cancellationToken
);
protected
abstract
ITypeSymbol
GetDeconstructInfo
(
SemanticModel
semanticModel
,
SyntaxNode
node
,
CancellationToken
cancellationToken
);
protected
abstract
ITypeSymbol
GetQueryClauseInfo
(
SemanticModel
semanticModel
,
SyntaxNode
node
,
CancellationToken
cancellationToken
);
protected
abstract
bool
IsViableExtensionMethod
(
IMethodSymbol
method
,
SyntaxNode
expression
,
SemanticModel
semanticModel
,
ISyntaxFactsService
syntaxFacts
,
CancellationToken
cancellationToken
);
...
...
@@ -56,59 +58,64 @@ internal abstract partial class AbstractAddImportCodeFixProvider<TSimpleNameSynt
protected
abstract
(
string
description
,
bool
hasExistingImport
)
GetDescription
(
Document
document
,
INamespaceOrTypeSymbol
symbol
,
SemanticModel
semanticModel
,
SyntaxNode
root
,
CancellationToken
cancellationToken
);
public
sealed
override
async
Task
RegisterCodeFixesAsync
(
CodeFixContext
context
)
{
// We might have multiple different diagnostics covering the same span. Have to
// process them all as we might produce different fixes for each diagnostic.
var
resultCount
=
0
;
foreach
(
var
diagnostic
in
context
.
Diagnostics
)
{
resultCount
+=
await
HandleDiagnosticAsync
(
context
,
diagnostic
).
ConfigureAwait
(
false
);
if
(
resultCount
>=
MaxResults
)
{
break
;
}
}
}
private
async
Task
<
int
>
HandleDiagnosticAsync
(
CodeFixContext
context
,
Diagnostic
diagnostic
)
{
var
document
=
context
.
Document
;
var
span
=
context
.
Span
;
var
diagnostics
=
context
.
Diagnostics
;
var
cancellationToken
=
context
.
CancellationToken
;
var
project
=
document
.
Project
;
var
diagnostic
=
diagnostics
.
First
();
var
root
=
await
document
.
GetSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
ancestors
=
root
.
FindToken
(
span
.
Start
,
findInsideTrivia
:
true
).
GetAncestors
<
SyntaxNode
>();
if
(!
ancestors
.
Any
())
{
return
;
}
var
node
=
root
.
FindToken
(
span
.
Start
,
findInsideTrivia
:
true
)
.
GetAncestor
(
n
=>
n
.
Span
.
Contains
(
span
)
&&
n
!=
root
);
var
node
=
ancestors
.
FirstOrDefault
(
n
=>
n
.
Span
.
Contains
(
span
)
&&
n
!=
root
)
;
if
(
node
=
=
null
)
var
count
=
0
;
if
(
node
!
=
null
)
{
return
;
}
var
documentOptions
=
await
document
.
GetOptionsAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
placeSystemNamespaceFirst
=
documentOptions
.
GetOption
(
GenerationOptions
.
PlaceSystemNamespaceFirst
);
var
documentOptions
=
await
document
.
GetOptionsAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
placeSystemNamespaceFirst
=
documentOptions
.
GetOption
(
GenerationOptions
.
PlaceSystemNamespaceFirst
);
using
(
Logger
.
LogBlock
(
FunctionId
.
Refactoring_AddImport
,
cancellationToken
))
{
if
(!
cancellationToken
.
IsCancellationRequested
)
using
(
Logger
.
LogBlock
(
FunctionId
.
Refactoring_AddImport
,
cancellationToken
))
{
if
(
this
.
CanAddImport
(
node
,
cancellationToken
)
)
if
(
!
cancellationToken
.
IsCancellationRequested
)
{
var
semanticModel
=
await
document
.
GetSemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
allSymbolReferences
=
await
FindResultsAsync
(
document
,
semanticModel
,
diagnostic
,
node
,
cancellationToken
).
ConfigureAwait
(
false
);
// Nothing found at all. No need to proceed.
if
(
allSymbolReferences
.
Length
==
0
)
if
(
this
.
CanAddImport
(
node
,
cancellationToken
))
{
return
;
}
var
semanticModel
=
await
document
.
GetSemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
)
;
var
allSymbolReferences
=
await
FindResultsAsync
(
document
,
semanticModel
,
diagnostic
,
node
,
cancellationToken
).
ConfigureAwait
(
false
);
foreach
(
var
reference
in
allSymbolReferences
)
{
cancellationToken
.
ThrowIfCancellationRequested
();
var
codeAction
=
await
reference
.
CreateCodeActionAsync
(
document
,
node
,
placeSystemNamespaceFirst
,
cancellationToken
).
ConfigureAwait
(
false
);
if
(
codeAction
!=
null
)
// Nothing found at all. No need to proceed.
foreach
(
var
reference
in
allSymbolReferences
)
{
context
.
RegisterCodeFix
(
codeAction
,
diagnostic
);
cancellationToken
.
ThrowIfCancellationRequested
();
var
codeAction
=
await
reference
.
CreateCodeActionAsync
(
document
,
node
,
placeSystemNamespaceFirst
,
cancellationToken
).
ConfigureAwait
(
false
);
if
(
codeAction
!=
null
)
{
context
.
RegisterCodeFix
(
codeAction
,
diagnostic
);
count
++;
}
}
}
}
}
}
return
count
;
}
private
async
Task
<
ImmutableArray
<
Reference
>>
FindResultsAsync
(
...
...
@@ -132,7 +139,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
// No exact matches found. Fall back to fuzzy searching.
// Only bother doing this for host workspaces. We don't want this for
// things like the Interactive workspace as this will cause us to
// create expensive bktrees which we won't even be able to save for
// create expensive bk
-
trees which we won't even be able to save for
// future use.
if
(!
IsHostOrTestWorkspace
(
project
))
{
...
...
@@ -265,7 +272,7 @@ private static bool IsHostOrTestWorkspace(Project project)
var
compilation
=
referenceToCompilation
.
GetOrAdd
(
reference
,
r
=>
CreateCompilation
(
project
,
r
));
// Ignore netmodules. First, they're incredibly esoteric and barely used.
// Second, the SymbolFinder
api
doesn't even support searching them.
// Second, the SymbolFinder
API
doesn't even support searching them.
var
assembly
=
compilation
.
GetAssemblyOrModuleSymbol
(
reference
)
as
IAssemblySymbol
;
if
(
assembly
!=
null
)
{
...
...
@@ -341,7 +348,7 @@ private bool IsInPackagesDirectory(PortableExecutableReference reference)
}
/// <summary>
/// Called when w
hen w
e want to search a metadata reference. We create a dummy compilation
/// Called when we want to search a metadata reference. We create a dummy compilation
/// containing just that reference and we search that. That way we can get actual symbols
/// returned.
///
...
...
@@ -377,7 +384,7 @@ private static HashSet<Project> GetViableUnreferencedProjects(Project project)
// Clearly we can't reference ourselves.
viableProjects
.
Remove
(
project
);
// We can't reference any project that transitively depends on
on
us. Doing so would
// We can't reference any project that transitively depends on us. Doing so would
// cause a circular reference between projects.
var
dependencyGraph
=
solution
.
GetProjectDependencyGraph
();
var
projectsThatTransitivelyDependOnThisProject
=
dependencyGraph
.
GetProjectsThatTransitivelyDependOnThisProject
(
project
.
Id
);
...
...
src/Features/Core/Portable/AddImport/SearchScope.cs
浏览文件 @
aedb97b9
...
...
@@ -50,7 +50,7 @@ protected SearchScope(AbstractAddImportCodeFixProvider<TSimpleNameSyntax> provid
if
(
Exact
)
{
// We did an exact, case insensitive, search. Case sensitive matches should
// be pref
fe
red though over insensitive ones.
// be pref
er
red though over insensitive ones.
return
symbols
.
SelectAsArray
(
s
=>
SymbolResult
.
Create
(
s
.
Name
,
nameNode
,
s
,
weight
:
s
.
Name
==
name
?
0
:
1
));
}
...
...
@@ -78,9 +78,9 @@ private abstract class ProjectSearchScope : SearchScope
public
ProjectSearchScope
(
AbstractAddImportCodeFixProvider
<
TSimpleNameSyntax
>
provider
,
Project
project
,
bool
ignoreCase
,
bool
exact
,
CancellationToken
cancellationToken
)
:
base
(
provider
,
ignoreCase
,
cancellationToken
)
:
base
(
provider
,
exact
,
cancellationToken
)
{
_project
=
project
;
}
...
...
@@ -102,9 +102,9 @@ private class AllSymbolsProjectSearchScope : ProjectSearchScope
public
AllSymbolsProjectSearchScope
(
AbstractAddImportCodeFixProvider
<
TSimpleNameSyntax
>
provider
,
Project
project
,
bool
ignoreCase
,
bool
exact
,
CancellationToken
cancellationToken
)
:
base
(
provider
,
project
,
ignoreCase
,
cancellationToken
)
:
base
(
provider
,
project
,
exact
,
cancellationToken
)
{
}
...
...
src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs
浏览文件 @
aedb97b9
...
...
@@ -120,6 +120,7 @@ private async Task<ImmutableArray<SymbolReference>> DoAsync(SearchScope searchSc
{
tasks
.
Add
(
this
.
GetReferencesForCollectionInitializerMethodsAsync
(
searchScope
));
tasks
.
Add
(
this
.
GetReferencesForQueryPatternsAsync
(
searchScope
));
tasks
.
Add
(
this
.
GetReferencesForDeconstructAsync
(
searchScope
));
}
await
Task
.
WhenAll
(
tasks
).
ConfigureAwait
(
false
);
...
...
@@ -407,24 +408,64 @@ private async Task<ImmutableArray<SymbolReference>> GetReferencesForQueryPattern
if
(
type
!=
null
)
{
// find extension methods named "Select"
var
symbols
=
await
searchScope
.
FindDeclarationsAsync
(
nameof
(
Enumerable
.
Select
),
nameNode
:
null
,
filter
:
SymbolFilter
.
Member
).
ConfigureAwait
(
false
);
// Note: there is no "desiredName" when doing this. We're not going to do any
// renames of the user code. We're just looking for an extension method called
// "Select", but that name has no bearing on the code in question that we're
// trying to fix up.
var
methodSymbols
=
OfType
<
IMethodSymbol
>(
symbols
).
SelectAsArray
(
s
=>
s
.
WithDesiredName
(
null
));
var
viableExtensionMethods
=
GetViableExtensionMethods
(
methodSymbols
,
type
,
searchScope
.
CancellationToken
);
var
namespaceSymbols
=
viableExtensionMethods
.
SelectAsArray
(
s
=>
s
.
WithSymbol
(
s
.
Symbol
.
ContainingNamespace
));
return
GetNamespaceSymbolReferences
(
searchScope
,
namespaceSymbols
);
return
await
GetReferencesForExtensionMethodAsync
(
searchScope
,
nameof
(
Enumerable
.
Select
),
type
).
ConfigureAwait
(
false
);
}
}
return
ImmutableArray
<
SymbolReference
>.
Empty
;
}
/// <summary>
/// Searches for extension methods exactly called 'Deconstruct'. Returns
/// <see cref="SymbolReference"/>s to the <see cref="INamespaceSymbol"/>s that contain
/// the static classes that those extension methods are contained in.
/// </summary>
private
async
Task
<
ImmutableArray
<
SymbolReference
>>
GetReferencesForDeconstructAsync
(
SearchScope
searchScope
)
{
searchScope
.
CancellationToken
.
ThrowIfCancellationRequested
();
if
(
_owner
.
CanAddImportForDeconstruct
(
_diagnostic
,
_node
))
{
var
type
=
_owner
.
GetDeconstructInfo
(
_semanticModel
,
_node
,
searchScope
.
CancellationToken
);
if
(
type
!=
null
)
{
// Note: we could check that the extension methods have the right number of out-params.
// But that would involve figuring out what we're trying to deconstruct into. For now
// we'll just be permissive, with the assumption that there won't be that many matching
// 'Deconstruct' extension methods for the type of node that we're on.
return
await
GetReferencesForExtensionMethodAsync
(
searchScope
,
"Deconstruct"
,
type
,
m
=>
m
.
ReturnsVoid
).
ConfigureAwait
(
false
);
}
}
return
ImmutableArray
<
SymbolReference
>.
Empty
;
}
private
async
Task
<
ImmutableArray
<
SymbolReference
>>
GetReferencesForExtensionMethodAsync
(
SearchScope
searchScope
,
string
name
,
ITypeSymbol
type
,
Func
<
IMethodSymbol
,
bool
>
predicate
=
null
)
{
var
symbols
=
await
searchScope
.
FindDeclarationsAsync
(
name
,
nameNode
:
null
,
filter
:
SymbolFilter
.
Member
).
ConfigureAwait
(
false
);
// Note: there is no "desiredName" when doing this. We're not going to do any
// renames of the user code. We're just looking for an extension method called
// "Select", but that name has no bearing on the code in question that we're
// trying to fix up.
var
methodSymbols
=
OfType
<
IMethodSymbol
>(
symbols
).
SelectAsArray
(
s
=>
s
.
WithDesiredName
(
null
));
var
viableExtensionMethods
=
GetViableExtensionMethods
(
methodSymbols
,
type
,
searchScope
.
CancellationToken
);
if
(
predicate
!=
null
)
{
viableExtensionMethods
=
viableExtensionMethods
.
WhereAsArray
(
s
=>
predicate
(
s
.
Symbol
));
}
var
namespaceSymbols
=
viableExtensionMethods
.
SelectAsArray
(
s
=>
s
.
WithSymbol
(
s
.
Symbol
.
ContainingNamespace
));
return
GetNamespaceSymbolReferences
(
searchScope
,
namespaceSymbols
);
}
protected
bool
ExpressionBinds
(
TSimpleNameSyntax
nameNode
,
bool
checkForExtensionMethods
,
CancellationToken
cancellationToken
)
{
...
...
src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportCodeFixProvider.vb
浏览文件 @
aedb97b9
...
...
@@ -189,6 +189,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddImport
Return
CanAddImportForTypeOrNamespaceCore
(
node
,
nameNode
)
End
Function
Protected
Overrides
Function
CanAddImportForDeconstruct
(
diagnostic
As
Diagnostic
,
node
As
SyntaxNode
)
As
Boolean
' Not supported yet.
Return
False
End
Function
Protected
Overrides
Function
CanAddImportForQuery
(
diagnostic
As
Diagnostic
,
node
As
SyntaxNode
)
As
Boolean
If
diagnostic
.
Id
<>
BC36593
Then
Return
False
...
...
@@ -281,6 +286,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddImport
Return semanticModel.GetImportNamespacesInScope(node)
End Function
Protected Overrides Function GetDeconstructInfo(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As ITypeSymbol
Return Nothing
End Function
Protected Overrides Function GetQueryClauseInfo(
model As SemanticModel,
node As SyntaxNode,
...
...
@@ -373,7 +382,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddImport
Dim importsStatement = GetImportsStatement(nameSyntax)
' Suppress diagnostics on the import we create. Because we only get here when we are
' adding a
nug
et package, it is certainly the case that in the preview this will not
' adding a
NuG
et package, it is certainly the case that in the preview this will not
' bind properly. It will look silly to show such an error, so we just suppress things.
importsStatement = importsStatement.WithAdditionalAnnotations(SuppressDiagnosticsAnnotation.Create())
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录