Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
6b513abf
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,发现更多精彩内容 >>
提交
6b513abf
编写于
10月 21, 2018
作者:
C
Cyrus Najmabadi
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add tests.
上级
f95133eb
变更
7
隐藏空白更改
内联
并排
Showing
7 changed file
with
194 addition
and
107 deletion
+194
-107
src/EditorFeatures/CSharpTest/UseIndexOperator/UseIndexOperatorTests.cs
...ures/CSharpTest/UseIndexOperator/UseIndexOperatorTests.cs
+11
-11
src/EditorFeatures/CSharpTest/UseIndexOperator/UseRangeOperatorTests.cs
...ures/CSharpTest/UseIndexOperator/UseRangeOperatorTests.cs
+6
-6
src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorCodeFixProvider.cs
...UseIndexOperator/CSharpUseRangeOperatorCodeFixProvider.cs
+128
-10
src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs
...tor/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs
+1
-1
src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.MemberInfo.cs
...or/CSharpUseRangeOperatorDiagnosticAnalyzer.MemberInfo.cs
+1
-1
src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs
...IndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs
+44
-78
src/Features/CSharp/Portable/UseIndexOperator/Helpers.cs
src/Features/CSharp/Portable/UseIndexOperator/Helpers.cs
+3
-0
未找到文件。
src/EditorFeatures/CSharpTest/UseIndexOperator/UseIndexOperatorTests.cs
浏览文件 @
6b513abf
...
...
@@ -37,7 +37,7 @@ void Goo(string s)
parseOptions
:
CSharpParseOptions
.
Default
.
WithLanguageVersion
(
LanguageVersion
.
CSharp7
)));
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
DefaultLiteral
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
IndexOperator
)]
public
async
Task
TestSimple
()
{
await
TestAsync
(
...
...
@@ -59,7 +59,7 @@ void Goo(string s)
}"
,
parseOptions
:
s_parseOptions
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
DefaultLiteral
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
IndexOperator
)]
public
async
Task
TestComplexSubtaction
()
{
await
TestAsync
(
...
...
@@ -81,7 +81,7 @@ void Goo(string s)
}"
,
parseOptions
:
s_parseOptions
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
DefaultLiteral
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
IndexOperator
)]
public
async
Task
TestComplexInstance
()
{
await
TestAsync
(
...
...
@@ -107,7 +107,7 @@ void Goo(string[] ss)
}"
,
parseOptions
:
s_parseOptions
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
DefaultLiteral
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
IndexOperator
)]
public
async
Task
TestNotWithoutSubtraction1
()
{
await
TestMissingAsync
(
...
...
@@ -121,7 +121,7 @@ void Goo(string s)
}"
,
parameters
:
s_testParameters
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
DefaultLiteral
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
IndexOperator
)]
public
async
Task
TestNotWithoutSubtraction2
()
{
await
TestMissingAsync
(
...
...
@@ -135,7 +135,7 @@ void Goo(string s)
}"
,
parameters
:
s_testParameters
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
DefaultLiteral
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
IndexOperator
)]
public
async
Task
TestNotWithoutMultipleArgs
()
{
await
TestMissingAsync
(
...
...
@@ -149,7 +149,7 @@ void Goo(string s)
}"
,
parameters
:
s_testParameters
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
DefaultLiteral
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
IndexOperator
)]
public
async
Task
TestUserDefinedTypeWithLength
()
{
await
TestAsync
(
...
...
@@ -175,7 +175,7 @@ void Goo(S s)
}"
,
parseOptions
:
s_parseOptions
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
DefaultLiteral
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
IndexOperator
)]
public
async
Task
TestUserDefinedTypeWithCount
()
{
await
TestAsync
(
...
...
@@ -201,7 +201,7 @@ void Goo(S s)
}"
,
parseOptions
:
s_parseOptions
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
DefaultLiteral
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
IndexOperator
)]
public
async
Task
TestUserDefinedTypeWithNoLengthOrCount
()
{
await
TestMissingAsync
(
...
...
@@ -217,7 +217,7 @@ void Goo(S s)
}"
,
parameters
:
s_testParameters
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
DefaultLiteral
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
IndexOperator
)]
public
async
Task
TestUserDefinedTypeWithNoInt32Indexer
()
{
await
TestMissingAsync
(
...
...
@@ -233,7 +233,7 @@ void Goo(S s)
}"
,
parameters
:
s_testParameters
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
DefaultLiteral
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
IndexOperator
)]
public
async
Task
TestUserDefinedTypeWithNoIndexIndexer
()
{
await
TestMissingAsync
(
...
...
src/EditorFeatures/CSharpTest/UseIndexOperator/UseRangeOperatorTests.cs
浏览文件 @
6b513abf
...
...
@@ -22,7 +22,7 @@ internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProvider
private
static
readonly
TestParameters
s_testParameters
=
new
TestParameters
(
parseOptions
:
s_parseOptions
);
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
Index
Operator
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
Range
Operator
)]
public
async
Task
TestNotInCSharp7
()
{
await
TestMissingAsync
(
...
...
@@ -37,7 +37,7 @@ void Goo(string s)
parseOptions
:
CSharpParseOptions
.
Default
.
WithLanguageVersion
(
LanguageVersion
.
CSharp7
)));
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
DefaultLiteral
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
RangeOperator
)]
public
async
Task
TestSimple
()
{
await
TestAsync
(
...
...
@@ -59,14 +59,14 @@ void Goo(string s)
}"
,
parseOptions
:
s_parseOptions
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
DefaultLiteral
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
RangeOperator
)]
public
async
Task
TestComplexSubstraction
()
{
await
TestAsync
(
@"
class C
{
void Goo(string s)
void Goo(string s
, int bar, int baz
)
{
var v = s.Substring([||]bar, s.Length - baz - bar);
}
...
...
@@ -74,14 +74,14 @@ void Goo(string s)
@"
class C
{
void Goo(string s)
void Goo(string s
, int bar, int baz
)
{
var v = s[bar..^baz];
}
}"
,
parseOptions
:
s_parseOptions
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
DefaultLiteral
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUse
RangeOperator
)]
public
async
Task
TestConstantSubtraction1
()
{
await
TestAsync
(
...
...
src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorCodeFixProvider.cs
浏览文件 @
6b513abf
...
...
@@ -7,11 +7,14 @@
using
System.Threading.Tasks
;
using
Microsoft.CodeAnalysis.CodeActions
;
using
Microsoft.CodeAnalysis.CodeFixes
;
using
Microsoft.CodeAnalysis.CSharp.CodeGeneration
;
using
Microsoft.CodeAnalysis.CSharp.Syntax
;
using
Microsoft.CodeAnalysis.Diagnostics
;
using
Microsoft.CodeAnalysis.Editing
;
using
Microsoft.CodeAnalysis.Formatting
;
using
Microsoft.CodeAnalysis.Operations
;
using
Microsoft.CodeAnalysis.Shared.Extensions
;
using
Roslyn.Utilities
;
namespace
Microsoft.CodeAnalysis.CSharp.UseIndexOperator
{
...
...
@@ -34,31 +37,29 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
return
Task
.
CompletedTask
;
}
protected
override
Task
FixAllAsync
(
protected
override
async
Task
FixAllAsync
(
Document
document
,
ImmutableArray
<
Diagnostic
>
diagnostics
,
SyntaxEditor
editor
,
CancellationToken
cancellationToken
)
{
var
semanticModel
=
await
document
.
GetSemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
foreach
(
var
diagnostic
in
diagnostics
)
{
FixOne
(
diagnostic
,
editor
,
cancellationToken
);
FixOne
(
semanticModel
,
editor
,
diagnostic
,
cancellationToken
);
}
return
Task
.
CompletedTask
;
}
private
void
FixOne
(
Diagnostic
diagnostic
,
SyntaxEditor
editor
,
CancellationToken
cancellationToken
)
SemanticModel
semanticModel
,
SyntaxEditor
editor
,
Diagnostic
diagnostic
,
CancellationToken
cancellationToken
)
{
var
invocation
=
(
InvocationExpressionSyntax
)
diagnostic
.
AdditionalLocations
[
0
].
FindNode
(
getInnermostNodeForTie
:
true
,
cancellationToken
);
var
expression
=
invocation
.
Expression
is
MemberAccessExpressionSyntax
memberAccess
?
memberAccess
.
Expression
:
invocation
.
Expression
;
var
start
=
(
ExpressionSyntax
)
diagnostic
.
AdditionalLocations
[
1
].
FindNode
(
getInnermostNodeForTie
:
true
,
cancellationToken
);
var
end
=
(
ExpressionSyntax
)
diagnostic
.
AdditionalLocations
[
2
].
FindNode
(
getInnermostNodeForTie
:
true
,
cancellationToken
);
var
rangeExpression
=
RangeExpression
(
GetExpression
(
diagnostic
.
Properties
,
start
,
OmitStart
,
StartFromEnd
),
GetExpression
(
diagnostic
.
Properties
,
end
,
OmitEnd
,
EndFromEnd
));
var
rangeExpression
=
CreateRangeExpression
(
semanticModel
,
diagnostic
,
invocation
,
cancellationToken
);
var
argList
=
invocation
.
ArgumentList
;
var
elementAccess
=
ElementAccessExpression
(
...
...
@@ -71,11 +72,128 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
editor
.
ReplaceNode
(
invocation
,
elementAccess
);
}
private
RangeExpressionSyntax
CreateRangeExpression
(
SemanticModel
semanticModel
,
Diagnostic
diagnostic
,
InvocationExpressionSyntax
invocation
,
CancellationToken
cancellationToken
)
{
var
properties
=
diagnostic
.
Properties
;
if
(
properties
.
ContainsKey
(
ComputedRange
))
{
return
CreateComputedRange
(
semanticModel
,
diagnostic
,
invocation
,
cancellationToken
);
}
else
if
(
properties
.
ContainsKey
(
ConstantRange
))
{
return
CreateConstantRange
(
semanticModel
,
diagnostic
,
cancellationToken
);
}
else
{
throw
ExceptionUtilities
.
Unreachable
;
}
}
private
RangeExpressionSyntax
CreateComputedRange
(
SemanticModel
semanticModel
,
Diagnostic
diagnostic
,
InvocationExpressionSyntax
invocation
,
CancellationToken
cancellationToken
)
{
var
startExpr
=
(
ExpressionSyntax
)
diagnostic
.
AdditionalLocations
[
1
].
FindNode
(
getInnermostNodeForTie
:
true
,
cancellationToken
);
var
endExpr
=
(
ExpressionSyntax
)
diagnostic
.
AdditionalLocations
[
2
].
FindNode
(
getInnermostNodeForTie
:
true
,
cancellationToken
);
// We have enough information now to generate `start..end`. However, this will often
// not be what the user wants. For example, generating `start..expr.Length` is not as
// desirable as `start..`. Similarly, `start..(expr.Length - 1)` is not as desirable as
// `start..^1`.
var
startFromEnd
=
false
;
var
endFromEnd
=
false
;
if
(
semanticModel
.
GetOperation
(
invocation
,
cancellationToken
)
is
IInvocationOperation
invocationOp
&&
invocationOp
.
Instance
!=
null
)
{
var
startOperation
=
semanticModel
.
GetOperation
(
startExpr
,
cancellationToken
);
var
endOperation
=
semanticModel
.
GetOperation
(
endExpr
,
cancellationToken
);
var
checker
=
new
InfoCache
(
semanticModel
.
Compilation
);
if
(
startOperation
!=
null
&&
endOperation
!=
null
&&
checker
.
TryGetMemberInfo
(
invocationOp
.
TargetMethod
.
ContainingType
,
out
var
memberInfo
))
{
var
lengthLikeProperty
=
memberInfo
.
LengthLikeProperty
;
// If our start-op is actually equivalent to `expr.Length - val`, then just change our
// start-op to be `val` and record that we should emit it as `^val`.
startFromEnd
=
IsFromEnd
(
lengthLikeProperty
,
invocationOp
.
Instance
,
ref
startOperation
);
startExpr
=
(
ExpressionSyntax
)
startOperation
.
Syntax
;
// Similarly, if our end-op is actually equivalent to `expr.Length - val`, then just
// change our end-op to be `val` and record that we should emit it as `^val`.
endFromEnd
=
IsFromEnd
(
lengthLikeProperty
,
invocationOp
.
Instance
,
ref
endOperation
);
endExpr
=
(
ExpressionSyntax
)
endOperation
.
Syntax
;
// If the range operation goes to 'expr.Length' then we can just leave off the end part
// of the range. i.e. `start..`
if
(
IsInstanceLengthCheck
(
lengthLikeProperty
,
invocationOp
.
Instance
,
endOperation
))
{
endExpr
=
null
;
}
// If we're starting the range operation from 0, then we can just leave off the start of
// the range. i.e. `..end`
if
(
startOperation
.
ConstantValue
.
HasValue
&&
startOperation
.
ConstantValue
.
Value
is
0
)
{
startExpr
=
null
;
}
}
}
return
RangeExpression
(
startExpr
!=
null
&&
startFromEnd
?
IndexExpression
(
startExpr
)
:
startExpr
,
endExpr
!=
null
&&
endFromEnd
?
IndexExpression
(
endExpr
)
:
endExpr
);
}
private
static
ExpressionSyntax
GetExpression
(
ImmutableDictionary
<
string
,
string
>
props
,
ExpressionSyntax
expr
,
string
omitKey
,
string
fromEndKey
)
=>
props
.
ContainsKey
(
omitKey
)
?
null
:
props
.
ContainsKey
(
fromEndKey
)
?
IndexExpression
(
expr
)
:
expr
;
private
static
RangeExpressionSyntax
CreateConstantRange
(
SemanticModel
semanticModel
,
Diagnostic
diagnostic
,
CancellationToken
cancellationToken
)
{
var
constant1Syntax
=
(
ExpressionSyntax
)
diagnostic
.
AdditionalLocations
[
1
].
FindNode
(
getInnermostNodeForTie
:
true
,
cancellationToken
);
var
constant2Syntax
=
(
ExpressionSyntax
)
diagnostic
.
AdditionalLocations
[
2
].
FindNode
(
getInnermostNodeForTie
:
true
,
cancellationToken
);
// the form is s.Slice(constant1, s.Length - constant2). Want to generate
// s[constant1..(constant2-constant1)]
var
constant1
=
GetInt32Value
(
semanticModel
.
GetOperation
(
constant1Syntax
));
var
constant2
=
GetInt32Value
(
semanticModel
.
GetOperation
(
constant2Syntax
));
var
endExpr
=
(
ExpressionSyntax
)
CSharpSyntaxGenerator
.
Instance
.
LiteralExpression
(
constant2
-
constant1
);
return
RangeExpression
(
constant1Syntax
,
IndexExpression
(
endExpr
));
}
private
static
int
GetInt32Value
(
IOperation
operation
)
=>
(
int
)
operation
.
ConstantValue
.
Value
;
/// <summary>
/// check if its the form: `expr.Length - value`. If so, update rangeOperation to then
/// point to 'value' so that we can generate '^value'.
/// </summary>
private
static
bool
IsFromEnd
(
IPropertySymbol
lengthLikeProperty
,
IOperation
instance
,
ref
IOperation
rangeOperation
)
{
if
(
rangeOperation
is
IBinaryOperation
binaryOperation
&&
binaryOperation
.
OperatorKind
==
BinaryOperatorKind
.
Subtract
&&
IsInstanceLengthCheck
(
lengthLikeProperty
,
instance
,
binaryOperation
.
LeftOperand
))
{
rangeOperation
=
binaryOperation
.
RightOperand
;
return
true
;
}
return
false
;
}
private
class
MyCodeAction
:
CodeAction
.
DocumentChangeAction
{
public
MyCodeAction
(
Func
<
CancellationToken
,
Task
<
Document
>>
createChangedDocument
)
...
...
src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs
浏览文件 @
6b513abf
...
...
@@ -12,7 +12,7 @@ internal partial class CSharpUseRangeOperatorDiagnosticAnalyzer
/// <summary>
/// Helper type to cache information about types while analyzing the compilation.
/// </summary>
p
rivate
class
InfoCache
p
ublic
class
InfoCache
{
/// <summary>
/// The System.Range type. Needed so that we only fixup code if we see the type
...
...
src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.MemberInfo.cs
浏览文件 @
6b513abf
...
...
@@ -4,7 +4,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseIndexOperator
{
internal
partial
class
CSharpUseRangeOperatorDiagnosticAnalyzer
{
p
rivate
struct
MemberInfo
p
ublic
struct
MemberInfo
{
public
readonly
IPropertySymbol
LengthLikeProperty
;
public
readonly
IMethodSymbol
SliceLikeMethod
;
...
...
src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs
浏览文件 @
6b513abf
...
...
@@ -26,13 +26,16 @@ namespace Microsoft.CodeAnalysis.CSharp.UseIndexOperator
[
DiagnosticAnalyzer
(
LanguageNames
.
CSharp
),
Shared
]
internal
partial
class
CSharpUseRangeOperatorDiagnosticAnalyzer
:
AbstractCodeStyleDiagnosticAnalyzer
{
// Flags to indicate if we should generate 'val' or '^val' for the start or end range values
public
const
string
StartFromEnd
=
nameof
(
StartFromEnd
);
public
const
string
EndFromEnd
=
nameof
(
EndFromEnd
);
public
const
string
ComputedRange
=
nameof
(
ComputedRange
);
public
const
string
ConstantRange
=
nameof
(
ConstantRange
);
// Flags to indicate if we should just omit the start/end value of the range entirely.
public
const
string
OmitStart
=
nameof
(
OmitStart
);
public
const
string
OmitEnd
=
nameof
(
OmitEnd
);
//// Flags to indicate if we should generate 'val' or '^val' for the start or end range values
//public const string StartFromEnd = nameof(StartFromEnd);
//public const string EndFromEnd = nameof(EndFromEnd);
//// Flags to indicate if we should just omit the start/end value of the range entirely.
//public const string OmitStart = nameof(OmitStart);
//public const string OmitEnd = nameof(OmitEnd);
public
CSharpUseRangeOperatorDiagnosticAnalyzer
()
:
base
(
IDEDiagnosticIds
.
UseRangeOperatorDiagnosticId
,
...
...
@@ -138,97 +141,60 @@ protected override void InitializeWorker(AnalysisContext context)
return
;
}
//
Make sure we have: (start, end - start). The start operation has to b
e
//
the same as the
right side of the subtraction.
//
See if we have: (start, end - start). The start operation has to be the same as th
e
// right side of the subtraction.
var
startOperation
=
invocation
.
Arguments
[
0
].
Value
;
var
endOperation
=
subtraction
.
LeftOperand
;
if
(
!
CSharpSyntaxFactsService
.
Instance
.
AreEquivalent
(
startOperation
.
Syntax
,
subtraction
.
RightOperand
.
Syntax
))
if
(
CSharpSyntaxFactsService
.
Instance
.
AreEquivalent
(
startOperation
.
Syntax
,
subtraction
.
RightOperand
.
Syntax
))
{
context
.
ReportDiagnostic
(
CreateDiagnostic
(
ComputedRange
,
option
,
invocationSyntax
,
memberInfo
,
startOperation
,
subtraction
.
LeftOperand
));
return
;
}
// We have enough information now to generate `start..end`. However, this will often
// not be what the user wants. For example, generating `start..expr.Length` is not as
// desirable as `start..`. Similarly, `start..(expr.Length - 1)` is not as desirable as
// `start..^1`. Look for these patterns and record what we have so we can produce more
// idiomatic results in the fixer.
//
// Note: we could also compute this in the fixer. But it's nice and easy to do here
// given that we already have the options, and it's cheap to do now.
var
properties
=
ImmutableDictionary
.
CreateBuilder
<
string
,
string
>();
var
lengthLikeProperty
=
memberInfo
.
LengthLikeProperty
;
// If our start-op is actually equivalent to `expr.Length - val`, then just change our
// start-op to be `val` and record that we should emit it as `^val`.
if
(
IsFromEnd
(
lengthLikeProperty
,
invocation
.
Instance
,
ref
startOperation
))
// See if we have: (constant1, s.Length - constant2). The constants don't have to be
// the same value. This will convert over to s[constant1..(constant - constant1)]
if
(
IsConstantInt32
(
startOperation
)
&&
IsConstantInt32
(
subtraction
.
RightOperand
)
&&
IsInstanceLengthCheck
(
memberInfo
.
LengthLikeProperty
,
invocation
.
Instance
,
subtraction
.
LeftOperand
))
{
properties
.
Add
(
StartFromEnd
,
StartFromEnd
);
}
// Similarly, if our end-op is actually equivalent to `expr.Length - val`, then just
// change our end-op to be `val` and record that we should emit it as `^val`.
if
(
IsFromEnd
(
lengthLikeProperty
,
invocation
.
Instance
,
ref
endOperation
))
{
properties
.
Add
(
EndFromEnd
,
EndFromEnd
);
}
// If the range operation goes to 'expr.Length' then we can just leave off the end part
// of the range. i.e. `start..`
if
(
IsInstanceLengthCheck
(
lengthLikeProperty
,
invocation
.
Instance
,
endOperation
))
{
properties
.
Add
(
OmitEnd
,
OmitEnd
);
context
.
ReportDiagnostic
(
CreateDiagnostic
(
ConstantRange
,
option
,
invocationSyntax
,
memberInfo
,
startOperation
,
subtraction
.
RightOperand
));
return
;
}
}
// If we're starting the range operation from 0, then we can just leave off the start of
// the range. i.e. `..end`
if
(
startOperation
.
ConstantValue
.
HasValue
&&
startOperation
.
ConstantValue
.
Value
is
0
)
{
properties
.
Add
(
OmitStart
,
OmitStart
);
}
private
Diagnostic
CreateDiagnostic
(
string
rangeKind
,
CodeStyleOption
<
bool
>
option
,
InvocationExpressionSyntax
invocation
,
MemberInfo
memberInfo
,
IOperation
op1
,
IOperation
op2
)
{
var
properties
=
ImmutableDictionary
<
string
,
string
>.
Empty
.
Add
(
rangeKind
,
rangeKind
);
// Keep track of the syntax nodes from the start/end ops so that we can easily
// generate the range-expression in the fixer.
var
additionalLocations
=
ImmutableArray
.
Create
(
invocation
Syntax
.
GetLocation
(),
startOperation
.
Syntax
.
GetLocation
(),
endOperation
.
Syntax
.
GetLocation
());
invocation
.
GetLocation
(),
op1
.
Syntax
.
GetLocation
(),
op2
.
Syntax
.
GetLocation
());
// Mark the span under the two arguments to .Slice(..., ...) as what we will be
// updating.
var
arguments
=
invocation
Syntax
.
ArgumentList
.
Arguments
;
var
location
=
Location
.
Create
(
s
yntaxTree
,
var
arguments
=
invocation
.
ArgumentList
.
Arguments
;
var
location
=
Location
.
Create
(
invocation
.
S
yntaxTree
,
TextSpan
.
FromBounds
(
arguments
.
First
().
SpanStart
,
arguments
.
Last
().
Span
.
End
));
context
.
ReportDiagnostic
(
DiagnosticHelper
.
Create
(
Descriptor
,
location
,
option
.
Notification
.
Severity
,
additionalLocations
,
properties
.
ToImmutable
(),
memberInfo
.
SliceLikeMethod
.
Name
));
return
DiagnosticHelper
.
Create
(
Descriptor
,
location
,
option
.
Notification
.
Severity
,
additionalLocations
,
properties
,
memberInfo
.
SliceLikeMethod
.
Name
);
}
/// <summary>
/// check if its the form: `expr.Length - value`. If so, update rangeOperation to then
/// point to 'value' so that we can generate '^value'.
/// </summary>
private
bool
IsFromEnd
(
IPropertySymbol
lengthLikeProperty
,
IOperation
instance
,
ref
IOperation
rangeOperation
)
{
if
(
rangeOperation
is
IBinaryOperation
binaryOperation
&&
binaryOperation
.
OperatorKind
==
BinaryOperatorKind
.
Subtract
&&
IsInstanceLengthCheck
(
lengthLikeProperty
,
instance
,
binaryOperation
.
LeftOperand
))
{
rangeOperation
=
binaryOperation
.
RightOperand
;
return
true
;
}
return
false
;
}
private
static
bool
IsConstantInt32
(
IOperation
operation
)
=>
operation
.
ConstantValue
.
HasValue
&&
operation
.
ConstantValue
.
Value
is
int
;
}
}
src/Features/CSharp/Portable/UseIndexOperator/Helpers.cs
浏览文件 @
6b513abf
...
...
@@ -60,5 +60,8 @@ public static bool IsSubtraction(IArgumentOperation arg, out IBinaryOperation su
subtraction
=
null
;
return
false
;
}
private
static
bool
IsConstantInt32
(
IOperation
operation
)
=>
operation
.
ConstantValue
.
HasValue
&&
operation
.
ConstantValue
.
Value
is
int
;
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录