diff --git a/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs
index df553606ed11..f44b86d0b9f2 100644
--- a/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs
@@ -276,15 +276,6 @@ public sealed override bool IsExtern
}
}
- ///
- /// 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.
- ///
- internal sealed override ObsoleteAttributeData ObsoleteAttributeData
- {
- get { return null; }
- }
-
public override ImmutableArray DeclaringSyntaxReferences
{
get
diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEAssemblySymbol.cs
index ba4f5fa114f7..bf58db1cbede 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEAssemblySymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEAssemblySymbol.cs
@@ -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)
@@ -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;
+ }
+ }
+ }
}
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs
index ce1cd6f4718e..472aa557609d 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs
@@ -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)
@@ -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;
+ }
+ }
+ }
}
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/MissingAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MissingAssemblySymbol.cs
index 94c8e988007f..cb2d98a6fe29 100644
--- a/src/Compilers/CSharp/Portable/Symbols/MissingAssemblySymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/MissingAssemblySymbol.cs
@@ -209,5 +209,8 @@ internal sealed override IEnumerable GetAllTopLevelForwardedTyp
{
return SpecializedCollections.EmptyEnumerable();
}
+
+#nullable enable
+ internal sealed override ObsoleteAttributeData? ObsoleteAttributeData => null;
}
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/MissingModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MissingModuleSymbol.cs
index 48277b000f8c..4154d729b902 100644
--- a/src/Compilers/CSharp/Portable/Symbols/MissingModuleSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/MissingModuleSymbol.cs
@@ -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
diff --git a/src/Compilers/CSharp/Portable/Symbols/ModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ModuleSymbol.cs
index 7c9546dfd9c5..b8d9cb2016cd 100644
--- a/src/Compilers/CSharp/Portable/Symbols/ModuleSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/ModuleSymbol.cs
@@ -185,15 +185,6 @@ public sealed override bool IsExtern
}
}
- ///
- /// 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.
- ///
- internal sealed override ObsoleteAttributeData ObsoleteAttributeData
- {
- get { return null; }
- }
-
public override ImmutableArray DeclaringSyntaxReferences
{
get
diff --git a/src/Compilers/CSharp/Portable/Symbols/ObsoleteAttributeHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/ObsoleteAttributeHelpers.cs
index f73759e6fa04..09b06ea32b6e 100644
--- a/src/Compilers/CSharp/Portable/Symbols/ObsoleteAttributeHelpers.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/ObsoleteAttributeHelpers.cs
@@ -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
{
@@ -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.
///
- private static ThreeState GetObsoleteContextState(Symbol symbol, bool forceComplete)
+ private static ThreeState GetObsoleteContextState(Symbol symbol, bool forceComplete, Func getStateFromSymbol)
{
while ((object)symbol != null)
{
@@ -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;
@@ -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
@@ -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 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;
+ }
}
}
@@ -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)
diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.cs
index 08ea5170c67b..62a92e425e3e 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.cs
@@ -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? visitedAssemblies)
{
diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingModuleSymbol.cs
index c6e35a0486b3..5c51325b0807 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingModuleSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingModuleSymbol.cs
@@ -318,5 +318,9 @@ public sealed override bool AreLocalsZeroed
}
internal override bool UseUpdatedEscapeRules => _underlyingModule.UseUpdatedEscapeRules;
+
+#nullable enable
+ internal sealed override ObsoleteAttributeData? ObsoleteAttributeData
+ => _underlyingModule.ObsoleteAttributeData;
}
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs
index b7bd00f63609..381307a6082a 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs
@@ -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;
@@ -1382,7 +1380,7 @@ private void LoadAndValidateNetModuleAttributes(ref CustomAttributesBag 0; i--)
{
- var peModuleSymbol = (Metadata.PE.PEModuleSymbol)_modules[i];
+ var peModuleSymbol = (PEModuleSymbol)_modules[i];
foreach (NamedTypeSymbol forwarded in peModuleSymbol.GetForwardedTypes())
{
@@ -2553,6 +2551,11 @@ private void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArguments().AssemblyAlgorithmIdAttributeSetting = algorithmId;
}
+ else if (attribute.IsTargetAttribute(this, AttributeDescription.ExperimentalAttribute))
+ {
+ var obsoleteData = attribute.DecodeExperimentalAttribute();
+ arguments.GetOrCreateData().ExperimentalAttributeData = obsoleteData;
+ }
}
// Checks that the integral arguments for the given well-known attribute are non-negative.
@@ -2860,6 +2863,41 @@ private static string DefaultValue(TypeSymbol type)
return null;
}
+ ///
+ /// 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.
+ ///
+ 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 GetAllTopLevelForwardedTypes()
diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs
index 1c1597da1300..0552c24da89c 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs
@@ -531,6 +531,10 @@ protected override void DecodeWellKnownAttributeImpl(ref DecodeWellKnownAttribut
{
CSharpAttributeData.DecodeSkipLocalsInitAttribute(DeclaringCompilation, ref arguments);
}
+ else if (attribute.IsTargetAttribute(this, AttributeDescription.ExperimentalAttribute))
+ {
+ arguments.GetOrCreateData().ExperimentalAttributeData = attribute.DecodeExperimentalAttribute();
+ }
}
#nullable enable
@@ -652,5 +656,30 @@ internal override bool UseUpdatedEscapeRules
return _lazyUseUpdatedEscapeRules == ThreeState.True;
}
}
+
+ ///
+ /// Returns data decoded from attribute or null if there is no attribute.
+ /// This property returns if attribute arguments haven't been decoded yet.
+ ///
+ 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;
+ }
+ }
}
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs
index feaabedd0fa0..3e246ed64d33 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs
@@ -1309,6 +1309,7 @@ internal static bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo
#endregion
+#nullable enable
///
/// True if this symbol has been marked with the attribute.
/// This property returns if the attribute hasn't been cracked yet.
@@ -1331,6 +1332,26 @@ internal ThreeState ObsoleteState
}
}
+ ///
+ /// True if this symbol has been marked with the System.Diagnostics.CodeAnalysis.ExperimentalAttribute attribute.
+ /// This property returns if the attribute hasn't been cracked yet.
+ ///
+ internal ThreeState ExperimentalState
+ {
+ get
+ {
+ switch (ObsoleteKind)
+ {
+ case ObsoleteAttributeKind.Experimental:
+ return ThreeState.True;
+ case ObsoleteAttributeKind.Uninitialized:
+ return ThreeState.Unknown;
+ default:
+ return ThreeState.False;
+ }
+ }
+ }
+
internal ObsoleteAttributeKind ObsoleteKind
{
get
@@ -1341,10 +1362,11 @@ internal ObsoleteAttributeKind ObsoleteKind
}
///
- /// Returns data decoded from attribute or null if there is no attribute.
+ /// Returns data decoded from /Experimental attribute or null if there is no /Experimental attribute.
/// This property returns if attribute arguments haven't been decoded yet.
///
- internal abstract ObsoleteAttributeData ObsoleteAttributeData { get; }
+ internal abstract ObsoleteAttributeData? ObsoleteAttributeData { get; }
+#nullable disable
///
/// Returns true and a from the first on the symbol,
diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs
index cede809659f7..b48bc91efbdc 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs
@@ -932,15 +932,18 @@ private bool ValidateAttributeUsage(
}
///
- /// Ensure that attributes are bound and the ObsoleteState of this symbol is known.
+ /// Ensure that attributes are bound and the ObsoleteState/ExperimentalState of this symbol is known.
///
internal void ForceCompleteObsoleteAttribute()
{
- if (this.ObsoleteState == ThreeState.Unknown)
+ if (this.ObsoleteKind == ObsoleteAttributeKind.Uninitialized)
{
this.GetAttributes();
}
Debug.Assert(this.ObsoleteState != ThreeState.Unknown, "ObsoleteState should be true or false now.");
+ Debug.Assert(this.ExperimentalState != ThreeState.Unknown, "ExperimentalState should be true or false now.");
+
+ this.ContainingSymbol?.ForceCompleteObsoleteAttribute();
}
}
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs
index fbbac6fdabdc..a27698822c9b 100644
--- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs
@@ -2060,7 +2060,7 @@ internal static bool IsWellKnownINumberBaseType(this TypeSymbol type)
IsContainedInNamespace(type, "System", "Numerics");
}
- private static bool IsWellKnownDiagnosticsCodeAnalysisTopLevelType(this TypeSymbol typeSymbol)
+ internal static bool IsWellKnownDiagnosticsCodeAnalysisTopLevelType(this TypeSymbol typeSymbol)
=> typeSymbol.ContainingType is null && IsContainedInNamespace(typeSymbol, "System", "Diagnostics", "CodeAnalysis");
private static bool IsContainedInNamespace(this TypeSymbol typeSymbol, string outerNS, string midNS, string? innerNS = null)
diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/ExperimentalAttributeTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/ExperimentalAttributeTests.cs
index 1274cf88ad88..ccc7900586c8 100644
--- a/src/Compilers/CSharp/Test/Emit2/Semantics/ExperimentalAttributeTests.cs
+++ b/src/Compilers/CSharp/Test/Emit2/Semantics/ExperimentalAttributeTests.cs
@@ -4,9 +4,13 @@
#nullable disable
+using System;
using System.Linq;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
+using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests;
@@ -63,9 +67,8 @@ public static void M() { }
}
[Fact]
- public void OnAssembly()
+ public void OnAssembly_UsedFromSource()
{
- // Ignored on assemblies
var libSrc = """
[assembly: System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
public class C
@@ -77,18 +80,815 @@ public static void M() { }
var src = """
C.M();
""";
+ var comp = CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc });
+ comp.VerifyDiagnostics();
+
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind);
+ }
+
+ [Fact]
+ public void OnAssembly_UsedFromMetadata()
+ {
+ var libSrc = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+public class C
+{
+ public static void M() { }
+}
+""";
+
+ var src = """
+C.M();
+""";
+ var comp = CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind);
+
+ // Note: the assembly-level [Experimental] is equivalent to marking every type and member as experimental,
+ // whereas a type-level [Experimental] is not equivalent to marking every nested type and member as experimental.
+ comp.VerifyDiagnostics(
+ // 0.cs(1,1): warning DiagID1: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.M();
+ Diagnostic("DiagID1", "C").WithArguments("C").WithLocation(1, 1),
+ // 0.cs(1,1): warning DiagID1: 'C.M()' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.M();
+ Diagnostic("DiagID1", "C.M()").WithArguments("C.M()").WithLocation(1, 1)
+ );
+
+ foreach (var diag in comp.GetDiagnostics())
+ {
+ Assert.Equal("DiagID1", diag.Id);
+ Assert.Equal(ErrorCode.WRN_Experimental, (ErrorCode)diag.Code);
+ Assert.Equal(DefaultHelpLinkUri, diag.Descriptor.HelpLinkUri);
+ }
+ }
+
+ [Fact]
+ public void OnAssembly_DefinedInMetadata_UsedFromSource()
+ {
+ var attrComp = CreateCompilation(experimentalAttributeSrc);
+ var attrRef = attrComp.EmitToImageReference();
+
+ var libSrc = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+public class C
+{
+ public static void M() { }
+}
+""";
+
+ var src = """
+C.M();
+""";
+
+ var comp = CreateCompilation(new[] { src, libSrc }, references: new[] { attrRef });
+ comp.VerifyDiagnostics();
+
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind);
+ Assert.Equal(ObsoleteAttributeKind.None, comp.GetTypeByMetadataName("C").ContainingModule.ObsoleteKind);
+ }
+
+ [Fact]
+ public void OnAssembly_DefinedInMetadata_UsedFromMetadata()
+ {
+ var attrComp = CreateCompilation(experimentalAttributeSrc);
+ var attrRef = attrComp.EmitToImageReference();
+
+ var libSrc = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+public class C
+{
+ public static void M() { }
+}
+""";
+
+ var src = """
+C.M();
+""";
+
+ var comp = CreateCompilation(src, references: new[] { CreateCompilation(libSrc, references: new[] { attrRef }).EmitToImageReference(), attrRef });
+
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind);
+ Assert.Equal(ObsoleteAttributeKind.None, comp.GetTypeByMetadataName("C").ContainingModule.ObsoleteKind);
+
+ comp.VerifyDiagnostics(
+ // 0.cs(1,1): warning DiagID1: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.M();
+ Diagnostic("DiagID1", "C").WithArguments("C").WithLocation(1, 1),
+ // 0.cs(1,1): warning DiagID1: 'C.M()' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.M();
+ Diagnostic("DiagID1", "C.M()").WithArguments("C.M()").WithLocation(1, 1)
+ );
+
+ foreach (var diag in comp.GetDiagnostics())
+ {
+ Assert.Equal("DiagID1", diag.Id);
+ Assert.Equal(ErrorCode.WRN_Experimental, (ErrorCode)diag.Code);
+ Assert.Equal(DefaultHelpLinkUri, diag.Descriptor.HelpLinkUri);
+ }
+ }
+
+ [Fact]
+ public void OnAssembly_DefinedInMetadata_UsedFromMetadata_ObsoleteType()
+ {
+ var attrComp = CreateCompilation(experimentalAttributeSrc);
+ var attrRef = attrComp.EmitToImageReference();
+
+ var libSrc = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+
+[System.Obsolete("error", true)]
+public class C
+{
+ public static void M() { }
+}
+""";
+
+ var src = """
+C.M();
+""";
+
+ var comp = CreateCompilation(src, references: new[] { CreateCompilation(libSrc, references: new[] { attrRef }).EmitToImageReference(), attrRef });
+
+ Assert.Equal(ObsoleteAttributeKind.Obsolete, comp.GetTypeByMetadataName("C").ObsoleteKind);
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind);
+ Assert.Equal(ObsoleteAttributeKind.None, comp.GetTypeByMetadataName("C").ContainingModule.ObsoleteKind);
+
+ comp.VerifyDiagnostics(
+ // (1,1): error CS0619: 'C' is obsolete: 'error'
+ // C.M();
+ Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "C").WithArguments("C", "error").WithLocation(1, 1),
+ // (1,1): warning DiagID1: 'C.M()' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.M();
+ Diagnostic("DiagID1", "C.M()").WithArguments("C.M()").WithLocation(1, 1)
+ );
+ }
+
+ [Fact]
+ public void OnAssembly_DefinedInMetadata_AppliedWithinModule_UsedFromSource()
+ {
+ var attrComp = CreateCompilation(experimentalAttributeSrc);
+ var attrRef = attrComp.EmitToImageReference();
+
+ var libSrc1 = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+""";
+ var moduleComp = CreateCompilation(libSrc1, options: TestOptions.DebugModule, references: new[] { attrRef });
+ var moduleRef = moduleComp.EmitToImageReference();
+
+ var libSrc = """
+public class C
+{
+ public static void M() { }
+}
+""";
+ var src = """
+C.M();
+""";
+
+ var comp = CreateCompilation(new[] { src, libSrc }, references: new[] { attrRef, moduleRef });
+ comp.VerifyDiagnostics();
+
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind);
+ }
+
+ [Fact]
+ public void OnAssembly_DefinedInMetadata_AppliedWithinModule_UsedFromMetadata()
+ {
+ // An assembly-level [Experimental] compiled into a module applies to the entire assembly
+ // the module gets compiled into
+ var attrComp = CreateCompilation(experimentalAttributeSrc);
+ var attrRef = attrComp.EmitToImageReference();
+
+ var libSrc1 = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+""";
+ var moduleComp = CreateCompilation(libSrc1, options: TestOptions.DebugModule, references: new[] { attrRef });
+ var moduleRef = moduleComp.EmitToImageReference();
+
+ var libSrc = """
+public class C
+{
+ public static void M() { }
+}
+""";
+ var src = """
+C.M();
+""";
+
+ var comp = CreateCompilation(src, references: new[] { CreateCompilation(libSrc, references: new[] { attrRef, moduleRef }).EmitToImageReference(), attrRef });
+
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind);
+
+ comp.VerifyDiagnostics(
+ // 0.cs(1,1): warning DiagID1: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.M();
+ Diagnostic("DiagID1", "C").WithArguments("C").WithLocation(1, 1),
+ // 0.cs(1,1): warning DiagID1: 'C.M()' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.M();
+ Diagnostic("DiagID1", "C.M()").WithArguments("C.M()").WithLocation(1, 1)
+ );
+
+ foreach (var diag in comp.GetDiagnostics())
+ {
+ Assert.Equal("DiagID1", diag.Id);
+ Assert.Equal(ErrorCode.WRN_Experimental, (ErrorCode)diag.Code);
+ Assert.Equal(DefaultHelpLinkUri, diag.Descriptor.HelpLinkUri);
+ }
+ }
+
+ [Fact]
+ public void OnAssembly_ObsoleteType()
+ {
+ var libSrc = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+
+[System.Obsolete("error", true)]
+public class C
+{
+ public static void M() { }
+}
+""";
+
+ var src = """
+C.M();
+""";
+ var comp = CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind);
+ Assert.Equal(ObsoleteAttributeKind.Obsolete, comp.GetTypeByMetadataName("C").ObsoleteKind);
+
+ comp.VerifyDiagnostics(
+ // (1,1): error CS0619: 'C' is obsolete: 'error'
+ // C.M();
+ Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "C").WithArguments("C", "error").WithLocation(1, 1),
+ // (1,1): warning DiagID1: 'C.M()' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.M();
+ Diagnostic("DiagID1", "C.M()").WithArguments("C.M()").WithLocation(1, 1)
+ );
+ }
+
+ [Fact]
+ public void OnType_ObsoleteType()
+ {
+ var libSrc = """
+[System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+[System.Obsolete("error", true)]
+public class C
+{
+ public static void M() { }
+}
+""";
+
+ var src = """
+C.M();
+""";
+ var comp = CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+
+ Assert.Equal(ObsoleteAttributeKind.Obsolete, comp.GetTypeByMetadataName("C").ObsoleteKind);
+
+ comp.VerifyDiagnostics(
+ // (1,1): error CS0619: 'C' is obsolete: 'error'
+ // C.M();
+ Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "C").WithArguments("C", "error").WithLocation(1, 1)
+ );
+ }
+
+ [Fact]
+ public void OnModule()
+ {
+ var libSrc = """
+[module: System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+public class C
+{
+ public static void M() { }
+}
+""";
+
+ var src = """
+C.M();
+""";
+
+ var comp = CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+ comp.VerifyDiagnostics(
+ // (1,1): warning DiagID1: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.M();
+ Diagnostic("DiagID1", "C").WithArguments("C").WithLocation(1, 1),
+ // (1,1): warning DiagID1: 'C.M()' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.M();
+ Diagnostic("DiagID1", "C.M()").WithArguments("C.M()").WithLocation(1, 1)
+ );
+ }
+
+ [Fact]
+ public void OnModule_DefinedInMetadata_UsedFromSource()
+ {
+ var attrComp = CreateCompilation(experimentalAttributeSrc);
+ var attrRef = attrComp.EmitToImageReference();
+
+ var libSrc = """
+[module: System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+public class C
+{
+ public static void M() { }
+}
+""";
+
+ var src = """
+C.M();
+""";
+
+ var comp = CreateCompilation(new[] { src, libSrc }, references: new[] { attrRef });
+ comp.VerifyDiagnostics();
+
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingModule.ObsoleteKind);
+ Assert.Equal(ObsoleteAttributeKind.None, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind);
+ }
+
+ [Fact]
+ public void OnModule_DefinedInMetadata_UsedFromMetadata()
+ {
+ var attrComp = CreateCompilation(experimentalAttributeSrc);
+ var attrRef = attrComp.EmitToImageReference();
+
+ var libSrc = """
+[module: System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+public class C
+{
+ public static void M() { }
+}
+""";
+
+ var src = """
+C.M();
+""";
+
+ var comp = CreateCompilation(src, references: new[] { CreateCompilation(libSrc, references: new[] { attrRef }).EmitToImageReference(), attrRef });
+
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingModule.ObsoleteKind);
+ Assert.Equal(ObsoleteAttributeKind.None, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind);
+
+ comp.VerifyDiagnostics(
+ // 0.cs(1,1): warning DiagID1: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.M();
+ Diagnostic("DiagID1", "C").WithArguments("C").WithLocation(1, 1),
+ // 0.cs(1,1): warning DiagID1: 'C.M()' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.M();
+ Diagnostic("DiagID1", "C.M()").WithArguments("C.M()").WithLocation(1, 1)
+ );
+
+ foreach (var diag in comp.GetDiagnostics())
+ {
+ Assert.Equal("DiagID1", diag.Id);
+ Assert.Equal(ErrorCode.WRN_Experimental, (ErrorCode)diag.Code);
+ Assert.Equal(DefaultHelpLinkUri, diag.Descriptor.HelpLinkUri);
+ }
+ }
+
+ [Fact]
+ public void OnModuleAndAssembly_UsedFromSource()
+ {
+ var attrComp = CreateCompilation(experimentalAttributeSrc);
+ var attrRef = attrComp.EmitToImageReference();
+
+ var libSrc = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("DiagAssembly")]
+[module: System.Diagnostics.CodeAnalysis.Experimental("DiagModule")]
+
+public class C
+{
+ public static void M() { }
+}
+""";
+
+ var src = """
+C.M();
+""";
+
+ var comp = CreateCompilation(new[] { src, libSrc }, references: new[] { attrRef });
+ comp.VerifyDiagnostics();
+
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingModule.ObsoleteKind);
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind);
+ }
+
+ [Fact]
+ public void OnModuleAndAssembly_UsedFromMetadata()
+ {
+ // Prefer reporting the module-level diagnostic
+ var attrComp = CreateCompilation(experimentalAttributeSrc);
+ var attrRef = attrComp.EmitToImageReference();
+
+ var libSrc = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("DiagAssembly")]
+[module: System.Diagnostics.CodeAnalysis.Experimental("DiagModule")]
+
+public class C
+{
+ public static void M() { }
+}
+""";
+
+ var src = """
+C.M();
+""";
+
+ var comp = CreateCompilation(src, references: new[] { CreateCompilation(libSrc, references: new[] { attrRef }).EmitToImageReference(), attrRef });
+
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingModule.ObsoleteKind);
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind);
+
+ comp.VerifyDiagnostics(
+ // 0.cs(1,1): warning DiagModule: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.M();
+ Diagnostic("DiagModule", "C").WithArguments("C").WithLocation(1, 1),
+ // 0.cs(1,1): warning DiagModule: 'C.M()' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.M();
+ Diagnostic("DiagModule", "C.M()").WithArguments("C.M()").WithLocation(1, 1)
+ );
+
+ foreach (var diag in comp.GetDiagnostics())
+ {
+ Assert.Equal("DiagModule", diag.Id);
+ Assert.Equal(ErrorCode.WRN_Experimental, (ErrorCode)diag.Code);
+ Assert.Equal(DefaultHelpLinkUri, diag.Descriptor.HelpLinkUri);
+ }
+ }
+
+ [Fact]
+ public void OnAssembly_CompiledIntoModule()
+ {
+ var attrComp = CreateCompilation(experimentalAttributeSrc);
+ var attrRef = attrComp.EmitToImageReference();
+
+ var libSrc = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("AssemblyDiagSetInModule")]
+
+public class C
+{
+ public static void M() { }
+}
+""";
+
+ var moduleComp = CreateCompilation(libSrc, options: TestOptions.ReleaseModule, references: new[] { attrRef });
+ moduleComp.VerifyDiagnostics();
+ var moduleRef = moduleComp.EmitToImageReference();
+
+ var libSrc2 = """
+public class D
+{
+ public static void M()
+ {
+ C.M();
+ }
+}
+""";
+ var assemblyComp = CreateCompilation(libSrc2, references: new[] { moduleRef, attrRef });
+ assemblyComp.VerifyDiagnostics();
+ var assemblyRef = assemblyComp.EmitToImageReference();
+
+ var src = """
+C.M();
+D.M();
+""";
+
+ // Since the module is referenced but not linked, we also need it here, but as
+ // a result the diagnostics are suppressed
+ var comp = CreateCompilation(src, references: new[] { assemblyRef, moduleRef, attrRef });
+ comp.VerifyDiagnostics();
+
+ Assert.Equal(ObsoleteAttributeKind.None, comp.GetTypeByMetadataName("C").ContainingModule.ObsoleteKind);
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind);
+
+ Assert.Equal(ObsoleteAttributeKind.None, comp.GetTypeByMetadataName("D").ContainingModule.ObsoleteKind);
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("D").ContainingAssembly.ObsoleteKind);
+ }
+
+ [Fact]
+ public void OnTypeAndMethodAndAssembly_UsedFromSource()
+ {
+ var attrComp = CreateCompilation(experimentalAttributeSrc);
+ var attrRef = attrComp.EmitToImageReference();
+
+ var libSrc = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("IGNORED")]
+
+[System.Diagnostics.CodeAnalysis.Experimental("DiagType")]
+public class C
+{
+ [System.Diagnostics.CodeAnalysis.Experimental("DiagMethod")]
+ public static void M() { }
+}
+""";
+
+ var src = """
+C.M();
+""";
+
+ var comp = CreateCompilation(new[] { src, libSrc }, references: new[] { attrRef });
+ comp.VerifyDiagnostics();
+
+ var c = comp.GetTypeByMetadataName("C");
+ Assert.Equal(ObsoleteAttributeKind.Experimental, c.ObsoleteKind);
+ Assert.Equal(ObsoleteAttributeKind.Experimental, c.ContainingAssembly.ObsoleteKind);
+
+ var m = comp.GetMember("C.M");
+ Assert.Equal(ObsoleteAttributeKind.Experimental, m.ObsoleteKind);
+ }
+
+ [Fact]
+ public void OnTypeAndMethodAndAssembly_UsedFromMetadata()
+ {
+ // Prefer reporting the type-level and method-level diagnostic
+ var attrComp = CreateCompilation(experimentalAttributeSrc);
+ var attrRef = attrComp.EmitToImageReference();
+
+ var libSrc = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("IGNORED")]
+
+[System.Diagnostics.CodeAnalysis.Experimental("DiagType")]
+public class C
+{
+ [System.Diagnostics.CodeAnalysis.Experimental("DiagMethod")]
+ public static void M() { }
+}
+""";
+
+ var src = """
+C.M();
+""";
+
+ var comp = CreateCompilation(src, references: new[] { CreateCompilation(libSrc, references: new[] { attrRef }).EmitToImageReference(), attrRef });
+
+ comp.VerifyDiagnostics(
+ // 0.cs(1,1): warning DiagType: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.M();
+ Diagnostic("DiagType", "C").WithArguments("C").WithLocation(1, 1),
+ // 0.cs(1,1): warning DiagMethod: 'C.M()' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.M();
+ Diagnostic("DiagMethod", "C.M()").WithArguments("C.M()").WithLocation(1, 1)
+ );
+
+ var c = comp.GetTypeByMetadataName("C");
+ Assert.Equal(ObsoleteAttributeKind.Experimental, c.ObsoleteKind);
+ Assert.Equal(ObsoleteAttributeKind.Experimental, c.ContainingAssembly.ObsoleteKind);
+
+ var m = comp.GetMember("C.M");
+ Assert.Equal(ObsoleteAttributeKind.Experimental, m.ObsoleteKind);
+ }
+
+ [Fact]
+ public void OnTypeAndAssembly_UsedFromSource()
+ {
+ var attrComp = CreateCompilation(experimentalAttributeSrc);
+ var attrRef = attrComp.EmitToImageReference();
+
+ var libSrc = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("DiagAssembly")]
+
+[System.Diagnostics.CodeAnalysis.Experimental("DiagType")]
+public class C
+{
+ public class Nested
+ {
+ public static void M() { }
+ }
+}
+""";
+
+ var src = """
+C.Nested.M();
+""";
+
+ var comp = CreateCompilation(new[] { src, libSrc }, references: new[] { attrRef });
+ comp.VerifyDiagnostics();
+ }
+
+ [Fact]
+ public void OnTypeAndAssembly_UsedFromMetadata()
+ {
+ // Prefer reporting the type-level and method-level diagnostic
+ var attrComp = CreateCompilation(experimentalAttributeSrc);
+ var attrRef = attrComp.EmitToImageReference();
+
+ var libSrc = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("DiagAssembly")]
+
+[System.Diagnostics.CodeAnalysis.Experimental("DiagType")]
+public class C
+{
+ public class Nested
+ {
+ public static void M() { }
+ }
+}
+""";
+
+ var src = """
+C.Nested.M();
+""";
+
+ var comp = CreateCompilation(src, references: new[] { CreateCompilation(libSrc, references: new[] { attrRef }).EmitToImageReference(), attrRef });
+
+ comp.VerifyDiagnostics(
+ // 0.cs(1,1): warning DiagType: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.Nested.M();
+ Diagnostic("DiagType", "C").WithArguments("C").WithLocation(1, 1),
+ // 0.cs(1,1): warning DiagAssembly: 'C.Nested' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.Nested.M();
+ Diagnostic("DiagAssembly", "C.Nested").WithArguments("C.Nested").WithLocation(1, 1),
+ // 0.cs(1,1): warning DiagAssembly: 'C.Nested.M()' is for evaluation purposes only and is subject to change or removal in future updates.
+ // C.Nested.M();
+ Diagnostic("DiagAssembly", "C.Nested.M()").WithArguments("C.Nested.M()").WithLocation(1, 1)
+ );
+ }
+
+ [Theory, CombinatorialData]
+ public void OnOverridden(bool inSource)
+ {
+ var libSrc = """
+public class C
+{
+ [System.Diagnostics.CodeAnalysis.Experimental("Diag")]
+ public virtual void M() { }
+}
+""";
+
+ var src = """
+public class Derived : C
+{
+ public override void M() { }
+
+ public void M2()
+ {
+ base.M();
+ M();
+ }
+}
+""";
+
+ var comp = inSource
+ ? CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc })
+ : CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+
+ comp.VerifyDiagnostics(
+ // 0.cs(7,9): warning Diag: 'C.M()' is for evaluation purposes only and is subject to change or removal in future updates.
+ // base.M();
+ Diagnostic("Diag", "base.M()").WithArguments("C.M()").WithLocation(7, 9),
+ // 0.cs(8,9): warning Diag: 'C.M()' is for evaluation purposes only and is subject to change or removal in future updates.
+ // M();
+ Diagnostic("Diag", "M()").WithArguments("C.M()").WithLocation(8, 9)
+ );
+ }
+
+ [Theory, CombinatorialData]
+ public void OnOverride(bool inSource)
+ {
+ var libSrc = """
+public class C
+{
+ public virtual void M() { }
+}
+""";
+
+ var src = """
+public class Derived : C
+{
+ [System.Diagnostics.CodeAnalysis.Experimental("Diag")]
+ public override void M() { }
+
+ public void M2()
+ {
+ base.M();
+ M();
+ }
+}
- var comp = CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+public class DerivedDerived : Derived
+{
+ public void M3()
+ {
+ base.M(); // 1
+ M();
+ }
+}
+""";
+
+ var comp = inSource
+ ? CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc })
+ : CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+
+ comp.VerifyDiagnostics(
+ // 0.cs(17,9): warning Diag: 'Derived.M()' is for evaluation purposes only and is subject to change or removal in future updates.
+ // base.M(); // 1
+ Diagnostic("Diag", "base.M()").WithArguments("Derived.M()").WithLocation(17, 9)
+ );
+ }
+
+ [Fact]
+ public void OnOverride_ExperimentalFromAssembly_UsedFromSource()
+ {
+ var libSrc = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("Diag")]
+public class C
+{
+ public virtual void M() { }
+}
+""";
+
+ var src = """
+public class Derived : C { public override void M() { } }
+""";
+
+ var comp = CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc });
comp.VerifyDiagnostics();
}
[Fact]
- public void OnModule()
+ public void OnOverride_ExperimentalFromAssembly_UsedFromMetadata()
{
- // Ignored on modules
var libSrc = """
-[module: System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("Diag")]
public class C
+{
+ public virtual void M() { }
+}
+""";
+
+ var src = """
+public class Derived : C { public override void M() { } }
+""";
+
+ var comp = CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+
+ // CONSIDER narrowing the location on constructor initializer obsolete/experimental attributes
+ comp.VerifyDiagnostics(
+ // 0.cs(1,1): warning Diag: 'C.C()' is for evaluation purposes only and is subject to change or removal in future updates.
+ // public class Derived : C { public override void M() { } }
+ Diagnostic("Diag", "public class Derived : C { public override void M() { } }").WithArguments("C.C()").WithLocation(1, 1),
+ // 0.cs(1,24): warning Diag: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ // public class Derived : C { public override void M() { } }
+ Diagnostic("Diag", "C").WithArguments("C").WithLocation(1, 24)
+ );
+ }
+
+ [Theory, CombinatorialData]
+ public void OnExplicitMethodImplementation(bool inSource)
+ {
+ var libSrc = """
+public interface I
+{
+ [System.Diagnostics.CodeAnalysis.Experimental("Diag")]
+ public void M();
+}
+""";
+
+ var src = """
+public class C : I
+{
+ void I.M() { }
+}
+""";
+
+ var comp = inSource
+ ? CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc })
+ : CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+
+ comp.VerifyDiagnostics();
+ }
+
+ [Theory, CombinatorialData]
+ public void OnExplicitMethodImplementation_Obsolete(bool inSource)
+ {
+ var libSrc = """
+public interface I
+{
+ [System.Obsolete("message", true)]
+ public void M();
+}
+""";
+
+ var src = """
+public class C : I
+{
+ void I.M() { }
+}
+""";
+
+ var comp = inSource
+ ? CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc })
+ : CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+
+ comp.VerifyDiagnostics();
+ }
+
+ [Fact]
+ public void MissingAssemblyAndModule()
+ {
+ var missingRef = CreateCompilation("public class Base { }", assemblyName: "missing").EmitToImageReference();
+
+ var libSrc = """
+public class C : Base
{
public static void M() { }
}
@@ -98,8 +898,148 @@ public static void M() { }
C.M();
""";
- var comp = CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+ var comp = CreateCompilation(src, references: new[] { CreateCompilation(libSrc, references: new[] { missingRef }).EmitToImageReference() });
+ comp.VerifyDiagnostics(
+ // (1,3): error CS0012: The type 'Base' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
+ // C.M();
+ Diagnostic(ErrorCode.ERR_NoTypeDef, "M").WithArguments("Base", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 3)
+ );
+
+ var missingType = comp.GlobalNamespace.GetTypeMember("C").BaseTypeNoUseSiteDiagnostics;
+ Assert.True(missingType.ContainingAssembly is MissingAssemblySymbol);
+ Assert.Equal(ObsoleteAttributeKind.None, missingType.ContainingAssembly.ObsoleteKind);
+ Assert.True(missingType.ContainingModule is MissingModuleSymbol);
+ Assert.Equal(ObsoleteAttributeKind.None, missingType.ContainingModule.ObsoleteKind);
+ }
+
+ [Fact]
+ public void RetargetingAssembly_Experimental()
+ {
+ var attrRef = CreateCompilation(experimentalAttributeSrc).EmitToImageReference();
+
+ var retargetedCode = """
+public class C { }
+""";
+
+ var originalC = CreateCompilation(new AssemblyIdentity("Ret", new Version(1, 0, 0, 0), isRetargetable: true), retargetedCode, TargetFrameworkUtil.StandardReferences);
+ var retargetedC = CreateCompilation(new AssemblyIdentity("Ret", new Version(2, 0, 0, 0), isRetargetable: true), retargetedCode, TargetFrameworkUtil.StandardReferences);
+
+ var derivedSrc = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+
+public class Derived : C { }
+""";
+
+ var derivedComp = CreateCompilation(derivedSrc, new[] { originalC.ToMetadataReference(), attrRef }, targetFramework: TargetFramework.Standard);
+ derivedComp.VerifyDiagnostics();
+
+ var comp = CreateCompilation("_ = new Derived();", new[] { derivedComp.ToMetadataReference(), retargetedC.ToMetadataReference() }, targetFramework: TargetFramework.Standard);
+ comp.VerifyDiagnostics(
+ // (1,5): warning DiagID1: 'Derived.Derived()' is for evaluation purposes only and is subject to change or removal in future updates.
+ // _ = new Derived();
+ Diagnostic("DiagID1", "new Derived()").WithArguments("Derived.Derived()").WithLocation(1, 5),
+ // (1,9): warning DiagID1: 'Derived' is for evaluation purposes only and is subject to change or removal in future updates.
+ // _ = new Derived();
+ Diagnostic("DiagID1", "Derived").WithArguments("Derived").WithLocation(1, 9)
+ );
+
+ var derived = comp.GetTypeByMetadataName("Derived");
+ Assert.IsType(derived);
+ Assert.IsType(derived.ContainingAssembly);
+ Assert.Equal(ObsoleteAttributeKind.Experimental, derived.ContainingAssembly.ObsoleteKind);
+ }
+
+ [Fact]
+ public void RetargetingAssembly_NotExperimental()
+ {
+ var attrRef = CreateCompilation(experimentalAttributeSrc).EmitToImageReference();
+
+ var retargetedCode = """
+public class C { }
+""";
+
+ var originalC = CreateCompilation(new AssemblyIdentity("Ret", new Version(1, 0, 0, 0), isRetargetable: true), retargetedCode, TargetFrameworkUtil.StandardReferences);
+ var retargetedC = CreateCompilation(new AssemblyIdentity("Ret", new Version(2, 0, 0, 0), isRetargetable: true), retargetedCode, TargetFrameworkUtil.StandardReferences);
+
+ var derivedSrc = """
+public class Derived : C { }
+""";
+
+ var derivedComp = CreateCompilation(derivedSrc, new[] { originalC.ToMetadataReference(), attrRef }, targetFramework: TargetFramework.Standard);
+ derivedComp.VerifyDiagnostics();
+
+ var comp = CreateCompilation("_ = new Derived();", new[] { derivedComp.ToMetadataReference(), retargetedC.ToMetadataReference() }, targetFramework: TargetFramework.Standard);
+ comp.VerifyDiagnostics();
+
+ var derived = comp.GetTypeByMetadataName("Derived");
+ Assert.IsType(derived);
+ Assert.IsType(derived.ContainingAssembly);
+ Assert.Equal(ObsoleteAttributeKind.None, derived.ContainingAssembly.ObsoleteKind);
+ }
+
+ [Fact]
+ public void RetargetingModule_Experimental()
+ {
+ var attrRef = CreateCompilation(experimentalAttributeSrc).EmitToImageReference();
+
+ var retargetedCode = """
+public class C { }
+""";
+
+ var originalC = CreateCompilation(new AssemblyIdentity("Ret", new Version(1, 0, 0, 0), isRetargetable: true), retargetedCode, TargetFrameworkUtil.StandardReferences);
+ var retargetedC = CreateCompilation(new AssemblyIdentity("Ret", new Version(2, 0, 0, 0), isRetargetable: true), retargetedCode, TargetFrameworkUtil.StandardReferences);
+
+ var @base = """
+[module: System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+
+public class Derived : C { }
+""";
+
+ var derivedComp = CreateCompilation(@base, new[] { originalC.ToMetadataReference(), attrRef }, targetFramework: TargetFramework.Standard);
+ derivedComp.VerifyDiagnostics();
+
+ var comp = CreateCompilation("_ = new Derived();", new[] { derivedComp.ToMetadataReference(), retargetedC.ToMetadataReference() }, targetFramework: TargetFramework.Standard);
+ comp.VerifyDiagnostics(
+ // (1,5): warning DiagID1: 'Derived.Derived()' is for evaluation purposes only and is subject to change or removal in future updates.
+ // _ = new Derived();
+ Diagnostic("DiagID1", "new Derived()").WithArguments("Derived.Derived()").WithLocation(1, 5),
+ // (1,9): warning DiagID1: 'Derived' is for evaluation purposes only and is subject to change or removal in future updates.
+ // _ = new Derived();
+ Diagnostic("DiagID1", "Derived").WithArguments("Derived").WithLocation(1, 9)
+ );
+
+ var derived = comp.GetTypeByMetadataName("Derived");
+ Assert.IsType(derived);
+ Assert.IsType(derived.ContainingModule);
+ Assert.Equal(ObsoleteAttributeKind.Experimental, derived.ContainingModule.ObsoleteKind);
+ }
+
+ [Fact]
+ public void RetargetingModule_NotExperimental()
+ {
+ var attrRef = CreateCompilation(experimentalAttributeSrc).EmitToImageReference();
+
+ var retargetedCode = """
+public class C { }
+""";
+
+ var originalC = CreateCompilation(new AssemblyIdentity("Ret", new Version(1, 0, 0, 0), isRetargetable: true), retargetedCode, TargetFrameworkUtil.StandardReferences);
+ var retargetedC = CreateCompilation(new AssemblyIdentity("Ret", new Version(2, 0, 0, 0), isRetargetable: true), retargetedCode, TargetFrameworkUtil.StandardReferences);
+
+ var @base = """
+public class Derived : C { }
+""";
+
+ var derivedComp = CreateCompilation(@base, new[] { originalC.ToMetadataReference(), attrRef }, targetFramework: TargetFramework.Standard);
+ derivedComp.VerifyDiagnostics();
+
+ var comp = CreateCompilation("_ = new Derived();", new[] { derivedComp.ToMetadataReference(), retargetedC.ToMetadataReference() }, targetFramework: TargetFramework.Standard);
comp.VerifyDiagnostics();
+
+ var derived = comp.GetTypeByMetadataName("Derived");
+ Assert.IsType(derived);
+ Assert.IsType(derived.ContainingModule);
+ Assert.Equal(ObsoleteAttributeKind.None, derived.ContainingModule.ObsoleteKind);
}
[Theory, CombinatorialData]
@@ -959,7 +1899,7 @@ void M2()
[Theory, CombinatorialData]
public void InExperimentalMethod(bool inSource)
{
- // Diagnostics for [Experimental] are not suppressed in [Experimental] members
+ // Diagnostics for [Experimental] are suppressed in [Experimental] context
var libSrc = """
public class C
{
@@ -983,11 +1923,130 @@ void M2()
? CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc })
: CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
- comp.VerifyDiagnostics(
- // (6,9): warning DiagID1: 'C.M()' is for evaluation purposes only and is subject to change or removal in future updates.
- // C.M();
- Diagnostic("DiagID1", "C.M()").WithArguments("C.M()").WithLocation(6, 9)
- );
+ comp.VerifyDiagnostics();
+ }
+
+ [Theory, CombinatorialData]
+ public void InExperimentalType(bool inSource)
+ {
+ // Diagnostics for [Experimental] are suppressed in [Experimental] context
+ var libSrc = """
+public class C
+{
+ [System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+ public static void M() { }
+}
+""";
+
+ var src = """
+[System.Diagnostics.CodeAnalysis.Experimental("DiagID2")]
+class D
+{
+ void M2()
+ {
+ C.M();
+ }
+}
+""";
+
+ var comp = inSource
+ ? CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc })
+ : CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+
+ comp.VerifyDiagnostics();
+ }
+
+ [Theory, CombinatorialData]
+ public void InExperimentalNestedType(bool inSource)
+ {
+ // Diagnostics for [Experimental] are suppressed in [Experimental] context
+ var libSrc = """
+public class C
+{
+ [System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+ public static void M() { }
+}
+""";
+
+ var src = """
+[System.Diagnostics.CodeAnalysis.Experimental("DiagID2")]
+class D
+{
+ class Nested
+ {
+ void M2()
+ {
+ C.M();
+ }
+ }
+}
+""";
+
+ var comp = inSource
+ ? CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc })
+ : CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+
+ comp.VerifyDiagnostics();
+ }
+
+ [Theory, CombinatorialData]
+ public void InExperimentalModule(bool inSource)
+ {
+ // Diagnostics for [Experimental] are suppressed in [Experimental] context
+ var libSrc = """
+public class C
+{
+ [System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+ public static void M() { }
+}
+""";
+
+ var src = """
+[module: System.Diagnostics.CodeAnalysis.Experimental("DiagID2")]
+class D
+{
+ void M2()
+ {
+ C.M();
+ }
+}
+""";
+
+ var comp = inSource
+ ? CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc })
+ : CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+
+ comp.VerifyDiagnostics();
+ }
+
+ [Theory, CombinatorialData]
+ public void InExperimentalAssembly(bool inSource)
+ {
+ // Diagnostics for [Experimental] are suppressed in [Experimental] context
+ var libSrc = """
+public class C
+{
+ [System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
+ public static void M() { }
+}
+""";
+
+ var src = """
+[assembly: System.Diagnostics.CodeAnalysis.Experimental("DiagID2")]
+class D
+{
+ void M2()
+ {
+ C.M();
+ }
+}
+""";
+
+ var comp = inSource
+ ? CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc })
+ : CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+
+ comp.VerifyDiagnostics();
}
[Theory, CombinatorialData]
diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MockAssemblySymbol.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MockAssemblySymbol.cs
index b6b446e4af80..730107de9ffe 100644
--- a/src/Compilers/CSharp/Test/Symbol/Symbols/MockAssemblySymbol.cs
+++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MockAssemblySymbol.cs
@@ -122,5 +122,9 @@ internal override IEnumerable GetAllTopLevelForwardedTypes()
{
throw new NotImplementedException();
}
+
+#nullable enable
+ internal sealed override ObsoleteAttributeData? ObsoleteAttributeData
+ => null;
}
}
diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/CommonAssemblyWellKnownAttributeData.cs b/src/Compilers/Core/Portable/Symbols/Attributes/CommonAssemblyWellKnownAttributeData.cs
index 166e8a073382..2cf541a5e19c 100644
--- a/src/Compilers/Core/Portable/Symbols/Attributes/CommonAssemblyWellKnownAttributeData.cs
+++ b/src/Compilers/Core/Portable/Symbols/Attributes/CommonAssemblyWellKnownAttributeData.cs
@@ -8,6 +8,7 @@
using System.Reflection;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Generic;
+using System.Diagnostics;
namespace Microsoft.CodeAnalysis
{
@@ -443,5 +444,27 @@ public HashSet ForwardedTypes
}
}
#endregion
+
+ #region ExperimentalAttribute
+ private ObsoleteAttributeData _experimentalAttributeData = ObsoleteAttributeData.Uninitialized;
+ public ObsoleteAttributeData ExperimentalAttributeData
+ {
+ get
+ {
+ VerifySealed(expected: true);
+ return _experimentalAttributeData.IsUninitialized ? null : _experimentalAttributeData;
+ }
+ set
+ {
+ VerifySealed(expected: false);
+ Debug.Assert(value != null);
+ Debug.Assert(!value.IsUninitialized);
+ Debug.Assert(value.Kind == ObsoleteAttributeKind.Experimental);
+
+ _experimentalAttributeData = value;
+ SetDataStored();
+ }
+ }
+ #endregion
}
}
diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs b/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs
index 646706093ac2..1ceb8f4f4e46 100644
--- a/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs
+++ b/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs
@@ -264,7 +264,7 @@ internal ObsoleteAttributeData DecodeObsoleteAttribute(ObsoleteAttributeKind kin
}
}
- private ObsoleteAttributeData DecodeExperimentalAttribute()
+ internal ObsoleteAttributeData DecodeExperimentalAttribute()
{
// ExperimentalAttribute(string diagnosticId)
Debug.Assert(this.CommonConstructorArguments.Length == 1);
diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/CommonModuleWellKnownAttributeData.cs b/src/Compilers/Core/Portable/Symbols/Attributes/CommonModuleWellKnownAttributeData.cs
index ffb1915e34dd..6f70dff95d90 100644
--- a/src/Compilers/Core/Portable/Symbols/Attributes/CommonModuleWellKnownAttributeData.cs
+++ b/src/Compilers/Core/Portable/Symbols/Attributes/CommonModuleWellKnownAttributeData.cs
@@ -64,5 +64,27 @@ internal static bool IsValidCharSet(CharSet value)
return value >= Cci.Constants.CharSet_None && value <= Cci.Constants.CharSet_Auto;
}
#endregion
+
+ #region ExperimentalAttribute
+ private ObsoleteAttributeData _experimentalAttributeData = ObsoleteAttributeData.Uninitialized;
+ public ObsoleteAttributeData ExperimentalAttributeData
+ {
+ get
+ {
+ VerifySealed(expected: true);
+ return _experimentalAttributeData.IsUninitialized ? null : _experimentalAttributeData;
+ }
+ set
+ {
+ VerifySealed(expected: false);
+ Debug.Assert(value != null);
+ Debug.Assert(!value.IsUninitialized);
+ Debug.Assert(value.Kind == ObsoleteAttributeKind.Experimental);
+
+ _experimentalAttributeData = value;
+ SetDataStored();
+ }
+ }
+ #endregion
}
}
diff --git a/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb b/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb
index a39b2dd93158..410cdd9e098b 100644
--- a/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb
+++ b/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb
@@ -33,6 +33,19 @@ Friend Module CompilationUtils
Return CreateEmptyCompilation(source, references, options, parseOptions, assemblyName)
End Function
+ Public Function CreateCompilationWithIdentity(
+ identity As AssemblyIdentity,
+ source As BasicTestSource,
+ Optional references As IEnumerable(Of MetadataReference) = Nothing,
+ Optional targetFramework As TargetFramework = TargetFramework.StandardAndVBRuntime) As VisualBasicCompilation
+
+ Dim c = CreateCompilation(source, references, assemblyName:=identity.Name)
+ Assert.NotNull(c.Assembly) ' force creation Of SourceAssemblySymbol
+ DirectCast(c.Assembly, SourceAssemblySymbol).m_lazyIdentity = identity
+
+ Return c
+ End Function
+
Public Function CreateEmptyCompilation(
source As BasicTestSource,
Optional references As IEnumerable(Of MetadataReference) = Nothing,
diff --git a/src/Compilers/Test/Utilities/VisualBasic/MockSymbols.vb b/src/Compilers/Test/Utilities/VisualBasic/MockSymbols.vb
index 28cdd322f7ce..ebbfcf124a72 100644
--- a/src/Compilers/Test/Utilities/VisualBasic/MockSymbols.vb
+++ b/src/Compilers/Test/Utilities/VisualBasic/MockSymbols.vb
@@ -747,6 +747,13 @@ Friend Class MockModuleSymbol
Public Overrides Function GetMetadata() As ModuleMetadata
Return Nothing
End Function
+
+ Friend NotOverridable Overrides ReadOnly Property ObsoleteAttributeData As ObsoleteAttributeData
+ Get
+ Return Nothing
+ End Get
+ End Property
+
End Class
Friend Class MockAssemblySymbol
@@ -861,4 +868,11 @@ Friend Class MockAssemblySymbol
Public Overrides Function GetMetadata() As AssemblyMetadata
Return Nothing
End Function
+
+ Friend Overrides ReadOnly Property ObsoleteAttributeData As ObsoleteAttributeData
+ Get
+ Return Nothing
+ End Get
+ End Property
+
End Class
diff --git a/src/Compilers/VisualBasic/Portable/Symbols/AssemblySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/AssemblySymbol.vb
index 09e42621e0c1..74da0a0cb68e 100644
--- a/src/Compilers/VisualBasic/Portable/Symbols/AssemblySymbol.vb
+++ b/src/Compilers/VisualBasic/Portable/Symbols/AssemblySymbol.vb
@@ -238,16 +238,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
End Get
End Property
- '''
- ''' 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.
- '''
- Friend NotOverridable Overrides ReadOnly Property ObsoleteAttributeData As ObsoleteAttributeData
- Get
- Return Nothing
- End Get
- End Property
-
'''
''' Lookup a top level type referenced from metadata, names should be
''' compared case-sensitively.
diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEAssemblySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEAssemblySymbol.vb
index 9306bafa7931..5a8a17aab70d 100644
--- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEAssemblySymbol.vb
+++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEAssemblySymbol.vb
@@ -68,6 +68,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Private _lazyCachedCompilerFeatureRequiredDiagnosticInfo As DiagnosticInfo = ErrorFactory.EmptyDiagnosticInfo
+ Private _lazyObsoleteAttributeData As ObsoleteAttributeData = ObsoleteAttributeData.Uninitialized
+
Friend Sub New(assembly As PEAssembly, documentationProvider As DocumentationProvider,
isLinked As Boolean, importOptions As MetadataImportOptions)
Debug.Assert(assembly IsNot Nothing)
@@ -286,5 +288,26 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Return MyBase.HasUnsupportedMetadata
End Get
End Property
+
+ Friend Overrides ReadOnly Property ObsoleteAttributeData As ObsoleteAttributeData
+ Get
+ If _lazyObsoleteAttributeData Is ObsoleteAttributeData.Uninitialized Then
+ Interlocked.CompareExchange(_lazyObsoleteAttributeData, ComputeObsoleteAttributeData(), ObsoleteAttributeData.Uninitialized)
+ End If
+
+ Return _lazyObsoleteAttributeData
+ End Get
+ End Property
+
+ Private Function ComputeObsoleteAttributeData() As ObsoleteAttributeData
+ For Each attrData In GetAttributes()
+ If attrData.IsTargetAttribute(Me, AttributeDescription.ExperimentalAttribute) Then
+ Return attrData.DecodeExperimentalAttribute()
+ End If
+ Next
+
+ Return Nothing
+ End Function
+
End Class
End Namespace
diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEModuleSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEModuleSymbol.vb
index e6af8729c47d..40b385c28a0b 100644
--- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEModuleSymbol.vb
+++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEModuleSymbol.vb
@@ -81,6 +81,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Private _lazyCachedCompilerFeatureRequiredDiagnosticInfo As DiagnosticInfo = ErrorFactory.EmptyDiagnosticInfo
+ Private _lazyObsoleteAttributeData As ObsoleteAttributeData = ObsoleteAttributeData.Uninitialized
+
Friend Sub New(assemblySymbol As PEAssemblySymbol, [module] As PEModule, importOptions As MetadataImportOptions, ordinal As Integer)
Me.New(DirectCast(assemblySymbol, AssemblySymbol), [module], importOptions, ordinal)
Debug.Assert(ordinal >= 0)
@@ -507,5 +509,26 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Return MyBase.HasUnsupportedMetadata
End Get
End Property
+
+ Friend Overrides ReadOnly Property ObsoleteAttributeData As ObsoleteAttributeData
+ Get
+ If _lazyObsoleteAttributeData Is ObsoleteAttributeData.Uninitialized Then
+ Interlocked.CompareExchange(_lazyObsoleteAttributeData, ComputeObsoleteAttributeData(), ObsoleteAttributeData.Uninitialized)
+ End If
+
+ Return _lazyObsoleteAttributeData
+ End Get
+ End Property
+
+ Private Function ComputeObsoleteAttributeData() As ObsoleteAttributeData
+ For Each attrData In GetAttributes()
+ If attrData.IsTargetAttribute(Me, AttributeDescription.ExperimentalAttribute) Then
+ Return attrData.DecodeExperimentalAttribute()
+ End If
+ Next
+
+ Return Nothing
+ End Function
+
End Class
End Namespace
diff --git a/src/Compilers/VisualBasic/Portable/Symbols/MissingAssemblySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/MissingAssemblySymbol.vb
index c6a1d7d5a493..8e84e812e0bf 100644
--- a/src/Compilers/VisualBasic/Portable/Symbols/MissingAssemblySymbol.vb
+++ b/src/Compilers/VisualBasic/Portable/Symbols/MissingAssemblySymbol.vb
@@ -165,6 +165,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
Public Overrides Function GetMetadata() As AssemblyMetadata
Return Nothing
End Function
+
+ Friend Overrides ReadOnly Property ObsoleteAttributeData As ObsoleteAttributeData
+ Get
+ Return Nothing
+ End Get
+ End Property
+
End Class
'''
diff --git a/src/Compilers/VisualBasic/Portable/Symbols/MissingModuleSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/MissingModuleSymbol.vb
index 5c62aa12a50d..1d88e088a130 100644
--- a/src/Compilers/VisualBasic/Portable/Symbols/MissingModuleSymbol.vb
+++ b/src/Compilers/VisualBasic/Portable/Symbols/MissingModuleSymbol.vb
@@ -171,6 +171,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
Public Overrides Function GetMetadata() As ModuleMetadata
Return Nothing
End Function
+
+ Friend NotOverridable Overrides ReadOnly Property ObsoleteAttributeData As ObsoleteAttributeData
+ Get
+ Return Nothing
+ End Get
+ End Property
+
End Class
Friend Class MissingModuleSymbolWithName
diff --git a/src/Compilers/VisualBasic/Portable/Symbols/ModuleSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/ModuleSymbol.vb
index 2753e72de51d..3dcb5d16caef 100644
--- a/src/Compilers/VisualBasic/Portable/Symbols/ModuleSymbol.vb
+++ b/src/Compilers/VisualBasic/Portable/Symbols/ModuleSymbol.vb
@@ -293,16 +293,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
'''
Friend MustOverride ReadOnly Property MightContainExtensionMethods As Boolean
- '''
- ''' 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.
- '''
- Friend NotOverridable Overrides ReadOnly Property ObsoleteAttributeData As ObsoleteAttributeData
- Get
- Return Nothing
- End Get
- End Property
-
#Region "IModuleSymbol"
Private ReadOnly Property IModuleSymbol_GlobalNamespace As INamespaceSymbol Implements IModuleSymbol.GlobalNamespace
Get
diff --git a/src/Compilers/VisualBasic/Portable/Symbols/ObsoleteAttributeHelpers.vb b/src/Compilers/VisualBasic/Portable/Symbols/ObsoleteAttributeHelpers.vb
index e8367ab52beb..a365857816ec 100644
--- a/src/Compilers/VisualBasic/Portable/Symbols/ObsoleteAttributeHelpers.vb
+++ b/src/Compilers/VisualBasic/Portable/Symbols/ObsoleteAttributeHelpers.vb
@@ -46,13 +46,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
''' symbol's Obsoleteness is Unknown. False, if we are certain that no symbol in the parent
''' hierarchy is Obsolete.
'''
- Private Shared Function GetObsoleteContextState(symbol As Symbol, forceComplete As Boolean) As ThreeState
+ Private Shared Function GetObsoleteContextState(symbol As Symbol, forceComplete As Boolean, getStateFromSymbol As Func(Of Symbol, ThreeState)) As ThreeState
While symbol IsNot Nothing
If forceComplete Then
symbol.ForceCompleteObsoleteAttribute()
End If
- Dim state = symbol.ObsoleteState
+ Dim state = getStateFromSymbol(symbol)
If state <> ThreeState.False Then
Return state
End If
@@ -74,9 +74,26 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
Select Case symbol.ObsoleteKind
Case ObsoleteAttributeKind.None
+ Dim moduleObsoleteKind As ObsoleteAttributeKind? = symbol.ContainingModule?.ObsoleteKind
+ Dim assemblyObsoleteKind As ObsoleteAttributeKind? = symbol.ContainingAssembly?.ObsoleteKind
+
+ If moduleObsoleteKind = Global.Microsoft.CodeAnalysis.ObsoleteAttributeKind.Experimental OrElse
+ assemblyObsoleteKind = Global.Microsoft.CodeAnalysis.ObsoleteAttributeKind.Experimental Then
+
+ Return GetDiagnosticKind(context, forceComplete, getStateFromSymbol:=Function(s) s.ExperimentalState)
+ End If
+
+ If moduleObsoleteKind = Global.Microsoft.CodeAnalysis.ObsoleteAttributeKind.Uninitialized OrElse
+ assemblyObsoleteKind = Global.Microsoft.CodeAnalysis.ObsoleteAttributeKind.Uninitialized Then
+
+ Return ObsoleteDiagnosticKind.Lazy
+ End If
+
Return ObsoleteDiagnosticKind.NotObsolete
- Case ObsoleteAttributeKind.WindowsExperimental, ObsoleteAttributeKind.Experimental
+ Case ObsoleteAttributeKind.WindowsExperimental
Return ObsoleteDiagnosticKind.Diagnostic
+ Case ObsoleteAttributeKind.Experimental
+ Return GetDiagnosticKind(context, forceComplete, getStateFromSymbol:=Function(s) s.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
@@ -85,12 +102,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
Return ObsoleteDiagnosticKind.Lazy
End Select
- Select Case GetObsoleteContextState(context, forceComplete)
+ Return GetDiagnosticKind(context, forceComplete, getStateFromSymbol:=Function(s) s.ObsoleteState)
+ End Function
+
+ Private Shared Function GetDiagnosticKind(containingMember As Symbol, forceComplete As Boolean, getStateFromSymbol As Func(Of Symbol, ThreeState)) As ObsoleteDiagnosticKind
+
+ Select Case GetObsoleteContextState(containingMember, forceComplete, 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.
+ ' If we are in a context that is already experimental/obsolete, there is no point reporting
+ ' more experimental/obsolete diagnostics.
Return ObsoleteDiagnosticKind.Suppressed
Case Else
' If the context is unknown, then store the symbol so that we can do this check at a
@@ -104,7 +126,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
''' the ObsoleteAttribute's arguments.
'''
Friend Shared Function CreateObsoleteDiagnostic(symbol As Symbol) As DiagnosticInfo
- Dim data = symbol.ObsoleteAttributeData
+ Dim data = If(If(symbol.ObsoleteAttributeData, symbol.ContainingModule.ObsoleteAttributeData), symbol.ContainingAssembly.ObsoleteAttributeData)
+
Debug.Assert(data IsNot Nothing)
If data Is Nothing Then
diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.vb
index 00d063d90fb9..20100cef60f0 100644
--- a/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.vb
+++ b/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.vb
@@ -268,5 +268,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Retargeting
Public Overrides Function GetMetadata() As AssemblyMetadata
Return _underlyingAssembly.GetMetadata()
End Function
+
+ Friend Overrides ReadOnly Property ObsoleteAttributeData As ObsoleteAttributeData
+ Get
+ Return _underlyingAssembly.ObsoleteAttributeData
+ End Get
+ End Property
+
End Class
End Namespace
diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingModuleSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingModuleSymbol.vb
index ed1cd2fc5246..99987031c11f 100644
--- a/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingModuleSymbol.vb
+++ b/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingModuleSymbol.vb
@@ -290,5 +290,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Retargeting
Public Overrides Function GetMetadata() As ModuleMetadata
Return _underlyingModule.GetMetadata()
End Function
+
+ Friend Overrides ReadOnly Property ObsoleteAttributeData As ObsoleteAttributeData
+ Get
+ Return _underlyingModule.ObsoleteAttributeData
+ End Get
+ End Property
+
End Class
End Namespace
diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceAssemblySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceAssemblySymbol.vb
index 7e522d31b6a9..7842f5985eb7 100644
--- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceAssemblySymbol.vb
+++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceAssemblySymbol.vb
@@ -5,10 +5,7 @@
Imports System.Collections.Concurrent
Imports System.Collections.Immutable
Imports System.Reflection
-Imports System.Reflection.Metadata
-Imports System.Runtime.CompilerServices
Imports System.Runtime.InteropServices
-Imports System.Security.Cryptography
Imports System.Threading
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Symbols
@@ -1092,6 +1089,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
arguments.GetOrCreateData(Of CommonAssemblyWellKnownAttributeData)().RuntimeCompatibilityWrapNonExceptionThrows = True
ElseIf attrData.IsTargetAttribute(Me, AttributeDescription.DebuggableAttribute) Then
arguments.GetOrCreateData(Of CommonAssemblyWellKnownAttributeData)().HasDebuggableAttribute = True
+ ElseIf attrData.IsTargetAttribute(Me, AttributeDescription.ExperimentalAttribute) Then
+ arguments.GetOrCreateData(Of CommonAssemblyWellKnownAttributeData)().ExperimentalAttributeData = attrData.DecodeExperimentalAttribute()
Else
Dim signature As Integer = attrData.GetTargetAttributeSignatureIndex(Me, AttributeDescription.AssemblyAlgorithmIdAttribute)
@@ -1780,5 +1779,30 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
Return _compilation
End Get
End Property
+
+ Friend Overrides ReadOnly Property ObsoleteAttributeData As ObsoleteAttributeData
+ Get
+ ' may have been specified in the assembly or one of the modules
+ Dim attributesBag As CustomAttributesBag(Of VisualBasicAttributeData) = Me._lazySourceAttributesBag
+ If attributesBag IsNot Nothing AndAlso attributesBag.IsDecodedWellKnownAttributeDataComputed Then
+ Dim experimentalData = DirectCast(attributesBag.DecodedWellKnownAttributeData, CommonAssemblyWellKnownAttributeData)?.ExperimentalAttributeData
+ If experimentalData IsNot Nothing Then
+ Return experimentalData
+ End If
+ End If
+
+ attributesBag = Me._lazyNetModuleAttributesBag
+ If attributesBag IsNot Nothing AndAlso attributesBag.IsDecodedWellKnownAttributeDataComputed Then
+ Return DirectCast(attributesBag.DecodedWellKnownAttributeData, CommonAssemblyWellKnownAttributeData)?.ExperimentalAttributeData
+ End If
+
+ If GetAttributeDeclarations().IsEmpty Then
+ Return Nothing
+ End If
+
+ Return ObsoleteAttributeData.Uninitialized
+ End Get
+ End Property
+
End Class
End Namespace
diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceModuleSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceModuleSymbol.vb
index e208ec8ce966..e151cdefa8ad 100644
--- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceModuleSymbol.vb
+++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceModuleSymbol.vb
@@ -1116,6 +1116,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
End If
ElseIf attrData.IsTargetAttribute(Me, AttributeDescription.DebuggableAttribute) Then
arguments.GetOrCreateData(Of CommonModuleWellKnownAttributeData).HasDebuggableAttribute = True
+ ElseIf attrData.IsTargetAttribute(Me, AttributeDescription.ExperimentalAttribute) Then
+ arguments.GetOrCreateData(Of CommonModuleWellKnownAttributeData).ExperimentalAttributeData = attrData.DecodeObsoleteAttribute(ObsoleteAttributeKind.Experimental)
End If
MyBase.DecodeWellKnownAttribute(arguments)
@@ -1220,5 +1222,22 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
Public Overrides Function GetMetadata() As ModuleMetadata
Return Nothing
End Function
+
+ Friend Overrides ReadOnly Property ObsoleteAttributeData As ObsoleteAttributeData
+ Get
+ Dim attributesBag As CustomAttributesBag(Of VisualBasicAttributeData) = Me._lazyCustomAttributesBag
+ If attributesBag IsNot Nothing AndAlso attributesBag.IsDecodedWellKnownAttributeDataComputed Then
+ Return DirectCast(attributesBag.DecodedWellKnownAttributeData, CommonModuleWellKnownAttributeData)?.ExperimentalAttributeData
+ End If
+
+ Dim mergedAttributes = DirectCast(Me.ContainingAssembly, SourceAssemblySymbol).GetAttributeDeclarations()
+ If mergedAttributes.IsEmpty Then
+ Return Nothing
+ End If
+
+ Return ObsoleteAttributeData.Uninitialized
+ End Get
+ End Property
+
End Class
End Namespace
diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Symbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Symbol.vb
index 9771862391d5..e5b8015696ee 100644
--- a/src/Compilers/VisualBasic/Portable/Symbols/Symbol.vb
+++ b/src/Compilers/VisualBasic/Portable/Symbols/Symbol.vb
@@ -456,6 +456,23 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End Get
End Property
+ '''
+ ''' True if this symbol has been marked with the Experimental attribute.
+ ''' This property returns Unknown if the Experimental attribute hasn't been cracked yet.
+ '''
+ Friend ReadOnly Property ExperimentalState As ThreeState
+ Get
+ Select Case ObsoleteKind
+ Case ObsoleteAttributeKind.Experimental
+ Return ThreeState.True
+ Case ObsoleteAttributeKind.Uninitialized
+ Return ThreeState.Unknown
+ Case Else
+ Return ThreeState.False
+ End Select
+ End Get
+ End Property
+
Friend ReadOnly Property ObsoleteKind As ObsoleteAttributeKind
Get
Dim data = Me.ObsoleteAttributeData
@@ -464,7 +481,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End Property
'''
- ''' Returns data decoded from Obsolete attribute or null if there is no Obsolete attribute.
+ ''' Returns data decoded from Obsolete/Experimental attribute or null if there is no Obsolete/Experimental attribute.
''' This property returns ObsoleteAttributeData.Uninitialized if attribute arguments haven't been decoded yet.
'''
Friend MustOverride ReadOnly Property ObsoleteAttributeData As ObsoleteAttributeData
diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Symbol_Attributes.vb b/src/Compilers/VisualBasic/Portable/Symbols/Symbol_Attributes.vb
index 924870153f8c..9ac12b073750 100644
--- a/src/Compilers/VisualBasic/Portable/Symbols/Symbol_Attributes.vb
+++ b/src/Compilers/VisualBasic/Portable/Symbols/Symbol_Attributes.vb
@@ -619,13 +619,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End Sub
'''
- ''' Ensure that attributes are bound and the ObsoleteState of this symbol is known.
+ ''' Ensure that attributes are bound and the ObsoleteState/ExperimentalState of this symbol is known.
'''
Friend Sub ForceCompleteObsoleteAttribute()
- If Me.ObsoleteState = ThreeState.Unknown Then
+ If Me.ObsoleteKind = ObsoleteAttributeKind.Uninitialized Then
Me.GetAttributes()
End If
Debug.Assert(Me.ObsoleteState <> ThreeState.Unknown, "ObsoleteState should be true or false now.")
+ Debug.Assert(Me.ExperimentalState <> ThreeState.Unknown, "ExperimentalState should be true or false now.")
+
+ Me.ContainingSymbol?.ForceCompleteObsoleteAttribute()
End Sub
End Class
End Namespace
diff --git a/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb b/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb
index a41614fd8878..62999f544783 100644
--- a/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb
+++ b/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb
@@ -5,12 +5,14 @@
Imports System.Collections.Immutable
Imports System.IO
Imports System.Reflection
+Imports System.Resources
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.Emit
Imports Microsoft.CodeAnalysis.Test.Utilities
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
+Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Retargeting
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Roslyn.Test.Utilities
@@ -5067,7 +5069,7 @@ DiagID1: 'C' is for evaluation purposes only and is subject to change or removal
~
]]>)
- Dim diag = Comp.GetDiagnostics().Single()
+ Dim diag = comp.GetDiagnostics().Single()
Assert.Equal("DiagID1", diag.Id)
Assert.Equal(ERRID.WRN_Experimental, diag.Code)
Assert.Equal("https://example.org/DiagID1", diag.Descriptor.HelpLinkUri)
@@ -5111,5 +5113,550 @@ DiagID1: 'C' is for evaluation purposes only and is subject to change or removal
Assert.Equal("https://example.org/DiagID1", diag.Descriptor.HelpLinkUri)
End Sub
+
+ Public Sub OnAssembly_UsedFromSource()
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+
+ Dim src =
+
+
+
+Class C
+End Class
+
+Class D
+ Sub M(c As C)
+ End Sub
+End Class
+]]>
+
+
+
+ Dim comp = CreateCompilation(src, references:={attrComp.EmitToImageReference()})
+ comp.AssertNoDiagnostics()
+
+ Assert.Equal(ObsoleteAttributeKind.None, comp.GetTypeByMetadataName("C").ContainingModule.ObsoleteKind)
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind)
+ End Sub
+
+
+ Public Sub OnAssembly_UsedFromMetadata()
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+ Dim attrRef = attrComp.EmitToImageReference()
+
+ Dim libSrc =
+
+
+
+Public Class C
+End Class
+]]>
+
+
+ Dim libComp = CreateCompilation(libSrc, references:={attrRef})
+ Dim libRef = libComp.EmitToImageReference()
+
+ Dim src =
+
+
+
+
+
+ Dim comp = CreateCompilation(src, references:={libRef, attrRef})
+
+ comp.AssertTheseDiagnostics(
+)
+
+ Assert.Equal(ObsoleteAttributeKind.None, comp.GetTypeByMetadataName("C").ContainingModule.ObsoleteKind)
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind)
+
+ Dim diag = comp.GetDiagnostics().Single()
+ Assert.Equal("DiagID1", diag.Id)
+ Assert.Equal(ERRID.WRN_Experimental, diag.Code)
+ Assert.Equal("https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(BC42380)", diag.Descriptor.HelpLinkUri)
+ End Sub
+
+
+ Public Sub OnAssembly_UsedFromMetadata_ObsoleteType()
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+ Dim attrRef = attrComp.EmitToImageReference()
+
+ Dim libSrc =
+
+
+
+
+Public Class C
+End Class
+]]>
+
+
+ Dim libComp = CreateCompilation(libSrc, references:={attrRef})
+ Dim libRef = libComp.EmitToImageReference()
+
+ Dim src =
+
+
+
+
+
+ Dim comp = CreateCompilation(src, references:={libRef, attrRef})
+
+ comp.AssertTheseDiagnostics(
+)
+
+ Assert.Equal(ObsoleteAttributeKind.Obsolete, comp.GetTypeByMetadataName("C").ObsoleteKind)
+ Assert.Equal(ObsoleteAttributeKind.None, comp.GetTypeByMetadataName("C").ContainingModule.ObsoleteKind)
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind)
+ End Sub
+
+
+ Public Sub OnAssembly_CompiledIntoModule()
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+ Dim attrRef = attrComp.EmitToImageReference()
+
+ Dim libSrc =
+
+
+
+Public Class C
+ Public Shared Sub M()
+ End Sub
+End Class
+]]>
+
+
+
+ Dim moduleComp = CreateCompilation(libSrc, options:=TestOptions.ReleaseModule, references:={attrRef})
+ moduleComp.VerifyDiagnostics()
+ Dim moduleRef = moduleComp.EmitToImageReference()
+
+ Dim libSrc2 =
+
+
+
+
+
+ Dim assemblyComp = CreateCompilation(libSrc2, references:={moduleRef, attrRef})
+ assemblyComp.VerifyDiagnostics()
+ Dim assemblyRef = assemblyComp.EmitToImageReference()
+
+ Dim src =
+
+
+
+
+
+ ' Since the module is referenced but not linked, we also need it here, but as
+ ' a result the diagnostics are suppressed
+ Dim comp = CreateCompilation(src, references:={assemblyRef, moduleRef, attrRef})
+ comp.VerifyDiagnostics()
+
+ Assert.Equal(ObsoleteAttributeKind.None, comp.GetTypeByMetadataName("C").ContainingModule.ObsoleteKind)
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind)
+
+ Assert.Equal(ObsoleteAttributeKind.None, comp.GetTypeByMetadataName("D").ContainingModule.ObsoleteKind)
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("D").ContainingAssembly.ObsoleteKind)
+ End Sub
+
+
+ Public Sub RetargetingAssembly_Experimental()
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+ Dim attrRef = attrComp.EmitToImageReference()
+
+ Dim retargetedCode =
+
+
+
+
+
+ Dim originalC = CreateCompilationWithIdentity(New AssemblyIdentity("Ret", New Version(1, 0, 0, 0), isRetargetable:=True), retargetedCode)
+ Dim retargetedC = CreateCompilationWithIdentity(New AssemblyIdentity("Ret", New Version(2, 0, 0, 0), isRetargetable:=True), retargetedCode)
+
+ Dim derivedSrc =
+
+
+
+Public Class Derived
+ Inherits C
+End Class
+]]>
+
+
+
+ Dim derivedComp = CreateCompilation(derivedSrc, references:={originalC.ToMetadataReference(), attrRef})
+ derivedComp.AssertNoDiagnostics()
+
+ Dim src =
+
+
+
+
+
+ Dim comp = CreateCompilation(src, references:={derivedComp.ToMetadataReference(), retargetedC.ToMetadataReference(), attrRef})
+ comp.AssertTheseDiagnostics()
+
+ Dim derivedType = comp.GetTypeByMetadataName("Derived")
+ Assert.IsType(Of RetargetingNamedTypeSymbol)(derivedType)
+ Assert.IsType(Of RetargetingAssemblySymbol)(derivedType.ContainingAssembly)
+ Assert.Equal(ObsoleteAttributeKind.Experimental, derivedType.ContainingAssembly.ObsoleteKind)
+ End Sub
+
+
+ Public Sub RetargetingAssembly_NotExperimental()
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+ Dim attrRef = attrComp.EmitToImageReference()
+
+ Dim retargetedCode =
+
+
+
+
+
+ Dim originalC = CreateCompilationWithIdentity(New AssemblyIdentity("Ret", New Version(1, 0, 0, 0), isRetargetable:=True), retargetedCode)
+ Dim retargetedC = CreateCompilationWithIdentity(New AssemblyIdentity("Ret", New Version(2, 0, 0, 0), isRetargetable:=True), retargetedCode)
+
+ Dim derivedSrc =
+
+
+
+
+
+ Dim derivedComp = CreateCompilation(derivedSrc, references:={originalC.ToMetadataReference(), attrRef})
+ derivedComp.AssertNoDiagnostics()
+
+ Dim src =
+
+
+
+
+
+ Dim comp = CreateCompilation(src, references:={derivedComp.ToMetadataReference(), retargetedC.ToMetadataReference(), attrRef})
+ derivedComp.AssertNoDiagnostics()
+
+ Dim derivedType = comp.GetTypeByMetadataName("Derived")
+ Assert.IsType(Of RetargetingNamedTypeSymbol)(derivedType)
+ Assert.IsType(Of RetargetingAssemblySymbol)(derivedType.ContainingAssembly)
+ Assert.Equal(ObsoleteAttributeKind.None, derivedType.ContainingAssembly.ObsoleteKind)
+ End Sub
+
+
+ Public Sub RetargetingModule_Experimental()
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+ Dim attrRef = attrComp.EmitToImageReference()
+
+ Dim retargetedCode =
+
+
+
+
+
+ Dim originalC = CreateCompilationWithIdentity(New AssemblyIdentity("Ret", New Version(1, 0, 0, 0), isRetargetable:=True), retargetedCode)
+ Dim retargetedC = CreateCompilationWithIdentity(New AssemblyIdentity("Ret", New Version(2, 0, 0, 0), isRetargetable:=True), retargetedCode)
+
+ Dim derivedSrc =
+
+
+
+Public Class Derived
+ Inherits C
+End Class
+]]>
+
+
+
+ Dim derivedComp = CreateCompilation(derivedSrc, references:={originalC.ToMetadataReference(), attrRef})
+ derivedComp.AssertNoDiagnostics()
+
+ Dim src =
+
+
+
+
+
+ Dim comp = CreateCompilation(src, references:={derivedComp.ToMetadataReference(), retargetedC.ToMetadataReference(), attrRef})
+ comp.AssertTheseDiagnostics()
+
+ Dim derivedType = comp.GetTypeByMetadataName("Derived")
+ Assert.IsType(Of RetargetingNamedTypeSymbol)(derivedType)
+ Assert.IsType(Of RetargetingModuleSymbol)(derivedType.ContainingModule)
+ Assert.Equal(ObsoleteAttributeKind.Experimental, derivedType.ContainingModule.ObsoleteKind)
+ End Sub
+
+
+ Public Sub MissingAssemblyAndModule()
+
+ Dim missingSrc =
+
+
+
+
+
+ Dim missingRef = CreateCompilation(missingSrc, assemblyName:="missing").EmitToImageReference()
+
+ Dim libSrc =
+
+
+
+
+
+ Dim libRef = CreateCompilation(libSrc, references:={missingRef}).EmitToImageReference()
+
+ Dim src =
+
+
+
+
+
+ Dim comp = CreateCompilation(src, references:={libRef})
+
+ comp.AssertTheseDiagnostics()
+
+ Dim missingType = comp.GlobalNamespace.GetTypeMember("C").BaseTypeNoUseSiteDiagnostics
+ Assert.True(TypeOf missingType.ContainingAssembly Is MissingAssemblySymbol)
+ Assert.Equal(ObsoleteAttributeKind.None, missingType.ContainingAssembly.ObsoleteKind)
+ Assert.True(TypeOf missingType.ContainingModule Is MissingModuleSymbol)
+ Assert.Equal(ObsoleteAttributeKind.None, missingType.ContainingModule.ObsoleteKind)
+ End Sub
+
+
+ Public Sub Cycle()
+
+ Dim src =
+
+
+
+Namespace System.Diagnostics.CodeAnalysis
+
+ Public Class ExperimentalAttribute
+ Inherits System.Attribute
+
+ Public Sub New(diagnosticId As String)
+ End Sub
+
+ Public Property UrlFormat As String
+ End Class
+End Namespace
+]]>
+
+
+
+ Dim comp = CreateCompilation(src)
+ comp.AssertNoDiagnostics()
+ End Sub
+
+
+ Public Sub OnModule_UsedFromMetadata()
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+ Dim attrRef = attrComp.EmitToImageReference()
+
+ Dim libSrc =
+
+
+
+Public Class C
+ Public Shared Sub M()
+ End Sub
+End Class
+]]>
+
+
+
+ Dim libComp = CreateCompilation(libSrc, references:={attrRef})
+ Dim libRef = libComp.EmitToImageReference()
+
+ Dim src =
+
+
+
+
+
+ Dim comp = CreateCompilation(src, references:={libRef, attrRef})
+
+ comp.AssertTheseDiagnostics(
+)
+
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingModule.ObsoleteKind)
+ Assert.Equal(ObsoleteAttributeKind.None, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind)
+
+ For Each diag In comp.GetDiagnostics()
+ Assert.Equal("DiagID1", diag.Id)
+ Assert.Equal(ERRID.WRN_Experimental, diag.Code)
+ Assert.Equal("https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(BC42380)", diag.Descriptor.HelpLinkUri)
+ Next
+ End Sub
+
+
+ Public Sub OnModuleAndAssembly()
+ ' Prefer reporting the module-level diagnostic
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+ Dim attrRef = attrComp.EmitToImageReference()
+
+ Dim libSrc =
+
+
+
+
+Public Class C
+ Public Shared Sub M()
+ End Sub
+End Class
+]]>
+
+
+
+ Dim libComp = CreateCompilation(libSrc, references:={attrRef})
+ Dim libRef = libComp.EmitToImageReference()
+
+ Dim src =
+
+
+
+
+
+ Dim comp = CreateCompilation(src, references:={libRef, attrRef})
+
+ comp.AssertTheseDiagnostics(
+)
+
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingModule.ObsoleteKind)
+ Assert.Equal(ObsoleteAttributeKind.Experimental, comp.GetTypeByMetadataName("C").ContainingAssembly.ObsoleteKind)
+
+ For Each diag In comp.GetDiagnostics()
+ Assert.Equal("DiagModule", diag.Id)
+ Next
+ End Sub
+
End Class
End Namespace