|
' 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.Threading
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic
''' <summary>
''' Data for Binder.BindImportClause that exposes dictionaries of
''' the members and aliases that have been bound during the
''' execution of BindImportClause. It is the responsibility of derived
''' classes to update the dictionaries in AddMember and AddAlias.
''' </summary>
Friend MustInherit Class ImportData
Protected Sub New(members As HashSet(Of NamespaceOrTypeSymbol),
aliases As Dictionary(Of String, AliasAndImportsClausePosition),
xmlNamespaces As Dictionary(Of String, XmlNamespaceAndImportsClausePosition))
Me.Members = members
Me.Aliases = aliases
Me.XmlNamespaces = xmlNamespaces
End Sub
Public ReadOnly Members As HashSet(Of NamespaceOrTypeSymbol)
Public ReadOnly Aliases As Dictionary(Of String, AliasAndImportsClausePosition)
Public ReadOnly XmlNamespaces As Dictionary(Of String, XmlNamespaceAndImportsClausePosition)
Public MustOverride Sub AddMember(syntaxRef As SyntaxReference, member As NamespaceOrTypeSymbol, importsClausePosition As Integer, dependencies As IReadOnlyCollection(Of AssemblySymbol), isProjectImportsDeclaration As Boolean)
Public MustOverride Sub AddAlias(syntaxRef As SyntaxReference, name As String, [alias] As AliasSymbol, importsClausePosition As Integer, dependencies As IReadOnlyCollection(Of AssemblySymbol))
End Class
Partial Friend Class Binder
' Bind one import clause, add it to the correct collection of imports.
' Warnings and errors are emitted to the diagnostic bag, including detecting duplicates.
' Note that this binder should have been already been set up to only bind to things in the global namespace.
Public Sub BindImportClause(importClauseSyntax As ImportsClauseSyntax,
data As ImportData,
diagBag As DiagnosticBag)
ImportsBinder.BindImportClause(importClauseSyntax, Me, data, diagBag)
End Sub
''' <summary>
''' The Imports binder handles binding of Imports statements in files, and also the project-level imports.
''' </summary>
Private Class ImportsBinder
' Bind one import clause, add it to the correct collection of imports.
' Warnings and errors are emitted to the diagnostic bag, including detecting duplicates.
' Note that the binder should have been already been set up to only bind to things in the global namespace.
Public Shared Sub BindImportClause(importClauseSyntax As ImportsClauseSyntax,
binder As Binder,
data As ImportData,
diagBag As DiagnosticBag)
Select Case importClauseSyntax.Kind
Case SyntaxKind.SimpleImportsClause
Dim simpleImportsClause = DirectCast(importClauseSyntax, SimpleImportsClauseSyntax)
If simpleImportsClause.Alias Is Nothing Then
BindMembersImportsClause(simpleImportsClause, binder, data, diagBag)
Else
BindAliasImportsClause(simpleImportsClause, binder, data, diagBag)
End If
Case SyntaxKind.XmlNamespaceImportsClause
BindXmlNamespaceImportsClause(DirectCast(importClauseSyntax, XmlNamespaceImportsClauseSyntax), binder, data, diagBag)
Case Else
End Select
End Sub
' Bind an alias imports clause. If it is OK, and also unique, add it to the dictionary.
Private Shared Sub BindAliasImportsClause(aliasImportSyntax As SimpleImportsClauseSyntax,
binder As Binder,
data As ImportData,
diagnostics As DiagnosticBag)
Dim diagBag = BindingDiagnosticBag.GetInstance()
Dim aliasesName = aliasImportSyntax.Name
Dim aliasTarget As NamespaceOrTypeSymbol = binder.BindNamespaceOrTypeSyntax(aliasesName, diagBag)
If aliasTarget.Kind <> SymbolKind.Namespace Then
Dim type = TryCast(aliasTarget, TypeSymbol)
If type Is Nothing OrElse type.IsDelegateType Then
Binder.ReportDiagnostic(diagBag,
aliasImportSyntax,
ERRID.ERR_InvalidTypeForAliasesImport2,
aliasTarget,
aliasTarget.Name)
End If
End If
Dim aliasIdentifier = aliasImportSyntax.Alias.Identifier
Dim aliasText = aliasIdentifier.ValueText
' Parser checks for type characters on alias text, so don't need to check again here.
' Check for duplicate symbol.
If data.Aliases.ContainsKey(aliasText) Then
Binder.ReportDiagnostic(diagBag, aliasIdentifier, ERRID.ERR_DuplicateNamedImportAlias1, aliasText)
Else
' Make sure that the Import's alias doesn't have the same name as a type or a namespace in the global namespace
Dim conflictsWith = binder.Compilation.GlobalNamespace.GetMembers(aliasText)
If Not conflictsWith.IsEmpty Then
' TODO: Note that symbol's name in this error message is supposed to include Class/Namespace word at the beginning.
' Might need to use special format for that parameter in the error message.
Binder.ReportDiagnostic(diagBag,
aliasImportSyntax,
ERRID.ERR_ImportAliasConflictsWithType2,
aliasText,
conflictsWith(0))
Else
If aliasTarget.Kind <> SymbolKind.ErrorType Then
Dim useSiteInfo As UseSiteInfo(Of AssemblySymbol) = aliasTarget.GetUseSiteInfo()
If ShouldReportUseSiteErrorForAlias(useSiteInfo.DiagnosticInfo) Then
Binder.ReportUseSite(diagBag, aliasImportSyntax, useSiteInfo)
Else
diagBag.AddDependencies(useSiteInfo)
End If
Else
diagBag.DependenciesBag.Clear()
End If
' We create the alias symbol even when the target is erroneous,
' so that we can bind to the alias and avoid cascading errors.
' As a result the further consumers of the aliases have to account for the error case.
Dim aliasSymbol = New AliasSymbol(binder.Compilation,
binder.ContainingNamespaceOrType,
aliasText,
aliasTarget,
If(binder.BindingLocation = BindingLocation.ProjectImportsDeclaration, NoLocation.Singleton, aliasIdentifier.GetLocation()))
data.AddAlias(binder.GetSyntaxReference(aliasImportSyntax), aliasText, aliasSymbol, aliasImportSyntax.SpanStart, DirectCast(diagBag.DependenciesBag, IReadOnlyCollection(Of AssemblySymbol)))
End If
End If
diagnostics.AddRange(diagBag.DiagnosticBag)
diagBag.Free()
End Sub
''' <summary>
''' Checks use site error and returns True in case it should be reported for the alias.
''' In current implementation checks for errors #36924 and #36925
''' </summary>
Private Shared Function ShouldReportUseSiteErrorForAlias(useSiteErrorInfo As DiagnosticInfo) As Boolean
Return useSiteErrorInfo IsNot Nothing AndAlso
useSiteErrorInfo.Code <> ERRID.ERR_CannotUseGenericTypeAcrossAssemblyBoundaries AndAlso
useSiteErrorInfo.Code <> ERRID.ERR_CannotUseGenericBaseTypeAcrossAssemblyBoundaries
End Function
' Bind a members imports clause. If it is OK, and also unique, add it to the members imports set.
Private Shared Sub BindMembersImportsClause(membersImportsSyntax As SimpleImportsClauseSyntax,
binder As Binder,
data As ImportData,
diagnostics As DiagnosticBag)
Dim diagBag = BindingDiagnosticBag.GetInstance()
Debug.Assert(membersImportsSyntax.Alias Is Nothing)
Dim importsName = membersImportsSyntax.Name
Dim importedSymbol As NamespaceOrTypeSymbol = binder.BindNamespaceOrTypeSyntax(importsName, diagBag)
If importedSymbol.Kind <> SymbolKind.Namespace Then
Dim type = TryCast(importedSymbol, TypeSymbol)
' Non-aliased interface imports are disallowed
If type Is Nothing OrElse type.IsDelegateType OrElse type.IsInterfaceType Then
Binder.ReportDiagnostic(diagBag,
membersImportsSyntax,
ERRID.ERR_NonNamespaceOrClassOnImport2,
importedSymbol,
importedSymbol.Name)
End If
End If
If importedSymbol.Kind <> SymbolKind.ErrorType Then
' Check for duplicate symbol.
If data.Members.Contains(importedSymbol) Then
' NOTE: Dev10 doesn't report this error for project level imports. We still
' generate the error but filter it out when bind project level imports
Binder.ReportDiagnostic(diagBag, importsName, ERRID.ERR_DuplicateImport1, importedSymbol)
Else
Dim importedSymbolIsValid As Boolean = True
' Disallow importing different instantiations of the same generic type.
If importedSymbol.Kind = SymbolKind.NamedType Then
Dim namedType = DirectCast(importedSymbol, NamedTypeSymbol)
If namedType.IsGenericType Then
namedType = namedType.OriginalDefinition
For Each contender In data.Members
If contender.OriginalDefinition Is namedType Then
importedSymbolIsValid = False
Binder.ReportDiagnostic(diagBag, importsName, ERRID.ERR_DuplicateRawGenericTypeImport1, namedType)
Exit For
End If
Next
End If
End If
If importedSymbolIsValid Then
data.AddMember(binder.GetSyntaxReference(importsName), importedSymbol, membersImportsSyntax.SpanStart, DirectCast(diagBag.DependenciesBag, IReadOnlyCollection(Of AssemblySymbol)), binder.BindingLocation = BindingLocation.ProjectImportsDeclaration)
End If
End If
End If
diagnostics.AddRange(diagBag.DiagnosticBag)
diagBag.Free()
End Sub
Private Shared Sub BindXmlNamespaceImportsClause(syntax As XmlNamespaceImportsClauseSyntax,
binder As Binder,
data As ImportData,
diagnostics As DiagnosticBag)
#If DEBUG Then
Dim diagBag = BindingDiagnosticBag.GetInstance()
#Else
Dim diagBag = BindingDiagnosticBag.GetInstance(withDiagnostics:=True, withDependencies:=False)
#End If
Dim prefix As String = Nothing
Dim namespaceName As String = Nothing
Dim [namespace] As BoundExpression = Nothing
Dim hasErrors As Boolean = False
If binder.TryGetXmlnsAttribute(syntax.XmlNamespace, prefix, namespaceName, [namespace], hasErrors, fromImport:=True, diagnostics:=diagBag) AndAlso
Not hasErrors Then
Debug.Assert([namespace] Is Nothing) ' Binding should have been skipped.
If data.XmlNamespaces.ContainsKey(prefix) Then
' "XML namespace prefix '{0}' is already declared."
Binder.ReportDiagnostic(diagBag, syntax, ERRID.ERR_DuplicatePrefix, prefix)
Else
' Do not expose any locations for project level xml namespaces. This matches the effective
' logic we have for aliases, which are given NoLocation.Singleton (which never translates to a
' DeclaringSyntaxReference).
Dim reference = If(binder.BindingLocation = BindingLocation.ProjectImportsDeclaration,
Nothing,
binder.GetSyntaxReference(syntax))
data.XmlNamespaces.Add(prefix, New XmlNamespaceAndImportsClausePosition(namespaceName, syntax.SpanStart, reference))
End If
End If
#If DEBUG Then
Debug.Assert(diagBag.DependenciesBag.Count = 0)
#End If
diagnostics.AddRange(diagBag.DiagnosticBag)
diagBag.Free()
End Sub
End Class
End Class
End Namespace
|