-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adjust implement interface service to work with static abstract operators #53941
Changes from 4 commits
9f0ba2f
9ce92bd
3ac631d
d9bd43b
38a4605
6fece0f
f6def2b
9c698bb
c0c96c8
ce3d776
b23f164
17b736e
35d1be4
7eca18a
00364cb
7a83274
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8984,5 +8984,122 @@ public int Foo | |
} | ||
", parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5)); | ||
} | ||
|
||
[WorkItem(53927, "https://github.com/dotnet/roslyn/issues/53927")] | ||
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)] | ||
public async Task TestStaticAbstractInterfaceOperator_OnlyExplicitlyImplementable() | ||
{ | ||
await TestInRegularAndScriptAsync(@" | ||
interface ITest | ||
{ | ||
static abstract int operator -(ITest x); | ||
} | ||
|
||
class C : [|ITest|] | ||
{ | ||
} | ||
", | ||
@" | ||
interface ITest | ||
{ | ||
static abstract int operator -(ITest x); | ||
} | ||
|
||
class C : ITest | ||
{ | ||
static int ITest.operator -(ITest x) | ||
{ | ||
throw new System.NotImplementedException(); | ||
} | ||
} | ||
", parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), index: 0, title: FeaturesResources.Implement_all_members_explicitly); | ||
} | ||
|
||
[WorkItem(53927, "https://github.com/dotnet/roslyn/issues/53927")] | ||
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)] | ||
public async Task TestStaticAbstractInterfaceOperator_ImplementImplicitly() | ||
{ | ||
await TestInRegularAndScriptAsync(@" | ||
interface ITest<T> where T : ITest<T> | ||
{ | ||
static abstract int operator -(T x); | ||
} | ||
|
||
class C : [|ITest<C>|] | ||
{ | ||
} | ||
", | ||
@" | ||
interface ITest<T> where T : ITest<T> | ||
{ | ||
static abstract int operator -(T x); | ||
} | ||
|
||
class C : ITest<C> | ||
{ | ||
public static int operator -(C x) | ||
{ | ||
throw new System.NotImplementedException(); | ||
} | ||
} | ||
", parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), index: 0, title: FeaturesResources.Implement_interface); | ||
} | ||
|
||
[WorkItem(53927, "https://github.com/dotnet/roslyn/issues/53927")] | ||
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)] | ||
public async Task TestStaticAbstractInterfaceOperator_ImplementExplicitly() | ||
{ | ||
await TestInRegularAndScriptAsync(@" | ||
interface ITest<T> where T : ITest<T> | ||
{ | ||
static abstract int operator -(T x); | ||
} | ||
|
||
class C : [|ITest<C>|] | ||
{ | ||
} | ||
", | ||
@" | ||
interface ITest<T> where T : ITest<T> | ||
{ | ||
static abstract int operator -(T x); | ||
} | ||
|
||
class C : ITest<C> | ||
{ | ||
static int ITest<C>.operator -(C x) | ||
{ | ||
throw new System.NotImplementedException(); | ||
} | ||
} | ||
", parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), index: 1, title: FeaturesResources.Implement_all_members_explicitly); | ||
} | ||
|
||
[WorkItem(53927, "https://github.com/dotnet/roslyn/issues/53927")] | ||
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)] | ||
public async Task TestStaticAbstractInterfaceOperator_ImplementAbstractly() | ||
{ | ||
await TestInRegularAndScriptAsync(@" | ||
interface ITest<T> where T : ITest<T> | ||
{ | ||
static abstract int operator -(T x); | ||
} | ||
|
||
abstract class C : [|ITest<C>|] | ||
{ | ||
} | ||
", | ||
@" | ||
interface ITest<T> where T : ITest<T> | ||
{ | ||
static abstract int operator -(T x); | ||
} | ||
|
||
abstract class C : ITest<C> | ||
{ | ||
public abstract static int operator -(C x); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'm curious. is this legal? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Abstract statics are allowed only in interfaces There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for noting @CyrusNajmabadi @AlekseyTs There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. implement abstractly needs to take the code from uncompilable state to compilable, attempting for each member it needs to generate to make that abstract. If the member doesn't need to be generated, we should not generate it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @CyrusNajmabadi The member needs to be generated to be in a compilable state, but it cannot be generated abstractly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not believe there is away to implement a static member abstractly. So, if there are static abstract members in the interface, the option probably shouldn't be offered. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
'GEnerate abstractly' means 'generate abstractly if possible'. If not possible, generate legally. This already occurs today where normal instance methods might cause errors if generated abstractly. In that case i believe we normally just generate explicitly. End goal of this feature is always compilable code. The different user facing choices are about how we should generate when we have such flexibility while still ensuring compilable. If we are constrained, then we generate in whatever manner the constraints force. |
||
} | ||
", parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), index: 1, title: FeaturesResources.Implement_interface_abstractly); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -10,7 +10,8 @@ | |||||
using Microsoft.CodeAnalysis.CSharp.CodeStyle; | ||||||
using Microsoft.CodeAnalysis.CSharp.Extensions; | ||||||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||||||
|
||||||
using Microsoft.CodeAnalysis.PooledObjects; | ||||||
using Microsoft.CodeAnalysis.Shared.Extensions; | ||||||
using static Microsoft.CodeAnalysis.CodeGeneration.CodeGenerationHelpers; | ||||||
using static Microsoft.CodeAnalysis.CSharp.CodeGeneration.CSharpCodeGenerationHelpers; | ||||||
|
||||||
|
@@ -74,7 +75,7 @@ private static OperatorDeclarationSyntax GenerateOperatorDeclarationWorker( | |||||
CodeGenerationOptions options, | ||||||
ParseOptions parseOptions) | ||||||
{ | ||||||
var hasNoBody = !options.GenerateMethodBodies || method.IsExtern; | ||||||
var hasNoBody = !options.GenerateMethodBodies || method.IsExtern || method.IsAbstract; | ||||||
|
||||||
var operatorSyntaxKind = SyntaxFacts.GetOperatorKind(method.MetadataName); | ||||||
if (operatorSyntaxKind == SyntaxKind.None) | ||||||
|
@@ -86,23 +87,36 @@ private static OperatorDeclarationSyntax GenerateOperatorDeclarationWorker( | |||||
|
||||||
var operatorDecl = SyntaxFactory.OperatorDeclaration( | ||||||
attributeLists: AttributeGenerator.GenerateAttributeLists(method.GetAttributes(), options), | ||||||
modifiers: GenerateModifiers(), | ||||||
modifiers: GenerateModifiers(method), | ||||||
returnType: method.ReturnType.GenerateTypeSyntax(), | ||||||
explicitInterfaceSpecifier: GenerateExplicitInterfaceSpecifier(method.ExplicitInterfaceImplementations), | ||||||
operatorKeyword: SyntaxFactory.Token(SyntaxKind.OperatorKeyword), | ||||||
operatorToken: operatorToken, | ||||||
parameterList: ParameterGenerator.GenerateParameterList(method.Parameters, isExplicit: false, options: options), | ||||||
body: hasNoBody ? null : StatementGenerator.GenerateBlock(method), | ||||||
expressionBody: null, | ||||||
semicolonToken: hasNoBody ? SyntaxFactory.Token(SyntaxKind.SemicolonToken) : new SyntaxToken()); | ||||||
|
||||||
operatorDecl = UseExpressionBodyIfDesired(options, operatorDecl, parseOptions); | ||||||
return operatorDecl; | ||||||
} | ||||||
|
||||||
private static SyntaxTokenList GenerateModifiers() | ||||||
private static SyntaxTokenList GenerateModifiers(IMethodSymbol method) | ||||||
{ | ||||||
return SyntaxFactory.TokenList( | ||||||
SyntaxFactory.Token(SyntaxKind.PublicKeyword), | ||||||
SyntaxFactory.Token(SyntaxKind.StaticKeyword)); | ||||||
var tokens = ArrayBuilder<SyntaxToken>.GetInstance(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TemporaryArray is nice when we expect 4 or less items commonly :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice to know about TemporaryArray! Thanks. I applied the change. |
||||||
|
||||||
if (method.ExplicitInterfaceImplementations.Length == 0) | ||||||
{ | ||||||
tokens.Add(SyntaxFactory.Token(SyntaxKind.PublicKeyword)); | ||||||
} | ||||||
|
||||||
if (method.IsAbstract) | ||||||
{ | ||||||
tokens.Add(SyntaxFactory.Token(SyntaxKind.AbstractKeyword)); | ||||||
} | ||||||
|
||||||
tokens.Add(SyntaxFactory.Token(SyntaxKind.StaticKeyword)); | ||||||
Youssef1313 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
return tokens.ToSyntaxTokenListAndFree(); | ||||||
} | ||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the future, if your'e interested, it would be good to update these to be sdk style tests. that way we can validate if our fix produces errors :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 I'm working on this change for ImplementInterfaceTests in a separate branch.