Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
f3e22d72
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,发现更多精彩内容 >>
提交
f3e22d72
编写于
12月 05, 2017
作者:
S
Steve Chovanec
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
UseNullPropagation codefix doesn't recognize `x is null ? null : x.y` pattern (#23043)
上级
65438bd5
变更
7
隐藏空白更改
内联
并排
Showing
7 changed file
with
279 addition
and
23 deletion
+279
-23
src/EditorFeatures/CSharpTest/UseNullPropagation/UseNullPropagationTests.cs
.../CSharpTest/UseNullPropagation/UseNullPropagationTests.cs
+120
-0
src/Features/CSharp/Portable/UseNullPropagation/CSharpUseNullPropagationDiagnosticAnalyzer.cs
...Propagation/CSharpUseNullPropagationDiagnosticAnalyzer.cs
+23
-0
src/Features/Core/Portable/UseNullPropagation/AbstractUseNullPropagationDiagnosticAnalyzer.cs
...opagation/AbstractUseNullPropagationDiagnosticAnalyzer.cs
+111
-14
src/Features/VisualBasic/Portable/UseNullPropagation/VisualBasicUseNullPropagationDiagnosticAnalyzer.vb
...gation/VisualBasicUseNullPropagationDiagnosticAnalyzer.vb
+8
-1
src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs
...arp/Portable/LanguageServices/CSharpSyntaxFactsService.cs
+10
-7
src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs
...anguageServices/SyntaxFactsService/ISyntaxFactsService.cs
+3
-1
src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb
...ortable/LanguageServices/VisualBasicSyntaxFactsService.vb
+4
-0
未找到文件。
src/EditorFeatures/CSharpTest/UseNullPropagation/UseNullPropagationTests.cs
浏览文件 @
f3e22d72
...
...
@@ -478,5 +478,125 @@ void Main(S? s)
}
"
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUseNullPropagation
)]
public
async
Task
TestWithNullableTypeAndIsNull
()
{
await
TestInRegularAndScriptAsync
(
@"
class C
{
public int? f;
void M(C c)
{
int? x = [||]c is null ? null : c.f;
}
}"
,
@"
class C
{
public int? f;
void M(C c)
{
int? x = c?.f;
}
}"
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUseNullPropagation
)]
public
async
Task
TestWithNullableTypeAndReferenceEquals
()
{
await
TestInRegularAndScriptAsync
(
@"
class C
{
public int? f;
void M(C c)
{
int? x = [||]ReferenceEquals(c, null) ? null : c.f;
}
}"
,
@"
class C
{
public int? f;
void M(C c)
{
int? x = c?.f;
}
}"
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUseNullPropagation
)]
public
async
Task
TestWithNullableTypeAndReferenceEqualsReversed
()
{
await
TestInRegularAndScriptAsync
(
@"
class C
{
public int? f;
void M(C c)
{
int? x = [||]ReferenceEquals(null, c) ? null : c.f;
}
}"
,
@"
class C
{
public int? f;
void M(C c)
{
int? x = c?.f;
}
}"
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUseNullPropagation
)]
public
async
Task
TestWithNullableTypeAndReferenceEqualsWithObject
()
{
await
TestInRegularAndScriptAsync
(
@"
class C
{
public int? f;
void M(C c)
{
int? x = [||]object.ReferenceEquals(c, null) ? null : c.f;
}
}"
,
@"
class C
{
public int? f;
void M(C c)
{
int? x = c?.f;
}
}"
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUseNullPropagation
)]
public
async
Task
TestWithNullableTypeAndReferenceEqualsWithObjectReversed
()
{
await
TestInRegularAndScriptAsync
(
@"
class C
{
public int? f;
void M(C c)
{
int? x = [||]object.ReferenceEquals(null, c) ? null : c.f;
}
}"
,
@"
class C
{
public int? f;
void M(C c)
{
int? x = c?.f;
}
}"
);
}
}
}
src/Features/CSharp/Portable/UseNullPropagation/CSharpUseNullPropagationDiagnosticAnalyzer.cs
浏览文件 @
f3e22d72
...
...
@@ -37,5 +37,28 @@ protected override bool IsEquals(BinaryExpressionSyntax condition)
protected
override
bool
IsNotEquals
(
BinaryExpressionSyntax
condition
)
=>
condition
.
Kind
()
==
SyntaxKind
.
NotEqualsExpression
;
protected
override
bool
TryAnalyzePatternCondition
(
SyntaxNode
conditionNode
,
out
SyntaxNode
conditionLeft
,
out
SyntaxNode
conditionRight
,
out
bool
isEquals
)
{
conditionLeft
=
null
;
conditionRight
=
null
;
isEquals
=
true
;
var
patternExpression
=
conditionNode
as
IsPatternExpressionSyntax
;
if
(
patternExpression
==
null
)
{
return
false
;
}
var
constantPattern
=
patternExpression
.
Pattern
as
ConstantPatternSyntax
;
if
(
constantPattern
==
null
)
{
return
false
;
}
conditionLeft
=
patternExpression
.
Expression
;
conditionRight
=
constantPattern
.
Expression
;
return
true
;
}
}
}
src/Features/Core/Portable/UseNullPropagation/AbstractUseNullPropagationDiagnosticAnalyzer.cs
浏览文件 @
f3e22d72
...
...
@@ -2,6 +2,7 @@
using
System
;
using
System.Collections.Immutable
;
using
System.Linq
;
using
Microsoft.CodeAnalysis.CodeStyle
;
using
Microsoft.CodeAnalysis.Diagnostics
;
using
Microsoft.CodeAnalysis.LanguageServices
;
...
...
@@ -49,18 +50,27 @@ protected AbstractUseNullPropagationDiagnosticAnalyzer()
protected
abstract
ISyntaxFactsService
GetSyntaxFactsService
();
protected
abstract
ISemanticFactsService
GetSemanticFactsService
();
protected
abstract
bool
TryAnalyzePatternCondition
(
SyntaxNode
conditionNode
,
out
SyntaxNode
conditionLeft
,
out
SyntaxNode
conditionRight
,
out
bool
isEquals
);
protected
override
void
InitializeWorker
(
AnalysisContext
context
)
{
context
.
RegisterCompilationStartAction
(
startContext
=>
{
var
expressionTypeOpt
=
startContext
.
Compilation
.
GetTypeByMetadataName
(
"System.Linq.Expressions.Expression`1"
);
var
objectType
=
startContext
.
Compilation
.
GetSpecialType
(
SpecialType
.
System_Object
);
var
referenceEqualsMethod
=
objectType
?.
GetMembers
(
nameof
(
ReferenceEquals
))
.
OfType
<
IMethodSymbol
>()
.
FirstOrDefault
(
m
=>
m
.
DeclaredAccessibility
==
Accessibility
.
Public
&&
m
.
Parameters
.
Length
==
2
);
startContext
.
RegisterSyntaxNodeAction
(
c
=>
AnalyzeSyntax
(
c
,
expressionTypeOpt
),
GetSyntaxKindToAnalyze
());
c
=>
AnalyzeSyntax
(
c
,
expressionTypeOpt
,
referenceEqualsMethod
),
GetSyntaxKindToAnalyze
());
});
}
private
void
AnalyzeSyntax
(
SyntaxNodeAnalysisContext
context
,
INamedTypeSymbol
expressionTypeOpt
)
private
void
AnalyzeSyntax
(
SyntaxNodeAnalysisContext
context
,
INamedTypeSymbol
expressionTypeOpt
,
IMethodSymbol
referenceEqualsMethod
)
{
var
conditionalExpression
=
(
TConditionalExpressionSyntax
)
context
.
Node
;
if
(!
ShouldAnalyze
(
conditionalExpression
.
SyntaxTree
.
Options
))
...
...
@@ -88,21 +98,13 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context, INamedTypeSymbol e
conditionNode
=
syntaxFacts
.
WalkDownParentheses
(
conditionNode
);
var
condition
=
conditionNode
as
TBinaryExpressionSyntax
;
if
(
condition
==
null
)
var
isEqualityLikeCondition
=
TryAnalyzeCondition
(
context
,
syntaxFacts
,
referenceEqualsMethod
,
conditionNode
,
out
var
conditionLeft
,
out
var
conditionRight
,
out
var
isEquals
);
if
(!
isEqualityLikeCondition
)
{
return
;
}
var
isEquals
=
IsEquals
(
condition
);
var
isNotEquals
=
IsNotEquals
(
condition
);
if
(!
isEquals
&&
!
isNotEquals
)
{
return
;
}
syntaxFacts
.
GetPartsOfBinaryExpression
(
condition
,
out
var
conditionLeft
,
out
var
conditionRight
);
var
conditionLeftIsNull
=
syntaxFacts
.
IsNullLiteralExpression
(
conditionLeft
);
var
conditionRightIsNull
=
syntaxFacts
.
IsNullLiteralExpression
(
conditionRight
);
...
...
@@ -125,7 +127,7 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context, INamedTypeSymbol e
return
;
}
if
(
isNot
Equals
&&
!
syntaxFacts
.
IsNullLiteralExpression
(
whenFalseNode
))
if
(
!
is
Equals
&&
!
syntaxFacts
.
IsNullLiteralExpression
(
whenFalseNode
))
{
return
;
}
...
...
@@ -181,9 +183,94 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context, INamedTypeSymbol e
properties
));
}
private
bool
TryAnalyzeCondition
(
SyntaxNodeAnalysisContext
context
,
ISyntaxFactsService
syntaxFacts
,
IMethodSymbol
referenceEqualsMethod
,
SyntaxNode
conditionNode
,
out
SyntaxNode
conditionLeft
,
out
SyntaxNode
conditionRight
,
out
bool
isEquals
)
{
switch
(
conditionNode
)
{
case
TBinaryExpressionSyntax
binaryExpression
:
return
TryAnalyzeBinaryExpressionCondition
(
syntaxFacts
,
binaryExpression
,
out
conditionLeft
,
out
conditionRight
,
out
isEquals
);
case
TInvocationExpression
invocation
:
return
TryAnalyzeInvocationCondition
(
context
,
syntaxFacts
,
referenceEqualsMethod
,
invocation
,
out
conditionLeft
,
out
conditionRight
,
out
isEquals
);
default
:
return
TryAnalyzePatternCondition
(
conditionNode
,
out
conditionLeft
,
out
conditionRight
,
out
isEquals
);
}
}
private
bool
TryAnalyzeBinaryExpressionCondition
(
ISyntaxFactsService
syntaxFacts
,
TBinaryExpressionSyntax
condition
,
out
SyntaxNode
conditionLeft
,
out
SyntaxNode
conditionRight
,
out
bool
isEquals
)
{
isEquals
=
IsEquals
(
condition
);
if
(!
isEquals
&&
!
IsNotEquals
(
condition
))
{
conditionLeft
=
null
;
conditionRight
=
null
;
return
false
;
}
else
{
syntaxFacts
.
GetPartsOfBinaryExpression
(
condition
,
out
conditionLeft
,
out
conditionRight
);
return
true
;
}
}
private
static
bool
TryAnalyzeInvocationCondition
(
SyntaxNodeAnalysisContext
context
,
ISyntaxFactsService
syntaxFacts
,
IMethodSymbol
referenceEqualsMethod
,
TInvocationExpression
invocation
,
out
SyntaxNode
conditionLeft
,
out
SyntaxNode
conditionRight
,
out
bool
isEquals
)
{
conditionLeft
=
null
;
conditionRight
=
null
;
isEquals
=
true
;
var
expression
=
syntaxFacts
.
GetExpressionOfInvocationExpression
(
invocation
);
var
nameNode
=
syntaxFacts
.
IsIdentifierName
(
expression
)
?
expression
:
syntaxFacts
.
IsSimpleMemberAccessExpression
(
expression
)
?
syntaxFacts
.
GetNameOfMemberAccessExpression
(
expression
)
:
null
;
if
(!
syntaxFacts
.
IsIdentifierName
(
nameNode
))
{
return
false
;
}
syntaxFacts
.
GetNameAndArityOfSimpleName
(
nameNode
,
out
var
name
,
out
_
);
if
(!
syntaxFacts
.
StringComparer
.
Equals
(
name
,
nameof
(
ReferenceEquals
)))
{
return
false
;
}
var
arguments
=
syntaxFacts
.
GetArgumentsOfInvocationExpression
(
invocation
);
if
(
arguments
.
Count
!=
2
)
{
return
false
;
}
var
semanticModel
=
context
.
SemanticModel
;
var
cancellationToken
=
context
.
CancellationToken
;
var
symbol
=
semanticModel
.
GetSymbolInfo
(
invocation
,
cancellationToken
).
Symbol
;
if
(!
referenceEqualsMethod
.
Equals
(
symbol
))
{
return
false
;
}
conditionLeft
=
syntaxFacts
.
GetExpressionOfArgument
(
arguments
[
0
]);
conditionRight
=
syntaxFacts
.
GetExpressionOfArgument
(
arguments
[
1
]);
return
true
;
}
internal
static
SyntaxNode
GetWhenPartMatch
(
ISyntaxFactsService
syntaxFacts
,
ISemanticFactsService
semanticFacts
,
SemanticModel
semanticModel
,
SyntaxNode
expressionToMatch
,
SyntaxNode
whenPart
)
{
expressionToMatch
=
GetExpressionIfArgument
(
syntaxFacts
,
expressionToMatch
);
expressionToMatch
=
RemoveObjectCastIfAny
(
syntaxFacts
,
semanticModel
,
expressionToMatch
);
var
current
=
whenPart
;
while
(
true
)
...
...
@@ -207,6 +294,16 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context, INamedTypeSymbol e
}
}
private
static
SyntaxNode
GetExpressionIfArgument
(
ISyntaxFactsService
syntaxFacts
,
SyntaxNode
node
)
{
if
(
syntaxFacts
.
IsArgument
(
node
))
{
return
syntaxFacts
.
GetExpressionOfArgument
(
node
);
}
return
node
;
}
private
static
SyntaxNode
RemoveObjectCastIfAny
(
ISyntaxFactsService
syntaxFacts
,
SemanticModel
semanticModel
,
SyntaxNode
node
)
{
if
(
syntaxFacts
.
IsCastExpression
(
node
))
...
...
src/Features/VisualBasic/Portable/UseNullPropagation/VisualBasicUseNullPropagationDiagnosticAnalyzer.vb
浏览文件 @
f3e22d72
...
...
@@ -27,7 +27,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseNullPropagation
End
Function
Protected
Overrides
Function
GetSemanticFactsService
()
As
ISemanticFactsService
Return
VisualBasicSemanticFactsService
.
i
nstance
Return
VisualBasicSemanticFactsService
.
I
nstance
End
Function
Protected
Overrides
Function
GetSyntaxKindToAnalyze
()
As
SyntaxKind
...
...
@@ -41,5 +41,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseNullPropagation
Protected
Overrides
Function
IsNotEquals
(
condition
As
BinaryExpressionSyntax
)
As
Boolean
Return
condition
.
Kind
()
=
SyntaxKind
.
IsNotExpression
End
Function
Protected
Overrides
Function
TryAnalyzePatternCondition
(
conditionNode
As
SyntaxNode
,
ByRef
conditionLeft
As
SyntaxNode
,
ByRef
conditionRight
As
SyntaxNode
,
ByRef
isEquals
As
Boolean
)
As
Boolean
conditionLeft
=
Nothing
conditionRight
=
Nothing
isEquals
=
False
Return
False
End
Function
End
Class
End
Namespace
src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs
浏览文件 @
f3e22d72
...
...
@@ -41,7 +41,7 @@ public SyntaxTrivia ElasticCarriageReturnLineFeed
protected
override
IDocumentationCommentService
DocumentationCommentService
=>
CSharpDocumentationCommentService
.
Instance
;
public
bool
SupportsIndexingInitializer
(
ParseOptions
options
)
public
bool
SupportsIndexingInitializer
(
ParseOptions
options
)
=>
((
CSharpParseOptions
)
options
).
LanguageVersion
>=
LanguageVersion
.
CSharp6
;
public
bool
SupportsThrowExpression
(
ParseOptions
options
)
...
...
@@ -280,10 +280,10 @@ public bool IsReturnStatement(SyntaxNode node)
public
bool
IsStatement
(
SyntaxNode
node
)
=>
node
is
StatementSyntax
;
public
bool
IsParameter
(
SyntaxNode
node
)
=>
node
is
ParameterSyntax
;
public
bool
IsVariableDeclarator
(
SyntaxNode
node
)
=>
node
is
VariableDeclaratorSyntax
;
...
...
@@ -651,6 +651,9 @@ public SyntaxNode GetExpressionOfArgument(SyntaxNode node)
public
RefKind
GetRefKindOfArgument
(
SyntaxNode
node
)
=>
(
node
as
ArgumentSyntax
).
GetRefKind
();
public
bool
IsArgument
(
SyntaxNode
node
)
=>
node
is
ArgumentSyntax
;
public
bool
IsSimpleArgument
(
SyntaxNode
node
)
{
var
argument
=
(
ArgumentSyntax
)
node
;
...
...
@@ -1443,7 +1446,7 @@ public bool IsDeclaration(SyntaxNode node)
private
static
readonly
SyntaxAnnotation
s_annotation
=
new
SyntaxAnnotation
();
public
void
AddFirstMissingCloseBrace
(
SyntaxNode
root
,
SyntaxNode
contextNode
,
SyntaxNode
root
,
SyntaxNode
contextNode
,
out
SyntaxNode
newRoot
,
out
SyntaxNode
newContextNode
)
{
// First, annotate the context node in the tree so that we can find it again
...
...
@@ -1608,9 +1611,9 @@ public override bool IsShebangDirectiveTrivia(SyntaxTrivia trivia)
public
override
bool
IsPreprocessorDirective
(
SyntaxTrivia
trivia
)
=>
SyntaxFacts
.
IsPreprocessorDirective
(
trivia
.
Kind
());
private
class
AddFirstMissingCloseBaceRewriter
:
CSharpSyntaxRewriter
private
class
AddFirstMissingCloseBaceRewriter
:
CSharpSyntaxRewriter
{
private
readonly
SyntaxNode
_contextNode
;
private
readonly
SyntaxNode
_contextNode
;
private
bool
_seenContextNode
=
false
;
private
bool
_addedFirstCloseCurly
=
false
;
...
...
@@ -1647,7 +1650,7 @@ public override SyntaxNode Visit(SyntaxNode node)
// then still ask to format its close curly to make sure all the
// curlies up the stack are properly formatted.
var
braces
=
rewritten
.
GetBraces
();
if
(
braces
.
openBrace
.
Kind
()
==
SyntaxKind
.
None
&&
if
(
braces
.
openBrace
.
Kind
()
==
SyntaxKind
.
None
&&
braces
.
closeBrace
.
Kind
()
==
SyntaxKind
.
None
)
{
// Not an item with braces. Just pass it up.
...
...
src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs
浏览文件 @
f3e22d72
...
...
@@ -160,6 +160,8 @@ internal interface ISyntaxFactsService : ILanguageService
SyntaxToken
GetIdentifierOfSimpleName
(
SyntaxNode
node
);
SyntaxToken
GetIdentifierOfVariableDeclarator
(
SyntaxNode
node
);
bool
IsArgument
(
SyntaxNode
node
);
/// <summary>
/// True if this is an argument with just an expression and nothing else (i.e. no ref/out,
/// no named params, no omitted args).
...
...
@@ -319,7 +321,7 @@ internal interface ISyntaxFactsService : ILanguageService
// updates root will be returned. The context node in that new tree will also
// be returned.
void
AddFirstMissingCloseBrace
(
SyntaxNode
root
,
SyntaxNode
contextNode
,
SyntaxNode
root
,
SyntaxNode
contextNode
,
out
SyntaxNode
newRoot
,
out
SyntaxNode
newContextNode
);
SyntaxNode
GetNextExecutableStatement
(
SyntaxNode
statement
);
...
...
src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb
浏览文件 @
f3e22d72
...
...
@@ -582,6 +582,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return
RefKind
.
None
End
Function
Public
Function
IsArgument
(
node
As
SyntaxNode
)
As
Boolean
Implements
ISyntaxFactsService
.
IsArgument
Return
TypeOf
node
Is
ArgumentSyntax
End
Function
Public
Function
IsSimpleArgument
(
node
As
SyntaxNode
)
As
Boolean
Implements
ISyntaxFactsService
.
IsSimpleArgument
Dim
argument
=
DirectCast
(
node
,
ArgumentSyntax
)
Return
Not
argument
.
IsNamed
AndAlso
Not
argument
.
IsOmitted
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录