forked from dotnet/roslyn
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathObsoleteAttributeHelpers.cs
More file actions
207 lines (183 loc) · 9.8 KB
/
ObsoleteAttributeHelpers.cs
File metadata and controls
207 lines (183 loc) · 9.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
// 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 disable
using System.Diagnostics;
using System.Reflection.Metadata;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal enum ObsoleteDiagnosticKind
{
NotObsolete,
Suppressed,
Diagnostic,
Lazy,
LazyPotentiallySuppressed,
}
internal static class ObsoleteAttributeHelpers
{
/// <summary>
/// Initialize the ObsoleteAttributeData by fetching attributes and decoding ObsoleteAttributeData. This can be
/// done for Metadata symbol easily whereas trying to do this for source symbols could result in cycles.
/// </summary>
internal static void InitializeObsoleteDataFromMetadata(ref ObsoleteAttributeData data, EntityHandle token, PEModuleSymbol containingModule, bool ignoreByRefLikeMarker, bool ignoreRequiredMemberMarker)
{
if (ReferenceEquals(data, ObsoleteAttributeData.Uninitialized))
{
ObsoleteAttributeData obsoleteAttributeData = GetObsoleteDataFromMetadata(token, containingModule, ignoreByRefLikeMarker, ignoreRequiredMemberMarker);
Interlocked.CompareExchange(ref data, obsoleteAttributeData, ObsoleteAttributeData.Uninitialized);
}
}
/// <summary>
/// Get the ObsoleteAttributeData by fetching attributes and decoding ObsoleteAttributeData. This can be
/// done for Metadata symbol easily whereas trying to do this for source symbols could result in cycles.
/// </summary>
internal static ObsoleteAttributeData GetObsoleteDataFromMetadata(EntityHandle token, PEModuleSymbol containingModule, bool ignoreByRefLikeMarker, bool ignoreRequiredMemberMarker)
{
var obsoleteAttributeData = containingModule.Module.TryGetDeprecatedOrExperimentalOrObsoleteAttribute(token, new MetadataDecoder(containingModule), ignoreByRefLikeMarker, ignoreRequiredMemberMarker);
Debug.Assert(obsoleteAttributeData == null || !obsoleteAttributeData.IsUninitialized);
return obsoleteAttributeData;
}
/// <summary>
/// This method checks to see if the given symbol is Obsolete or if any symbol in the parent hierarchy is Obsolete.
/// </summary>
/// <returns>
/// True if some symbol in the parent hierarchy is known to be Obsolete. Unknown if any
/// symbol's Obsoleteness is Unknown. False, if we are certain that no symbol in the parent
/// hierarchy is Obsolete.
/// </returns>
private static ThreeState GetObsoleteContextState(Symbol symbol, bool forceComplete)
{
while ((object)symbol != null)
{
if (symbol.Kind == SymbolKind.Field)
{
// If this is the backing field of an event, look at the event instead.
var associatedSymbol = ((FieldSymbol)symbol).AssociatedSymbol;
if ((object)associatedSymbol != null)
{
symbol = associatedSymbol;
}
}
if (forceComplete)
{
symbol.ForceCompleteObsoleteAttribute();
}
var state = symbol.ObsoleteState;
if (state != ThreeState.False)
{
return state;
}
// For property or event accessors, check the associated property or event next.
if (symbol.IsAccessor())
{
symbol = ((MethodSymbol)symbol).AssociatedSymbol;
}
else
{
symbol = symbol.ContainingSymbol;
}
}
return ThreeState.False;
}
internal static ObsoleteDiagnosticKind GetObsoleteDiagnosticKind(Symbol symbol, Symbol containingMember, bool forceComplete = false)
{
switch (symbol.ObsoleteKind)
{
case ObsoleteAttributeKind.None:
return ObsoleteDiagnosticKind.NotObsolete;
case ObsoleteAttributeKind.Experimental:
case ObsoleteAttributeKind.NewExperimental:
return ObsoleteDiagnosticKind.Diagnostic;
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
// ObsoleteAttribute, store the symbol so that we can report diagnostics at a
// later stage.
return ObsoleteDiagnosticKind.Lazy;
}
switch (GetObsoleteContextState(containingMember, forceComplete))
{
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;
}
}
/// <summary>
/// Create a diagnostic for the given symbol. This could be an error or a warning based on
/// the ObsoleteAttribute's arguments.
/// </summary>
internal static DiagnosticInfo CreateObsoleteDiagnostic(Symbol symbol, BinderFlags location)
{
DiagnosticInfo result = createObsoleteDiagnostic(symbol, location);
Debug.Assert(result?.IsObsoleteDiagnostic() != false);
return result;
static DiagnosticInfo createObsoleteDiagnostic(Symbol symbol, BinderFlags location)
{
var data = symbol.ObsoleteAttributeData;
Debug.Assert(data != null);
if (data == null)
{
return null;
}
// At this point, we are going to issue diagnostics and therefore the data shouldn't be
// uninitialized.
Debug.Assert(!data.IsUninitialized);
// The native compiler suppresses Obsolete diagnostics in these locations.
if (location.Includes(BinderFlags.SuppressObsoleteChecks))
{
return null;
}
if (data.Kind == ObsoleteAttributeKind.Experimental)
{
Debug.Assert(data.Message == null);
Debug.Assert(!data.IsError);
// Provide an explicit format for fully-qualified type names.
return new CSDiagnosticInfo(ErrorCode.WRN_Experimental, new FormattedSymbol(symbol, SymbolDisplayFormat.CSharpErrorMessageFormat));
}
if (data.Kind == ObsoleteAttributeKind.NewExperimental)
{
Debug.Assert(data.Message is null);
Debug.Assert(!data.IsError);
// Provide an explicit format for fully-qualified type names.
return new CustomObsoleteDiagnosticInfo(MessageProvider.Instance, (int)ErrorCode.WRN_Experimental,
data, new FormattedSymbol(symbol, SymbolDisplayFormat.CSharpErrorMessageFormat));
}
// Issue a specialized diagnostic for add methods of collection initializers
var isColInit = location.Includes(BinderFlags.CollectionInitializerAddMethod);
Debug.Assert(!isColInit || symbol.Name == WellKnownMemberNames.CollectionInitializerAddMethodName);
var errorCode = (message: data.Message, isError: data.IsError, isColInit) switch
{
// 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);
}
}
internal static bool IsObsoleteDiagnostic(this DiagnosticInfo diagnosticInfo)
{
return (ErrorCode)diagnosticInfo.Code is
(ErrorCode.WRN_Experimental or ErrorCode.WRN_DeprecatedCollectionInitAdd or
ErrorCode.WRN_DeprecatedSymbol or ErrorCode.ERR_DeprecatedCollectionInitAddStr or
ErrorCode.ERR_DeprecatedSymbolStr or ErrorCode.WRN_DeprecatedCollectionInitAddStr or
ErrorCode.WRN_DeprecatedSymbolStr);
}
}
}