Skip to content

Commit d0d2c14

Browse files
authored
Emit CompilerFeatureRequiredAttribute for instance operators (#78163)
This prevents old compilers and other compilers (VB, etc.) from consuming the new APIs in an unintended way.
1 parent 0155691 commit d0d2c14

File tree

9 files changed

+858
-444
lines changed

9 files changed

+858
-444
lines changed

Diff for: src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs

+11-3
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
{

Diff for: src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs

+20
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
}

Diff for: src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs

+5
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)