Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
36f71913
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,发现更多精彩内容 >>
未验证
提交
36f71913
编写于
5月 02, 2020
作者:
C
CyrusNajmabadi
提交者:
GitHub
5月 02, 2020
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #41680 from ThadHouse/refstructdisallowIEquatable
Disallow generating IEquatable for Ref Struct
上级
ed310b2a
44cf2274
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
90 addition
and
22 deletion
+90
-22
src/EditorFeatures/CSharpTest/GenerateFromMembers/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs
...omMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs
+52
-0
src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider.cs
...EqualsAndGetHashCodeFromMembersCodeRefactoringProvider.cs
+26
-19
src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions_CreateEqualsMethod.cs
...xtensions/SyntaxGeneratorExtensions_CreateEqualsMethod.cs
+12
-3
未找到文件。
src/EditorFeatures/CSharpTest/GenerateFromMembers/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs
浏览文件 @
36f71913
...
@@ -1940,6 +1940,58 @@ public bool Equals(Program other)
...
@@ -1940,6 +1940,58 @@ public bool Equals(Program other)
parameters
:
CSharp6Implicit
);
parameters
:
CSharp6Implicit
);
}
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsGenerateEqualsAndGetHashCode
)]
[
WorkItem
(
25708
,
"https://github.com/dotnet/roslyn/issues/25708"
)]
public
async
Task
TestOverrideEqualsOnRefStructReturnsFalse
()
{
await
TestWithPickMembersDialogAsync
(
@"
ref struct Program
{
public string s;
[||]
}"
,
@"
ref struct Program
{
public string s;
public override bool Equals(object obj)
{
return false;
}
}"
,
chosenSymbols
:
null
);
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsGenerateEqualsAndGetHashCode
)]
[
WorkItem
(
25708
,
"https://github.com/dotnet/roslyn/issues/25708"
)]
public
async
Task
TestImplementIEquatableOnRefStructSkipsIEquatable
()
{
await
TestWithPickMembersDialogAsync
(
@"
ref struct Program
{
public string s;
[||]
}"
,
@"
ref struct Program
{
public string s;
public override bool Equals(object obj)
{
return false;
}
}"
,
chosenSymbols
:
null
,
// We are forcefully enabling the ImplementIEquatable option, as that is our way
// to test that the option does nothing. The VS mode will ensure if the option
// is not available it will not be shown.
optionsCallback
:
options
=>
EnableOption
(
options
,
ImplementIEquatableId
));
}
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsGenerateEqualsAndGetHashCode
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
CodeActionsGenerateEqualsAndGetHashCode
)]
public
async
Task
TestImplementIEquatableOnStructInNullableContextWithUnannotatedMetadata
()
public
async
Task
TestImplementIEquatableOnStructInNullableContextWithUnannotatedMetadata
()
{
{
...
...
src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider.cs
浏览文件 @
36f71913
...
@@ -2,6 +2,8 @@
...
@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// See the LICENSE file in the project root for more information.
#
nullable
enable
using
System
;
using
System
;
using
System.Collections.Immutable
;
using
System.Collections.Immutable
;
using
System.Composition
;
using
System.Composition
;
...
@@ -35,7 +37,7 @@ internal partial class GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringPro
...
@@ -35,7 +37,7 @@ internal partial class GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringPro
private
const
string
EqualsName
=
nameof
(
object
.
Equals
);
private
const
string
EqualsName
=
nameof
(
object
.
Equals
);
private
const
string
GetHashCodeName
=
nameof
(
object
.
GetHashCode
);
private
const
string
GetHashCodeName
=
nameof
(
object
.
GetHashCode
);
private
readonly
IPickMembersService
_pickMembersService_forTestingPurposes
;
private
readonly
IPickMembersService
?
_pickMembersService_forTestingPurposes
;
[
ImportingConstructor
]
[
ImportingConstructor
]
[
Obsolete
(
MefConstruction
.
ImportingConstructorMessage
,
error
:
true
)]
[
Obsolete
(
MefConstruction
.
ImportingConstructorMessage
,
error
:
true
)]
...
@@ -45,7 +47,7 @@ public GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider()
...
@@ -45,7 +47,7 @@ public GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider()
}
}
[
SuppressMessage
(
"RoslynDiagnosticsReliability"
,
"RS0034:Exported parts should have [ImportingConstructor]"
,
Justification
=
"Used incorrectly by tests"
)]
[
SuppressMessage
(
"RoslynDiagnosticsReliability"
,
"RS0034:Exported parts should have [ImportingConstructor]"
,
Justification
=
"Used incorrectly by tests"
)]
public
GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider
(
IPickMembersService
pickMembersService
)
public
GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider
(
IPickMembersService
?
pickMembersService
)
=>
_pickMembersService_forTestingPurposes
=
pickMembersService
;
=>
_pickMembersService_forTestingPurposes
=
pickMembersService
;
public
override
async
Task
ComputeRefactoringsAsync
(
CodeRefactoringContext
context
)
public
override
async
Task
ComputeRefactoringsAsync
(
CodeRefactoringContext
context
)
...
@@ -69,9 +71,9 @@ private async Task HandleNonSelectionAsync(CodeRefactoringContext context)
...
@@ -69,9 +71,9 @@ private async Task HandleNonSelectionAsync(CodeRefactoringContext context)
{
{
var
(
document
,
textSpan
,
cancellationToken
)
=
context
;
var
(
document
,
textSpan
,
cancellationToken
)
=
context
;
var
syntaxFacts
=
document
.
GetLanguageService
<
ISyntaxFactsService
>();
var
syntaxFacts
=
document
.
Get
Required
LanguageService
<
ISyntaxFactsService
>();
var
sourceText
=
await
document
.
GetTextAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
sourceText
=
await
document
.
GetTextAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
root
=
await
document
.
GetSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
root
=
await
document
.
Get
Required
SyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
// We offer the refactoring when the user is either on the header of a class/struct,
// We offer the refactoring when the user is either on the header of a class/struct,
// or if they're between any members of a class/struct and are on a blank line.
// or if they're between any members of a class/struct and are on a blank line.
...
@@ -81,7 +83,7 @@ private async Task HandleNonSelectionAsync(CodeRefactoringContext context)
...
@@ -81,7 +83,7 @@ private async Task HandleNonSelectionAsync(CodeRefactoringContext context)
return
;
return
;
}
}
var
semanticModel
=
await
document
.
GetSemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
semanticModel
=
await
document
.
Get
Required
SemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
// Only supported on classes/structs.
// Only supported on classes/structs.
var
containingType
=
semanticModel
.
GetDeclaredSymbol
(
typeDeclaration
)
as
INamedTypeSymbol
;
var
containingType
=
semanticModel
.
GetDeclaredSymbol
(
typeDeclaration
)
as
INamedTypeSymbol
;
...
@@ -134,13 +136,21 @@ private bool HasOperator(INamedTypeSymbol containingType, string operatorName)
...
@@ -134,13 +136,21 @@ private bool HasOperator(INamedTypeSymbol containingType, string operatorName)
private
bool
CanImplementIEquatable
(
private
bool
CanImplementIEquatable
(
SemanticModel
semanticModel
,
INamedTypeSymbol
containingType
,
SemanticModel
semanticModel
,
INamedTypeSymbol
containingType
,
[
NotNullWhen
(
true
)]
out
INamedTypeSymbol
constructedType
)
[
NotNullWhen
(
true
)]
out
INamedTypeSymbol
?
constructedType
)
{
{
var
equatableTypeOpt
=
semanticModel
.
Compilation
.
GetTypeByMetadataName
(
typeof
(
IEquatable
<>).
FullName
);
// A ref struct can never implement an interface, therefore never add IEquatable to the selection
if
(
equatableTypeOpt
!=
null
)
// options if the type is a ref struct.
if
(!
containingType
.
IsRefLikeType
)
{
{
constructedType
=
equatableTypeOpt
.
Construct
(
containingType
);
var
equatableTypeOpt
=
semanticModel
.
Compilation
.
GetTypeByMetadataName
(
typeof
(
IEquatable
<>).
FullName
!);
return
!
containingType
.
AllInterfaces
.
Contains
(
constructedType
);
if
(
equatableTypeOpt
!=
null
)
{
constructedType
=
equatableTypeOpt
.
Construct
(
containingType
);
// A ref struct can never implement an interface, therefore never add IEquatable to the selection
// options if the type is a ref struct.
return
!
containingType
.
AllInterfaces
.
Contains
(
constructedType
);
}
}
}
constructedType
=
null
;
constructedType
=
null
;
...
@@ -174,8 +184,8 @@ private void GetExistingMemberInfo(INamedTypeSymbol containingType, out bool has
...
@@ -174,8 +184,8 @@ private void GetExistingMemberInfo(INamedTypeSymbol containingType, out bool has
GetExistingMemberInfo
(
GetExistingMemberInfo
(
info
.
ContainingType
,
out
var
hasEquals
,
out
var
hasGetHashCode
);
info
.
ContainingType
,
out
var
hasEquals
,
out
var
hasGetHashCode
);
var
syntaxFacts
=
document
.
GetLanguageService
<
ISyntaxFactsService
>();
var
syntaxFacts
=
document
.
Get
Required
LanguageService
<
ISyntaxFactsService
>();
var
root
=
await
document
.
GetSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
root
=
await
document
.
Get
Required
SyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
typeDeclaration
=
syntaxFacts
.
GetContainingTypeDeclaration
(
root
,
textSpan
.
Start
);
var
typeDeclaration
=
syntaxFacts
.
GetContainingTypeDeclaration
(
root
,
textSpan
.
Start
);
return
await
CreateActionsAsync
(
return
await
CreateActionsAsync
(
...
@@ -240,15 +250,12 @@ private void GetExistingMemberInfo(INamedTypeSymbol containingType, out bool has
...
@@ -240,15 +250,12 @@ private void GetExistingMemberInfo(INamedTypeSymbol containingType, out bool has
Document
document
,
SyntaxNode
typeDeclaration
,
INamedTypeSymbol
containingType
,
ImmutableArray
<
ISymbol
>
members
,
Document
document
,
SyntaxNode
typeDeclaration
,
INamedTypeSymbol
containingType
,
ImmutableArray
<
ISymbol
>
members
,
bool
generateEquals
,
bool
generateGetHashCode
,
CancellationToken
cancellationToken
)
bool
generateEquals
,
bool
generateGetHashCode
,
CancellationToken
cancellationToken
)
{
{
var
semanticModel
=
await
document
.
GetSemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
semanticModel
=
await
document
.
Get
Required
SemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
options
=
await
document
.
GetOptionsAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
options
=
await
document
.
GetOptionsAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
using
var
_
=
ArrayBuilder
<
PickMembersOption
>.
GetInstance
(
out
var
pickMembersOptions
);
using
var
_
=
ArrayBuilder
<
PickMembersOption
>.
GetInstance
(
out
var
pickMembersOptions
);
var
canImplementIEquatable
=
CanImplementIEquatable
(
semanticModel
,
containingType
,
out
var
equatableTypeOpt
);
if
(
CanImplementIEquatable
(
semanticModel
,
containingType
,
out
var
equatableTypeOpt
))
var
hasExistingOperators
=
HasOperators
(
containingType
);
if
(
canImplementIEquatable
)
{
{
var
value
=
options
.
GetOption
(
GenerateEqualsAndGetHashCodeFromMembersOptions
.
ImplementIEquatable
);
var
value
=
options
.
GetOption
(
GenerateEqualsAndGetHashCodeFromMembersOptions
.
ImplementIEquatable
);
...
@@ -262,7 +269,7 @@ private void GetExistingMemberInfo(INamedTypeSymbol containingType, out bool has
...
@@ -262,7 +269,7 @@ private void GetExistingMemberInfo(INamedTypeSymbol containingType, out bool has
value
));
value
));
}
}
if
(!
hasExistingOperators
)
if
(!
HasOperators
(
containingType
)
)
{
{
var
value
=
options
.
GetOption
(
GenerateEqualsAndGetHashCodeFromMembersOptions
.
GenerateOperators
);
var
value
=
options
.
GetOption
(
GenerateEqualsAndGetHashCodeFromMembersOptions
.
GenerateOperators
);
pickMembersOptions
.
Add
(
new
PickMembersOption
(
pickMembersOptions
.
Add
(
new
PickMembersOption
(
...
@@ -287,7 +294,7 @@ private void GetExistingMemberInfo(INamedTypeSymbol containingType, out bool has
...
@@ -287,7 +294,7 @@ private void GetExistingMemberInfo(INamedTypeSymbol containingType, out bool has
{
{
// if we're generating equals for a struct, then also add IEquatable<S> support as
// if we're generating equals for a struct, then also add IEquatable<S> support as
// well as operators (as long as the struct does not already have them).
// well as operators (as long as the struct does not already have them).
var
semanticModel
=
await
document
.
GetSemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
semanticModel
=
await
document
.
Get
Required
SemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
implementIEquatable
=
CanImplementIEquatable
(
semanticModel
,
containingType
,
out
_
);
implementIEquatable
=
CanImplementIEquatable
(
semanticModel
,
containingType
,
out
_
);
generateOperators
=
!
HasOperators
(
containingType
);
generateOperators
=
!
HasOperators
(
containingType
);
}
}
...
...
src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions_CreateEqualsMethod.cs
浏览文件 @
36f71913
...
@@ -99,7 +99,16 @@ public static IMethodSymbol CreateEqualsMethod(this Compilation compilation, Imm
...
@@ -99,7 +99,16 @@ public static IMethodSymbol CreateEqualsMethod(this Compilation compilation, Imm
ImmutableArray
<
ISymbol
>
members
,
ImmutableArray
<
ISymbol
>
members
,
string
localNameOpt
)
string
localNameOpt
)
{
{
var
statements
=
ArrayBuilder
<
SyntaxNode
>.
GetInstance
();
using
var
_1
=
ArrayBuilder
<
SyntaxNode
>.
GetInstance
(
out
var
statements
);
// A ref like type can not be boxed. Because of this an overloaded Equals taking object in the general case
// can never be true, because an equivalent object can never be boxed into the object itself. Therefore only
// need to return false.
if
(
containingType
.
IsRefLikeType
)
{
statements
.
Add
(
factory
.
ReturnStatement
(
factory
.
FalseLiteralExpression
()));
return
statements
.
ToImmutable
();
}
// Come up with a good name for the local variable we're going to compare against.
// Come up with a good name for the local variable we're going to compare against.
// For example, if the class name is "CustomerOrder" then we'll generate:
// For example, if the class name is "CustomerOrder" then we'll generate:
...
@@ -113,7 +122,7 @@ public static IMethodSymbol CreateEqualsMethod(this Compilation compilation, Imm
...
@@ -113,7 +122,7 @@ public static IMethodSymbol CreateEqualsMethod(this Compilation compilation, Imm
// These will be all the expressions that we'll '&&' together inside the final
// These will be all the expressions that we'll '&&' together inside the final
// return statement of 'Equals'.
// return statement of 'Equals'.
using
var
_
=
ArrayBuilder
<
SyntaxNode
>.
GetInstance
(
out
var
expressions
);
using
var
_
2
=
ArrayBuilder
<
SyntaxNode
>.
GetInstance
(
out
var
expressions
);
if
(
factory
.
SupportsPatterns
(
parseOptions
))
if
(
factory
.
SupportsPatterns
(
parseOptions
))
{
{
...
@@ -189,7 +198,7 @@ public static IMethodSymbol CreateEqualsMethod(this Compilation compilation, Imm
...
@@ -189,7 +198,7 @@ public static IMethodSymbol CreateEqualsMethod(this Compilation compilation, Imm
statements
.
Add
(
factory
.
ReturnStatement
(
statements
.
Add
(
factory
.
ReturnStatement
(
expressions
.
Aggregate
(
factory
.
LogicalAndExpression
)));
expressions
.
Aggregate
(
factory
.
LogicalAndExpression
)));
return
statements
.
ToImmutable
AndFree
();
return
statements
.
ToImmutable
();
}
}
private
static
void
AddMemberChecks
(
private
static
void
AddMemberChecks
(
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录