File: CommandLine\VisualBasicCompiler.vb
Web Access
Project: src\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.Immutable
Imports System.IO
Imports System.Threading
Imports Microsoft.CodeAnalysis.Collections
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic
 
    Friend MustInherit Class VisualBasicCompiler
        Inherits CommonCompiler
 
        Friend Const ResponseFileName As String = "vbc.rsp"
        Friend Const VbcCommandLinePrefix = "vbc : " 'Common prefix String For VB diagnostic output with no location.
        Private ReadOnly _diagnosticFormatter As CommandLineDiagnosticFormatter
        Private ReadOnly _tempDirectory As String
        Private _additionalTextFiles As ImmutableArray(Of AdditionalTextFile)
 
        Protected Sub New(parser As VisualBasicCommandLineParser, responseFile As String, args As String(), buildPaths As BuildPaths, additionalReferenceDirectories As String, analyzerLoader As IAnalyzerAssemblyLoader, Optional driverCache As GeneratorDriverCache = Nothing, Optional fileSystem As ICommonCompilerFileSystem = Nothing)
            MyBase.New(parser, responseFile, args, buildPaths, additionalReferenceDirectories, analyzerLoader, driverCache, fileSystem)
 
            _diagnosticFormatter = New CommandLineDiagnosticFormatter(buildPaths.WorkingDirectory, AddressOf GetAdditionalTextFiles)
            _additionalTextFiles = Nothing
            _tempDirectory = buildPaths.TempDirectory
 
            Debug.Assert(Arguments.OutputFileName IsNot Nothing OrElse Arguments.Errors.Length > 0 OrElse parser.IsScriptCommandLineParser)
        End Sub
 
        Private Function GetAdditionalTextFiles() As ImmutableArray(Of AdditionalTextFile)
            Debug.Assert(Not _additionalTextFiles.IsDefault, "GetAdditionalTextFiles called before ResolveAdditionalFilesFromArguments")
            Return _additionalTextFiles
        End Function
 
        Protected Overrides Function ResolveAdditionalFilesFromArguments(diagnostics As List(Of DiagnosticInfo), messageProvider As CommonMessageProvider, touchedFilesLogger As TouchedFileLogger) As ImmutableArray(Of AdditionalTextFile)
            _additionalTextFiles = MyBase.ResolveAdditionalFilesFromArguments(diagnostics, messageProvider, touchedFilesLogger)
            Return _additionalTextFiles
        End Function
 
        Friend Overloads ReadOnly Property Arguments As VisualBasicCommandLineArguments
            Get
                Return DirectCast(MyBase.Arguments, VisualBasicCommandLineArguments)
            End Get
        End Property
 
        Public Overrides ReadOnly Property DiagnosticFormatter As DiagnosticFormatter
            Get
                Return _diagnosticFormatter
            End Get
        End Property
 
        Private Function ParseFile(consoleOutput As TextWriter,
                                   parseOptions As VisualBasicParseOptions,
                                   scriptParseOptions As VisualBasicParseOptions,
                                   ByRef hadErrors As Boolean,
                                   file As CommandLineSourceFile,
                                   errorLogger As ErrorLogger) As SyntaxTree
 
            Dim fileReadDiagnostics As New List(Of DiagnosticInfo)()
            Dim content = TryReadFileContent(file, fileReadDiagnostics)
 
            If content Is Nothing Then
                ReportDiagnostics(fileReadDiagnostics, consoleOutput, errorLogger, compilation:=Nothing)
                fileReadDiagnostics.Clear()
                hadErrors = True
                Return Nothing
            End If
 
            Dim tree = VisualBasicSyntaxTree.ParseText(
                content,
                If(file.IsScript, scriptParseOptions, parseOptions),
                file.Path)
 
            ' prepopulate line tables.
            ' we will need line tables anyways and it is better to Not wait until we are in emit
            ' where things run sequentially.
            Dim isHiddenDummy As Boolean
            tree.GetMappedLineSpanAndVisibility(Nothing, isHiddenDummy)
 
            Return tree
        End Function
 
        Public Overrides Function CreateCompilation(consoleOutput As TextWriter,
                                                    touchedFilesLogger As TouchedFileLogger,
                                                    errorLogger As ErrorLogger,
                                                    analyzerConfigOptions As ImmutableArray(Of AnalyzerConfigOptionsResult),
                                                    globalAnalyzerConfigOptions As AnalyzerConfigOptionsResult) As Compilation
            Dim parseOptions = Arguments.ParseOptions
 
            ' We compute script parse options once so we don't have to do it repeatedly in
            ' case there are many script files.
            Dim scriptParseOptions = parseOptions.WithKind(SourceCodeKind.Script)
 
            Dim hadErrors As Boolean = False
 
            Dim sourceFiles As ImmutableArray(Of CommandLineSourceFile) = Arguments.SourceFiles
            Dim trees(sourceFiles.Length - 1) As SyntaxTree
 
            If Arguments.CompilationOptions.ConcurrentBuild Then
                RoslynParallel.For(
                    0,
                    sourceFiles.Length,
                    UICultureUtilities.WithCurrentUICulture(Of Integer)(
                        Sub(i As Integer)
                            ' NOTE: order of trees is important!!
                            trees(i) = ParseFile(
                                consoleOutput,
                                parseOptions,
                                scriptParseOptions,
                                hadErrors,
                                sourceFiles(i),
                                errorLogger)
                        End Sub),
                    CancellationToken.None)
            Else
                For i = 0 To sourceFiles.Length - 1
                    ' NOTE: order of trees is important!!
                    trees(i) = ParseFile(
                        consoleOutput,
                        parseOptions,
                        scriptParseOptions,
                        hadErrors,
                        sourceFiles(i),
                        errorLogger)
                Next
            End If
 
            If hadErrors Then
                Return Nothing
            End If
 
            If Arguments.TouchedFilesPath IsNot Nothing Then
                For Each file In sourceFiles
                    touchedFilesLogger.AddRead(file.Path)
                Next
            End If
 
            Dim diagnostics = New List(Of DiagnosticInfo)()
 
            Dim assemblyIdentityComparer = DesktopAssemblyIdentityComparer.Default
 
            Dim referenceDirectiveResolver As MetadataReferenceResolver = Nothing
            Dim resolvedReferences = ResolveMetadataReferences(diagnostics, touchedFilesLogger, referenceDirectiveResolver)
 
            If ReportDiagnostics(diagnostics, consoleOutput, errorLogger, compilation:=Nothing) Then
                Return Nothing
            End If
 
            If Arguments.OutputLevel = OutputLevel.Verbose Then
                PrintReferences(resolvedReferences, consoleOutput)
            End If
 
            Dim xmlFileResolver = New LoggingXmlFileResolver(Arguments.BaseDirectory, touchedFilesLogger)
 
            ' TODO: support for #load search paths
            Dim sourceFileResolver = New LoggingSourceFileResolver(ImmutableArray(Of String).Empty, Arguments.BaseDirectory, Arguments.PathMap, touchedFilesLogger)
 
            Dim loggingFileSystem = New LoggingStrongNameFileSystem(touchedFilesLogger, _tempDirectory)
            Dim syntaxTreeOptions = New CompilerSyntaxTreeOptionsProvider(trees, analyzerConfigOptions, globalAnalyzerConfigOptions)
 
            Return VisualBasicCompilation.Create(
                 Arguments.CompilationName,
                 trees,
                 resolvedReferences,
                 Arguments.CompilationOptions.
                     WithMetadataReferenceResolver(referenceDirectiveResolver).
                     WithAssemblyIdentityComparer(assemblyIdentityComparer).
                     WithXmlReferenceResolver(xmlFileResolver).
                     WithStrongNameProvider(Arguments.GetStrongNameProvider(loggingFileSystem)).
                     WithSourceReferenceResolver(sourceFileResolver).
                     WithSyntaxTreeOptionsProvider(syntaxTreeOptions))
        End Function
 
        Protected Overrides Function GetOutputFileName(compilation As Compilation, cancellationToken As CancellationToken) As String
            ' The only case this is Nothing is when there are errors during parsing in which case this should never get called
            Debug.Assert(Arguments.OutputFileName IsNot Nothing)
            Return Arguments.OutputFileName
        End Function
 
        Private Sub PrintReferences(resolvedReferences As List(Of MetadataReference), consoleOutput As TextWriter)
            For Each reference In resolvedReferences
                If reference.Properties.Kind = MetadataImageKind.Module Then
                    consoleOutput.WriteLine(ErrorFactory.IdToString(ERRID.IDS_MSG_ADDMODULE, Culture), reference.Display)
                ElseIf reference.Properties.EmbedInteropTypes Then
                    consoleOutput.WriteLine(ErrorFactory.IdToString(ERRID.IDS_MSG_ADDLINKREFERENCE, Culture), reference.Display)
                Else
                    consoleOutput.WriteLine(ErrorFactory.IdToString(ERRID.IDS_MSG_ADDREFERENCE, Culture), reference.Display)
                End If
            Next
 
            consoleOutput.WriteLine()
        End Sub
 
        Friend Overrides Function SuppressDefaultResponseFile(args As IEnumerable(Of String)) As Boolean
            For Each arg In args
                Select Case arg.ToLowerInvariant
                    Case "/noconfig", "-noconfig", "/nostdlib", "-nostdlib"
                        Return True
                End Select
            Next
            Return False
        End Function
 
        ''' <summary>
        ''' Print compiler logo
        ''' </summary>
        ''' <param name="consoleOutput"></param>
        Public Overrides Sub PrintLogo(consoleOutput As TextWriter)
            consoleOutput.WriteLine(ErrorFactory.IdToString(ERRID.IDS_LogoLine1, Culture), GetToolName(), GetCompilerVersion())
            consoleOutput.WriteLine(ErrorFactory.IdToString(ERRID.IDS_LogoLine2, Culture))
            consoleOutput.WriteLine()
        End Sub
 
        Friend Overrides Function GetToolName() As String
            Return ErrorFactory.IdToString(ERRID.IDS_ToolName, Culture)
        End Function
 
        Friend Overrides ReadOnly Property Type As Type
            Get
                ' We do not use Me.GetType() so that we don't break mock subtypes
                Return GetType(VisualBasicCompiler)
            End Get
        End Property
 
        ''' <summary>
        ''' Print Commandline help message (up to 80 English characters per line)
        ''' </summary>
        ''' <param name="consoleOutput"></param>
        Public Overrides Sub PrintHelp(consoleOutput As TextWriter)
            consoleOutput.WriteLine(ErrorFactory.IdToString(ERRID.IDS_VBCHelp, Culture))
        End Sub
 
        Public Overrides Sub PrintLangVersions(consoleOutput As TextWriter)
            consoleOutput.WriteLine(ErrorFactory.IdToString(ERRID.IDS_LangVersions, Culture))
            Dim defaultVersion = LanguageVersion.Default.MapSpecifiedToEffectiveVersion()
            Dim latestVersion = LanguageVersion.Latest.MapSpecifiedToEffectiveVersion()
            For Each v As LanguageVersion In System.Enum.GetValues(GetType(LanguageVersion))
                If v = defaultVersion Then
                    consoleOutput.WriteLine($"{v.ToDisplayString()} (default)")
                ElseIf v = latestVersion Then
                    consoleOutput.WriteLine($"{v.ToDisplayString()} (latest)")
                Else
                    consoleOutput.WriteLine(v.ToDisplayString())
                End If
            Next
            consoleOutput.WriteLine()
        End Sub
 
        Protected Overrides Function TryGetCompilerDiagnosticCode(diagnosticId As String, ByRef code As UInteger) As Boolean
            Return CommonCompiler.TryGetCompilerDiagnosticCode(diagnosticId, "BC", code)
        End Function
 
        Protected Overrides Sub ResolveAnalyzersFromArguments(
            diagnostics As List(Of DiagnosticInfo),
            messageProvider As CommonMessageProvider,
            compilationOptions As CompilationOptions,
            skipAnalyzers As Boolean,
            ByRef analyzers As ImmutableArray(Of DiagnosticAnalyzer),
            ByRef generators As ImmutableArray(Of ISourceGenerator))
 
            Arguments.ResolveAnalyzersFromArguments(LanguageNames.VisualBasic, diagnostics, messageProvider, AssemblyLoader, compilationOptions, skipAnalyzers, analyzers, generators)
        End Sub
 
        Protected Overrides Sub ResolveEmbeddedFilesFromExternalSourceDirectives(
            tree As SyntaxTree,
            resolver As SourceReferenceResolver,
            embeddedFiles As OrderedSet(Of String),
            diagnostics As DiagnosticBag)
 
            For Each directive As ExternalSourceDirectiveTriviaSyntax In tree.GetRoot().GetDirectives(
                Function(d) d.Kind() = SyntaxKind.ExternalSourceDirectiveTrivia)
 
                If directive.ExternalSource.IsMissing Then
                    Continue For
                End If
 
                Dim path = CStr(directive.ExternalSource.Value)
                If path Is Nothing Then
                    Continue For
                End If
 
                Dim resolvedPath = resolver.ResolveReference(path, tree.FilePath)
                If resolvedPath Is Nothing Then
                    diagnostics.Add(
                        MessageProvider.CreateDiagnostic(
                            MessageProvider.ERR_FileNotFound,
                            directive.ExternalSource.GetLocation(),
                            path))
 
                    Continue For
                End If
 
                embeddedFiles.Add(resolvedPath)
            Next
        End Sub
 
        Private Protected Overrides Function CreateGeneratorDriver(baseDirectory As String, parseOptions As ParseOptions, generators As ImmutableArray(Of ISourceGenerator), analyzerConfigOptionsProvider As AnalyzerConfigOptionsProvider, additionalTexts As ImmutableArray(Of AdditionalText)) As GeneratorDriver
            Return VisualBasicGeneratorDriver.Create(generators, additionalTexts, DirectCast(parseOptions, VisualBasicParseOptions), analyzerConfigOptionsProvider,
                                                     driverOptions:=New GeneratorDriverOptions(disabledOutputs:=IncrementalGeneratorOutputKind.Host, baseDirectory:=baseDirectory))
        End Function
 
        Private Protected Overrides Sub DiagnoseBadAccesses(consoleOutput As TextWriter, errorLogger As ErrorLogger, compilation As Compilation, diagnostics As ImmutableArray(Of Diagnostic))
            Dim newDiagnostics = DiagnosticBag.GetInstance()
 
            For Each diag In diagnostics
                Dim symbol As Symbol
                Select Case diag.Code
                    Case ERRID.ERR_InaccessibleSymbol2,
                         ERRID.ERR_InaccessibleMember3,
                         ERRID.ERR_InAccessibleCoClass3,
                         ERRID.ERR_CannotOverrideInAccessibleMember,
                         ERRID.ERR_InaccessibleReturnTypeOfMember2
 
                        Dim symbolDiagnostic = DirectCast(DirectCast(diag, DiagnosticWithInfo).Info, BadSymbolDiagnostic)
                        symbol = symbolDiagnostic.BadSymbol
 
                    Case Else
                        Continue For
                End Select
 
                newDiagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.ERR_SymbolDefinedInAssembly, symbol, symbol.ContainingAssembly), diag.Location))
            Next
 
            ReportDiagnostics(newDiagnostics.ToReadOnlyAndFree(), consoleOutput, errorLogger, compilation)
        End Sub
 
    End Class
End Namespace