Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
60a2369d
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,发现更多精彩内容 >>
提交
60a2369d
编写于
11月 01, 2018
作者:
A
Andrew Hall (METAL)
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Fix symbol resolution by applying groups at a time
上级
443784e3
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
171 addition
and
102 deletion
+171
-102
src/EditorFeatures/CSharpTest/InlineDeclaration/CSharpInlineDeclarationTests_FixAllTests.cs
...neDeclaration/CSharpInlineDeclarationTests_FixAllTests.cs
+47
-0
src/Features/CSharp/Portable/InlineDeclaration/CSharpInlineDeclarationCodeFixProvider.cs
...lineDeclaration/CSharpInlineDeclarationCodeFixProvider.cs
+124
-102
未找到文件。
src/EditorFeatures/CSharpTest/InlineDeclaration/CSharpInlineDeclarationTests_FixAllTests.cs
浏览文件 @
60a2369d
...
...
@@ -2,6 +2,7 @@
using
System.Threading.Tasks
;
using
Microsoft.CodeAnalysis.Test.Utilities
;
using
Roslyn.Test.Utilities
;
using
Xunit
;
namespace
Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InlineDeclaration
...
...
@@ -78,6 +79,7 @@ void M1()
public
async
Task
FixAllInDocument3
()
{
await
TestInRegularAndScriptAsync
(
@"class C
{
...
...
@@ -94,6 +96,51 @@ void M()
{
GetExeAndArguments(useCmdShell, executable, arguments, out string finalExecutable, out string finalArguments);
}
}"
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsInlineDeclaration
)]
[
WorkItem
(
29935
,
"https://github.com/dotnet/roslyn/issues/29935"
)]
public
async
Task
FixAllInDocumentSymbolResolution
()
{
await
TestInRegularAndScriptAsync
(
@"class C
{
void M()
{
string {|FixAllInDocument:s|};
bool b;
A(out s, out b);
}
void A(out string s, out bool b)
{
s = string.Empty;
b = false;
}
void A(out string s, out string s2)
{
s = s2 = string.Empty;
}
}"
,
@"class C
{
void M()
{
A(out string s, out bool b);
}
void A(out string s, out bool b)
{
s = string.Empty;
b = false;
}
void A(out string s, out string s2)
{
s = s2 = string.Empty;
}
}"
);
}
}
...
...
src/Features/CSharp/Portable/InlineDeclaration/CSharpInlineDeclarationCodeFixProvider.cs
浏览文件 @
60a2369d
...
...
@@ -49,133 +49,151 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
// of the local. This is necessary in some cases (for example, when the
// type of the out-var-decl affects overload resolution or generic instantiation).
foreach
(
var
diagnostic
in
diagnostics
)
// Group by the ExpressionSyntax that the modified expression targets
var
diagnosticGroups
=
diagnostics
.
GroupBy
(
d
=>
d
.
AdditionalLocations
[
2
]);
foreach
(
var
diagnosticGroup
in
diagnosticGroups
)
{
cancellationToken
.
ThrowIfCancellationRequested
();
await
AddEditsAsync
(
document
,
editor
,
diagnostic
,
document
,
editor
,
diagnostic
Group
,
options
,
cancellationToken
).
ConfigureAwait
(
false
);
}
}
private
async
Task
AddEditsAsync
(
Document
document
,
SyntaxEditor
editor
,
Diagnostic
diagnostic
,
Document
document
,
SyntaxEditor
editor
,
IGrouping
<
Location
,
Diagnostic
>
diagnosticGroup
,
OptionSet
options
,
CancellationToken
cancellationToken
)
{
var
semanticModel
=
await
document
.
GetSemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
sourceText
=
await
document
.
GetTextAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
// Recover the nodes we care about.
var
declaratorLocation
=
diagnostic
.
AdditionalLocations
[
0
];
var
identifierLocation
=
diagnostic
.
AdditionalLocations
[
1
];
var
invocationOrCreationLocation
=
diagnostic
.
AdditionalLocations
[
2
];
var
outArgumentContainingStatementLocation
=
diagnostic
.
AdditionalLocations
[
3
];
var
root
=
declaratorLocation
.
SourceTree
.
GetRoot
(
cancellationToken
);
var
declarationsToAdd
=
new
List
<(
ILocalSymbol
,
DeclarationExpressionSyntax
,
IdentifierNameSyntax
)>();
var
declarator
=
(
VariableDeclaratorSyntax
)
declaratorLocation
.
FindNode
(
cancellationToken
);
var
identifier
=
(
IdentifierNameSyntax
)
identifierLocation
.
FindNode
(
cancellationToken
);
var
invocationOrCreationLocation
=
diagnosticGroup
.
Key
;
var
invocationOrCreation
=
(
ExpressionSyntax
)
invocationOrCreationLocation
.
FindNode
(
getInnermostNodeForTie
:
true
,
cancellationToken
:
cancellationToken
);
var
outArgumentContainingStatement
=
(
StatementSyntax
)
outArgumentContainingStatementLocation
.
FindNode
(
cancellationToken
);
var
declaration
=
(
VariableDeclarationSyntax
)
declarator
.
Parent
;
var
singleDeclarator
=
declaration
.
Variables
.
Count
==
1
;
getInnermostNodeForTie
:
true
,
cancellationToken
:
cancellationToken
);
if
(
singleDeclarator
)
foreach
(
var
diagnostic
in
diagnosticGroup
)
{
// This was a local statement with a single variable in it. Just Remove
// the entire local declaration statement. Note that comments belonging to
// this local statement will be moved to be above the statement containing
// the out-var.
var
localDeclarationStatement
=
(
LocalDeclarationStatementSyntax
)
declaration
.
Parent
;
var
block
=
(
BlockSyntax
)
localDeclarationStatement
.
Parent
;
var
declarationIndex
=
block
.
Statements
.
IndexOf
(
localDeclarationStatement
);
if
(
declarationIndex
>
0
&&
sourceText
.
AreOnSameLine
(
block
.
Statements
[
declarationIndex
-
1
].
GetLastToken
(),
localDeclarationStatement
.
GetFirstToken
()))
// Recover the nodes we care about.
var
declaratorLocation
=
diagnostic
.
AdditionalLocations
[
0
];
var
identifierLocation
=
diagnostic
.
AdditionalLocations
[
1
];
var
root
=
declaratorLocation
.
SourceTree
.
GetRoot
(
cancellationToken
);
var
declarator
=
(
VariableDeclaratorSyntax
)
declaratorLocation
.
FindNode
(
cancellationToken
);
var
identifier
=
(
IdentifierNameSyntax
)
identifierLocation
.
FindNode
(
cancellationToken
);
var
declaration
=
(
VariableDeclarationSyntax
)
declarator
.
Parent
;
var
singleDeclarator
=
declaration
.
Variables
.
Count
==
1
;
if
(
singleDeclarator
)
{
// There's another statement on the same line as this declaration statement.
// i.e. int a; int b;
//
// Just move all trivia from our statement to be trailing trivia of the previous
// statement
editor
.
ReplaceNode
(
block
.
Statements
[
declarationIndex
-
1
],
(
s
,
g
)
=>
s
.
WithAppendedTrailingTrivia
(
localDeclarationStatement
.
GetTrailingTrivia
()));
// This was a local statement with a single variable in it. Just Remove
// the entire local declaration statement. Note that comments belonging to
// this local statement will be moved to be above the statement containing
// the out-var.
var
localDeclarationStatement
=
(
LocalDeclarationStatementSyntax
)
declaration
.
Parent
;
var
block
=
(
BlockSyntax
)
localDeclarationStatement
.
Parent
;
var
declarationIndex
=
block
.
Statements
.
IndexOf
(
localDeclarationStatement
);
if
(
declarationIndex
>
0
&&
sourceText
.
AreOnSameLine
(
block
.
Statements
[
declarationIndex
-
1
].
GetLastToken
(),
localDeclarationStatement
.
GetFirstToken
()))
{
// There's another statement on the same line as this declaration statement.
// i.e. int a; int b;
//
// Just move all trivia from our statement to be trailing trivia of the previous
// statement
editor
.
ReplaceNode
(
block
.
Statements
[
declarationIndex
-
1
],
(
s
,
g
)
=>
s
.
WithAppendedTrailingTrivia
(
localDeclarationStatement
.
GetTrailingTrivia
()));
}
else
{
// Trivia on the local declaration will move to the next statement.
// use the callback form as the next statement may be the place where we're
// inlining the declaration, and thus need to see the effects of that change.
editor
.
ReplaceNode
(
block
.
Statements
[
declarationIndex
+
1
],
(
s
,
g
)
=>
s
.
WithPrependedNonIndentationTriviaFrom
(
localDeclarationStatement
));
}
editor
.
RemoveNode
(
localDeclarationStatement
,
SyntaxRemoveOptions
.
KeepUnbalancedDirectives
);
}
else
{
// Trivia on the local declaration will move to the next statement.
// use the callback form as the next statement may be the place where we're
// inlining the declaration, and thus need to see the effects of that change.
editor
.
ReplaceNode
(
block
.
Statements
[
declarationIndex
+
1
],
(
s
,
g
)
=>
s
.
WithPrependedNonIndentationTriviaFrom
(
localDeclarationStatement
));
}
editor
.
RemoveNode
(
localDeclarationStatement
,
SyntaxRemoveOptions
.
KeepUnbalancedDirectives
);
}
else
{
// Otherwise, just remove the single declarator. Note: we'll move the comments
// 'on' the declarator to the out-var location. This is a little bit trickier
// than normal due to how our comment-association rules work. i.e. if you have:
//
// var /*c1*/ i /*c2*/, /*c3*/ j /*c4*/;
//
// In this case 'c1' is owned by the 'var' token, not 'i', and 'c3' is owned by
// the comment token not 'j'.
editor
.
RemoveNode
(
declarator
);
if
(
declarator
==
declaration
.
Variables
[
0
])
{
// If we're removing the first declarator, and it's on the same line
// as the previous token, then we want to remove all the trivia belonging
// to the previous token. We're going to move it along with this declarator.
// If we don't, then the comment will stay with the previous token.
// Otherwise, just remove the single declarator. Note: we'll move the comments
// 'on' the declarator to the out-var location. This is a little bit trickier
// than normal due to how our comment-association rules work. i.e. if you have:
//
// Note that the moving of the comment happens later on when we make the
// declaration expression.
if
(
sourceText
.
AreOnSameLine
(
declarator
.
GetFirstToken
(),
declarator
.
GetFirstToken
().
GetPreviousToken
(
includeSkipped
:
true
)))
// var /*c1*/ i /*c2*/, /*c3*/ j /*c4*/;
//
// In this case 'c1' is owned by the 'var' token, not 'i', and 'c3' is owned by
// the comment token not 'j'.
editor
.
RemoveNode
(
declarator
);
if
(
declarator
==
declaration
.
Variables
[
0
])
{
editor
.
ReplaceNode
(
declaration
.
Type
,
(
t
,
g
)
=>
t
.
WithTrailingTrivia
(
SyntaxFactory
.
ElasticSpace
).
WithoutAnnotations
(
Formatter
.
Annotation
));
// If we're removing the first declarator, and it's on the same line
// as the previous token, then we want to remove all the trivia belonging
// to the previous token. We're going to move it along with this declarator.
// If we don't, then the comment will stay with the previous token.
//
// Note that the moving of the comment happens later on when we make the
// declaration expression.
if
(
sourceText
.
AreOnSameLine
(
declarator
.
GetFirstToken
(),
declarator
.
GetFirstToken
().
GetPreviousToken
(
includeSkipped
:
true
)))
{
editor
.
ReplaceNode
(
declaration
.
Type
,
(
t
,
g
)
=>
t
.
WithTrailingTrivia
(
SyntaxFactory
.
ElasticSpace
).
WithoutAnnotations
(
Formatter
.
Annotation
));
}
}
}
}
// get the type that we want to put in the out-var-decl based on the user's options.
// i.e. prefer 'out var' if that is what the user wants. Note: if we have:
//
// Method(out var x)
//
// Then the type is not-apparent, and we should not use var if the user only wants
// it for apparent types
// get the type that we want to put in the out-var-decl based on the user's options.
// i.e. prefer 'out var' if that is what the user wants. Note: if we have:
//
// Method(out var x)
//
// Then the type is not-apparent, and we should not use var if the user only wants
// it for apparent types
var
local
=
(
ILocalSymbol
)
semanticModel
.
GetDeclaredSymbol
(
declarator
);
var
newType
=
GenerateTypeSyntaxOrVar
(
local
.
Type
,
options
);
var
local
=
(
ILocalSymbol
)
semanticModel
.
GetDeclaredSymbol
(
declarator
);
var
newType
=
GenerateTypeSyntaxOrVar
(
local
.
Type
,
options
);
var
declarationExpression
=
GetDeclarationExpression
(
sourceText
,
identifier
,
newType
,
singleDeclarator
?
null
:
declarator
);
var
declarationExpression
=
GetDeclarationExpression
(
sourceText
,
identifier
,
newType
,
singleDeclarator
?
null
:
declarator
);
declarationsToAdd
.
Add
((
local
,
declarationExpression
,
identifier
));
}
// Check if using out-var changed problem semantics.
var
semanticsChanged
=
await
SemanticsChangedAsync
(
document
,
declaration
,
invocationOrCreation
,
newType
,
identifier
,
declarationExpression
,
cancellationToken
).
ConfigureAwait
(
false
);
if
(
semanticsChanged
&&
newType
.
IsVar
)
document
,
declarationsToAdd
,
invocationOrCreation
,
cancellationToken
).
ConfigureAwait
(
false
);
if
(
semanticsChanged
)
{
// Switching to 'var' changed semantics. Just use the original type of the local.
// If the user originally wrote it something other than 'var', then use what they
// wrote. Otherwise, synthesize the actual type of the local.
var
explicitType
=
declaration
.
Type
.
IsVar
?
local
.
Type
?.
GenerateTypeSyntax
()
:
declaration
.
Type
;
declarationExpression
=
GetDeclarationExpression
(
sourceText
,
identifier
,
explicitType
,
singleDeclarator
?
null
:
declarator
);
}
foreach
((
var
local
,
var
declaration
,
var
identifier
)
in
declarationsToAdd
)
{
var
explicitType
=
declaration
.
Type
.
IsVar
?
local
.
Type
?.
GenerateTypeSyntax
()
:
declaration
.
Type
;
var
newDeclaration
=
SyntaxFactory
.
DeclarationExpression
(
explicitType
,
declaration
.
Designation
);
editor
.
ReplaceNode
(
identifier
,
declarationExpression
);
editor
.
ReplaceNode
(
identifier
,
newDeclaration
);
}
}
else
{
foreach
((
var
_
,
var
declaration
,
var
identifier
)
in
declarationsToAdd
)
{
editor
.
ReplaceNode
(
identifier
,
declaration
);
}
}
}
public
static
TypeSyntax
GenerateTypeSyntaxOrVar
(
...
...
@@ -253,15 +271,14 @@ private static IEnumerable<SyntaxTrivia> MassageTrivia(IEnumerable<SyntaxTrivia>
}
private
async
Task
<
bool
>
SemanticsChangedAsync
(
Document
document
,
VariableDeclarationSyntax
declaration
,
ExpressionSyntax
invocationOrCreation
,
TypeSyntax
newType
,
IdentifierNameSyntax
identifier
,
DeclarationExpressionSyntax
declarationExpression
,
Document
document
,
List
<(
ILocalSymbol
,
DeclarationExpressionSyntax
,
IdentifierNameSyntax
)>
declarationsToAdd
,
ExpressionSyntax
invocationOrCreation
,
CancellationToken
cancellationToken
)
{
if
(
newType
.
IsVar
)
var
anyVar
=
declarationsToAdd
.
Any
(
t
=>
t
.
Item2
.
Type
.
IsVar
);
if
(
anyVar
)
{
// Options want us to use 'var' if we can. Make sure we didn't change
// the semantics of the call by doing this.
...
...
@@ -282,10 +299,15 @@ private static IEnumerable<SyntaxTrivia> MassageTrivia(IEnumerable<SyntaxTrivia>
return
true
;
}
var
updatedTopmostContainer
=
topmostContainer
;
var
annotation
=
new
SyntaxAnnotation
();
var
updatedTopmostContainer
=
topmostContainer
.
ReplaceNode
(
invocationOrCreation
,
invocationOrCreation
.
ReplaceNode
(
identifier
,
declarationExpression
)
foreach
((
var
_
,
var
declaration
,
var
identifier
)
in
declarationsToAdd
)
{
updatedTopmostContainer
=
updatedTopmostContainer
.
ReplaceNode
(
invocationOrCreation
,
invocationOrCreation
.
ReplaceNode
(
identifier
,
declaration
)
.
WithAdditionalAnnotations
(
annotation
));
}
if
(!
TryGetSpeculativeSemanticModel
(
semanticModel
,
topmostContainer
.
SpanStart
,
updatedTopmostContainer
,
out
var
speculativeModel
))
...
...
@@ -307,7 +329,7 @@ private static IEnumerable<SyntaxTrivia> MassageTrivia(IEnumerable<SyntaxTrivia>
return
false
;
}
private
SyntaxNode
GetTopmostContainer
(
ExpressionSyntax
expression
)
private
SyntaxNode
GetTopmostContainer
(
CSharpSyntaxNode
expression
)
{
return
expression
.
GetAncestorsOrThis
(
a
=>
a
is
StatementSyntax
||
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录