Skip to content

Commit

Permalink
Add support for assembly/module-level [Experimental] and contextual…
Browse files Browse the repository at this point in the history
… suppression (#69316)
  • Loading branch information
jcouv authored Aug 10, 2023
1 parent d4aa197 commit 73689cc
Show file tree
Hide file tree
Showing 35 changed files with 2,074 additions and 89 deletions.
9 changes: 0 additions & 9 deletions src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,15 +276,6 @@ public sealed override bool IsExtern
}
}

/// <summary>
/// Returns data decoded from Obsolete attribute or null if there is no Obsolete attribute.
/// This property returns ObsoleteAttributeData.Uninitialized if attribute arguments haven't been decoded yet.
/// </summary>
internal sealed override ObsoleteAttributeData ObsoleteAttributeData
{
get { return null; }
}

public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ internal sealed class PEAssemblySymbol : MetadataOrSourceAssemblySymbol

#nullable enable
private DiagnosticInfo? _lazyCachedCompilerFeatureRequiredDiagnosticInfo = CSDiagnosticInfo.EmptyErrorInfo;

private ObsoleteAttributeData? _lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized;
#nullable disable

internal PEAssemblySymbol(PEAssembly assembly, DocumentationProvider documentationProvider, bool isLinked, MetadataImportOptions importOptions)
Expand Down Expand Up @@ -311,5 +313,31 @@ internal PEModuleSymbol PrimaryModule

public override bool HasUnsupportedMetadata
=> GetCompilerFeatureRequiredDiagnostic()?.Code == (int)ErrorCode.ERR_UnsupportedCompilerFeature || base.HasUnsupportedMetadata;

internal sealed override ObsoleteAttributeData? ObsoleteAttributeData
{
get
{
if (_lazyObsoleteAttributeData == ObsoleteAttributeData.Uninitialized)
{
Interlocked.CompareExchange(ref _lazyObsoleteAttributeData, computeObsoleteAttributeData(), ObsoleteAttributeData.Uninitialized);
}

return _lazyObsoleteAttributeData;

ObsoleteAttributeData? computeObsoleteAttributeData()
{
foreach (var attrData in GetAttributes())
{
if (attrData.IsTargetAttribute(this, AttributeDescription.ExperimentalAttribute))
{
return attrData.DecodeExperimentalAttribute();
}
}

return null;
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ internal enum RefSafetyRulesAttributeVersion

#nullable enable
private DiagnosticInfo? _lazyCachedCompilerFeatureRequiredDiagnosticInfo = CSDiagnosticInfo.EmptyErrorInfo;

private ObsoleteAttributeData? _lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized;
#nullable disable

internal PEModuleSymbol(PEAssemblySymbol assemblySymbol, PEModule module, MetadataImportOptions importOptions, int ordinal)
Expand Down Expand Up @@ -857,5 +859,31 @@ RefSafetyRulesAttributeVersion getAttributeVersion()
}
}
}

internal sealed override ObsoleteAttributeData? ObsoleteAttributeData
{
get
{
if (_lazyObsoleteAttributeData == ObsoleteAttributeData.Uninitialized)
{
Interlocked.CompareExchange(ref _lazyObsoleteAttributeData, computeObsoleteAttributeData(), ObsoleteAttributeData.Uninitialized);
}

return _lazyObsoleteAttributeData;

ObsoleteAttributeData? computeObsoleteAttributeData()
{
foreach (var attrData in GetAttributes())
{
if (attrData.IsTargetAttribute(this, AttributeDescription.ExperimentalAttribute))
{
return attrData.DecodeExperimentalAttribute();
}
}

return null;
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -209,5 +209,8 @@ internal sealed override IEnumerable<NamedTypeSymbol> GetAllTopLevelForwardedTyp
{
return SpecializedCollections.EmptyEnumerable<NamedTypeSymbol>();
}

#nullable enable
internal sealed override ObsoleteAttributeData? ObsoleteAttributeData => null;
}
}
4 changes: 4 additions & 0 deletions src/Compilers/CSharp/Portable/Symbols/MissingModuleSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ public sealed override bool AreLocalsZeroed
}

internal sealed override bool UseUpdatedEscapeRules => false;

#nullable enable
internal sealed override ObsoleteAttributeData? ObsoleteAttributeData => null;
#nullable disable
}

internal sealed class MissingModuleSymbolWithName : MissingModuleSymbol
Expand Down
9 changes: 0 additions & 9 deletions src/Compilers/CSharp/Portable/Symbols/ModuleSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,15 +185,6 @@ public sealed override bool IsExtern
}
}

/// <summary>
/// Returns data decoded from Obsolete attribute or null if there is no Obsolete attribute.
/// This property returns ObsoleteAttributeData.Uninitialized if attribute arguments haven't been decoded yet.
/// </summary>
internal sealed override ObsoleteAttributeData ObsoleteAttributeData
{
get { return null; }
}

