Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
5e36ef9f
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,发现更多精彩内容 >>
提交
5e36ef9f
编写于
11月 05, 2015
作者:
C
Cyrus Najmabadi
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Provide feature to simplify delegate invocations with C# 6 syntax.
上级
817ad74b
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
262 addition
and
0 deletion
+262
-0
src/Features/CSharp/Portable/CSharpFeatures.csproj
src/Features/CSharp/Portable/CSharpFeatures.csproj
+2
-0
src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs
...tures/CSharp/Portable/CSharpFeaturesResources.Designer.cs
+9
-0
src/Features/CSharp/Portable/CSharpFeaturesResources.resx
src/Features/CSharp/Portable/CSharpFeaturesResources.resx
+3
-0
src/Features/CSharp/Portable/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessAnalyzer.cs
...onalAccess/InvokeDelegateWithConditionalAccessAnalyzer.cs
+171
-0
src/Features/CSharp/Portable/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessCodeFixProvider.cs
...ess/InvokeDelegateWithConditionalAccessCodeFixProvider.cs
+76
-0
src/Features/Core/Portable/Diagnostics/Analyzers/IDEDiagnosticIds.cs
...s/Core/Portable/Diagnostics/Analyzers/IDEDiagnosticIds.cs
+1
-0
未找到文件。
src/Features/CSharp/Portable/CSharpFeatures.csproj
浏览文件 @
5e36ef9f
...
...
@@ -319,6 +319,8 @@
<Compile
Include=
"IntroduceVariable\CSharpIntroduceVariableService_IntroduceField.cs"
/>
<Compile
Include=
"IntroduceVariable\CSharpIntroduceVariableService_IntroduceLocal.cs"
/>
<Compile
Include=
"IntroduceVariable\CSharpIntroduceVariableService_IntroduceQueryLocal.cs"
/>
<Compile
Include=
"InvokeDelegateWithConditionalAccess\InvokeDelegateWithConditionalAccessAnalyzer.cs"
/>
<Compile
Include=
"InvokeDelegateWithConditionalAccess\InvokeDelegateWithConditionalAccessCodeFixProvider.cs"
/>
<Compile
Include=
"LanguageServices\CSharpAnonymousTypeDisplayService.cs"
/>
<Compile
Include=
"LanguageServices\CSharpSymbolDisplayService.cs"
/>
<Compile
Include=
"LanguageServices\CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs"
/>
...
...
src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs
浏览文件 @
5e36ef9f
...
...
@@ -232,6 +232,15 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Delegate invocation can be simplified..
/// </summary>
internal
static
string
DelegateInvocationCanBeSimplified
{
get
{
return
ResourceManager
.
GetString
(
"DelegateInvocationCanBeSimplified"
,
resourceCulture
);
}
}
/// <summary>
/// Looks up a localized string similar to deprecated.
/// </summary>
...
...
src/Features/CSharp/Portable/CSharpFeaturesResources.resx
浏览文件 @
5e36ef9f
...
...
@@ -437,4 +437,7 @@
<data
name=
"HideBase"
xml:space=
"preserve"
>
<value>
Hide base member
</value>
</data>
<data
name=
"DelegateInvocationCanBeSimplified"
xml:space=
"preserve"
>
<value>
Delegate invocation can be simplified.
</value>
</data>
</root>
\ No newline at end of file
src/Features/CSharp/Portable/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessAnalyzer.cs
0 → 100644
浏览文件 @
5e36ef9f
using
System.Collections.Generic
;
using
System.Collections.Immutable
;
using
Microsoft.CodeAnalysis.CSharp.Syntax
;
using
Microsoft.CodeAnalysis.Diagnostics
;
using
Microsoft.CodeAnalysis.Text
;
namespace
Microsoft.CodeAnalysis.CSharp.InvokeDelegateWithConditionalAccess
{
[
DiagnosticAnalyzer
(
LanguageNames
.
CSharp
)]
internal
class
InvokeDelegateWithConditionalAccessAnalyzer
:
DiagnosticAnalyzer
{
private
static
readonly
DiagnosticDescriptor
descriptor
=
new
DiagnosticDescriptor
(
IDEDiagnosticIds
.
InvokeDelegateWithConditionalAccessId
,
CSharpFeaturesResources
.
DelegateInvocationCanBeSimplified
,
CSharpFeaturesResources
.
DelegateInvocationCanBeSimplified
,
DiagnosticCategory
.
Style
,
DiagnosticSeverity
.
Hidden
,
isEnabledByDefault
:
true
,
customTags
:
DiagnosticCustomTags
.
Unnecessary
);
public
override
ImmutableArray
<
DiagnosticDescriptor
>
SupportedDiagnostics
{
get
;
}
=
ImmutableArray
.
Create
(
descriptor
);
public
override
void
Initialize
(
AnalysisContext
context
)
{
context
.
RegisterSyntaxNodeAction
(
SyntaxNodeAction
,
SyntaxKind
.
IfStatement
);
}
private
void
SyntaxNodeAction
(
SyntaxNodeAnalysisContext
syntaxContext
)
{
// look for the form "if (a != null)" or "if (null != a)"
var
ifStatement
=
(
IfStatementSyntax
)
syntaxContext
.
Node
;
if
(!
ifStatement
.
Condition
.
IsKind
(
SyntaxKind
.
NotEqualsExpression
))
{
return
;
}
if
(
ifStatement
.
Else
!=
null
)
{
return
;
}
var
binaryExpression
=
(
BinaryExpressionSyntax
)
ifStatement
.
Condition
;
if
(!
IsNotEqualsTest
(
binaryExpression
.
Left
,
binaryExpression
.
Right
)
&&
!
IsNotEqualsTest
(
binaryExpression
.
Right
,
binaryExpression
.
Left
))
{
return
;
}
// Check for both: "if (...) { a(); }" and "if (...) a();"
var
statement
=
ifStatement
.
Statement
;
if
(
statement
.
IsKind
(
SyntaxKind
.
Block
))
{
var
block
=
(
BlockSyntax
)
statement
;
if
(
block
.
Statements
.
Count
!=
1
)
{
return
;
}
statement
=
block
.
Statements
[
0
];
}
if
(!
statement
.
Parent
.
IsKind
(
SyntaxKind
.
Block
))
{
return
;
}
if
(!
statement
.
IsKind
(
SyntaxKind
.
ExpressionStatement
))
{
return
;
}
var
expressionStatement
=
(
ExpressionStatementSyntax
)
statement
;
// Check that it's of the form: "if (a != null) { a(); }}
var
invocationExpression
=
((
ExpressionStatementSyntax
)
statement
).
Expression
;
if
(!
invocationExpression
.
IsKind
(
SyntaxKind
.
InvocationExpression
))
{
return
;
}
var
expression
=
((
InvocationExpressionSyntax
)
invocationExpression
).
Expression
;
if
(!
expression
.
IsKind
(
SyntaxKind
.
IdentifierName
))
{
return
;
}
var
conditionName
=
binaryExpression
.
Left
is
IdentifierNameSyntax
?
(
IdentifierNameSyntax
)
binaryExpression
.
Left
:
(
IdentifierNameSyntax
)
binaryExpression
.
Right
;
var
invocationName
=
(
IdentifierNameSyntax
)
expression
;
if
(!
Equals
(
conditionName
.
Identifier
.
ValueText
,
invocationName
.
Identifier
.
ValueText
))
{
return
;
}
// Now make sure the previous statement is "var a = ..."
var
parentBlock
=
(
BlockSyntax
)
ifStatement
.
Parent
;
var
ifIndex
=
parentBlock
.
Statements
.
IndexOf
(
ifStatement
);
if
(
ifIndex
==
0
)
{
return
;
}
var
previousStatement
=
parentBlock
.
Statements
[
ifIndex
-
1
];
if
(!
previousStatement
.
IsKind
(
SyntaxKind
.
LocalDeclarationStatement
))
{
return
;
}
var
localDeclarationStatement
=
(
LocalDeclarationStatementSyntax
)
previousStatement
;
var
variableDeclaration
=
localDeclarationStatement
.
Declaration
;
if
(
variableDeclaration
.
Variables
.
Count
!=
1
)
{
return
;
}
var
declarator
=
variableDeclaration
.
Variables
[
0
];
if
(
declarator
.
Initializer
==
null
)
{
return
;
}
if
(!
Equals
(
declarator
.
Identifier
.
ValueText
,
conditionName
.
Identifier
.
ValueText
))
{
return
;
}
// Syntactically this looks good. Now make sure that the local is a delegate type.
var
semanticModel
=
syntaxContext
.
SemanticModel
;
var
localSymbol
=
(
ILocalSymbol
)
semanticModel
.
GetDeclaredSymbol
(
declarator
);
if
(
localSymbol
.
Type
.
TypeKind
!=
TypeKind
.
Delegate
)
{
return
;
}
// Ok, we made a local just to check it for null and invoke it. Looks like something
// we can suggest an improvement for!
// But first make sure we're only using the local only within the body of this if statement.
var
analysis
=
semanticModel
.
AnalyzeDataFlow
(
localDeclarationStatement
,
ifStatement
);
if
(
analysis
.
ReadOutside
.
Contains
(
localSymbol
)
||
analysis
.
WrittenOutside
.
Contains
(
localSymbol
))
{
return
;
}
// Looks good!
var
tree
=
semanticModel
.
SyntaxTree
;
var
additionalLocations
=
new
List
<
Location
>
{
Location
.
Create
(
tree
,
localDeclarationStatement
.
Span
),
Location
.
Create
(
tree
,
ifStatement
.
Span
),
Location
.
Create
(
tree
,
expressionStatement
.
Span
)
};
syntaxContext
.
ReportDiagnostic
(
Diagnostic
.
Create
(
descriptor
,
Location
.
Create
(
tree
,
TextSpan
.
FromBounds
(
localDeclarationStatement
.
SpanStart
,
invocationExpression
.
SpanStart
)),
additionalLocations
));
if
(
localDeclarationStatement
.
Span
.
End
!=
ifStatement
.
Span
.
End
)
{
syntaxContext
.
ReportDiagnostic
(
Diagnostic
.
Create
(
descriptor
,
Location
.
Create
(
tree
,
TextSpan
.
FromBounds
(
expressionStatement
.
Span
.
End
,
ifStatement
.
Span
.
End
)),
additionalLocations
));
}
}
private
bool
IsNotEqualsTest
(
ExpressionSyntax
left
,
ExpressionSyntax
right
)
=>
left
.
IsKind
(
SyntaxKind
.
IdentifierName
)
&&
right
.
IsKind
(
SyntaxKind
.
NullLiteralExpression
);
}
}
\ No newline at end of file
src/Features/CSharp/Portable/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessCodeFixProvider.cs
0 → 100644
浏览文件 @
5e36ef9f
using
System
;
using
System.Collections.Immutable
;
using
System.Composition
;
using
System.Linq
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Microsoft.CodeAnalysis.CodeActions
;
using
Microsoft.CodeAnalysis.CodeFixes
;
using
Microsoft.CodeAnalysis.CSharp.Extensions
;
using
Microsoft.CodeAnalysis.CSharp.Syntax
;
using
Microsoft.CodeAnalysis.Diagnostics
;
using
Microsoft.CodeAnalysis.Formatting
;
using
Roslyn.Utilities
;
namespace
Microsoft.CodeAnalysis.CSharp.InvokeDelegateWithConditionalAccess
{
[
ExportCodeFixProvider
(
LanguageNames
.
CSharp
,
Name
=
nameof
(
InvokeDelegateWithConditionalAccessCodeFixProvider
)),
Shared
]
internal
class
InvokeDelegateWithConditionalAccessCodeFixProvider
:
CodeFixProvider
{
public
override
ImmutableArray
<
string
>
FixableDiagnosticIds
{
get
;
}
=
ImmutableArray
.
Create
(
IDEDiagnosticIds
.
InvokeDelegateWithConditionalAccessId
);
public
override
FixAllProvider
GetFixAllProvider
()
=>
BatchFixAllProvider
.
Instance
;
public
override
Task
RegisterCodeFixesAsync
(
CodeFixContext
context
)
{
context
.
RegisterCodeFix
(
new
MyCodeAction
(
CSharpFeaturesResources
.
DelegateInvocationCanBeSimplified
,
async
c
=>
await
UpdateDocumentAsync
(
context
).
ConfigureAwait
(
false
),
equivalenceKey
:
nameof
(
InvokeDelegateWithConditionalAccessCodeFixProvider
)),
context
.
Diagnostics
);
return
Task
.
FromResult
(
false
);
}
private
async
Task
<
Document
>
UpdateDocumentAsync
(
CodeFixContext
context
)
{
var
document
=
context
.
Document
;
var
cancellationToken
=
context
.
CancellationToken
;
var
root
=
await
document
.
GetSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
diagnostic
=
context
.
Diagnostics
.
First
();
var
localDeclarationLocation
=
diagnostic
.
AdditionalLocations
[
0
];
var
ifStatementLocation
=
diagnostic
.
AdditionalLocations
[
1
];
var
expressionStatementLocation
=
diagnostic
.
AdditionalLocations
[
2
];
var
localDeclarationStatement
=
(
LocalDeclarationStatementSyntax
)
root
.
FindNode
(
localDeclarationLocation
.
SourceSpan
);
var
expressionStatement
=
(
ExpressionStatementSyntax
)
root
.
FindNode
(
expressionStatementLocation
.
SourceSpan
);
var
ifStatement
=
(
IfStatementSyntax
)
root
.
FindNode
(
ifStatementLocation
.
SourceSpan
);
var
invocationExpression
=
(
InvocationExpressionSyntax
)
expressionStatement
.
Expression
;
var
parentBlock
=
(
BlockSyntax
)
localDeclarationStatement
.
Parent
;
var
newStatement
=
expressionStatement
.
WithExpression
(
SyntaxFactory
.
ConditionalAccessExpression
(
localDeclarationStatement
.
Declaration
.
Variables
[
0
].
Initializer
.
Value
.
Parenthesize
(),
SyntaxFactory
.
InvocationExpression
(
SyntaxFactory
.
MemberBindingExpression
(
SyntaxFactory
.
IdentifierName
(
"Invoke"
)),
invocationExpression
.
ArgumentList
)));
newStatement
=
newStatement
.
WithAdditionalAnnotations
(
Formatter
.
Annotation
);
var
newStatements
=
parentBlock
.
Statements
.
TakeWhile
(
s
=>
s
!=
localDeclarationStatement
)
.
Concat
(
newStatement
)
.
Concat
(
parentBlock
.
Statements
.
SkipWhile
(
s
=>
s
!=
ifStatement
).
Skip
(
1
));
var
newBlock
=
parentBlock
.
WithStatements
(
SyntaxFactory
.
List
(
newStatements
));
return
document
.
WithSyntaxRoot
(
root
.
ReplaceNode
(
parentBlock
,
newBlock
));
}
private
class
MyCodeAction
:
CodeAction
.
DocumentChangeAction
{
public
MyCodeAction
(
string
title
,
Func
<
CancellationToken
,
Task
<
Document
>>
createChangedDocument
,
string
equivalenceKey
)
:
base
(
title
,
createChangedDocument
,
equivalenceKey
)
{
}
}
}
}
src/Features/Core/Portable/Diagnostics/Analyzers/IDEDiagnosticIds.cs
浏览文件 @
5e36ef9f
...
...
@@ -15,5 +15,6 @@ internal static class IDEDiagnosticIds
public
const
string
AnalyzerDependencyConflictId
=
"IDE1002"
;
public
const
string
MissingAnalyzerReferenceId
=
"IDE1003"
;
public
const
string
ErrorReadingRulesetId
=
"IDE1004"
;
public
const
string
InvokeDelegateWithConditionalAccessId
=
"IDE1005"
;
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录