Skip to content

Commit

Permalink
Add support for CompilerFeatureRequiredAttribute
Browse files Browse the repository at this point in the history
Adds support for decoding and reporting errors when `CompilerFeatureRequiredAttribute` is encountered on metadata type symbols. We also block applying the attribute by hand in both C# and VB.

Test plan: dotnet#57046
  • Loading branch information
333fred committed May 3, 2022
1 parent 252a1d1 commit b81e291
Show file tree
Hide file tree
Showing 55 changed files with 2,175 additions and 52 deletions.
3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -7130,4 +7130,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="IDS_FeatureUnsignedRightShift" xml:space="preserve">
<value>unsigned right shift</value>
</data>
<data name="ERR_UnsupportedCompilerFeature" xml:space="preserve">
<value>'{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler.</value>
</data>
</root>
1 change: 1 addition & 0 deletions src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2087,5 +2087,6 @@ internal enum ErrorCode
ERR_RequiredMembersBaseTypeInvalid = 9509,
ERR_ChainingToSetsRequiredMembersRequiresSetsRequiredMembers = 9510,
ERR_NewConstraintCannotHaveRequiredMembers = 9511,
ERR_UnsupportedCompilerFeature = 9512,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.CodeAnalysis.CSharp.DocumentationComments;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -468,6 +469,7 @@ internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
{
UseSiteInfo<AssemblySymbol> result = new UseSiteInfo<AssemblySymbol>(primaryDependency);
CalculateUseSiteDiagnostic(ref result);
DeriveUseSiteInfoFromCompilerFeatureRequiredAttributes(ref result, Handle, allowedFeatures: CompilerFeatureRequiredFeatures.None);
_lazyCachedUseSiteInfo.Initialize(primaryDependency, result);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.DocumentationComments;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
Expand Down Expand Up @@ -590,6 +591,7 @@ internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
{
UseSiteInfo<AssemblySymbol> result = new UseSiteInfo<AssemblySymbol>(primaryDependency);
CalculateUseSiteDiagnostic(ref result);
DeriveUseSiteInfoFromCompilerFeatureRequiredAttributes(ref result, Handle, allowedFeatures: CompilerFeatureRequiredFeatures.None);
_lazyCachedUseSiteInfo.Initialize(primaryDependency, result);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Microsoft.CodeAnalysis.CSharp.DocumentationComments;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
Expand Down Expand Up @@ -886,9 +887,17 @@ private ImmutableArray<TypeParameterSymbol> LoadTypeParameters(ref DiagnosticInf
else
{
var ownedParams = ImmutableArray.CreateBuilder<TypeParameterSymbol>(gpHandles.Count);
UseSiteInfo<AssemblySymbol> typeParamUseSiteInfo = default;
for (int i = 0; i < gpHandles.Count; i++)
{
ownedParams.Add(new PETypeParameterSymbol(moduleSymbol, this, (ushort)i, gpHandles[i]));
var typeParam = new PETypeParameterSymbol(moduleSymbol, this, (ushort)i, gpHandles[i]);
ownedParams.Add(typeParam);
MergeUseSiteInfo(ref typeParamUseSiteInfo, typeParam.GetUseSiteInfo());
}

if (typeParamUseSiteInfo.DiagnosticInfo != null)
{
diagnosticInfo = typeParamUseSiteInfo.DiagnosticInfo;
}

return ownedParams.ToImmutable();
Expand Down Expand Up @@ -1356,6 +1365,18 @@ internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
{
UseSiteInfo<AssemblySymbol> result = new UseSiteInfo<AssemblySymbol>(PrimaryDependency);
CalculateUseSiteDiagnostic(ref result);
DeriveUseSiteInfoFromCompilerFeatureRequiredAttributes(ref result, Handle, allowedFeatures: MethodKind == MethodKind.Constructor ? CompilerFeatureRequiredFeatures.RequiredMembers : CompilerFeatureRequiredFeatures.None);

if (result.DiagnosticInfo == null || !IsHighestPriorityUseSiteError(result.DiagnosticInfo))
{
foreach (var param in Parameters)
{
if (MergeUseSiteInfo(ref result, param.GetUseSiteInfo()))
{
break;
}
}
}

var diagnosticInfo = result.DiagnosticInfo;
EnsureTypeParametersAreLoaded(ref diagnosticInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
using System.Reflection.Metadata.Ecma335;
using Microsoft.CodeAnalysis.Symbols;

namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
{
Expand Down Expand Up @@ -2016,48 +2017,54 @@ internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
if (!_lazyCachedUseSiteInfo.IsInitialized)
{
AssemblySymbol primaryDependency = PrimaryDependency;
_lazyCachedUseSiteInfo.Initialize(primaryDependency, new UseSiteInfo<AssemblySymbol>(primaryDependency).AdjustDiagnosticInfo(GetUseSiteDiagnosticImpl()));
var result = new UseSiteInfo<AssemblySymbol>(primaryDependency);
GetUseSiteDiagnosticImpl(ref result);
_lazyCachedUseSiteInfo.Initialize(primaryDependency, result);
}

return _lazyCachedUseSiteInfo.ToUseSiteInfo(PrimaryDependency);
}

protected virtual DiagnosticInfo GetUseSiteDiagnosticImpl()
protected virtual void GetUseSiteDiagnosticImpl(ref UseSiteInfo<AssemblySymbol> result)
{
DiagnosticInfo diagnostic = null;
if (MergeUseSiteInfo(ref result, new UseSiteInfo<AssemblySymbol>(CalculateUseSiteDiagnostic())))
{
return;
}

if (!MergeUseSiteDiagnostics(ref diagnostic, CalculateUseSiteDiagnostic()))
// Check if this type is marked by RequiredAttribute attribute.
// If so mark the type as bad, because it relies upon semantics that are not understood by the C# compiler.
if (this.ContainingPEModule.Module.HasRequiredAttributeAttribute(_handle))
{
// Check if this type is marked by RequiredAttribute attribute.
// If so mark the type as bad, because it relies upon semantics that are not understood by the C# compiler.
if (this.ContainingPEModule.Module.HasRequiredAttributeAttribute(_handle))
{
diagnostic = new CSDiagnosticInfo(ErrorCode.ERR_BogusType, this);
}
else if (TypeKind == TypeKind.Class && SpecialType != SpecialType.System_Enum)
result = result.AdjustDiagnosticInfo(new CSDiagnosticInfo(ErrorCode.ERR_BogusType, this));
return;
}
else if (TypeKind == TypeKind.Class && SpecialType != SpecialType.System_Enum)
{
TypeSymbol @base = GetDeclaredBaseType(null);
if (@base?.SpecialType == SpecialType.None && @base.ContainingAssembly?.IsMissing == true)
{
TypeSymbol @base = GetDeclaredBaseType(null);
if (@base?.SpecialType == SpecialType.None && @base.ContainingAssembly?.IsMissing == true)
var missingType = @base as MissingMetadataTypeSymbol.TopLevel;
if ((object)missingType != null && missingType.Arity == 0)
{
var missingType = @base as MissingMetadataTypeSymbol.TopLevel;
if ((object)missingType != null && missingType.Arity == 0)
string emittedName = MetadataHelpers.BuildQualifiedName(missingType.NamespaceName, missingType.MetadataName);
switch (SpecialTypes.GetTypeFromMetadataName(emittedName))
{
string emittedName = MetadataHelpers.BuildQualifiedName(missingType.NamespaceName, missingType.MetadataName);
switch (SpecialTypes.GetTypeFromMetadataName(emittedName))
{
case SpecialType.System_Enum:
case SpecialType.System_MulticastDelegate:
case SpecialType.System_ValueType:
// This might be a structure, an enum, or a delegate
diagnostic = missingType.GetUseSiteInfo().DiagnosticInfo;
break;
}
case SpecialType.System_Enum:
case SpecialType.System_MulticastDelegate:
case SpecialType.System_ValueType:
// This might be a structure, an enum, or a delegate
if (MergeUseSiteInfo(ref result, missingType.GetUseSiteInfo()))
{
return;
}
break;
}
}
}
}

return diagnostic;
DeriveUseSiteInfoFromCompilerFeatureRequiredAttributes(ref result, Handle, allowedFeatures: IsRefLikeType ? CompilerFeatureRequiredFeatures.RefStructs : CompilerFeatureRequiredFeatures.None);
}

internal string DefaultMemberName
Expand Down Expand Up @@ -2515,21 +2522,28 @@ private void EnsureTypeParametersAreLoaded()
}
}

protected override DiagnosticInfo GetUseSiteDiagnosticImpl()
protected override void GetUseSiteDiagnosticImpl(ref UseSiteInfo<AssemblySymbol> result)
{
DiagnosticInfo diagnostic = null;
base.GetUseSiteDiagnosticImpl(ref result);
if (result.DiagnosticInfo != null && IsHighestPriorityUseSiteError(result.DiagnosticInfo))
{
return;
}

if (!MergeUseSiteDiagnostics(ref diagnostic, base.GetUseSiteDiagnosticImpl()))
foreach (var typeParameter in this.TypeParameters)
{
// Verify type parameters for containing types
// match those on the containing types.
if (!MatchesContainingTypeParameters())
if (MergeUseSiteInfo(ref result, typeParameter.GetUseSiteInfo()))
{
diagnostic = new CSDiagnosticInfo(ErrorCode.ERR_BogusType, this);
return;
}
}

return diagnostic;
// Verify type parameters for containing types
// match those on the containing types.
if (!MatchesContainingTypeParameters())
{
result = result.AdjustDiagnosticInfo(new CSDiagnosticInfo(ErrorCode.ERR_BogusType, this));
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
Expand Down Expand Up @@ -160,6 +161,8 @@ public bool TryGetFlowAnalysisAnnotations(out FlowAnalysisAnnotations value)

private PackedFlags _packedFlags;

private CachedUseSiteInfo<AssemblySymbol> _lazyCachedUseSiteInfo = CachedUseSiteInfo<AssemblySymbol>.Uninitialized;

internal static PEParameterSymbol Create(
PEModuleSymbol moduleSymbol,
PEMethodSymbol containingSymbol,
Expand Down Expand Up @@ -1068,5 +1071,20 @@ public sealed override bool Equals(Symbol other, TypeCompareKind compareKind)
nps.Equals(this, compareKind) :
base.Equals(other, compareKind);
}

#nullable enable
internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
{
AssemblySymbol primaryDependency = PrimaryDependency;

if (!_lazyCachedUseSiteInfo.IsInitialized)
{
UseSiteInfo<AssemblySymbol> result = new UseSiteInfo<AssemblySymbol>(primaryDependency);
DeriveUseSiteInfoFromCompilerFeatureRequiredAttributes(ref result, Handle, allowedFeatures: CompilerFeatureRequiredFeatures.None);
_lazyCachedUseSiteInfo.Initialize(primaryDependency, result);
}

return _lazyCachedUseSiteInfo.ToUseSiteInfo(primaryDependency);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using Microsoft.CodeAnalysis.CSharp.DocumentationComments;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
Expand Down Expand Up @@ -784,6 +785,7 @@ internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
{
var result = new UseSiteInfo<AssemblySymbol>(primaryDependency);
CalculateUseSiteDiagnostic(ref result);
DeriveUseSiteInfoFromCompilerFeatureRequiredAttributes(ref result, Handle, allowedFeatures: CompilerFeatureRequiredFeatures.None);
_lazyCachedUseSiteInfo.Initialize(primaryDependency, result);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using System.Reflection.Metadata.Ecma335;
using System.Threading;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
Expand Down Expand Up @@ -42,6 +43,7 @@ internal sealed class PETypeParameterSymbol
private TypeParameterBounds _lazyBounds = TypeParameterBounds.Unset;
private ImmutableArray<TypeWithAnnotations> _lazyDeclaredConstraintTypes;
private ImmutableArray<CSharpAttributeData> _lazyCustomAttributes;
private CachedUseSiteInfo<AssemblySymbol> _lazyCachedUseSiteInfo = CachedUseSiteInfo<AssemblySymbol>.Uninitialized;

internal PETypeParameterSymbol(
PEModuleSymbol moduleSymbol,
Expand Down Expand Up @@ -705,5 +707,20 @@ private NamedTypeSymbol GetDefaultBaseType()
{
get { return null; }
}

#nullable enable
internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
{
AssemblySymbol primaryDependency = PrimaryDependency;

if (!_lazyCachedUseSiteInfo.IsInitialized)
{
UseSiteInfo<AssemblySymbol> result = new UseSiteInfo<AssemblySymbol>(primaryDependency);
DeriveUseSiteInfoFromCompilerFeatureRequiredAttributes(ref result, Handle, allowedFeatures: CompilerFeatureRequiredFeatures.None);
_lazyCachedUseSiteInfo.Initialize(primaryDependency, result);
}

return _lazyCachedUseSiteInfo.ToUseSiteInfo(primaryDependency);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -424,11 +424,7 @@ internal sealed override void DecodeWellKnownAttribute(ref DecodeWellKnownAttrib
Debug.Assert(!attribute.HasErrors);
Debug.Assert(arguments.SymbolPart == AttributeLocation.None);

if (attribute.IsTargetAttribute(this, AttributeDescription.NullableAttribute))
{
// NullableAttribute should not be set explicitly.
((BindingDiagnosticBag)arguments.Diagnostics).Add(ErrorCode.ERR_ExplicitNullableAttribute, arguments.AttributeSyntaxOpt.Location);
}
ReportExplicitUseOfReservedAttributes(in arguments, ReservedAttributes.NullableAttribute);

base.DecodeWellKnownAttribute(ref arguments);
}
Expand Down
24 changes: 22 additions & 2 deletions src/Compilers/CSharp/Portable/Symbols/Symbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -987,7 +987,7 @@ internal bool MergeUseSiteDiagnostics(ref DiagnosticInfo result, DiagnosticInfo
return false;
}

if (info.Severity == DiagnosticSeverity.Error && (info.Code == HighestPriorityUseSiteError || HighestPriorityUseSiteError == Int32.MaxValue))
if (IsHighestPriorityUseSiteError(info))
{
// this error is final, no other error can override it:
result = info;
Expand All @@ -1005,6 +1005,10 @@ internal bool MergeUseSiteDiagnostics(ref DiagnosticInfo result, DiagnosticInfo
return false;
}

protected bool IsHighestPriorityUseSiteError(DiagnosticInfo info)
=> info.Severity == DiagnosticSeverity.Error
&& (info.Code == HighestPriorityUseSiteError || HighestPriorityUseSiteError == int.MaxValue);

/// <summary>
/// Merges given diagnostic and dependencies to the existing result.
/// </summary>
Expand Down Expand Up @@ -1122,7 +1126,6 @@ internal bool DeriveUseSiteInfoFromParameters(ref UseSiteInfo<AssemblySymbol> re
return false;
}


[Flags]
internal enum AllowedRequiredModifierType
{
Expand Down Expand Up @@ -1196,6 +1199,18 @@ internal bool DeriveUseSiteInfoFromCustomModifiers(ref UseSiteInfo<AssemblySymbo
return false;
}

#nullable enable
internal void DeriveUseSiteInfoFromCompilerFeatureRequiredAttributes(ref UseSiteInfo<AssemblySymbol> result, System.Reflection.Metadata.EntityHandle handle, CompilerFeatureRequiredFeatures allowedFeatures)
{
string? disallowedFeature = CompilerFeatureRequiredHelpers.GetUnsupportedCompilerFeature(handle, ((PEModuleSymbol)this.ContainingModule).Module, allowedFeatures);
if (disallowedFeature != null)
{
// '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler.
result = result.AdjustDiagnosticInfo(new CSDiagnosticInfo(ErrorCode.ERR_UnsupportedCompilerFeature, this, disallowedFeature));
}
}
#nullable disable

internal static bool GetUnificationUseSiteDiagnosticRecursive<T>(ref DiagnosticInfo result, ImmutableArray<T> types, Symbol owner, ref HashSet<TypeSymbol> checkedTypes) where T : TypeSymbol
{
foreach (var t in types)
Expand Down Expand Up @@ -1445,6 +1460,11 @@ internal bool ReportExplicitUseOfReservedAttributes(in DecodeWellKnownAttributeA
// Do not use 'System.Runtime.CompilerServices.RequiredMemberAttribute'. Use the 'required' keyword on required fields and properties instead.
diagnostics.Add(ErrorCode.ERR_ExplicitRequiredMember, arguments.AttributeSyntaxOpt.Location);
}
else if (attribute.IsTargetAttribute(this, AttributeDescription.CompilerFeatureRequiredAttribute))
{
// This is always disallowed, so we don't bother with a flag.
reportExplicitUseOfReservedAttribute(attribute, arguments, AttributeDescription.CompilerFeatureRequiredAttribute);
}
else
{
return false;
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit b81e291

Please sign in to comment.