|
' 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.
Imports System.Collections.Generic
Imports System.Collections.Immutable
Imports System.Globalization
Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Emit
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports TypeKind = Microsoft.CodeAnalysis.TypeKind
Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
Friend MustInherit Class SourceFieldSymbol
Inherits FieldSymbol
Implements IAttributeTargetSymbol
' Flags associated with the field
Protected ReadOnly m_memberFlags As SourceMemberFlags
Private ReadOnly _containingType As SourceMemberContainerTypeSymbol
Private ReadOnly _name As String
' The syntax reference for this field (points to the name of the field)
Private ReadOnly _syntaxRef As SyntaxReference
Private _lazyDocComment As String
Private _lazyExpandedDocComment As String
Private _lazyCustomAttributesBag As CustomAttributesBag(Of VisualBasicAttributeData)
''' <summary>
''' See <see cref="StateFlags"/>
''' </summary>
Protected _lazyState As Integer
<Flags>
Protected Enum StateFlags As Integer
TypeConstraintsChecked = &H1
EventProduced = &H2
End Enum
Protected Sub New(container As SourceMemberContainerTypeSymbol,
syntaxRef As SyntaxReference,
name As String,
memberFlags As SourceMemberFlags)
Debug.Assert(container IsNot Nothing)
Debug.Assert(syntaxRef IsNot Nothing)
Debug.Assert(name IsNot Nothing)
_name = name
_containingType = container
_syntaxRef = syntaxRef
m_memberFlags = memberFlags
End Sub
Protected Overridable Sub GenerateDeclarationErrorsImpl(cancellationToken As CancellationToken)
MyBase.GenerateDeclarationErrors(cancellationToken)
Dim unusedType = Me.Type
GetConstantValue(ConstantFieldsInProgress.Empty)
End Sub
Friend NotOverridable Overrides Sub GenerateDeclarationErrors(cancellationToken As CancellationToken)
GenerateDeclarationErrorsImpl(cancellationToken)
' We want declaration events to be last, after all compilation analysis is done, so we produce them here
Dim sourceModule = DirectCast(Me.ContainingModule, SourceModuleSymbol)
If ThreadSafeFlagOperations.Set(_lazyState, StateFlags.EventProduced) AndAlso Not Me.IsImplicitlyDeclared Then
sourceModule.DeclaringCompilation.SymbolDeclaredEvent(Me)
End If
End Sub
''' <summary>
''' Gets the syntax tree.
''' </summary>
Friend ReadOnly Property SyntaxTree As SyntaxTree
Get
Return _syntaxRef.SyntaxTree
End Get
End Property
Friend ReadOnly Property Syntax As VisualBasicSyntaxNode
Get
Return _syntaxRef.GetVisualBasicSyntax()
End Get
End Property
Friend MustOverride ReadOnly Property DeclarationSyntax As VisualBasicSyntaxNode
''' <summary>
''' Field initializer's declaration syntax node.
''' It can be a EqualsValueSyntax or AsNewClauseSyntax.
''' </summary>
Friend Overridable ReadOnly Property EqualsValueOrAsNewInitOpt As VisualBasicSyntaxNode
Get
Return Nothing
End Get
End Property
Public NotOverridable Overrides ReadOnly Property Name As String
Get
Return _name
End Get
End Property
Public NotOverridable Overrides ReadOnly Property ContainingSymbol As Symbol
Get
Return _containingType
End Get
End Property
Public NotOverridable Overrides ReadOnly Property ContainingType As NamedTypeSymbol
Get
Return _containingType
End Get
End Property
Public ReadOnly Property ContainingSourceType As SourceMemberContainerTypeSymbol
Get
Return _containingType
End Get
End Property
Public Overrides Function GetDocumentationCommentXml(Optional preferredCulture As CultureInfo = Nothing, Optional expandIncludes As Boolean = False, Optional cancellationToken As CancellationToken = Nothing) As String
If expandIncludes Then
Return GetAndCacheDocumentationComment(Me, preferredCulture, expandIncludes, _lazyExpandedDocComment, cancellationToken)
Else
Return GetAndCacheDocumentationComment(Me, preferredCulture, expandIncludes, _lazyDocComment, cancellationToken)
End If
End Function
''' <summary>
''' Gets a value indicating whether this instance has declared type. This means not an inferred type.
''' </summary>
''' <value>
''' <c>true</c> if this instance has declared type; otherwise, <c>false</c>.
''' </value>
Friend Overrides ReadOnly Property HasDeclaredType As Boolean
Get
Return (m_memberFlags And SourceMemberFlags.InferredFieldType) = 0
End Get
End Property
Public Overrides ReadOnly Property CustomModifiers As ImmutableArray(Of CustomModifier)
Get
Return ImmutableArray(Of CustomModifier).Empty
End Get
End Property
Public NotOverridable Overrides ReadOnly Property IsRequired As Boolean
Get
Return False
End Get
End Property
Public Overrides ReadOnly Property AssociatedSymbol As Symbol
Get
Return Nothing
End Get
End Property
Public Overrides ReadOnly Property DeclaredAccessibility As Accessibility
Get
Return CType((m_memberFlags And SourceMemberFlags.AccessibilityMask), Accessibility)
End Get
End Property
Public Overrides ReadOnly Property IsReadOnly As Boolean
Get
Return (m_memberFlags And SourceMemberFlags.ReadOnly) <> 0
End Get
End Property
Public Overrides ReadOnly Property IsConst As Boolean
Get
Return (m_memberFlags And SourceMemberFlags.Const) <> 0
End Get
End Property
''' <summary>
''' Gets the constant value.
''' </summary>
''' <param name="inProgress">Used to detect dependencies between constant field values.</param>
Friend Overrides Function GetConstantValue(inProgress As ConstantFieldsInProgress) As ConstantValue
Return Nothing
End Function
''' <summary>
''' Helper to get a constant value of the field with cycle detection.
''' Also avoids deep recursion due to references to other constant fields in the value.
''' Derived types utilizing this helper should provide storage for the lazily calculated
''' <see cref="EvaluatedConstant"/> value and should implement the following APIs:
''' <see cref="GetLazyConstantTuple()"/>,
''' <see cref="SetLazyConstantTuple(EvaluatedConstant, BindingDiagnosticBag)"/>,
''' <see cref="MakeConstantTuple(ConstantFieldsInProgress.Dependencies, BindingDiagnosticBag)"/>.
''' </summary>
Protected Function GetConstantValueImpl(inProgress As ConstantFieldsInProgress) As ConstantValue
Dim constantTuple As EvaluatedConstant = GetLazyConstantTuple()
If constantTuple IsNot Nothing Then
Return constantTuple.Value
End If
If Not inProgress.IsEmpty Then
' Add this field as a dependency of the original field, and
' return ConstantValue.Bad. The outer caller will call
' this method again after evaluating any dependencies.
inProgress.AddDependency(Me)
Return CodeAnalysis.ConstantValue.Bad
End If
' Order dependencies.
Dim order = ArrayBuilder(Of ConstantValueUtils.FieldInfo).GetInstance()
OrderAllDependencies(order)
' Evaluate fields in order.
For Each info In order
' Bind the field value regardless of whether the field represents
' the start of a cycle. In the cycle case, there will be unevaluated
' dependencies and the result will be ConstantValue.Bad plus cycle error.
info.Field.BindConstantTupleIfNecessary(info.StartsCycle)
Next
order.Free()
' Return the value of this field.
Return GetLazyConstantTuple().Value
End Function
Private Sub BindConstantTupleIfNecessary(startsCycle As Boolean)
If GetLazyConstantTuple() Is Nothing Then
Dim builder = PooledHashSet(Of SourceFieldSymbol).GetInstance()
Dim dependencies As New ConstantFieldsInProgress.Dependencies(builder)
Dim diagnostics = BindingDiagnosticBag.GetInstance()
Dim constantTuple As EvaluatedConstant = MakeConstantTuple(dependencies, diagnostics)
dependencies.Freeze()
If startsCycle Then
diagnostics.Clear()
diagnostics.Add(ERRID.ERR_CircularEvaluation1, Locations(0), CustomSymbolDisplayFormatter.ShortErrorName(Me))
End If
SetLazyConstantTuple(constantTuple, diagnostics)
diagnostics.Free()
builder.Free()
End If
End Sub
''' <summary>
''' Generate a list containing the field and all dependencies
''' of that field that require evaluation. The list is ordered by
''' dependencies, with fields with no dependencies first. Cycles are
''' broken at the first field lexically in the cycle. If multiple threads
''' call this method with the same field, the order of the fields
''' returned should be the same, although some fields may be missing
''' from the lists in some threads as other threads evaluate fields.
''' </summary>
Private Sub OrderAllDependencies(order As ArrayBuilder(Of ConstantValueUtils.FieldInfo))
Debug.Assert(order.Count = 0)
Dim graph = PooledDictionary(Of SourceFieldSymbol, DependencyInfo).GetInstance()
CreateGraph(graph)
Debug.Assert(graph.Count >= 1)
CheckGraph(graph)
#If DEBUG Then
Dim fields = ArrayBuilder(Of SourceFieldSymbol).GetInstance()
fields.AddRange(graph.Keys)
#End If
OrderGraph(graph, order)
#If DEBUG Then
' Verify all entries in the graph are in the ordered list.
Dim map = New HashSet(Of SourceFieldSymbol)(order.Select(Function(o) o.Field).Distinct())
Debug.Assert(fields.All(Function(f) map.Contains(f)))
fields.Free()
#End If
graph.Free()
End Sub
Private Structure DependencyInfo
''' <summary>
''' The set of fields on which the field depends.
''' </summary>
Public Dependencies As ImmutableHashSet(Of SourceFieldSymbol)
''' <summary>
''' The set of fields that depend on the field.
''' </summary>
Public DependedOnBy As ImmutableHashSet(Of SourceFieldSymbol)
End Structure
''' <summary>
''' Build a dependency graph (a map from
''' field to dependencies).
''' </summary>
Private Sub CreateGraph(graph As Dictionary(Of SourceFieldSymbol, DependencyInfo))
Dim pending = ArrayBuilder(Of SourceFieldSymbol).GetInstance()
pending.Push(Me)
While pending.Count > 0
Dim field As SourceFieldSymbol = pending.Pop()
Dim node As DependencyInfo = Nothing
If graph.TryGetValue(field, node) Then
If node.Dependencies IsNot Nothing Then
' Already visited node.
Continue While
End If
Else
node = New DependencyInfo()
node.DependedOnBy = ImmutableHashSet(Of SourceFieldSymbol).Empty
End If
Dim dependencies As ImmutableHashSet(Of SourceFieldSymbol) = field.GetConstantValueDependencies()
' GetConstantValueDependencies will return an empty set if
' the constant value has already been calculated. That avoids
' calculating the full graph repeatedly. For instance with
' "Enum E : M0 = 0 : M1 = M0 + 1 : ... : Mn = Mn-1 + 1 : End Enum", we'll calculate
' the graph M0, ..., Mi for the first field we evaluate, Mi. But for
' the next field, Mj, we should only calculate the graph Mi, ..., Mj.
node.Dependencies = dependencies
graph(field) = node
For Each dependency As SourceFieldSymbol In dependencies
pending.Push(dependency)
If Not graph.TryGetValue(dependency, node) Then
node = New DependencyInfo()
node.DependedOnBy = ImmutableHashSet(Of SourceFieldSymbol).Empty
End If
node.DependedOnBy = node.DependedOnBy.Add(field)
graph(dependency) = node
Next
End While
pending.Free()
End Sub
''' <summary>
''' Return the constant value dependencies. Compute the dependencies
''' if necessary by evaluating the constant value but only persist the
''' constant value if there were no dependencies. (If there are dependencies,
''' the constant value will be re-evaluated after evaluating dependencies.)
''' </summary>
Private Function GetConstantValueDependencies() As ImmutableHashSet(Of SourceFieldSymbol)
Dim valueTuple = GetLazyConstantTuple()
If valueTuple IsNot Nothing Then
' Constant value already determined. No need to
' compute dependencies since the constant values
' of all dependencies should be evaluated as well.
Return ImmutableHashSet(Of SourceFieldSymbol).Empty
End If
Dim builder = PooledHashSet(Of SourceFieldSymbol).GetInstance()
Dim dependencies As New ConstantFieldsInProgress.Dependencies(builder)
Dim diagnostics = BindingDiagnosticBag.GetInstance()
valueTuple = MakeConstantTuple(dependencies, diagnostics)
dependencies.Freeze()
Dim result As ImmutableHashSet(Of SourceFieldSymbol)
' Only persist if there are no dependencies and the calculation
' completed successfully. (We could probably persist in other
' scenarios but it's probably not worth the added complexity.)
If (builder.Count = 0) AndAlso
Not valueTuple.Value.IsBad AndAlso
Not diagnostics.HasAnyResolvedErrors() Then
SetLazyConstantTuple(valueTuple, diagnostics)
result = ImmutableHashSet(Of SourceFieldSymbol).Empty
Else
result = ImmutableHashSet(Of SourceFieldSymbol).Empty.Union(builder)
End If
diagnostics.Free()
builder.Free()
Return result
End Function
<Conditional("DEBUG")>
Private Shared Sub CheckGraph(graph As Dictionary(Of SourceFieldSymbol, DependencyInfo))
' Avoid O(n^2) behavior by checking
' a maximum number of entries.
Dim i As Integer = 10
For Each pair In graph
Dim field As SourceFieldSymbol = pair.Key
Dim node As DependencyInfo = pair.Value
Debug.Assert(node.Dependencies IsNot Nothing)
Debug.Assert(node.DependedOnBy IsNot Nothing)
For Each dependency As SourceFieldSymbol In node.Dependencies
Dim n As DependencyInfo = Nothing
Dim ok = graph.TryGetValue(dependency, n)
Debug.Assert(ok)
Debug.Assert(n.DependedOnBy.Contains(field))
Next
For Each dependedOnBy As SourceFieldSymbol In node.DependedOnBy
Dim n As DependencyInfo = Nothing
Dim ok = graph.TryGetValue(dependedOnBy, n)
Debug.Assert(ok)
Debug.Assert(n.Dependencies.Contains(field))
Next
i -= 1
If i = 0 Then
Exit For
End If
Next
Debug.Assert(graph.Values.Sum(Function(n) n.DependedOnBy.Count) = graph.Values.Sum(Function(n) n.Dependencies.Count))
End Sub
Private Shared Sub OrderGraph(graph As Dictionary(Of SourceFieldSymbol, DependencyInfo), order As ArrayBuilder(Of FieldInfo))
Debug.Assert(graph.Count > 0)
Dim lastUpdated As PooledHashSet(Of SourceFieldSymbol) = Nothing
Dim fieldsInvolvedInCycles As ArrayBuilder(Of SourceFieldSymbol) = Nothing
While graph.Count > 0
' Get the set of fields in the graph that have no dependencies.
Dim search = If(DirectCast(lastUpdated, IEnumerable(Of SourceFieldSymbol)), graph.Keys)
Dim [set] = ArrayBuilder(Of SourceFieldSymbol).GetInstance()
For Each field In search
Dim node As DependencyInfo = Nothing
If graph.TryGetValue(field, node) AndAlso node.Dependencies.Count = 0 Then
[set].Add(field)
End If
Next
lastUpdated?.Free()
lastUpdated = Nothing
If [set].Count > 0 Then
Dim updated = PooledHashSet(Of SourceFieldSymbol).GetInstance()
' Remove fields with no dependencies from the graph.
For Each field In [set]
Dim node = graph(field)
' Remove the field from the Dependencies
' of each field that depends on it.
For Each dependedOnBy In node.DependedOnBy
Dim n = graph(dependedOnBy)
n.Dependencies = n.Dependencies.Remove(field)
graph(dependedOnBy) = n
updated.Add(dependedOnBy)
Next
graph.Remove(field)
Next
CheckGraph(graph)
' Add the set to the ordered list.
For Each item In [set]
order.Add(New FieldInfo(item, startsCycle:=False))
Next
lastUpdated = updated
Else
' All fields have dependencies which means all fields are involved
' in cycles. Break the first cycle found. (Note some fields may have
' dependencies but are not strictly part of any cycle. For instance,
' B And C in: "Enum E : A = A + B : B = C : C = D : D = D : End Enum").
Dim field = GetStartOfFirstCycle(graph, fieldsInvolvedInCycles)
' Break the dependencies.
Dim node = graph(field)
' Remove the field from the DependedOnBy
' of each field it has as a dependency.
For Each dependency In node.Dependencies
Dim n = graph(dependency)
n.DependedOnBy = n.DependedOnBy.Remove(field)
graph(dependency) = n
Next
node = graph(field)
Dim updated = PooledHashSet(Of SourceFieldSymbol).GetInstance()
' Remove the field from the Dependencies
' of each field that depends on it.
For Each dependedOnBy In node.DependedOnBy
Dim n = graph(dependedOnBy)
n.Dependencies = n.Dependencies.Remove(field)
graph(dependedOnBy) = n
updated.Add(dependedOnBy)
Next
graph.Remove(field)
CheckGraph(graph)
' Add the start of the cycle to the ordered list.
order.Add(New FieldInfo(field, startsCycle:=True))
lastUpdated = updated
End If
[set].Free()
End While
lastUpdated?.Free()
fieldsInvolvedInCycles?.Free()
End Sub
Private Shared Function GetStartOfFirstCycle(
graph As Dictionary(Of SourceFieldSymbol, DependencyInfo),
ByRef fieldsInvolvedInCycles As ArrayBuilder(Of SourceFieldSymbol)
) As SourceFieldSymbol
Debug.Assert(graph.Count > 0)
If fieldsInvolvedInCycles Is Nothing Then
fieldsInvolvedInCycles = ArrayBuilder(Of SourceFieldSymbol).GetInstance(graph.Count)
' We sort fields that belong to the same compilation by location to process cycles in deterministic order.
' Relative order between compilations is not important, cycles do not cross compilation boundaries.
fieldsInvolvedInCycles.AddRange(graph.Keys.GroupBy(Function(f) f.DeclaringCompilation).
SelectMany(Function(g) g.OrderByDescending(Function(f1, f2) g.Key.CompareSourceLocations(f1.Locations(0), f2.Locations(0)))))
End If
Do
Dim field As SourceFieldSymbol = fieldsInvolvedInCycles.Pop()
If graph.ContainsKey(field) AndAlso IsPartOfCycle(graph, field) Then
Return field
End If
Loop
End Function
Private Shared Function IsPartOfCycle(graph As Dictionary(Of SourceFieldSymbol, DependencyInfo), field As SourceFieldSymbol) As Boolean
Dim [set] = PooledHashSet(Of SourceFieldSymbol).GetInstance()
Dim stack = ArrayBuilder(Of SourceFieldSymbol).GetInstance()
Dim stopAt As SourceFieldSymbol = field
Dim result As Boolean = False
stack.Push(field)
While stack.Count > 0
field = stack.Pop()
Dim node = graph(field)
If node.Dependencies.Contains(stopAt) Then
result = True
Exit While
End If
For Each dependency In node.Dependencies
If [set].Add(dependency) Then
stack.Push(dependency)
End If
Next
End While
stack.Free()
[set].Free()
Return result
End Function
''' <summary>
''' Should be overridden by types utilizing <see cref="GetConstantValueImpl(ConstantFieldsInProgress)"/> helper.
''' </summary>
Protected Overridable Function GetLazyConstantTuple() As EvaluatedConstant
Throw ExceptionUtilities.Unreachable
End Function
''' <summary>
''' Should be overridden by types utilizing <see cref="GetConstantValueImpl(ConstantFieldsInProgress)"/> helper.
''' </summary>
Protected Overridable Sub SetLazyConstantTuple(constantTuple As EvaluatedConstant, diagnostics As BindingDiagnosticBag)
Throw ExceptionUtilities.Unreachable
End Sub
''' <summary>
''' Should be overridden by types utilizing <see cref="GetConstantValueImpl(ConstantFieldsInProgress)"/> helper.
''' </summary>
Protected Overridable Function MakeConstantTuple(dependencies As ConstantFieldsInProgress.Dependencies, diagnostics As BindingDiagnosticBag) As EvaluatedConstant
Throw ExceptionUtilities.Unreachable
End Function
Public Overrides ReadOnly Property IsShared As Boolean
Get
Return (m_memberFlags And SourceMemberFlags.Shared) <> 0
End Get
End Property
Public Overrides ReadOnly Property IsImplicitlyDeclared As Boolean
Get
Return _containingType.AreMembersImplicitlyDeclared
End Get
End Property
Friend Overrides ReadOnly Property ShadowsExplicitly As Boolean
Get
Return (m_memberFlags And SourceMemberFlags.Shadows) <> 0
End Get
End Property
Friend Overrides Function GetLexicalSortKey() As LexicalSortKey
' WARNING: this should not allocate memory!
Return New LexicalSortKey(_syntaxRef, Me.DeclaringCompilation)
End Function
Public Overrides ReadOnly Property Locations As ImmutableArray(Of Location)
Get
Return ImmutableArray.Create(Of Location)(GetSymbolLocation(_syntaxRef))
End Get
End Property
Public Overrides ReadOnly Property DeclaringSyntaxReferences As ImmutableArray(Of SyntaxReference)
Get
Return GetDeclaringSyntaxReferenceHelper(_syntaxRef)
End Get
End Property
Friend MustOverride ReadOnly Property GetAttributeDeclarations() As OneOrMany(Of SyntaxList(Of AttributeListSyntax))
Public ReadOnly Property DefaultAttributeLocation As AttributeLocation Implements IAttributeTargetSymbol.DefaultAttributeLocation
Get
Return AttributeLocation.Field
End Get
End Property
''' <summary>
''' Gets the attributes applied on this symbol.
''' Returns an empty array if there are no attributes.
''' </summary>
''' <remarks>
''' NOTE: This method should always be kept as a NotOverridable method.
''' If you want to override attribute binding logic for a sub-class, then override <see cref="GetAttributesBag"/> method.
''' </remarks>
Public NotOverridable Overloads Overrides Function GetAttributes() As ImmutableArray(Of VisualBasicAttributeData)
Return Me.GetAttributesBag().Attributes
End Function
Private Function GetAttributesBag() As CustomAttributesBag(Of VisualBasicAttributeData)
If _lazyCustomAttributesBag Is Nothing OrElse Not _lazyCustomAttributesBag.IsSealed Then
LoadAndValidateAttributes(GetAttributeDeclarations(), _lazyCustomAttributesBag)
End If
Return _lazyCustomAttributesBag
End Function
Private Function GetDecodedWellKnownAttributeData() As CommonFieldWellKnownAttributeData
Dim attributesBag As CustomAttributesBag(Of VisualBasicAttributeData) = Me._lazyCustomAttributesBag
If attributesBag Is Nothing OrElse Not attributesBag.IsDecodedWellKnownAttributeDataComputed Then
attributesBag = Me.GetAttributesBag()
End If
Return DirectCast(attributesBag.DecodedWellKnownAttributeData, CommonFieldWellKnownAttributeData)
End Function
' This should be called at most once after the attributes are bound. Attributes must be bound after the class
' and members are fully declared to avoid infinite recursion.
Friend Sub SetCustomAttributeData(attributeData As CustomAttributesBag(Of VisualBasicAttributeData))
Debug.Assert(attributeData IsNot Nothing)
Debug.Assert(_lazyCustomAttributesBag Is Nothing)
_lazyCustomAttributesBag = attributeData
End Sub
Friend Overrides Sub AddSynthesizedAttributes(moduleBuilder As PEModuleBuilder, ByRef attributes As ArrayBuilder(Of SynthesizedAttributeData))
MyBase.AddSynthesizedAttributes(moduleBuilder, attributes)
If Me.IsConst Then
If Me.GetConstantValue(ConstantFieldsInProgress.Empty) IsNot Nothing Then
Dim data = GetDecodedWellKnownAttributeData()
If data Is Nothing OrElse data.ConstValue = CodeAnalysis.ConstantValue.Unset Then
If Me.Type.SpecialType = SpecialType.System_DateTime Then
Dim attributeValue = DirectCast(Me.ConstantValue, DateTime)
Dim specialTypeInt64 = Me.ContainingAssembly.GetSpecialType(SpecialType.System_Int64)
' NOTE: used from emit, so shouldn't have gotten here if there were errors
Debug.Assert(specialTypeInt64.GetUseSiteInfo().DiagnosticInfo Is Nothing)
Dim compilation = Me.DeclaringCompilation
AddSynthesizedAttribute(attributes, compilation.TrySynthesizeAttribute(
WellKnownMember.System_Runtime_CompilerServices_DateTimeConstantAttribute__ctor,
ImmutableArray.Create(
New TypedConstant(specialTypeInt64, TypedConstantKind.Primitive, attributeValue.Ticks))))
ElseIf Me.Type.SpecialType = SpecialType.System_Decimal Then
Dim attributeValue = DirectCast(Me.ConstantValue, Decimal)
Dim compilation = Me.DeclaringCompilation
AddSynthesizedAttribute(attributes, compilation.SynthesizeDecimalConstantAttribute(attributeValue))
End If
End If
End If
End If
If Me.Type.ContainsTupleNames() Then
AddSynthesizedAttribute(attributes, DeclaringCompilation.SynthesizeTupleNamesAttribute(Type))
End If
End Sub
Friend NotOverridable Overrides Function EarlyDecodeWellKnownAttribute(ByRef arguments As EarlyDecodeWellKnownAttributeArguments(Of EarlyWellKnownAttributeBinder, NamedTypeSymbol, AttributeSyntax, AttributeLocation)) As VisualBasicAttributeData
Debug.Assert(arguments.AttributeType IsNot Nothing)
Debug.Assert(Not arguments.AttributeType.IsErrorType())
Dim BoundAttribute As VisualBasicAttributeData = Nothing
Dim obsoleteData As ObsoleteAttributeData = Nothing
If EarlyDecodeDeprecatedOrExperimentalOrObsoleteAttribute(arguments, BoundAttribute, obsoleteData) Then
If obsoleteData IsNot Nothing Then
arguments.GetOrCreateData(Of CommonFieldEarlyWellKnownAttributeData)().ObsoleteAttributeData = obsoleteData
End If
Return BoundAttribute
End If
Return MyBase.EarlyDecodeWellKnownAttribute(arguments)
End Function
Friend NotOverridable Overrides Sub DecodeWellKnownAttribute(ByRef arguments As DecodeWellKnownAttributeArguments(Of AttributeSyntax, VisualBasicAttributeData, AttributeLocation))
Debug.Assert(arguments.AttributeSyntaxOpt IsNot Nothing)
Dim attrData = arguments.Attribute
Debug.Assert(arguments.SymbolPart = AttributeLocation.None)
Dim diagnostics = DirectCast(arguments.Diagnostics, BindingDiagnosticBag)
If attrData.IsTargetAttribute(AttributeDescription.TupleElementNamesAttribute) Then
diagnostics.Add(ERRID.ERR_ExplicitTupleElementNamesAttribute, arguments.AttributeSyntaxOpt.Location)
End If
If attrData.IsTargetAttribute(AttributeDescription.SpecialNameAttribute) Then
arguments.GetOrCreateData(Of CommonFieldWellKnownAttributeData)().HasSpecialNameAttribute = True
ElseIf attrData.IsTargetAttribute(AttributeDescription.NonSerializedAttribute) Then
If Me.ContainingType.IsSerializable Then
arguments.GetOrCreateData(Of CommonFieldWellKnownAttributeData)().HasNonSerializedAttribute = True
Else
diagnostics.Add(ERRID.ERR_InvalidNonSerializedUsage, arguments.AttributeSyntaxOpt.GetLocation())
End If
ElseIf attrData.IsTargetAttribute(AttributeDescription.FieldOffsetAttribute) Then
Dim offset = attrData.CommonConstructorArguments(0).DecodeValue(Of Integer)(SpecialType.System_Int32)
If offset < 0 Then
diagnostics.Add(ERRID.ERR_BadAttribute1, VisualBasicAttributeData.GetFirstArgumentLocation(arguments.AttributeSyntaxOpt), attrData.AttributeClass)
offset = 0
End If
arguments.GetOrCreateData(Of CommonFieldWellKnownAttributeData)().SetFieldOffset(offset)
ElseIf attrData.IsTargetAttribute(AttributeDescription.MarshalAsAttribute) Then
MarshalAsAttributeDecoder(Of CommonFieldWellKnownAttributeData, AttributeSyntax, VisualBasicAttributeData, AttributeLocation).Decode(arguments, AttributeTargets.Field, MessageProvider.Instance)
ElseIf attrData.IsTargetAttribute(AttributeDescription.DateTimeConstantAttribute) Then
VerifyConstantValueMatches(attrData.DecodeDateTimeConstantValue(), arguments)
ElseIf attrData.IsTargetAttribute(AttributeDescription.DecimalConstantAttribute) Then
VerifyConstantValueMatches(attrData.DecodeDecimalConstantValue(), arguments)
Else
MyBase.DecodeWellKnownAttribute(arguments)
End If
End Sub
''' <summary>
''' Verify the constant value matches the default value from any earlier attribute
''' (DateTimeConstantAttribute or DecimalConstantAttribute).
''' If not, report ERR_FieldHasMultipleDistinctConstantValues.
''' </summary>
Private Sub VerifyConstantValueMatches(attrValue As ConstantValue, ByRef arguments As DecodeWellKnownAttributeArguments(Of AttributeSyntax, VisualBasicAttributeData, AttributeLocation))
Dim data = arguments.GetOrCreateData(Of CommonFieldWellKnownAttributeData)()
Dim constValue As ConstantValue
Dim diagnostics = DirectCast(arguments.Diagnostics, BindingDiagnosticBag)
If Me.IsConst Then
If Me.Type.IsDecimalType() OrElse Me.Type.IsDateTimeType() Then
constValue = Me.GetConstantValue(ConstantFieldsInProgress.Empty)
If constValue IsNot Nothing AndAlso Not constValue.IsBad AndAlso constValue <> attrValue Then
diagnostics.Add(ERRID.ERR_FieldHasMultipleDistinctConstantValues, arguments.AttributeSyntaxOpt.GetLocation())
End If
Else
diagnostics.Add(ERRID.ERR_FieldHasMultipleDistinctConstantValues, arguments.AttributeSyntaxOpt.GetLocation())
End If
If data.ConstValue = CodeAnalysis.ConstantValue.Unset Then
data.ConstValue = attrValue
End If
Else
constValue = data.ConstValue
If constValue <> CodeAnalysis.ConstantValue.Unset Then
If constValue <> attrValue Then
diagnostics.Add(ERRID.ERR_FieldHasMultipleDistinctConstantValues, arguments.AttributeSyntaxOpt.GetLocation())
End If
Else
data.ConstValue = attrValue
End If
End If
End Sub
Friend NotOverridable Overrides ReadOnly Property HasSpecialName As Boolean
Get
If HasRuntimeSpecialName Then
Return True
End If
Dim data = GetDecodedWellKnownAttributeData()
Return data IsNot Nothing AndAlso data.HasSpecialNameAttribute
End Get
End Property
Friend NotOverridable Overrides ReadOnly Property HasRuntimeSpecialName As Boolean
Get
Return Name = WellKnownMemberNames.EnumBackingFieldName
End Get
End Property
Friend NotOverridable Overrides ReadOnly Property IsNotSerialized As Boolean
Get
Dim data = GetDecodedWellKnownAttributeData()
Return data IsNot Nothing AndAlso data.HasNonSerializedAttribute
End Get
End Property
Friend NotOverridable Overrides ReadOnly Property MarshallingInformation As MarshalPseudoCustomAttributeData
Get
Dim data = GetDecodedWellKnownAttributeData()
Return If(data IsNot Nothing, data.MarshallingInformation, Nothing)
End Get
End Property
Friend NotOverridable Overrides ReadOnly Property TypeLayoutOffset As Integer?
Get
Dim data = GetDecodedWellKnownAttributeData()
Return If(data IsNot Nothing, data.Offset, Nothing)
End Get
End Property
Friend NotOverridable Overrides ReadOnly Property ObsoleteAttributeData As ObsoleteAttributeData
Get
' If there are no attributes then this symbol is not Obsolete.
If (Not Me._containingType.AnyMemberHasAttributes) Then
Return Nothing
End If
Dim lazyCustomAttributesBag = Me._lazyCustomAttributesBag
If (lazyCustomAttributesBag IsNot Nothing AndAlso lazyCustomAttributesBag.IsEarlyDecodedWellKnownAttributeDataComputed) Then
Dim data = DirectCast(_lazyCustomAttributesBag.EarlyDecodedWellKnownAttributeData, CommonFieldEarlyWellKnownAttributeData)
Return If(data IsNot Nothing, data.ObsoleteAttributeData, Nothing)
End If
Return ObsoleteAttributeData.Uninitialized
End Get
End Property
' Given a syntax ref, get the symbol location to return. We return the location of the name
' of the method.
Private Shared Function GetSymbolLocation(syntaxRef As SyntaxReference) As Location
Dim syntaxNode = syntaxRef.GetSyntax()
Dim syntaxTree = syntaxRef.SyntaxTree
Return syntaxTree.GetLocation(GetFieldLocationFromSyntax(DirectCast(syntaxNode, ModifiedIdentifierSyntax).Identifier))
End Function
' Get the location of a field given the syntax for its modified identifier. We use the span of the base part
' of the identifier.
Private Shared Function GetFieldLocationFromSyntax(node As SyntaxToken) As TextSpan
Return node.Span
End Function
' Given the syntax declaration, and a container, get the field or WithEvents property symbol declared from that syntax.
' This is done by lookup up the name from the declaration in the container, handling duplicates and
' so forth correctly.
Friend Shared Function FindFieldOrWithEventsSymbolFromSyntax(variableName As SyntaxToken,
tree As SyntaxTree,
container As NamedTypeSymbol) As Symbol
Dim fieldName As String = variableName.ValueText
Dim nameSpan As TextSpan = GetFieldLocationFromSyntax(variableName)
Return container.FindFieldOrProperty(fieldName, nameSpan, tree)
End Function
End Class
End Namespace
|