public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences
{
get
Expand Down
50 changes: 35 additions & 15 deletions src/Compilers/CSharp/Portable/Symbols/ObsoleteAttributeHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

#nullable disable

using System;
using System.Diagnostics;
using System.Reflection.Metadata;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
Expand Down Expand Up @@ -54,7 +56,7 @@ internal static ObsoleteAttributeData GetObsoleteDataFromMetadata(EntityHandle t
/// symbol's Obsoleteness is Unknown. False, if we are certain that no symbol in the parent
/// hierarchy is Obsolete.
/// </returns>
private static ThreeState GetObsoleteContextState(Symbol symbol, bool forceComplete)
private static ThreeState GetObsoleteContextState(Symbol symbol, bool forceComplete, Func<Symbol, ThreeState> getStateFromSymbol)
{
while ((object)symbol != null)
{
Expand All @@ -73,7 +75,7 @@ private static ThreeState GetObsoleteContextState(Symbol symbol, bool forceCompl
symbol.ForceCompleteObsoleteAttribute();
}

var state = symbol.ObsoleteState;
var state = getStateFromSymbol(symbol);
if (state != ThreeState.False)
{
return state;
Expand All @@ -98,10 +100,23 @@ internal static ObsoleteDiagnosticKind GetObsoleteDiagnosticKind(Symbol symbol,
switch (symbol.ObsoleteKind)
{
case ObsoleteAttributeKind.None:
if (symbol.ContainingModule.ObsoleteKind is ObsoleteAttributeKind.Experimental
|| symbol.ContainingAssembly.ObsoleteKind is ObsoleteAttributeKind.Experimental)
{
return getDiagnosticKind(containingMember, forceComplete, getStateFromSymbol: static (symbol) => symbol.ExperimentalState);
}

if (symbol.ContainingModule.ObsoleteKind is ObsoleteAttributeKind.Uninitialized
|| symbol.ContainingAssembly.ObsoleteKind is ObsoleteAttributeKind.Uninitialized)
{
return ObsoleteDiagnosticKind.Lazy;
}

return ObsoleteDiagnosticKind.NotObsolete;
case ObsoleteAttributeKind.WindowsExperimental:
case ObsoleteAttributeKind.Experimental:
return ObsoleteDiagnosticKind.Diagnostic;
case ObsoleteAttributeKind.Experimental:
return getDiagnosticKind(containingMember, forceComplete, getStateFromSymbol: static (symbol) => symbol.ExperimentalState);
case ObsoleteAttributeKind.Uninitialized:
// If we haven't cracked attributes on the symbol at all or we haven't
// cracked attribute arguments enough to be able to report diagnostics for
Expand All @@ -110,18 +125,23 @@ internal static ObsoleteDiagnosticKind GetObsoleteDiagnosticKind(Symbol symbol,
return ObsoleteDiagnosticKind.Lazy;
}

switch (GetObsoleteContextState(containingMember, forceComplete))
return getDiagnosticKind(containingMember, forceComplete, getStateFromSymbol: static (symbol) => symbol.ObsoleteState);

static ObsoleteDiagnosticKind getDiagnosticKind(Symbol containingMember, bool forceComplete, Func<Symbol, ThreeState> getStateFromSymbol)
{
case ThreeState.False:
return ObsoleteDiagnosticKind.Diagnostic;
case ThreeState.True:
// If we are in a context that is already obsolete, there is no point reporting
// more obsolete diagnostics.
return ObsoleteDiagnosticKind.Suppressed;
default:
// If the context is unknown, then store the symbol so that we can do this check at a
// later stage
return ObsoleteDiagnosticKind.LazyPotentiallySuppressed;
switch (GetObsoleteContextState(containingMember, forceComplete, getStateFromSymbol))
{
case ThreeState.False:
return ObsoleteDiagnosticKind.Diagnostic;
case ThreeState.True:
// If we are in a context that is already experimental/obsolete, there is no point reporting
// more experimental/obsolete diagnostics.
return ObsoleteDiagnosticKind.Suppressed;
default:
// If the context is unknown, then store the symbol so that we can do this check at a
// later stage
return ObsoleteDiagnosticKind.LazyPotentiallySuppressed;
}
}
}

Expand All @@ -137,7 +157,7 @@ internal static DiagnosticInfo CreateObsoleteDiagnostic(Symbol symbol, BinderFla

static DiagnosticInfo createObsoleteDiagnostic(Symbol symbol, BinderFlags location)
{
var data = symbol.ObsoleteAttributeData;
var data = symbol.ObsoleteAttributeData ?? symbol.ContainingModule.ObsoleteAttributeData ?? symbol.ContainingAssembly.ObsoleteAttributeData;
Debug.Assert(data != null);

if (data == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,8 @@ internal override bool GetGuidString(out string guidString)
}

#nullable enable
internal sealed override ObsoleteAttributeData? ObsoleteAttributeData
=> _underlyingAssembly.ObsoleteAttributeData;

internal override NamedTypeSymbol? TryLookupForwardedMetadataTypeWithCycleDetection(ref MetadataTypeName emittedName, ConsList<AssemblySymbol>? visitedAssemblies)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,5 +318,9 @@ public sealed override bool AreLocalsZeroed
}

internal override bool UseUpdatedEscapeRules => _underlyingModule.UseUpdatedEscapeRules;

#nullable enable
internal sealed override ObsoleteAttributeData? ObsoleteAttributeData
=> _underlyingModule.ObsoleteAttributeData;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
Expand Down Expand Up @@ -1382,7 +1380,7 @@ private void LoadAndValidateNetModuleAttributes(ref CustomAttributesBag<CSharpAt
// This affects only diagnostics.
for (int i = _modules.Length - 1; i > 0; i--)
{
var peModuleSymbol = (Metadata.PE.PEModuleSymbol)_modules[i];
var peModuleSymbol = (PEModuleSymbol)_modules[i];

foreach (NamedTypeSymbol forwarded in peModuleSymbol.GetForwardedTypes())
{
Expand Down Expand Up @@ -2553,6 +2551,11 @@ private void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArguments<Attr

arguments.GetOrCreateData<CommonAssemblyWellKnownAttributeData>().AssemblyAlgorithmIdAttributeSetting = algorithmId;
}
else if (attribute.IsTargetAttribute(this, AttributeDescription.ExperimentalAttribute))
{
var obsoleteData = attribute.DecodeExperimentalAttribute();
arguments.GetOrCreateData<CommonAssemblyWellKnownAttributeData>().ExperimentalAttributeData = obsoleteData;
}
}

// Checks that the integral arguments for the given well-known attribute are non-negative.
Expand Down Expand Up @@ -2860,6 +2863,41 @@ private static string DefaultValue(TypeSymbol type)
return null;
}

/// <summary>
/// Returns data decoded from Obsolete attribute or null if there is no Obsolete attribute.
/// This property returns ObsoleteAttributeData.Uninitialized if attribute arguments haven't been decoded yet.
/// </summary>
internal sealed override ObsoleteAttributeData? ObsoleteAttributeData
{
get
{
// [assembly: Experimental] may have been specified in the assembly or one of the modules
var lazySourceAttributesBag = _lazySourceAttributesBag;
if (lazySourceAttributesBag != null && lazySourceAttributesBag.IsDecodedWellKnownAttributeDataComputed)
{
var data = (CommonAssemblyWellKnownAttributeData)lazySourceAttributesBag.DecodedWellKnownAttributeData;
if (data?.ExperimentalAttributeData is { } experimentalAttributeData)
{
return experimentalAttributeData;
}
}

var lazyNetModuleAttributesBag = _lazyNetModuleAttributesBag;
if (lazyNetModuleAttributesBag != null && lazyNetModuleAttributesBag.IsDecodedWellKnownAttributeDataComputed)
{
var data = (CommonAssemblyWellKnownAttributeData)lazyNetModuleAttributesBag.DecodedWellKnownAttributeData;
return data?.ExperimentalAttributeData;
}

if (GetAttributeDeclarations().IsEmpty)
{
return null;
}

return ObsoleteAttributeData.Uninitialized;
}
}

#nullable disable

internal override IEnumerable<NamedTypeSymbol> GetAllTopLevelForwardedTypes()
Expand Down
29 changes: 29 additions & 0 deletions src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,10 @@ protected override void DecodeWellKnownAttributeImpl(ref DecodeWellKnownAttribut
{
CSharpAttributeData.DecodeSkipLocalsInitAttribute<ModuleWellKnownAttributeData>(DeclaringCompilation, ref arguments);
}
else if (attribute.IsTargetAttribute(this, AttributeDescription.ExperimentalAttribute))
{
arguments.GetOrCreateData<ModuleWellKnownAttributeData>().ExperimentalAttributeData = attribute.DecodeExperimentalAttribute();
}
}

#nullable enable
Expand Down Expand Up @@ -652,5 +656,30 @@ internal override bool UseUpdatedEscapeRules
return _lazyUseUpdatedEscapeRules == ThreeState.True;
}
}

/// <summary>
/// Returns data decoded from <see cref="ObsoleteAttribute"/> attribute or null if there is no <see cref="ObsoleteAttribute"/> attribute.
/// This property returns <see cref="Microsoft.CodeAnalysis.ObsoleteAttributeData.Uninitialized"/> if attribute arguments haven't been decoded yet.
/// </summary>
internal sealed override ObsoleteAttributeData? ObsoleteAttributeData
{
get
{
var attributesBag = _lazyCustomAttributesBag;
if (attributesBag != null && attributesBag.IsDecodedWellKnownAttributeDataComputed)
{
var decodedData = (ModuleWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData;
return decodedData?.ExperimentalAttributeData;
}

var attributesDeclarations = ((SourceAssemblySymbol)ContainingAssembly).GetAttributeDeclarations();
if (attributesDeclarations.IsEmpty)
{
return null;
}

return ObsoleteAttributeData.Uninitialized;
}
}
}
}
Loading

0 comments on commit 73689cc

Please sign in to comment.