| File: Symbols\Source\QuickAttributeChecker.vb | Web Access |
| Project: src\roslyn\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic) |
' 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 Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ''' <summary> ''' The QuickAttributeChecker applies a simple fast heuristic for determining probable ''' attributes without binding attribute types, just by looking at the final syntax of an ''' attribute usage. It is accessed via the QuickAttributeChecker property on Binder. ''' </summary> ''' <remarks> ''' It works by maintaining a dictionary of all possible simple names that might map to a particular ''' attribute. ''' </remarks> Friend Class QuickAttributeChecker ' Dictionary mapping a name to quick attribute(s) Private ReadOnly _nameToAttributeMap As Dictionary(Of String, QuickAttributes) ' If true, can no longer add new names. Private _sealed As Boolean Public Sub New() _nameToAttributeMap = New Dictionary(Of String, QuickAttributes)(IdentifierComparison.Comparer) End Sub Public Sub New(other As QuickAttributeChecker) _nameToAttributeMap = New Dictionary(Of String, QuickAttributes)(other._nameToAttributeMap, IdentifierComparison.Comparer) End Sub ''' <summary> ''' Add a mapping from name to some attributes. ''' </summary> Public Sub AddName(name As String, newAttributes As QuickAttributes) Debug.Assert(Not _sealed) Dim current = QuickAttributes.None _nameToAttributeMap.TryGetValue(name, current) _nameToAttributeMap(name) = newAttributes Or current ' We allow "Goo" to bind to "GooAttribute". If name.EndsWith("Attribute", StringComparison.OrdinalIgnoreCase) Then _nameToAttributeMap(name.Substring(0, name.Length - "Attribute".Length)) = newAttributes Or current End If End Sub ''' <summary> ''' Process an alias clause and any imported mappings from it. ''' E.g., If you have an alias Ex=Blah.Extension, add any mapping for Extension to those for Ex. ''' Note that although, in VB, an alias cannot reference another alias, this code doesn't not attempt ''' to distinguish between aliases and regular names, as that would add complexity to the data structure ''' and would be unlikely to matter. This entire class is probabilistic anyone and is only used for quick ''' checks. ''' </summary> Public Sub AddAlias(aliasSyntax As SimpleImportsClauseSyntax) Debug.Assert(Not _sealed) Debug.Assert(aliasSyntax.Alias IsNot Nothing) Dim finalName = GetFinalName(aliasSyntax.Name) If finalName IsNot Nothing Then Dim current As QuickAttributes = QuickAttributes.None If _nameToAttributeMap.TryGetValue(finalName, current) Then AddName(aliasSyntax.Alias.Identifier.ValueText, current) End If End If End Sub Public Sub Seal() _sealed = True End Sub ''' <summary> ''' Check attribute lists quickly to see what attributes might be referenced. ''' </summary> Public Function CheckAttributes(attributeLists As SyntaxList(Of AttributeListSyntax)) As QuickAttributes Debug.Assert(_sealed) Dim quickAttrs As QuickAttributes = QuickAttributes.None If attributeLists.Count > 0 Then For Each attrList In attributeLists For Each attr In attrList.Attributes quickAttrs = quickAttrs Or CheckAttribute(attr) Next Next End If Return quickAttrs End Function Public Function CheckAttribute(attr As AttributeSyntax) As QuickAttributes Dim attrTypeSyntax = attr.Name Dim finalName = GetFinalName(attrTypeSyntax) If finalName IsNot Nothing Then Dim quickAttributes As QuickAttributes If _nameToAttributeMap.TryGetValue(finalName, quickAttributes) Then Return quickAttributes End If End If Return QuickAttributes.None End Function ' Return the last name in a TypeSyntax, or Nothing if there isn't one. Public Shared Function GetFinalName(typeSyntax As TypeSyntax) As String Dim node As VisualBasicSyntaxNode = typeSyntax Do Select Case node.Kind Case SyntaxKind.IdentifierName Return DirectCast(node, IdentifierNameSyntax).Identifier.ValueText Case SyntaxKind.QualifiedName node = DirectCast(node, QualifiedNameSyntax).Right Case Else Return Nothing End Select Loop End Function End Class ''' <summary> ''' Indicate which attributes might be present. Could be extended to other attributes ''' if desired. ''' </summary> <Flags> Friend Enum QuickAttributes As Byte None = 0 Extension = 1 << 0 Obsolete = 1 << 1 MyGroupCollection = 1 << 2 TypeIdentifier = 1 << 3 Last = TypeIdentifier End Enum Friend Class QuickAttributeHelpers ''' <summary> ''' Returns the <see cref="QuickAttributes"/> that corresponds to the particular type ''' <paramref name="name"/> passed in. If <paramref name="inAttribute"/> Is <see langword="true"/> ''' then the name will be checked both as-Is as well as with the 'Attribute' suffix. ''' </summary> Public Shared Function GetQuickAttributes(name As String, inAttribute As Boolean) As QuickAttributes ' Update this code if we add New quick attributes. Debug.Assert(QuickAttributes.Last = QuickAttributes.TypeIdentifier) Dim result = QuickAttributes.None If Matches(name, inAttribute, AttributeDescription.CaseInsensitiveExtensionAttribute) Then result = result Or QuickAttributes.Extension ElseIf Matches(name, inAttribute, AttributeDescription.ObsoleteAttribute) Then result = result Or QuickAttributes.Obsolete ElseIf Matches(name, inAttribute, AttributeDescription.DeprecatedAttribute) Then result = result Or QuickAttributes.Obsolete ElseIf Matches(name, inAttribute, AttributeDescription.ExperimentalAttribute) Then result = result Or QuickAttributes.Obsolete ElseIf Matches(name, inAttribute, AttributeDescription.MyGroupCollectionAttribute) Then result = result Or QuickAttributes.TypeIdentifier ElseIf Matches(name, inAttribute, AttributeDescription.TypeIdentifierAttribute) Then result = result Or QuickAttributes.MyGroupCollection End If Return result End Function Private Shared Function Matches(name As String, inAttribute As Boolean, description As AttributeDescription) As Boolean Debug.Assert(description.Name.EndsWith(NameOf(System.Attribute))) If IdentifierComparison.Comparer.Equals(name, description.Name) Then Return True End If ' In an attribute context the name might be referenced as the full name (Like 'TypeForwardedToAttribute') ' Or the short name (Like 'TypeForwardedTo'). If inAttribute AndAlso (name.Length + NameOf(Attribute).Length) = description.Name.Length AndAlso description.Name.StartsWith(name, StringComparison.OrdinalIgnoreCase) Then Return True End If Return False End Function End Class End Namespace