Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
32081b89
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,发现更多精彩内容 >>
提交
32081b89
编写于
10月 21, 2018
作者:
C
Cyrus Najmabadi
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
UI options. Also support arrays.
上级
ef1482ae
变更
7
隐藏空白更改
内联
并排
Showing
7 changed file
with
197 addition
and
26 deletion
+197
-26
src/EditorFeatures/CSharpTest/UseIndexOrRangeOperator/UseIndexOperatorTests.cs
...harpTest/UseIndexOrRangeOperator/UseIndexOperatorTests.cs
+22
-0
src/Features/CSharp/Portable/UseIndexOrRangeOperator/CSharpUseIndexOperatorDiagnosticAnalyzer.InfoCache.cs
...tor/CSharpUseIndexOperatorDiagnosticAnalyzer.InfoCache.cs
+36
-5
src/Features/CSharp/Portable/UseIndexOrRangeOperator/CSharpUseIndexOperatorDiagnosticAnalyzer.cs
...RangeOperator/CSharpUseIndexOperatorDiagnosticAnalyzer.cs
+66
-19
src/Features/CSharp/Portable/UseIndexOrRangeOperator/Helpers.cs
...atures/CSharp/Portable/UseIndexOrRangeOperator/Helpers.cs
+1
-1
src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs
...alStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs
+48
-1
src/VisualStudio/Core/Def/ServicesVSResources.Designer.cs
src/VisualStudio/Core/Def/ServicesVSResources.Designer.cs
+18
-0
src/VisualStudio/Core/Def/ServicesVSResources.resx
src/VisualStudio/Core/Def/ServicesVSResources.resx
+6
-0
未找到文件。
src/EditorFeatures/CSharpTest/UseIndexOrRangeOperator/UseIndexOperatorTests.cs
浏览文件 @
32081b89
...
...
@@ -306,5 +306,27 @@ void Goo(S s)
}
}"
,
parameters
:
s_testParameters
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUseIndexOperator
)]
public
async
Task
TestArray
()
{
await
TestAsync
(
@"
class C
{
void Goo(string[] s)
{
var v = s[[||]s.Length - 1];
}
}"
,
@"
class C
{
void Goo(string[] s)
{
var v = s[^1];
}
}"
,
parseOptions
:
s_parseOptions
);
}
}
}
src/Features/CSharp/Portable/UseIndexOrRangeOperator/CSharpUseIndexOperatorDiagnosticAnalyzer.InfoCache.cs
浏览文件 @
32081b89
...
...
@@ -5,6 +5,7 @@
namespace
Microsoft.CodeAnalysis.CSharp.UseIndexOrRangeOperator
{
using
System
;
using
static
Helpers
;
internal
partial
class
CSharpUseIndexOperatorDiagnosticAnalyzer
...
...
@@ -19,13 +20,24 @@ private class InfoCache
/// we're using has an indexer that takes an Index.
/// </summary>
private
readonly
INamedTypeSymbol
_indexType
;
/// <summary>
/// Mapping from a method like 'MyType.Get(int)' to the Length/Count property for
/// 'MyType' as well as the optional 'MyType.Get(System.Index)' member if it exists.
/// </summary>
private
readonly
ConcurrentDictionary
<
IMethodSymbol
,
MemberInfo
>
_methodToMemberInfo
;
/// <summary>
/// Mapping from an array symbol (like 'int[]') to the Length property for it.
/// </summary>
private
readonly
ConcurrentDictionary
<
ITypeSymbol
,
MemberInfo
>
_arrayTypeToMemberInfo
;
public
InfoCache
(
Compilation
compilation
)
{
_indexType
=
compilation
.
GetTypeByMetadataName
(
"System.Index"
);
_methodToMemberInfo
=
new
ConcurrentDictionary
<
IMethodSymbol
,
MemberInfo
>();
_arrayTypeToMemberInfo
=
new
ConcurrentDictionary
<
ITypeSymbol
,
MemberInfo
>();
// Always allow using System.Index indexers with System.String. The compiler has
// hard-coded knowledge on how to use this type, even if there is no this[Index]
...
...
@@ -38,18 +50,37 @@ public InfoCache(Compilation compilation)
_methodToMemberInfo
[
indexer
.
GetMethod
]
=
ComputeMemberInfo
(
indexer
.
GetMethod
,
requireIndexMember
:
false
);
}
public
bool
TryGetMemberInfo
(
IMethodSymbol
methodSymbol
,
out
MemberInfo
memberInfo
)
public
bool
TryGetMemberInfo
(
IMethodSymbol
methodSymbolOpt
,
ITypeSymbol
typeSymbol
,
out
MemberInfo
memberInfo
)
{
if
(!
IsIntIndexingMethod
(
methodSymbol
))
memberInfo
=
default
;
if
(
methodSymbolOpt
!=
null
)
{
if
(
IsIntIndexingMethod
(
methodSymbolOpt
))
{
memberInfo
=
_methodToMemberInfo
.
GetOrAdd
(
methodSymbolOpt
,
m
=>
ComputeMemberInfo
(
m
,
requireIndexMember
:
true
));
}
}
else
if
(
typeSymbol
!=
null
)
{
memberInfo
=
default
;
return
false
;
memberInfo
=
_arrayTypeToMemberInfo
.
GetOrAdd
(
typeSymbol
,
t
=>
ComputeMemberInfo
(
t
));
}
memberInfo
=
_methodToMemberInfo
.
GetOrAdd
(
methodSymbol
,
m
=>
ComputeMemberInfo
(
m
,
requireIndexMember
:
true
));
return
memberInfo
.
LengthLikeProperty
!=
null
;
}
private
MemberInfo
ComputeMemberInfo
(
ITypeSymbol
type
)
{
if
(
type
is
IArrayTypeSymbol
)
{
var
lengthProperty
=
GetNoArgInt32Property
(
type
.
BaseType
,
nameof
(
Array
.
Length
));
return
new
MemberInfo
(
lengthProperty
,
overloadedMethodOpt
:
null
);
}
return
default
;
}
private
MemberInfo
ComputeMemberInfo
(
IMethodSymbol
method
,
bool
requireIndexMember
)
{
Debug
.
Assert
(
IsIntIndexingMethod
(
method
));
...
...
src/Features/CSharp/Portable/UseIndexOrRangeOperator/CSharpUseIndexOperatorDiagnosticAnalyzer.cs
浏览文件 @
32081b89
...
...
@@ -51,10 +51,20 @@ protected override void InitializeWorker(AnalysisContext context)
// compilation. Cache information we compute in this object so we don't have to
// continually recompute it.
var
infoCache
=
new
InfoCache
(
startContext
.
Compilation
);
// Register to hear property references, so we can hear about calls to indexers
// like: s[s.Length - n]
context
.
RegisterOperationAction
(
c
=>
AnalyzePropertyReference
(
c
,
infoCache
),
OperationKind
.
PropertyReference
);
// Array indexing is represented with a different operation kind. Register
// specifically for that.
context
.
RegisterOperationAction
(
c
=>
AnalyzeArrayElementReference
(
c
,
infoCache
),
OperationKind
.
ArrayElementReference
);
// Register to hear about methods for: s.Get(s.Length - n)
context
.
RegisterOperationAction
(
c
=>
AnalyzeInvocation
(
c
,
infoCache
),
OperationKind
.
Invocation
);
...
...
@@ -66,6 +76,11 @@ private void AnalyzeInvocation(OperationAnalysisContext context, InfoCache infoC
var
cancellationToken
=
context
.
CancellationToken
;
var
invocationOperation
=
(
IInvocationOperation
)
context
.
Operation
;
if
(
invocationOperation
.
Arguments
.
Length
!=
1
)
{
return
;
}
// Make sure we're actually on an invocation something like `s.Get(...)`.
var
invocationSyntax
=
invocationOperation
.
Syntax
as
InvocationExpressionSyntax
;
if
(
invocationSyntax
is
null
)
...
...
@@ -73,13 +88,13 @@ private void AnalyzeInvocation(OperationAnalysisContext context, InfoCache infoC
return
;
}
var
instance
=
invocationOperation
.
Instance
;
var
targetMethod
=
invocationOperation
.
TargetMethod
;
var
arguments
=
invocationOperation
.
Arguments
;
AnalyzeInvokedMember
(
context
,
infoCache
,
invocationOperation
,
instance
,
targetMethod
,
arguments
,
cancellationToken
);
context
,
infoCache
,
invocationSyntax
,
invocationOperation
.
Instance
,
invocationOperation
.
TargetMethod
,
invocationOperation
.
Arguments
[
0
].
Value
,
cancellationToken
);
}
private
void
AnalyzePropertyReference
(
...
...
@@ -87,10 +102,14 @@ private void AnalyzeInvocation(OperationAnalysisContext context, InfoCache infoC
{
var
cancellationToken
=
context
.
CancellationToken
;
var
propertyReference
=
(
IPropertyReferenceOperation
)
context
.
Operation
;
var
property
=
propertyReference
.
Property
;
// Only analyze indexer calls.
if
(!
property
.
IsIndexer
)
if
(!
propertyReference
.
Property
.
IsIndexer
)
{
return
;
}
if
(
propertyReference
.
Arguments
.
Length
!=
1
)
{
return
;
}
...
...
@@ -102,22 +121,50 @@ private void AnalyzeInvocation(OperationAnalysisContext context, InfoCache infoC
return
;
}
var
instance
=
propertyReference
.
Instance
;
var
targetMethod
=
property
.
GetMethod
;
var
arguments
=
propertyReference
.
Arguments
;
AnalyzeInvokedMember
(
context
,
infoCache
,
elementAccess
,
propertyReference
.
Instance
,
propertyReference
.
Property
.
GetMethod
,
propertyReference
.
Arguments
[
0
].
Value
,
cancellationToken
);
}
private
void
AnalyzeArrayElementReference
(
OperationAnalysisContext
context
,
InfoCache
infoCache
)
{
var
cancellationToken
=
context
.
CancellationToken
;
var
arrayElementReference
=
(
IArrayElementReferenceOperation
)
context
.
Operation
;
// Has to be a single-dimensional element access.
if
(
arrayElementReference
.
Indices
.
Length
!=
1
)
{
return
;
}
// Make sure we're actually on something like `s[...]`.
var
elementAccess
=
arrayElementReference
.
Syntax
as
ElementAccessExpressionSyntax
;
if
(
elementAccess
is
null
)
{
return
;
}
AnalyzeInvokedMember
(
context
,
infoCache
,
propertyReference
,
instance
,
targetMethod
,
arguments
,
cancellationToken
);
context
,
infoCache
,
elementAccess
,
arrayElementReference
.
ArrayReference
,
targetMethodOpt
:
null
,
arrayElementReference
.
Indices
[
0
],
cancellationToken
);
}
private
void
AnalyzeInvokedMember
(
OperationAnalysisContext
context
,
InfoCache
infoCache
,
IOperation
invocation
,
IOperation
instance
,
IMethodSymbol
targetMethod
,
ImmutableArray
<
IArgumentOperation
>
arguments
,
OperationAnalysisContext
context
,
InfoCache
infoCache
,
SyntaxNode
syntax
,
IOperation
instance
,
IMethodSymbol
targetMethod
Opt
,
IOperation
argumentValue
,
CancellationToken
cancellationToken
)
{
// Only supported on C# 8 and above.
var
syntaxTree
=
invocation
.
S
yntax
.
SyntaxTree
;
var
syntaxTree
=
s
yntax
.
SyntaxTree
;
var
parseOptions
=
(
CSharpParseOptions
)
syntaxTree
.
Options
;
if
(
parseOptions
.
LanguageVersion
<
LanguageVersion
.
CSharp8
)
{
...
...
@@ -142,8 +189,7 @@ private void AnalyzeInvocation(OperationAnalysisContext context, InfoCache infoC
// Needs to have the one arg for `s.Length - value`, and that arg needs to be
// a subtraction.
if
(
instance
is
null
||
arguments
.
Length
!=
1
||
!
IsSubtraction
(
arguments
[
0
].
Value
,
out
var
subtraction
))
!
IsSubtraction
(
argumentValue
,
out
var
subtraction
))
{
return
;
}
...
...
@@ -154,7 +200,8 @@ private void AnalyzeInvocation(OperationAnalysisContext context, InfoCache infoC
//
// Also ensure that the left side of the subtraction : `s.Length - value` is actually
// getting the length off the same instance we're indexing into.
if
(!
infoCache
.
TryGetMemberInfo
(
targetMethod
,
out
var
memberInfo
)
||
if
(!
infoCache
.
TryGetMemberInfo
(
targetMethodOpt
,
instance
.
Type
,
out
var
memberInfo
)
||
!
IsInstanceLengthCheck
(
memberInfo
.
LengthLikeProperty
,
instance
,
subtraction
.
LeftOperand
))
{
return
;
...
...
src/Features/CSharp/Portable/UseIndexOrRangeOperator/Helpers.cs
浏览文件 @
32081b89
...
...
@@ -14,7 +14,7 @@ public static IPropertySymbol GetLengthOrCountProperty(INamedTypeSymbol namedTyp
=>
GetNoArgInt32Property
(
namedType
,
nameof
(
string
.
Length
))
??
GetNoArgInt32Property
(
namedType
,
nameof
(
ICollection
.
Count
));
p
rivate
static
IPropertySymbol
GetNoArgInt32Property
(
INamed
TypeSymbol
type
,
string
name
)
p
ublic
static
IPropertySymbol
GetNoArgInt32Property
(
I
TypeSymbol
type
,
string
name
)
=>
type
.
GetMembers
(
name
)
.
OfType
<
IPropertySymbol
>()
.
Where
(
p
=>
IsPublicInstance
(
p
)
&&
...
...
src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs
浏览文件 @
32081b89
...
...
@@ -716,7 +716,6 @@ void M2(string value)
private
static
readonly
string
s_preferCompoundAssignments
=
$@"
using System;
class Customer
{{
void
M1
(
int
value
)
...
...
@@ -734,6 +733,51 @@ void M2(int value)
//]
}}
}}
"
;
private
static
readonly
string
s_preferIndexOperator
=
$@"
using System;
class Customer
{{
void
M1
(
string
value
)
{{
//[
// {ServicesVSResources.Prefer_colon}
var
ch
=
value
[^
1
];
//]
}}
void
M2
(
string
value
)
{{
//[
// {ServicesVSResources.Over_colon}
var
ch
=
value
[
value
.
Length
-
1
];
//]
}}
}}
"
;
private
static
readonly
string
s_preferRangeOperator
=
$@"
using System;
class Customer
{{
void
M1
(
string
value
)
{{
//[
// {ServicesVSResources.Prefer_colon}
var
sub
=
value
[
1.
.^
1
];
//]
}}
void
M2
(
string
value
)
{{
//[
// {ServicesVSResources.Over_colon}
var
sub
=
value
.
Substring
(
1
,
value
.
Length
-
2
);
>>>>>>>
UI
options
.
Also
support
arrays
.
//]
}}
}}
"
;
private
static
readonly
string
s_preferIsNullOverReferenceEquals
=
$@"
...
...
@@ -1193,6 +1237,9 @@ internal StyleViewModel(OptionSet optionSet, IServiceProvider serviceProvider) :
CodeStyleItems
.
Add
(
new
BooleanCodeStyleOptionViewModel
(
CSharpCodeStyleOptions
.
PreferLocalOverAnonymousFunction
,
ServicesVSResources
.
Prefer_local_function_over_anonymous_function
,
s_preferLocalFunctionOverAnonymousFunction
,
s_preferLocalFunctionOverAnonymousFunction
,
this
,
optionSet
,
expressionPreferencesGroupTitle
));
CodeStyleItems
.
Add
(
new
BooleanCodeStyleOptionViewModel
(
CodeStyleOptions
.
PreferCompoundAssignment
,
ServicesVSResources
.
Prefer_compound_assignments
,
s_preferCompoundAssignments
,
s_preferCompoundAssignments
,
this
,
optionSet
,
expressionPreferencesGroupTitle
));
CodeStyleItems
.
Add
(
new
BooleanCodeStyleOptionViewModel
(
CSharpCodeStyleOptions
.
PreferIndexOperator
,
ServicesVSResources
.
Prefer_index_operator
,
s_preferIndexOperator
,
s_preferIndexOperator
,
this
,
optionSet
,
expressionPreferencesGroupTitle
));
CodeStyleItems
.
Add
(
new
BooleanCodeStyleOptionViewModel
(
CSharpCodeStyleOptions
.
PreferRangeOperator
,
ServicesVSResources
.
Prefer_range_operator
,
s_preferRangeOperator
,
s_preferRangeOperator
,
this
,
optionSet
,
expressionPreferencesGroupTitle
));
AddExpressionBodyOptions
(
optionSet
,
expressionPreferencesGroupTitle
);
// Variable preferences
...
...
src/VisualStudio/Core/Def/ServicesVSResources.Designer.cs
浏览文件 @
32081b89
...
...
@@ -1857,6 +1857,15 @@ internal class ServicesVSResources {
}
}
/// <summary>
/// Looks up a localized string similar to Prefer index operator.
/// </summary>
internal
static
string
Prefer_index_operator
{
get
{
return
ResourceManager
.
GetString
(
"Prefer_index_operator"
,
resourceCulture
);
}
}
/// <summary>
/// Looks up a localized string similar to Prefer inferred anonymous type member names.
/// </summary>
...
...
@@ -1920,6 +1929,15 @@ internal class ServicesVSResources {
}
}
/// <summary>
/// Looks up a localized string similar to Prefer range operator.
/// </summary>
internal
static
string
Prefer_range_operator
{
get
{
return
ResourceManager
.
GetString
(
"Prefer_range_operator"
,
resourceCulture
);
}
}
/// <summary>
/// Looks up a localized string similar to Prefer readonly.
/// </summary>
...
...
src/VisualStudio/Core/Def/ServicesVSResources.resx
浏览文件 @
32081b89
...
...
@@ -1078,4 +1078,10 @@ I agree to all of the foregoing:</value>
<data
name=
"Kind"
xml:space=
"preserve"
>
<value>
Kind
</value>
</data>
<data
name=
"Prefer_index_operator"
xml:space=
"preserve"
>
<value>
Prefer index operator
</value>
</data>
<data
name=
"Prefer_range_operator"
xml:space=
"preserve"
>
<value>
Prefer range operator
</value>
</data>
</root>
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录