Skip to content

Commit 50a18fd

Browse files
committed
Emit CompilerFeatureRequiredAttribute for instance operators
This prevents old compilers and other compilers (VB, etc.) from consuming the new APIs in an unintended way.
1 parent d63dc75 commit 50a18fd

File tree

9 files changed

+858
-444
lines changed

9 files changed

+858
-444
lines changed

src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,18 +1010,19 @@ public override ImmutableArray<CSharpAttributeData> GetAttributes()
10101010
: IsValidReadOnlyTarget;
10111011

10121012
bool checkForRequiredMembers = this.ShouldCheckRequiredMembers() && this.ContainingType.HasAnyRequiredMembers;
1013+
bool isInstanceIncrementDecrementOrCompoundAssignmentOperator = SourceMethodSymbol.IsInstanceIncrementDecrementOrCompoundAssignmentOperator(this);
10131014

10141015
bool isExtensionMethod = false;
10151016
bool isReadOnly = false;
1016-
if (checkForExtension || checkForIsReadOnly || checkForRequiredMembers)
1017+
if (checkForExtension || checkForIsReadOnly || checkForRequiredMembers || isInstanceIncrementDecrementOrCompoundAssignmentOperator)
10171018
{
10181019
attributeData = containingPEModuleSymbol.GetCustomAttributesForToken(_handle,
10191020
filteredOutAttribute1: out CustomAttributeHandle extensionAttribute,
10201021
filterOut1: AttributeDescription.CaseSensitiveExtensionAttribute,
10211022
filteredOutAttribute2: out CustomAttributeHandle isReadOnlyAttribute,
10221023
filterOut2: AttributeDescription.IsReadOnlyAttribute,
10231024
filteredOutAttribute3: out _,
1024-
filterOut3: (checkForRequiredMembers && DeriveCompilerFeatureRequiredDiagnostic() is null) ? AttributeDescription.CompilerFeatureRequiredAttribute : default,
1025+
filterOut3: ((checkForRequiredMembers || isInstanceIncrementDecrementOrCompoundAssignmentOperator) && DeriveCompilerFeatureRequiredDiagnostic() is null) ? AttributeDescription.CompilerFeatureRequiredAttribute : default,
10251026
filteredOutAttribute4: out _,
10261027
filterOut4: (checkForRequiredMembers && ObsoleteAttributeData is null) ? AttributeDescription.ObsoleteAttribute : default,
10271028
filteredOutAttribute5: out _,
@@ -1520,7 +1521,14 @@ private DiagnosticInfo DeriveCompilerFeatureRequiredDiagnostic()
15201521
{
15211522
var containingModule = _containingType.ContainingPEModule;
15221523
var decoder = new MetadataDecoder(containingModule, this);
1523-
var diag = PEUtilities.DeriveCompilerFeatureRequiredAttributeDiagnostic(this, containingModule, Handle, allowedFeatures: MethodKind == MethodKind.Constructor ? CompilerFeatureRequiredFeatures.RequiredMembers : CompilerFeatureRequiredFeatures.None, decoder);
1524+
var diag = PEUtilities.DeriveCompilerFeatureRequiredAttributeDiagnostic(
1525+
this, containingModule, Handle,
1526+
allowedFeatures: MethodKind == MethodKind.Constructor ?
1527+
CompilerFeatureRequiredFeatures.RequiredMembers :
1528+
(SourceMethodSymbol.IsInstanceIncrementDecrementOrCompoundAssignmentOperator(this) ?
1529+
CompilerFeatureRequiredFeatures.UserDefinedCompoundAssignmentOperators :
1530+
CompilerFeatureRequiredFeatures.None),
1531+
decoder);
15241532

15251533
if (diag != null)
15261534
{

src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,26 @@ SynthesizedEventAccessorSymbol or
224224
{
225225
AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Diagnostics_DebuggerHiddenAttribute__ctor));
226226
}
227+
228+
if (IsInstanceIncrementDecrementOrCompoundAssignmentOperator(target))
229+
{
230+
AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute__ctor,
231+
ImmutableArray.Create(new TypedConstant(compilation.GetSpecialType(SpecialType.System_String), TypedConstantKind.Primitive, nameof(CompilerFeatureRequiredFeatures.UserDefinedCompoundAssignmentOperators)))
232+
));
233+
}
234+
}
235+
236+
internal static bool IsInstanceIncrementDecrementOrCompoundAssignmentOperator(MethodSymbol target)
237+
{
238+
if (target.MethodKind == MethodKind.UserDefinedOperator && !target.IsStatic)
239+
{
240+
SyntaxKind syntaxKind = SyntaxFacts.GetOperatorKind(target.Name);
241+
242+
return syntaxKind is (SyntaxKind.PlusPlusToken or SyntaxKind.MinusMinusToken) ||
243+
SyntaxFacts.IsOverloadableCompoundAssignmentOperator(syntaxKind);
244+
}
245+
246+
return false;
227247
}
228248
}
229249
}

src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,11 @@ protected SourceUserDefinedOperatorSymbolBase(
181181
// SPEC: It is an error for the same modifier to appear multiple times in an
182182
// SPEC: operator declaration.
183183
ModifierUtils.CheckAccessibility(this.DeclarationModifiers, this, isExplicitInterfaceImplementation: false, diagnostics, location);
184+
185+
if (!IsStatic && (isIncrementDecrement || isCompoundAssignment))
186+
{
187+
_ = Binder.GetWellKnownTypeMember(DeclaringCompilation, WellKnownMember.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute__ctor, diagnostics, location);
188+
}
184189
}
185190

186191
private static (bool isIncrementDecrement, bool isCompoundAssignment) IsAssignmentOperatorDeclaration(CSharpSyntaxNode syntax)

0 commit comments

Comments
 (0)