Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
71360a43
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,发现更多精彩内容 >>
提交
71360a43
编写于
7月 06, 2020
作者:
A
Alexander Luzgarev
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Support basic cases in UseRangeOperator
上级
31381123
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
275 addition
and
12 deletion
+275
-12
src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs
...tor/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs
+36
-0
src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.Result.cs
...erator/CSharpUseRangeOperatorDiagnosticAnalyzer.Result.cs
+5
-1
src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs
...RangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs
+44
-3
src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/Helpers.cs
...yzers/CSharp/Analyzers/UseIndexOrRangeOperator/Helpers.cs
+15
-0
src/Analyzers/CSharp/CodeFixes/UseIndexOrRangeOperator/CSharpUseRangeOperatorCodeFixProvider.cs
...xOrRangeOperator/CSharpUseRangeOperatorCodeFixProvider.cs
+21
-8
src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseRangeOperatorTests.cs
...rp/Tests/UseIndexOrRangeOperator/UseRangeOperatorTests.cs
+154
-0
未找到文件。
src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs
浏览文件 @
71360a43
...
@@ -69,6 +69,42 @@ public bool TryGetMemberInfo(IMethodSymbol method, out MemberInfo memberInfo)
...
@@ -69,6 +69,42 @@ public bool TryGetMemberInfo(IMethodSymbol method, out MemberInfo memberInfo)
return
memberInfo
.
LengthLikeProperty
!=
null
;
return
memberInfo
.
LengthLikeProperty
!=
null
;
}
}
public
bool
TryGetMemberInfoOneArgument
(
IMethodSymbol
method
,
out
MemberInfo
memberInfo
)
{
if
(!
IsSliceLikeMethodWithOneArgument
(
method
))
{
memberInfo
=
default
;
return
false
;
}
if
(
_methodToMemberInfo
.
TryGetValue
(
method
,
out
memberInfo
))
{
return
memberInfo
.
LengthLikeProperty
!=
null
;
}
// Find overload of our method that is a slice-like method with two arguments.
// Computing member info for this method will also check that the containing type
// has an int32 'Length' or 'Count' property, and has a suitable indexer,
// so we don't have to.
var
overloadWithTwoArguments
=
method
.
ContainingType
.
GetMembers
(
method
.
Name
)
.
OfType
<
IMethodSymbol
>()
.
FirstOrDefault
(
s
=>
IsSliceLikeMethod
(
s
));
if
(
overloadWithTwoArguments
is
null
)
{
memberInfo
=
default
;
return
false
;
}
// Since the search is expensive, we keep both the original one-argument and
// two-arguments overload as keys in the cache, pointing to the same
// member information object.
var
newMemberInfo
=
_methodToMemberInfo
.
GetOrAdd
(
overloadWithTwoArguments
,
_
=>
ComputeMemberInfo
(
overloadWithTwoArguments
,
requireRangeMember
:
true
));
_methodToMemberInfo
.
GetOrAdd
(
method
,
_
=>
newMemberInfo
);
memberInfo
=
newMemberInfo
;
return
memberInfo
.
LengthLikeProperty
!=
null
;
}
private
MemberInfo
ComputeMemberInfo
(
IMethodSymbol
sliceLikeMethod
,
bool
requireRangeMember
)
private
MemberInfo
ComputeMemberInfo
(
IMethodSymbol
sliceLikeMethod
,
bool
requireRangeMember
)
{
{
Debug
.
Assert
(
IsSliceLikeMethod
(
sliceLikeMethod
));
Debug
.
Assert
(
IsSliceLikeMethod
(
sliceLikeMethod
));
...
...
src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.Result.cs
浏览文件 @
71360a43
...
@@ -12,7 +12,7 @@ internal partial class CSharpUseRangeOperatorDiagnosticAnalyzer
...
@@ -12,7 +12,7 @@ internal partial class CSharpUseRangeOperatorDiagnosticAnalyzer
{
{
public
enum
ResultKind
public
enum
ResultKind
{
{
// like s.Substring(expr, s.Length - expr). 'expr' has to match on both sides.
// like s.Substring(expr, s.Length - expr)
or s.Substring(expr)
. 'expr' has to match on both sides.
Computed
,
Computed
,
// like s.Substring(constant1, s.Length - constant2). the constants don't have to match.
// like s.Substring(constant1, s.Length - constant2). the constants don't have to match.
...
@@ -28,6 +28,10 @@ public enum ResultKind
...
@@ -28,6 +28,10 @@ public enum ResultKind
public
readonly
IMethodSymbol
SliceLikeMethod
;
public
readonly
IMethodSymbol
SliceLikeMethod
;
public
readonly
MemberInfo
MemberInfo
;
public
readonly
MemberInfo
MemberInfo
;
public
readonly
IOperation
Op1
;
public
readonly
IOperation
Op1
;
/// <summary>
/// Can be null, if we are dealing with one-argument call to a slice-like method.
/// </summary>
public
readonly
IOperation
Op2
;
public
readonly
IOperation
Op2
;
public
Result
(
public
Result
(
...
...
src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs
浏览文件 @
71360a43
...
@@ -111,13 +111,54 @@ protected override void InitializeWorker(AnalysisContext context)
...
@@ -111,13 +111,54 @@ protected override void InitializeWorker(AnalysisContext context)
}
}
}
}
// look for `s.Slice(e1, end - e2)`
// look for `s.Slice(e1, end - e2)` or `s.Slice(e1)`
if
(
invocation
.
Instance
is
null
||
if
(
invocation
.
Instance
is
null
)
invocation
.
Arguments
.
Length
!=
2
)
{
{
return
null
;
return
null
;
}
}
return
invocation
.
Arguments
.
Length
switch
{
1
=>
AnalyzeInvocationWithOneArgument
(
invocation
,
infoCache
,
invocationSyntax
,
option
),
2
=>
AnalyzeInvocationWithTwoArguments
(
invocation
,
infoCache
,
invocationSyntax
,
option
),
_
=>
null
,
};
}
private
static
Result
?
AnalyzeInvocationWithOneArgument
(
IInvocationOperation
invocation
,
InfoCache
infoCache
,
InvocationExpressionSyntax
invocationSyntax
,
CodeStyleOption2
<
bool
>
option
)
{
// See if the call is to something slice-like.
var
targetMethod
=
invocation
.
TargetMethod
;
// Try to see if we're calling into some sort of Slice method with a matching
// indexer or overload
if
(!
infoCache
.
TryGetMemberInfoOneArgument
(
targetMethod
,
out
var
memberInfo
))
{
return
null
;
}
var
startOperation
=
invocation
.
Arguments
[
0
].
Value
;
return
new
Result
(
ResultKind
.
Computed
,
option
,
invocation
,
invocationSyntax
,
targetMethod
,
memberInfo
,
startOperation
,
null
);
// secondOperation is null: the range will run to the end.
}
private
static
Result
?
AnalyzeInvocationWithTwoArguments
(
IInvocationOperation
invocation
,
InfoCache
infoCache
,
InvocationExpressionSyntax
invocationSyntax
,
CodeStyleOption2
<
bool
>
option
)
{
// See if the call is to something slice-like.
// See if the call is to something slice-like.
var
targetMethod
=
invocation
.
TargetMethod
;
var
targetMethod
=
invocation
.
TargetMethod
;
...
...
src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/Helpers.cs
浏览文件 @
71360a43
...
@@ -91,6 +91,21 @@ public static bool IsSliceLikeMethod(IMethodSymbol method)
...
@@ -91,6 +91,21 @@ public static bool IsSliceLikeMethod(IMethodSymbol method)
IsSliceFirstParameter
(
method
.
OriginalDefinition
.
Parameters
[
0
])
&&
IsSliceFirstParameter
(
method
.
OriginalDefinition
.
Parameters
[
0
])
&&
IsSliceSecondParameter
(
method
.
OriginalDefinition
.
Parameters
[
1
]);
IsSliceSecondParameter
(
method
.
OriginalDefinition
.
Parameters
[
1
]);
/// <summary>
/// Look for methods like "SomeType MyType.Slice(int start)". Note that the
/// name of the parameter is checked to ensure it is appropriate slice-like.
/// This name was picked by examining the patterns in the BCL for slicing members.
/// </summary>
public
static
bool
IsSliceLikeMethodWithOneArgument
(
IMethodSymbol
method
)
=>
method
!=
null
&&
IsPublicInstance
(
method
)
&&
method
.
Parameters
.
Length
==
1
&&
// From: https://github.com/dotnet/csharplang/blob/master/proposals/csharp-8.0/ranges.md#decisions-made-during-implementation
//
// When looking for the pattern members, we look for original definitions, not
// constructed members
IsSliceFirstParameter
(
method
.
OriginalDefinition
.
Parameters
[
0
]);
private
static
bool
IsSliceFirstParameter
(
IParameterSymbol
parameter
)
private
static
bool
IsSliceFirstParameter
(
IParameterSymbol
parameter
)
=>
parameter
.
Type
.
SpecialType
==
SpecialType
.
System_Int32
&&
=>
parameter
.
Type
.
SpecialType
==
SpecialType
.
System_Int32
&&
(
parameter
.
Name
==
"start"
||
parameter
.
Name
==
"startIndex"
);
(
parameter
.
Name
==
"start"
||
parameter
.
Name
==
"startIndex"
);
...
...
src/Analyzers/CSharp/CodeFixes/UseIndexOrRangeOperator/CSharpUseRangeOperatorCodeFixProvider.cs
浏览文件 @
71360a43
...
@@ -150,15 +150,28 @@ private static RangeExpressionSyntax CreateComputedRange(Result result)
...
@@ -150,15 +150,28 @@ private static RangeExpressionSyntax CreateComputedRange(Result result)
var
startFromEnd
=
IsFromEnd
(
lengthLikeProperty
,
instance
,
ref
startOperation
);
var
startFromEnd
=
IsFromEnd
(
lengthLikeProperty
,
instance
,
ref
startOperation
);
var
startExpr
=
(
ExpressionSyntax
)
startOperation
.
Syntax
;
var
startExpr
=
(
ExpressionSyntax
)
startOperation
.
Syntax
;
// Similarly, if our end-op is actually equivalent to `expr.Length - val`, then just
var
endFromEnd
=
false
;
// change our end-op to be `val` and record that we should emit it as `^val`.
ExpressionSyntax
endExpr
;
var
endFromEnd
=
IsFromEnd
(
lengthLikeProperty
,
instance
,
ref
endOperation
);
var
endExpr
=
(
ExpressionSyntax
)
endOperation
.
Syntax
;
if
(!(
endOperation
is
null
))
{
// If the range operation goes to 'expr.Length' then we can just leave off the end part
// We need to do the same for the second argument, since it's present.
// of the range. i.e. `start..`
// Similarly, if our end-op is actually equivalent to `expr.Length - val`, then just
if
(
IsInstanceLengthCheck
(
lengthLikeProperty
,
instance
,
endOperation
))
// change our end-op to be `val` and record that we should emit it as `^val`.
endFromEnd
=
IsFromEnd
(
lengthLikeProperty
,
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
,
instance
,
endOperation
))
{
endExpr
=
null
;
}
}
else
{
{
// We are dealing with one-argument case, so we should leave off
// the end part of the range: `start..`.
endExpr
=
null
;
endExpr
=
null
;
}
}
...
...
src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseRangeOperatorTests.cs
浏览文件 @
71360a43
...
@@ -204,6 +204,96 @@ void Goo(string s, int bar, int baz)
...
@@ -204,6 +204,96 @@ void Goo(string s, int bar, int baz)
}.
RunAsync
();
}.
RunAsync
();
}
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUseRangeOperator
)]
public
async
Task
TestSubstringOneArgument
()
{
var
source
=
@"
class C
{
void Goo(string s)
{
var v = s.Substring([|1|]);
}
}"
;
var
fixedSource
=
@"
class C
{
void Goo(string s)
{
var v = s[1..];
}
}"
;
await
new
VerifyCS
.
Test
{
ReferenceAssemblies
=
ReferenceAssemblies
.
NetCore
.
NetCoreApp31
,
TestCode
=
source
,
FixedCode
=
fixedSource
,
}.
RunAsync
();
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUseRangeOperator
)]
public
async
Task
TestSliceOneArgument
()
{
var
source
=
@"
class C
{
void Goo(Span<int> s)
{
var v = s.Slice([|1|]);
}
}"
;
var
fixedSource
=
@"
class C
{
void Goo(Span<int> s)
{
var v = s[1..];
}
}"
;
await
new
VerifyCS
.
Test
{
ReferenceAssemblies
=
ReferenceAssemblies
.
NetCore
.
NetCoreApp31
,
TestCode
=
source
,
FixedCode
=
fixedSource
,
}.
RunAsync
();
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUseRangeOperator
)]
public
async
Task
TestExpressionOneArgument
()
{
var
source
=
@"
class C
{
void Goo(string s, int bar)
{
var v = s.Substring([|bar|]);
}
}"
;
var
fixedSource
=
@"
class C
{
void Goo(string s, int bar)
{
var v = s[bar..];
}
}"
;
await
new
VerifyCS
.
Test
{
ReferenceAssemblies
=
ReferenceAssemblies
.
NetCore
.
NetCoreApp31
,
TestCode
=
source
,
FixedCode
=
fixedSource
,
}.
RunAsync
();
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUseRangeOperator
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUseRangeOperator
)]
public
async
Task
TestConstantSubtraction1
()
public
async
Task
TestConstantSubtraction1
()
{
{
...
@@ -416,6 +506,38 @@ void Goo(string s, string t)
...
@@ -416,6 +506,38 @@ void Goo(string s, string t)
}.
RunAsync
();
}.
RunAsync
();
}
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUseRangeOperator
)]
public
async
Task
TestFixAllInvocationToElementAccess2
()
{
// Note: once the IOp tree has support for range operators, this should
// simplify even further.
var
source
=
@"
class C
{
void Goo(string s, string t)
{
var v = t.Substring([|s.Substring([|1|])[0]|]);
}
}"
;
var
fixedSource
=
@"
class C
{
void Goo(string s, string t)
{
var v = t[s[1..][0]..];
}
}"
;
await
new
VerifyCS
.
Test
{
ReferenceAssemblies
=
ReferenceAssemblies
.
NetCore
.
NetCoreApp31
,
TestCode
=
source
,
FixedCode
=
fixedSource
,
}.
RunAsync
();
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUseRangeOperator
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUseRangeOperator
)]
public
async
Task
TestWithTypeWithActualSliceMethod1
()
public
async
Task
TestWithTypeWithActualSliceMethod1
()
{
{
...
@@ -479,5 +601,37 @@ void Goo(Span<int> s)
...
@@ -479,5 +601,37 @@ void Goo(Span<int> s)
FixedCode
=
fixedSource
,
FixedCode
=
fixedSource
,
}.
RunAsync
();
}.
RunAsync
();
}
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsUseRangeOperator
)]
public
async
Task
TestWithTypeWithActualSliceMethod3
()
{
var
source
=
@"
using System;
class C
{
void Goo(Span<int> s)
{
var v = s.Slice([|1|]);
}
}"
;
var
fixedSource
=
@"
using System;
class C
{
void Goo(Span<int> s)
{
var v = s[1..];
}
}"
;
await
new
VerifyCS
.
Test
{
ReferenceAssemblies
=
ReferenceAssemblies
.
NetCore
.
NetCoreApp31
,
TestCode
=
source
,
FixedCode
=
fixedSource
,
}.
RunAsync
();
}
}
}
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录