未验证 提交 34fdb697 编写于 作者: R Rikki Gibson 提交者: GitHub

Customizable Obsolete diagnostics (#42518)

上级 14f447e8
......@@ -644,6 +644,22 @@ internal string DecodeGuidAttribute(AttributeSyntax? nodeOpt, DiagnosticBag diag
return guidString;
}
private protected sealed override bool IsStringProperty(string memberName)
{
if (AttributeClass is object)
{
foreach (var member in AttributeClass.GetMembers(memberName))
{
if (member is PropertySymbol { Type: { SpecialType: SpecialType.System_String } })
{
return true;
}
}
}
return false;
}
#endregion
/// <summary>
......
......@@ -39,8 +39,7 @@ internal static void InitializeObsoleteDataFromMetadata(ref ObsoleteAttributeDat
/// </summary>
internal static ObsoleteAttributeData GetObsoleteDataFromMetadata(EntityHandle token, PEModuleSymbol containingModule, bool ignoreByRefLikeMarker)
{
ObsoleteAttributeData obsoleteAttributeData;
obsoleteAttributeData = containingModule.Module.TryGetDeprecatedOrExperimentalOrObsoleteAttribute(token, ignoreByRefLikeMarker);
var obsoleteAttributeData = containingModule.Module.TryGetDeprecatedOrExperimentalOrObsoleteAttribute(token, new MetadataDecoder(containingModule), ignoreByRefLikeMarker);
Debug.Assert(obsoleteAttributeData == null || !obsoleteAttributeData.IsUninitialized);
return obsoleteAttributeData;
}
......@@ -156,22 +155,23 @@ internal static DiagnosticInfo CreateObsoleteDiagnostic(Symbol symbol, BinderFla
}
// Issue a specialized diagnostic for add methods of collection initializers
bool isColInit = location.Includes(BinderFlags.CollectionInitializerAddMethod);
if (data.Message == null)
{
// It seems like we should be able to assert that data.IsError is false, but we can't because dev11 had
// a bug in this area (i.e. always produce a warning when there's no message) and we have to match it.
// Debug.Assert(!data.IsError);
return new CSDiagnosticInfo(isColInit ? ErrorCode.WRN_DeprecatedCollectionInitAdd : ErrorCode.WRN_DeprecatedSymbol, symbol);
}
else
var isColInit = location.Includes(BinderFlags.CollectionInitializerAddMethod);
var errorCode = (message: data.Message, isError: data.IsError, isColInit) switch
{
ErrorCode errorCode = data.IsError
? (isColInit ? ErrorCode.ERR_DeprecatedCollectionInitAddStr : ErrorCode.ERR_DeprecatedSymbolStr)
: (isColInit ? ErrorCode.WRN_DeprecatedCollectionInitAddStr : ErrorCode.WRN_DeprecatedSymbolStr);
return new CSDiagnosticInfo(errorCode, symbol, data.Message);
}
// dev11 had a bug in this area (i.e. always produce a warning when there's no message) and we have to match it.
(message: null, isError: _, isColInit: true) => ErrorCode.WRN_DeprecatedCollectionInitAdd,
(message: null, isError: _, isColInit: false) => ErrorCode.WRN_DeprecatedSymbol,
(message: { }, isError: true, isColInit: true) => ErrorCode.ERR_DeprecatedCollectionInitAddStr,
(message: { }, isError: true, isColInit: false) => ErrorCode.ERR_DeprecatedSymbolStr,
(message: { }, isError: false, isColInit: true) => ErrorCode.WRN_DeprecatedCollectionInitAddStr,
(message: { }, isError: false, isColInit: false) => ErrorCode.WRN_DeprecatedSymbolStr
};
var arguments = data.Message is string message
? new object[] { symbol, message }
: new object[] { symbol };
return new CustomObsoleteDiagnosticInfo(MessageProvider.Instance, (int)errorCode, data, arguments);
}
}
}
......@@ -999,7 +999,7 @@ private static void AssertReferencedIsByRefLike(TypeSymbol type, bool hasObsolet
Assert.Empty(peType.GetAttributes());
var peModule = (PEModuleSymbol)peType.ContainingModule;
var obsoleteAttribute = peModule.Module.TryGetDeprecatedOrExperimentalOrObsoleteAttribute(peType.Handle, ignoreByRefLikeMarker: false);
var obsoleteAttribute = peModule.Module.TryGetDeprecatedOrExperimentalOrObsoleteAttribute(peType.Handle, new MetadataDecoder(peModule), ignoreByRefLikeMarker: false);
if (hasObsolete)
{
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis.PooledObjects;
namespace Microsoft.CodeAnalysis
{
internal sealed class CustomObsoleteDiagnosticInfo : DiagnosticInfo
{
private DiagnosticDescriptor? _descriptor;
internal ObsoleteAttributeData Data { get; }
internal CustomObsoleteDiagnosticInfo(CommonMessageProvider messageProvider, int errorCode, ObsoleteAttributeData data, params object[] arguments)
: base(messageProvider, errorCode, arguments)
{
Data = data;
}
public override string MessageIdentifier
{
get
{
var id = Data.DiagnosticId;
if (!string.IsNullOrEmpty(id))
{
return id;
}
return base.MessageIdentifier;
}
}
public override DiagnosticDescriptor Descriptor
{
get
{
if (_descriptor == null)
{
Interlocked.CompareExchange(ref _descriptor, CreateDescriptor(), null);
}
return _descriptor;
}
}
private DiagnosticDescriptor CreateDescriptor()
{
var baseDescriptor = base.Descriptor;
var diagnosticId = Data.DiagnosticId;
var urlFormat = Data.UrlFormat;
if (diagnosticId is null && urlFormat is null)
{
return baseDescriptor;
}
var id = MessageIdentifier;
var helpLinkUri = baseDescriptor.HelpLinkUri;
if (urlFormat is object)
{
try
{
helpLinkUri = string.Format(urlFormat, id);
}
catch
{
// if string.Format fails we just want to use the default (non-user specified) URI.
}
}
ImmutableArray<string> customTags;
if (diagnosticId is null)
{
customTags = baseDescriptor.CustomTags.ToImmutableArray();
}
else
{
var capacity = 1;
if (baseDescriptor.CustomTags is ICollection<string> { Count: int count })
{
capacity += count;
}
var tagsBuilder = ArrayBuilder<string>.GetInstance(capacity);
tagsBuilder.AddRange(baseDescriptor.CustomTags);
tagsBuilder.Add(WellKnownDiagnosticTags.CustomObsolete);
customTags = tagsBuilder.ToImmutableAndFree();
}
return new DiagnosticDescriptor(
id: id,
title: baseDescriptor.Title,
messageFormat: baseDescriptor.MessageFormat,
category: baseDescriptor.Category,
defaultSeverity: baseDescriptor.DefaultSeverity,
isEnabledByDefault: baseDescriptor.IsEnabledByDefault,
description: baseDescriptor.Description,
helpLinkUri: helpLinkUri,
customTags: customTags);
}
}
}
......@@ -199,7 +199,7 @@ protected DiagnosticInfo(ObjectReader reader)
/// </summary>
public int Code { get { return _errorCode; } }
public DiagnosticDescriptor Descriptor
public virtual DiagnosticDescriptor Descriptor
{
get
{
......@@ -314,7 +314,7 @@ public virtual IReadOnlyList<Location> AdditionalLocations
/// Get the message id (for example "CS1001") for the message. This includes both the error number
/// and a prefix identifying the source.
/// </summary>
public string MessageIdentifier
public virtual string MessageIdentifier
{
get
{
......
......@@ -44,5 +44,11 @@ public static class WellKnownDiagnosticTags
/// Indicates that the diagnostic is related to an exception thrown by a <see cref="DiagnosticAnalyzer"/>.
/// </summary>
public const string AnalyzerException = nameof(AnalyzerException);
/// <summary>
/// Indicates that the diagnostic is an obsolete diagnostic with a custom ID
/// specified by the 'DiagnosticId' property on 'ObsoleteAttribute'.
/// </summary>
public const string CustomObsolete = nameof(CustomObsolete);
}
}
......@@ -71,8 +71,14 @@ internal LocalInfo<TypeSymbol> WithSignature(byte[] signature)
}
#nullable enable
internal interface IAttributeNamedArgumentDecoder
{
(KeyValuePair<string, TypedConstant> nameValuePair, bool isProperty, SerializationTypeCode typeCode) DecodeCustomAttributeNamedArgumentOrThrow(ref BlobReader argReader);
}
internal abstract class MetadataDecoder<ModuleSymbol, TypeSymbol, MethodSymbol, FieldSymbol, Symbol> :
TypeNameDecoder<ModuleSymbol, TypeSymbol>
TypeNameDecoder<ModuleSymbol, TypeSymbol>,
IAttributeNamedArgumentDecoder
where ModuleSymbol : class, IModuleSymbolInternal
where TypeSymbol : class, Symbol, ITypeSymbolInternal
where MethodSymbol : class, Symbol, IMethodSymbolInternal
......@@ -1602,7 +1608,7 @@ private static TypedConstantKind GetPrimitiveOrEnumTypedConstantKind(TypeSymbol
/// <exception cref="UnsupportedSignatureContent">If the encoded named argument is invalid.</exception>
/// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
private KeyValuePair<string, TypedConstant> DecodeCustomAttributeNamedArgumentOrThrow(ref BlobReader argReader)
public (KeyValuePair<string, TypedConstant> nameValuePair, bool isProperty, SerializationTypeCode typeCode) DecodeCustomAttributeNamedArgumentOrThrow(ref BlobReader argReader)
{
// Ecma-335 23.3 - A NamedArg is simply a FixedArg preceded by information to identify which field or
// property it represents. [Note: Recall that the CLI allows fields and properties to have the same name; so
......@@ -1629,7 +1635,7 @@ private static TypedConstantKind GetPrimitiveOrEnumTypedConstantKind(TypeSymbol
? DecodeCustomAttributeElementArrayOrThrow(ref argReader, elementTypeCode, elementType, type)
: DecodeCustomAttributeElementOrThrow(ref argReader, typeCode, type);
return new KeyValuePair<string, TypedConstant>(name, value);
return (new KeyValuePair<string, TypedConstant>(name, value), kind == CustomAttributeNamedArgumentKind.Property, typeCode);
}
internal bool IsTargetAttribute(
......@@ -1675,7 +1681,7 @@ internal int GetTargetAttributeSignatureIndex(CustomAttributeHandle customAttrib
try
{
positionalArgs = Array.Empty<TypedConstant>();
namedArgs = Array.Empty<KeyValuePair<String, TypedConstant>>();
namedArgs = Array.Empty<KeyValuePair<string, TypedConstant>>();
// We could call decoder.GetSignature and use that to decode the arguments. However, materializing the
// constructor signature is more work. We try to decode the arguments directly from the metadata bytes.
......@@ -1730,7 +1736,7 @@ internal int GetTargetAttributeSignatureIndex(CustomAttributeHandle customAttrib
for (int i = 0; i < namedArgs.Length; i++)
{
namedArgs[i] = DecodeCustomAttributeNamedArgumentOrThrow(ref argsReader);
(namedArgs[i], _, _) = DecodeCustomAttributeNamedArgumentOrThrow(ref argsReader);
}
}
......
......@@ -7,6 +7,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
......@@ -93,7 +94,6 @@ internal sealed class PEModule : IDisposable
private static readonly AttributeValueExtractor<ImmutableArray<bool>> s_attributeBoolArrayValueExtractor = CrackBoolArrayInAttributeValue;
private static readonly AttributeValueExtractor<ImmutableArray<byte>> s_attributeByteArrayValueExtractor = CrackByteArrayInAttributeValue;
private static readonly AttributeValueExtractor<ImmutableArray<string>> s_attributeStringArrayValueExtractor = CrackStringArrayInAttributeValue;
private static readonly AttributeValueExtractor<ObsoleteAttributeData> s_attributeObsoleteDataExtractor = CrackObsoleteAttributeData;
private static readonly AttributeValueExtractor<ObsoleteAttributeData> s_attributeDeprecatedDataExtractor = CrackDeprecatedAttributeData;
private static readonly AttributeValueExtractor<BoolAndStringArrayData> s_attributeBoolAndStringArrayValueExtractor = CrackBoolAndStringArrayInAttributeValue;
private static readonly AttributeValueExtractor<BoolAndStringData> s_attributeBoolAndStringValueExtractor = CrackBoolAndStringInAttributeValue;
......@@ -1080,6 +1080,7 @@ internal bool HasIsByRefLikeAttribute(EntityHandle token)
internal ObsoleteAttributeData TryGetDeprecatedOrExperimentalOrObsoleteAttribute(
EntityHandle token,
IAttributeNamedArgumentDecoder decoder,
bool ignoreByRefLikeMarker)
{
AttributeInfo info;
......@@ -1093,7 +1094,7 @@ internal bool HasIsByRefLikeAttribute(EntityHandle token)
info = FindTargetAttribute(token, AttributeDescription.ObsoleteAttribute);
if (info.HasValue)
{
ObsoleteAttributeData obsoleteData = TryExtractObsoleteDataFromAttribute(info);
ObsoleteAttributeData obsoleteData = TryExtractObsoleteDataFromAttribute(info, decoder);
switch (obsoleteData?.Message)
{
case ByRefLikeMarker when ignoreByRefLikeMarker:
......@@ -1332,37 +1333,74 @@ private ArrayBuilder<string> ExtractStringValuesFromAttributes(List<AttributeInf
return result;
}
private ObsoleteAttributeData TryExtractObsoleteDataFromAttribute(AttributeInfo attributeInfo)
#nullable enable
private ObsoleteAttributeData? TryExtractObsoleteDataFromAttribute(AttributeInfo attributeInfo, IAttributeNamedArgumentDecoder decoder)
{
Debug.Assert(attributeInfo.HasValue);
if (!TryGetAttributeReader(attributeInfo.Handle, out var sig))
{
return null;
}
string? message = null;
bool isError = false;
switch (attributeInfo.SignatureIndex)
{
case 0:
// ObsoleteAttribute()
return new ObsoleteAttributeData(ObsoleteAttributeKind.Obsolete, message: null, isError: false);
break;
case 1:
// ObsoleteAttribute(string)
string message;
if (TryExtractStringValueFromAttribute(attributeInfo.Handle, out message))
if (sig.RemainingBytes > 0 && CrackStringInAttributeValue(out message, ref sig))
{
return new ObsoleteAttributeData(ObsoleteAttributeKind.Obsolete, message, isError: false);
break;
}
return null;
case 2:
// ObsoleteAttribute(string, bool)
return TryExtractValueFromAttribute(attributeInfo.Handle, out var obsoleteData, s_attributeObsoleteDataExtractor) ?
obsoleteData :
null;
if (sig.RemainingBytes > 0 && CrackStringInAttributeValue(out message, ref sig) &&
sig.RemainingBytes > 0 && CrackBooleanInAttributeValue(out isError, ref sig))
{
break;
}
return null;
default:
throw ExceptionUtilities.UnexpectedValue(attributeInfo.SignatureIndex);
}
(string? diagnosticId, string? urlFormat) = sig.RemainingBytes > 0 ? CrackObsoleteProperties(ref sig, decoder) : default;
return new ObsoleteAttributeData(ObsoleteAttributeKind.Obsolete, message, isError, diagnosticId, urlFormat);
}
private bool TryGetAttributeReader(CustomAttributeHandle handle, out BlobReader blobReader)
{
Debug.Assert(!handle.IsNil);
try
{
var valueBlob = GetCustomAttributeValueOrThrow(handle);
if (!valueBlob.IsNil)
{
blobReader = MetadataReader.GetBlobReader(valueBlob);
if (blobReader.Length >= 4)
{
// check prolog
if (blobReader.ReadInt16() == 1)
{
return true;
}
}
}
}
catch (BadImageFormatException)
{ }
blobReader = default;
return false;
}
#nullable restore
private ObsoleteAttributeData TryExtractDeprecatedDataFromAttribute(AttributeInfo attributeInfo)
{
Debug.Assert(attributeInfo.HasValue);
......@@ -1643,26 +1681,56 @@ private bool HasStringAndIntValuedAttribute(EntityHandle token, AttributeDescrip
}
}
private static bool CrackObsoleteAttributeData(out ObsoleteAttributeData value, ref BlobReader sig)
#nullable enable
/// <summary>
/// Gets the well-known optional named properties on ObsoleteAttribute, if present.
/// Both 'diagnosticId' and 'urlFormat' may be present, or only one, or neither.
/// </summary>
/// <remarks>
/// Failure to find any of these properties does not imply failure to decode the ObsoleteAttribute,
/// so we don't return a value indicating success or failure.
/// </remarks>
private static (string? diagnosticId, string? urlFormat) CrackObsoleteProperties(ref BlobReader sig, IAttributeNamedArgumentDecoder decoder)
{
string message;
if (CrackStringInAttributeValue(out message, ref sig) && sig.RemainingBytes >= 1)
string? diagnosticId = null;
string? urlFormat = null;
try
{
bool isError = sig.ReadBoolean();
value = new ObsoleteAttributeData(ObsoleteAttributeKind.Obsolete, message, isError);
return true;
// See CIL spec section II.23.3 Custom attributes
//
// Next is a description of the optional “named” fields and properties.
// This starts with NumNamed– an unsigned int16 giving the number of “named” properties or fields that follow.
var numNamed = sig.ReadUInt16();
for (int i = 0; i < numNamed && (diagnosticId is null || urlFormat is null); i++)
{
var ((name, value), isProperty, typeCode) = decoder.DecodeCustomAttributeNamedArgumentOrThrow(ref sig);
if (typeCode == SerializationTypeCode.String && isProperty && value.ValueInternal is string stringValue)
{
if (diagnosticId is null && name == ObsoleteAttributeData.DiagnosticIdPropertyName)
{
diagnosticId = stringValue;
}
else if (urlFormat is null && name == ObsoleteAttributeData.UrlFormatPropertyName)
{
urlFormat = stringValue;
}
}
}
}
catch (BadImageFormatException) { }
catch (UnsupportedSignatureContent) { }
value = null;
return false;
return (diagnosticId, urlFormat);
}
#nullable restore
private static bool CrackDeprecatedAttributeData(out ObsoleteAttributeData value, ref BlobReader sig)
{
StringAndInt args;
if (CrackStringAndIntInAttributeValue(out args, ref sig))
{
value = new ObsoleteAttributeData(ObsoleteAttributeKind.Deprecated, args.StringValue, args.IntValue == 1);
value = new ObsoleteAttributeData(ObsoleteAttributeKind.Deprecated, args.StringValue, args.IntValue == 1, diagnosticId: null, urlFormat: null);
return true;
}
......
Microsoft.CodeAnalysis.CommandLineSourceFile.CommandLineSourceFile(string path, bool isScript, bool isInputRedirected) -> void
Microsoft.CodeAnalysis.CommandLineSourceFile.IsInputRedirected.get -> bool
\ No newline at end of file
Microsoft.CodeAnalysis.CommandLineSourceFile.IsInputRedirected.get -> bool
const Microsoft.CodeAnalysis.WellKnownDiagnosticTags.CustomObsolete = "CustomObsolete" -> string
......@@ -9,6 +9,7 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
......@@ -292,9 +293,37 @@ private ObsoleteAttributeData DecodeObsoleteAttribute()
}
}
return new ObsoleteAttributeData(ObsoleteAttributeKind.Obsolete, message, isError);
string? diagnosticId = null;
string? urlFormat = null;
foreach (var (name, value) in this.CommonNamedArguments)
{
if (diagnosticId is null && name == ObsoleteAttributeData.DiagnosticIdPropertyName && IsStringProperty(ObsoleteAttributeData.DiagnosticIdPropertyName))
{
diagnosticId = value.ValueInternal as string;
}
else if (urlFormat is null && name == ObsoleteAttributeData.UrlFormatPropertyName && IsStringProperty(ObsoleteAttributeData.UrlFormatPropertyName))
{
urlFormat = value.ValueInternal as string;
}
if (diagnosticId is object && urlFormat is object)
{
break;
}
}
return new ObsoleteAttributeData(ObsoleteAttributeKind.Obsolete, message, isError, diagnosticId, urlFormat);
}
// Note: it is disallowed to declare a property and a field
// with the same name in C# or VB source, even if it is allowed in IL.
//
// We use a virtual method and override to prevent having to realize the public symbols just to decode obsolete attributes.
// Ideally we would use an abstract method, but that would require making the method visible to
// public consumers who inherit from this class, which we don't want to do.
// Therefore we just make it a 'private protected virtual' method instead.
private protected virtual bool IsStringProperty(string memberName) => throw ExceptionUtilities.Unreachable;
/// <summary>
/// Decode the arguments to DeprecatedAttribute. DeprecatedAttribute can have 3 or 4 arguments.
/// </summary>
......@@ -316,7 +345,7 @@ private ObsoleteAttributeData DecodeDeprecatedAttribute()
isError = ((int)args[1].ValueInternal == 1);
}
return new ObsoleteAttributeData(ObsoleteAttributeKind.Deprecated, message, isError);
return new ObsoleteAttributeData(ObsoleteAttributeKind.Deprecated, message, isError, diagnosticId: null, urlFormat: null);
}
/// <summary>
......
......@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
namespace Microsoft.CodeAnalysis
......@@ -20,14 +22,19 @@ internal enum ObsoleteAttributeKind
/// </summary>
internal sealed class ObsoleteAttributeData
{
public static readonly ObsoleteAttributeData Uninitialized = new ObsoleteAttributeData(ObsoleteAttributeKind.Uninitialized, null, false);
public static readonly ObsoleteAttributeData Experimental = new ObsoleteAttributeData(ObsoleteAttributeKind.Experimental, null, false);
public static readonly ObsoleteAttributeData Uninitialized = new ObsoleteAttributeData(ObsoleteAttributeKind.Uninitialized, message: null, isError: false, diagnosticId: null, urlFormat: null);
public static readonly ObsoleteAttributeData Experimental = new ObsoleteAttributeData(ObsoleteAttributeKind.Experimental, message: null, isError: false, diagnosticId: null, urlFormat: null);
public const string DiagnosticIdPropertyName = "DiagnosticId";
public const string UrlFormatPropertyName = "UrlFormat";
public ObsoleteAttributeData(ObsoleteAttributeKind kind, string message, bool isError)
public ObsoleteAttributeData(ObsoleteAttributeKind kind, string? message, bool isError, string? diagnosticId, string? urlFormat)
{
Kind = kind;
Message = message;
IsError = isError;
DiagnosticId = diagnosticId;
UrlFormat = urlFormat;
}
public readonly ObsoleteAttributeKind Kind;
......@@ -41,7 +48,31 @@ public ObsoleteAttributeData(ObsoleteAttributeKind kind, string message, bool is
/// <summary>
/// The message that will be shown when an error/warning is created for <see cref="ObsoleteAttribute"/>.
/// </summary>
public readonly string Message;
public readonly string? Message;
/// <summary>
/// The custom diagnostic ID to use for obsolete diagnostics.
/// If null, diagnostics are produced using the compiler default diagnostic IDs.
/// </summary>
public readonly string? DiagnosticId;
/// <summary>
/// <para>
/// The custom help URL format string for obsolete diagnostics.
/// Expected to contain zero or one format items.
/// </para>
/// <para>
/// When specified, the obsolete diagnostic's <see cref="DiagnosticDescriptor.HelpLinkUri"/> will be produced
/// by formatting this string using the <see cref="DiagnosticId"/> as the single argument.
/// </para>
///
/// <example>
/// e.g. with a <see cref="DiagnosticId"/> value <c>"TEST1"</c>,
/// and a <see cref="UrlFormat"/> value <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/{0}/"/>,<br/>
/// the diagnostic will have the HelpLinkUri <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/TEST1/"/>.
/// </example>
/// </summary>
public readonly string? UrlFormat;
internal bool IsUninitialized
{
......
......@@ -73,6 +73,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return New DiagnosticInfo(MessageProvider.Instance, id, arguments)
End Function
Public Shared Function ObsoleteErrorInfo(id As ERRID, data As ObsoleteAttributeData, ParamArray arguments As Object()) As CustomObsoleteDiagnosticInfo
Return New CustomObsoleteDiagnosticInfo(MessageProvider.Instance, id, data, arguments)
End Function
Public Shared Function ErrorInfo(id As ERRID, ByRef syntaxToken As SyntaxToken) As DiagnosticInfo
Return ErrorInfo(id, SyntaxFacts.GetText(syntaxToken.Kind))
End Function
......
......@@ -488,6 +488,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
Return Me.GetConstructorArgument(Of String)(0, SpecialType.System_String)
End Function
Private Protected NotOverridable Overrides Function IsStringProperty(memberName As String) As Boolean
If AttributeClass IsNot Nothing Then
For Each member In AttributeClass.GetMembers(memberName)
Dim prop = TryCast(member, PropertySymbol)
If prop?.Type.SpecialType = SpecialType.System_String Then
Return True
End If
Next
End If
Return False
End Function
#End Region
''' <summary>
......
......@@ -33,7 +33,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
Friend Shared Function GetObsoleteDataFromMetadata(token As EntityHandle, containingModule As PEModuleSymbol) As ObsoleteAttributeData
Dim obsoleteAttributeData As ObsoleteAttributeData = Nothing
' ignoreByRefLikeMarker := False, since VB does not support ref-like types
obsoleteAttributeData = containingModule.Module.TryGetDeprecatedOrExperimentalOrObsoleteAttribute(token, ignoreByRefLikeMarker:=False)
obsoleteAttributeData = containingModule.Module.TryGetDeprecatedOrExperimentalOrObsoleteAttribute(token, New MetadataDecoder(containingModule), ignoreByRefLikeMarker:=False)
Debug.Assert(obsoleteAttributeData Is Nothing OrElse Not obsoleteAttributeData.IsUninitialized)
Return obsoleteAttributeData
End Function
......@@ -129,17 +129,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
Dim accessorString = If(accessorSymbol.MethodKind = MethodKind.PropertyGet, "Get", "Set")
If String.IsNullOrEmpty(data.Message) Then
Return ErrorFactory.ErrorInfo(If(data.IsError, ERRID.ERR_UseOfObsoletePropertyAccessor2, ERRID.WRN_UseOfObsoletePropertyAccessor2),
Return ErrorFactory.ObsoleteErrorInfo(If(data.IsError, ERRID.ERR_UseOfObsoletePropertyAccessor2, ERRID.WRN_UseOfObsoletePropertyAccessor2), data,
accessorString, accessorSymbol.AssociatedSymbol)
Else
Return ErrorFactory.ErrorInfo(If(data.IsError, ERRID.ERR_UseOfObsoletePropertyAccessor3, ERRID.WRN_UseOfObsoletePropertyAccessor3),
Return ErrorFactory.ObsoleteErrorInfo(If(data.IsError, ERRID.ERR_UseOfObsoletePropertyAccessor3, ERRID.WRN_UseOfObsoletePropertyAccessor3), data,
accessorString, accessorSymbol.AssociatedSymbol, data.Message)
End If
Else
If String.IsNullOrEmpty(data.Message) Then
Return ErrorFactory.ErrorInfo(If(data.IsError, ERRID.ERR_UseOfObsoleteSymbolNoMessage1, ERRID.WRN_UseOfObsoleteSymbolNoMessage1), symbol)
Return ErrorFactory.ObsoleteErrorInfo(If(data.IsError, ERRID.ERR_UseOfObsoleteSymbolNoMessage1, ERRID.WRN_UseOfObsoleteSymbolNoMessage1), data, symbol)
Else
Return ErrorFactory.ErrorInfo(If(data.IsError, ERRID.ERR_UseOfObsoleteSymbol2, ERRID.WRN_UseOfObsoleteSymbol2), symbol, data.Message)
Return ErrorFactory.ObsoleteErrorInfo(If(data.IsError, ERRID.ERR_UseOfObsoleteSymbol2, ERRID.WRN_UseOfObsoleteSymbol2), data, symbol, data.Message)
End If
End If
......
......@@ -13,6 +13,7 @@
using Xunit;
using Roslyn.Test.Utilities;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.CSharp;
namespace Microsoft.CodeAnalysis.Test.Utilities
{
......@@ -116,7 +117,7 @@ public DiagnosticDescription(Diagnostic d, bool errorCodeOnly, bool includeDefau
_effectiveSeverityOpt = includeEffectiveSeverity ? d.Severity : (DiagnosticSeverity?)null;
DiagnosticWithInfo dinfo = null;
if (d.Code == 0)
if (d.Code == 0 || d.Descriptor.CustomTags.Contains(WellKnownDiagnosticTags.CustomObsolete))
{
_code = d.Id;
_errorCodeType = typeof(string);
......@@ -421,7 +422,7 @@ public static string GetAssertText(DiagnosticDescription[] expected, IEnumerable
{
const int CSharp = 1;
const int VisualBasic = 2;
var language = actual.Any() && actual.First().Id.StartsWith("CS", StringComparison.Ordinal) ? CSharp : VisualBasic;
var language = actual.Any() && actual.First() is CSDiagnostic ? CSharp : VisualBasic;
var includeDiagnosticMessagesAsComments = (language == CSharp);
int indentDepth = (language == CSharp) ? 4 : 1;
var includeDefaultSeverity = expected.Any() && expected.All(d => d.DefaultSeverity != null);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册