' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Collections.Immutable
Imports System.Globalization
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Text
Imports Microsoft.CodeAnalysis.Emit
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.VisualBasic.SyntaxFacts
Imports Roslyn.Utilities
Namespace Microsoft.CodeAnalysis.VisualBasic
'''
''' The VisualBasicCommandLineParser class contains members used to perform various Visual Basic command line parsing operations.
'''
Public Class VisualBasicCommandLineParser
Inherits CommandLineParser
'''
''' Gets the current command line parser.
'''
Public Shared ReadOnly [Default] As VisualBasicCommandLineParser = New VisualBasicCommandLineParser()
'''
''' Gets the current interactive command line parser.
'''
Public Shared ReadOnly Interactive As VisualBasicCommandLineParser = New VisualBasicCommandLineParser(isInteractive:=True)
'''
''' Creates a new command line parser.
'''
''' An optional parameter indicating indicating whether to create a interactive command line parser.
Public Sub New(Optional isInteractive As Boolean = False)
MyBase.New(VisualBasic.MessageProvider.Instance, isInteractive)
End Sub
Private Const c_Win32Manifest As String = "win32manifest"
Private Const c_Win32Icon As String = "win32icon"
Private Const c_Win32Res As String = "win32resource"
'''
''' Gets the standard Visual Basic source file extension
'''
''' A string representing the standard Visual Basic source file extension.
Protected Overrides ReadOnly Property RegularFileExtension As String
Get
Return ".vb"
End Get
End Property
'''
''' Gets the standard Visual Basic script file extension.
'''
''' A string representing the standard Visual Basic script file extension.
Protected Overrides ReadOnly Property ScriptFileExtension As String
Get
Return ".vbx"
End Get
End Property
Friend NotOverridable Overrides Function CommonParse(args As IEnumerable(Of String), baseDirectory As String, additionalReferencePaths As String) As CommandLineArguments
Return Parse(args, baseDirectory, additionalReferencePaths)
End Function
'''
''' Parses a command line.
'''
''' A collection of strings representing the command line arguments.
''' The base directory used for qualifying file locations.
''' A string representing additional reference paths.
''' A CommandLineArguments object representing the parsed command line.
Public Shadows Function Parse(args As IEnumerable(Of String), baseDirectory As String, Optional additionalReferencePaths As String = Nothing) As VisualBasicCommandLineArguments
Const GenerateFileNameForDocComment As String = "USE-OUTPUT-NAME"
Dim diagnostics As List(Of Diagnostic) = New List(Of Diagnostic)()
Dim flattenedArgs As List(Of String) = New List(Of String)()
Dim scriptArgs As List(Of String) = If(IsInteractive, New List(Of String)(), Nothing)
' normalized paths to directories containing response files:
Dim responsePaths As New List(Of String)
FlattenArgs(args, diagnostics, flattenedArgs, scriptArgs, baseDirectory, responsePaths)
Dim displayLogo As Boolean = True
Dim displayHelp As Boolean = False
Dim outputLevel As OutputLevel = OutputLevel.Normal
Dim optimize As Boolean = False
Dim checkOverflow As Boolean = True
Dim concurrentBuild As Boolean = True
Dim emitPdb As Boolean = False
Dim noStdLib As Boolean = False
Dim utf8output As Boolean = False
Dim outputFileName As String = Nothing
Dim outputDirectory As String = baseDirectory
Dim documentationPath As String = Nothing
Dim parseDocumentationComments As Boolean = False ' Don't just null check documentationFileName because we want to do this even if the file name is invalid.
Dim outputKind As OutputKind = OutputKind.ConsoleApplication
Dim ssVersion As SubsystemVersion = SubsystemVersion.None
Dim languageVersion As LanguageVersion = LanguageVersion.VisualBasic14
Dim mainTypeName As String = Nothing
Dim win32ManifestFile As String = Nothing
Dim win32ResourceFile As String = Nothing
Dim win32IconFile As String = Nothing
Dim noWin32Manifest As Boolean = False
Dim managedResources = New List(Of ResourceDescription)()
Dim sourceFiles = New List(Of CommandLineSourceFile)()
Dim hasSourceFiles = False
Dim additionalFiles = New List(Of CommandLineSourceFile)()
Dim codepage As Encoding = Nothing
Dim checksumAlgorithm = SourceHashAlgorithm.Sha1
Dim defines As IReadOnlyDictionary(Of String, Object) = Nothing
Dim metadataReferences = New List(Of CommandLineReference)()
Dim analyzers = New List(Of CommandLineAnalyzerReference)()
Dim sdkPaths As New List(Of String)()
Dim libPaths As New List(Of String)()
Dim keyFileSearchPaths = New List(Of String)()
Dim globalImports = New List(Of GlobalImport)
Dim rootNamespace As String = ""
Dim optionStrict As OptionStrict = OptionStrict.Off
Dim optionInfer As Boolean = False ' MSDN says: ...The compiler default for this option is /optioninfer-.
Dim optionExplicit As Boolean = True
Dim optionCompareText As Boolean = False
Dim embedVbCoreRuntime As Boolean = False
Dim platform As Platform = Platform.AnyCpu
Dim preferredUILang As CultureInfo = Nothing
Dim fileAlignment As Integer = 0
Dim baseAddress As ULong = 0
Dim highEntropyVA As Boolean = False
Dim vbRuntimePath As String = Nothing
Dim includeVbRuntimeReference As Boolean = True
Dim generalDiagnosticOption As ReportDiagnostic = ReportDiagnostic.Default
' Diagnostic ids specified via /nowarn /warnaserror must be processed in case-insensitive fashion.
Dim specificDiagnosticOptionsFromRuleSet = New Dictionary(Of String, ReportDiagnostic)(CaseInsensitiveComparison.Comparer)
Dim specificDiagnosticOptionsFromGeneralArguments = New Dictionary(Of String, ReportDiagnostic)(CaseInsensitiveComparison.Comparer)
Dim specificDiagnosticOptionsFromSpecificArguments = New Dictionary(Of String, ReportDiagnostic)(CaseInsensitiveComparison.Comparer)
Dim specificDiagnosticOptionsFromNoWarnArguments = New Dictionary(Of String, ReportDiagnostic)(CaseInsensitiveComparison.Comparer)
Dim keyFileSetting As String = Nothing
Dim keyContainerSetting As String = Nothing
Dim delaySignSetting As Boolean? = Nothing
Dim moduleAssemblyName As String = Nothing
Dim moduleName As String = Nothing
Dim sqmsessionguid As Guid = Nothing
Dim touchedFilesPath As String = Nothing
Dim features = New List(Of String)()
' Process ruleset files first so that diagnostic severity settings specified on the command line via
' /nowarn and /warnaserror can override diagnostic severity settings specified in the ruleset file.
If Not IsInteractive Then
For Each arg In flattenedArgs
Dim name As String = Nothing
Dim value As String = Nothing
If TryParseOption(arg, name, value) AndAlso (name = "ruleset") Then
Dim unquoted = RemoveAllQuotes(value)
If String.IsNullOrEmpty(unquoted) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":")
Continue For
End If
generalDiagnosticOption = GetDiagnosticOptionsFromRulesetFile(specificDiagnosticOptionsFromRuleSet, diagnostics, unquoted, baseDirectory)
End If
Next
End If
For Each arg In flattenedArgs
Debug.Assert(Not arg.StartsWith("@", StringComparison.Ordinal))
Dim name As String = Nothing
Dim value As String = Nothing
If Not TryParseOption(arg, name, value) Then
sourceFiles.AddRange(ParseFileArgument(arg, baseDirectory, diagnostics))
hasSourceFiles = True
Continue For
End If
Select Case name
Case "?", "help"
If value IsNot Nothing Then
Exit Select
End If
displayHelp = True
Continue For
Case "r", "reference"
metadataReferences.AddRange(ParseAssemblyReferences(name, value, diagnostics, embedInteropTypes:=False))
Continue For
Case "a", "analyzer"
analyzers.AddRange(ParseAnalyzers(name, value, diagnostics))
Continue For
Case "d", "define"
If String.IsNullOrEmpty(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":")
Continue For
End If
Dim conditionalCompilationDiagnostics As IEnumerable(Of Diagnostic) = Nothing
defines = ParseConditionalCompilationSymbols(value, conditionalCompilationDiagnostics, defines)
diagnostics.AddRange(conditionalCompilationDiagnostics)
Continue For
Case "imports", "import"
If String.IsNullOrEmpty(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, If(name = "import", ":", ":"))
Continue For
End If
ParseGlobalImports(value, globalImports, diagnostics)
Continue For
Case "optionstrict"
If value Is Nothing Then
optionStrict = VisualBasic.OptionStrict.On
ElseIf String.Equals(value, "custom", StringComparison.OrdinalIgnoreCase) Then
optionStrict = VisualBasic.OptionStrict.Custom
Else
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "optionstrict", ":custom")
End If
Continue For
Case "optionstrict+"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optionstrict")
Continue For
End If
optionStrict = VisualBasic.OptionStrict.On
Continue For
Case "optionstrict-"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optionstrict")
Continue For
End If
optionStrict = VisualBasic.OptionStrict.Off
Continue For
Case "optioncompare"
If value Is Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "optioncompare", ":binary|text")
ElseIf String.Equals(value, "text", StringComparison.OrdinalIgnoreCase) Then
optionCompareText = True
ElseIf String.Equals(value, "binary", StringComparison.OrdinalIgnoreCase) Then
optionCompareText = False
Else
AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, value, "optioncompare")
End If
Continue For
Case "optionexplicit", "optionexplicit+"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optionexplicit")
Continue For
End If
optionExplicit = True
Continue For
Case "optionexplicit-"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optionexplicit")
Continue For
End If
optionExplicit = False
Continue For
Case "optioninfer", "optioninfer+"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optioninfer")
Continue For
End If
optionInfer = True
Continue For
Case "optioninfer-"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optioninfer")
Continue For
End If
optionInfer = False
Continue For
Case "codepage"
If String.IsNullOrEmpty(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "codepage", ":")
Continue For
End If
Dim encoding = TryParseEncodingName(value)
If encoding Is Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_BadCodepage, value)
Continue For
End If
codepage = encoding
Continue For
Case "checksumalgorithm"
If String.IsNullOrEmpty(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "checksumalgorithm", ":")
Continue For
End If
Dim newChecksumAlgorithm = TryParseHashAlgorithmName(value)
If newChecksumAlgorithm = SourceHashAlgorithm.None Then
AddDiagnostic(diagnostics, ERRID.ERR_BadChecksumAlgorithm, value)
Continue For
End If
checksumAlgorithm = newChecksumAlgorithm
Continue For
Case "removeintchecks", "removeintchecks+"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "removeintchecks")
Continue For
End If
checkOverflow = False
Continue For
Case "removeintchecks-"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "removeintchecks")
Continue For
End If
checkOverflow = True
Continue For
Case "sqmsessionguid"
If String.IsNullOrWhiteSpace(value) = True Then
AddDiagnostic(diagnostics, ERRID.ERR_MissingGuidForOption, value, name)
Else
If Not Guid.TryParse(value, sqmsessionguid) Then
AddDiagnostic(diagnostics, ERRID.ERR_InvalidFormatForGuidForOption, value, name)
End If
End If
Continue For
Case "preferreduilang"
If (String.IsNullOrEmpty(value)) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":")
Continue For
End If
Try
preferredUILang = New CultureInfo(value)
Catch ex As CultureNotFoundException
AddDiagnostic(diagnostics, ERRID.WRN_BadUILang, value)
End Try
Continue For
#If DEBUG Then
Case "attachdebugger"
Debugger.Launch()
Continue For
#End If
End Select
If IsInteractive Then
Select Case name
Case "rp", "referencepath"
' TODO: should it really go to /libpath?
libPaths.AddRange(ParseSeparatedPaths(value))
Continue For
End Select
Else
Select Case name
Case "out"
If String.IsNullOrWhiteSpace(value) Then
' When the value has " " (e.g., "/out: ")
' the Roslyn VB compiler reports "BC 2006 : option 'out' requires ':',
' While the Dev11 VB compiler reports "BC2012 : can't open ' ' for writing,
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":")
Else
' Even when value is neither null or whitespace, the output file name still could be invalid. (e.g., "/out:sub\ ")
' While the Dev11 VB compiler reports "BC2012: can't open 'sub\ ' for writing,
' the Roslyn VB compiler reports "BC2032: File name 'sub\ ' is empty, contains invalid characters, ..."
' which is generated by the following ParseOutputFile.
ParseOutputFile(value, diagnostics, baseDirectory, outputFileName, outputDirectory)
End If
Continue For
Case "t", "target"
outputKind = ParseTarget(name, value, diagnostics)
Continue For
Case "moduleassemblyname"
value = If(value IsNot Nothing, value.Unquote(), Nothing)
Dim identity As AssemblyIdentity = Nothing
' Note that native compiler also extracts public key, but Roslyn doesn't use it.
If String.IsNullOrEmpty(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "moduleassemblyname", ":")
ElseIf Not AssemblyIdentity.TryParseDisplayName(value, identity) OrElse
Not MetadataHelpers.IsValidAssemblyOrModuleName(identity.Name) Then
AddDiagnostic(diagnostics, ERRID.ERR_InvalidAssemblyName, value, arg)
Else
moduleAssemblyName = identity.Name
End If
Continue For
Case "rootnamespace"
If String.IsNullOrEmpty(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "rootnamespace", ":")
Continue For
End If
rootNamespace = value
Continue For
Case "doc"
parseDocumentationComments = True
If value Is Nothing Then
' Illegal in C#, but works in VB
documentationPath = GenerateFileNameForDocComment
Continue For
End If
Dim unquoted = RemoveAllQuotes(value)
If unquoted.Length = 0 Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "doc", ":")
Else
documentationPath = ParseGenericPathToFile(unquoted, diagnostics, baseDirectory, generateDiagnostic:=False)
If String.IsNullOrWhiteSpace(documentationPath) Then
AddDiagnostic(diagnostics, ERRID.WRN_XMLCannotWriteToXMLDocFile2, unquoted, New LocalizableErrorArgument(ERRID.IDS_TheSystemCannotFindThePathSpecified))
documentationPath = Nothing
End If
End If
Continue For
Case "doc+"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "doc")
End If
' Seems redundant with default values, but we need to clobber any preceding /doc switches
documentationPath = GenerateFileNameForDocComment
parseDocumentationComments = True
Continue For
Case "doc-"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "doc")
End If
' Seems redundant with default values, but we need to clobber any preceding /doc switches
documentationPath = Nothing
parseDocumentationComments = False
Continue For
Case "netcf"
' Do nothing as we no longer have any use for implementing this switch and
' want to avoid failing with any warnings/errors
Continue For
Case "libpath"
If String.IsNullOrEmpty(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "libpath", ":")
Continue For
End If
libPaths.AddRange(ParseSeparatedPaths(RemoveAllQuotes(value)))
Continue For
Case "sdkpath"
If String.IsNullOrEmpty(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "sdkpath", ":")
Continue For
End If
sdkPaths.Clear()
sdkPaths.AddRange(ParseSeparatedPaths(RemoveAllQuotes(value)))
Continue For
Case "recurse"
If String.IsNullOrEmpty(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "recurse", ":")
Continue For
End If
Dim before As Integer = sourceFiles.Count
sourceFiles.AddRange(ParseRecurseArgument(value, baseDirectory, diagnostics))
If sourceFiles.Count > before Then
hasSourceFiles = True
End If
Continue For
Case "addmodule"
If String.IsNullOrEmpty(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "addmodule", ":")
Continue For
End If
' NOTE(tomat): Dev10 reports "Command line error BC2017 : could not find library."
' Since we now support /referencePaths option we would need to search them to see if the resolved path is a directory.
' An error will be reported by the assembly manager anyways.
metadataReferences.AddRange(
ParseSeparatedPaths(value).Select(
Function(path) New CommandLineReference(path, New MetadataReferenceProperties(MetadataImageKind.Module))))
Continue For
Case "l", "link"
metadataReferences.AddRange(ParseAssemblyReferences(name, value, diagnostics, embedInteropTypes:=True))
Continue For
Case "win32resource"
win32ResourceFile = GetWin32Setting(c_Win32Res, value, diagnostics)
Continue For
Case "win32icon"
win32IconFile = GetWin32Setting(c_Win32Icon, value, diagnostics)
Continue For
Case "win32manifest"
win32ManifestFile = GetWin32Setting(c_Win32Manifest, value, diagnostics)
Continue For
Case "nowin32manifest"
If value IsNot Nothing Then
Exit Select
End If
noWin32Manifest = True
Continue For
Case "res", "resource"
Dim embeddedResource = ParseResourceDescription(name, value, baseDirectory, diagnostics, embedded:=True)
If embeddedResource IsNot Nothing Then
managedResources.Add(embeddedResource)
End If
Continue For
Case "linkres", "linkresource"
Dim linkedResource = ParseResourceDescription(name, value, baseDirectory, diagnostics, embedded:=False)
If linkedResource IsNot Nothing Then
managedResources.Add(linkedResource)
End If
Continue For
Case "debug"
' parse only for backwards compat
If value IsNot Nothing Then
If String.IsNullOrEmpty(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "debug", ":pdbonly|full")
ElseIf Not String.Equals(value, "full", StringComparison.OrdinalIgnoreCase) AndAlso
Not String.Equals(value, "pdbonly", StringComparison.OrdinalIgnoreCase) Then
AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, value, "debug")
End If
End If
emitPdb = True
Continue For
Case "debug+"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "debug")
End If
emitPdb = True
Continue For
Case "debug-"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "debug")
End If
emitPdb = False
Continue For
Case "optimize", "optimize+"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optimize")
Continue For
End If
optimize = True
Continue For
Case "optimize-"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optimize")
Continue For
End If
optimize = False
Continue For
Case "parallel", "p"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, name)
Continue For
End If
concurrentBuild = True
Continue For
Case "parallel+", "p+"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, name.Substring(0, name.Length - 1))
Continue For
End If
concurrentBuild = True
Continue For
Case "parallel-", "p-"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, name.Substring(0, name.Length - 1))
Continue For
End If
concurrentBuild = False
Continue For
Case "warnaserror", "warnaserror+"
If value Is Nothing Then
generalDiagnosticOption = ReportDiagnostic.Error
specificDiagnosticOptionsFromGeneralArguments.Clear()
For Each pair In specificDiagnosticOptionsFromRuleSet
If pair.Value = ReportDiagnostic.Warn Then
specificDiagnosticOptionsFromGeneralArguments.Add(pair.Key, ReportDiagnostic.Error)
End If
Next
Continue For
End If
AddWarnings(specificDiagnosticOptionsFromSpecificArguments, ReportDiagnostic.Error, ParseWarnings(value))
Continue For
Case "warnaserror-"
If value Is Nothing Then
If generalDiagnosticOption <> ReportDiagnostic.Suppress Then
generalDiagnosticOption = ReportDiagnostic.Default
End If
specificDiagnosticOptionsFromGeneralArguments.Clear()
Continue For
End If
For Each id In ParseWarnings(value)
Dim ruleSetValue As ReportDiagnostic
If specificDiagnosticOptionsFromRuleSet.TryGetValue(id, ruleSetValue) Then
specificDiagnosticOptionsFromSpecificArguments(id) = ruleSetValue
Else
specificDiagnosticOptionsFromSpecificArguments(id) = ReportDiagnostic.Default
End If
Next
Continue For
Case "nowarn"
If value Is Nothing Then
generalDiagnosticOption = ReportDiagnostic.Suppress
specificDiagnosticOptionsFromGeneralArguments.Clear()
For Each pair In specificDiagnosticOptionsFromRuleSet
If pair.Value <> ReportDiagnostic.Error Then
specificDiagnosticOptionsFromGeneralArguments.Add(pair.Key, ReportDiagnostic.Suppress)
End If
Next
Continue For
End If
AddWarnings(specificDiagnosticOptionsFromNoWarnArguments, ReportDiagnostic.Suppress, ParseWarnings(value))
Continue For
Case "langversion"
If value Is Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "langversion", ":")
Continue For
End If
If String.IsNullOrEmpty(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "langversion", ":")
Else
Select Case value.ToLowerInvariant()
Case "9", "9.0"
languageVersion = LanguageVersion.VisualBasic9
Case "10", "10.0"
languageVersion = LanguageVersion.VisualBasic10
Case "11", "11.0"
languageVersion = LanguageVersion.VisualBasic11
Case "12", "12.0"
languageVersion = LanguageVersion.VisualBasic12
Case "14", "14.0"
languageVersion = LanguageVersion.VisualBasic12
Case Else
AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, value, "langversion")
End Select
End If
Continue For
Case "delaysign", "delaysign+"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "delaysign")
Continue For
End If
delaySignSetting = True
Continue For
Case "delaysign-"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "delaysign")
Continue For
End If
delaySignSetting = False
Continue For
Case "keycontainer"
' NOTE: despite what MSDN says, Dev11 resets '/keyfile' in this case:
'
' MSDN: In case both /keyfile and /keycontainer are specified (either by command-line
' MSDN: option or by custom attribute) in the same compilation, the compiler first tries
' MSDN: the key container. If that succeeds, then the assembly is signed with the
' MSDN: information in the key container. If the compiler does not find the key container,
' MSDN: it tries the file specified with /keyfile. If this succeeds, the assembly is
' MSDN: signed with the information in the key file, and the key information is installed
' MSDN: in the key container (similar to sn -i) so that on the next compilation,
' MSDN: the key container will be valid.
keyFileSetting = Nothing
If String.IsNullOrEmpty(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "keycontainer", ":")
Else
keyContainerSetting = value
End If
Continue For
Case "keyfile"
' NOTE: despite what MSDN says, Dev11 resets '/keycontainer' in this case:
'
' MSDN: In case both /keyfile and /keycontainer are specified (either by command-line
' MSDN: option or by custom attribute) in the same compilation, the compiler first tries
' MSDN: the key container. If that succeeds, then the assembly is signed with the
' MSDN: information in the key container. If the compiler does not find the key container,
' MSDN: it tries the file specified with /keyfile. If this succeeds, the assembly is
' MSDN: signed with the information in the key file, and the key information is installed
' MSDN: in the key container (similar to sn -i) so that on the next compilation,
' MSDN: the key container will be valid.
keyContainerSetting = Nothing
If String.IsNullOrWhiteSpace(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "keyfile", ":")
Else
keyFileSetting = RemoveAllQuotes(value)
End If
Continue For
Case "highentropyva", "highentropyva+"
If value IsNot Nothing Then
Exit Select
End If
highEntropyVA = True
Continue For
Case "highentropyva-"
If value IsNot Nothing Then
Exit Select
End If
highEntropyVA = False
Continue For
Case "nologo", "nologo+"
If value IsNot Nothing Then
Exit Select
End If
displayLogo = False
Continue For
Case "nologo-"
If value IsNot Nothing Then
Exit Select
End If
displayLogo = True
Continue For
Case "quiet+"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "quiet")
Continue For
End If
outputLevel = VisualBasic.OutputLevel.Quiet
Continue For
Case "quiet"
If value IsNot Nothing Then
Exit Select
End If
outputLevel = VisualBasic.OutputLevel.Quiet
Continue For
Case "verbose"
If value IsNot Nothing Then
Exit Select
End If
outputLevel = VisualBasic.OutputLevel.Verbose
Continue For
Case "verbose+"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "verbose")
Continue For
End If
outputLevel = VisualBasic.OutputLevel.Verbose
Continue For
Case "quiet-", "verbose-"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, name.Substring(0, name.Length - 1))
Continue For
End If
outputLevel = VisualBasic.OutputLevel.Normal
Continue For
Case "utf8output", "utf8output+"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "utf8output")
End If
utf8output = True
Continue For
Case "utf8output-"
If value IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "utf8output")
End If
utf8output = False
Continue For
Case "noconfig"
' It is already handled (see CommonCommandLineCompiler.cs).
Continue For
Case "bugreport"
' Do nothing as we no longer have any use for implementing this switch and
' want to avoid failing with any warnings/errors
' We do no further checking as to a value provided or not and '
' this will cause no diagnostics for invalid values.
Continue For
Case "errorreport"
' Allows any value to be entered and will just silently do nothing
' previously we would validate value for prompt, send Or Queue
' This will cause no diagnostics for invalid values.
Continue For
Case "novbruntimeref"
' The switch is no longer supported and for backwards compat ignored.
Continue For
Case "m", "main"
' MSBuild can result in maintypename being passed in quoted when cyrillic namespace was being used resulting
' in ERRID.ERR_StartupCodeNotFound1 diagnostic. The additional quotes cause problems and quotes are not a
' valid character in typename.
value = RemoveAllQuotes(value)
If String.IsNullOrEmpty(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":")
Continue For
End If
mainTypeName = value
Continue For
Case "subsystemversion"
If String.IsNullOrEmpty(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":")
Continue For
End If
Dim version As SubsystemVersion = Nothing
If SubsystemVersion.TryParse(value, version) Then
ssVersion = version
Else
AddDiagnostic(diagnostics, ERRID.ERR_InvalidSubsystemVersion, value)
End If
Continue For
Case "touchedfiles"
Dim unquoted = RemoveAllQuotes(value)
If (String.IsNullOrEmpty(unquoted)) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":")
Continue For
Else
touchedFilesPath = unquoted
End If
Continue For
Case "fullpaths", "errorendlocation"
UnimplementedSwitch(diagnostics, name)
Continue For
Case "nostdlib"
If value IsNot Nothing Then
Exit Select
End If
noStdLib = True
Continue For
Case "vbruntime"
If value Is Nothing Then
GoTo lVbRuntimePlus
End If
' NOTE: that Dev11 does not report errors on empty or invalid file specified
vbRuntimePath = RemoveAllQuotes(value)
includeVbRuntimeReference = True
embedVbCoreRuntime = False
Continue For
Case "vbruntime+"
If value IsNot Nothing Then
Exit Select
End If
lVbRuntimePlus:
vbRuntimePath = Nothing
includeVbRuntimeReference = True
embedVbCoreRuntime = False
Continue For
Case "vbruntime-"
If value IsNot Nothing Then
Exit Select
End If
vbRuntimePath = Nothing
includeVbRuntimeReference = False
embedVbCoreRuntime = False
Continue For
Case "vbruntime*"
If value IsNot Nothing Then
Exit Select
End If
vbRuntimePath = Nothing
includeVbRuntimeReference = False
embedVbCoreRuntime = True
Continue For
Case "platform"
If value IsNot Nothing Then
platform = ParsePlatform(name, value, diagnostics)
Else
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "platform", ":")
End If
Continue For
Case "filealign"
fileAlignment = ParseFileAlignment(name, value, diagnostics)
Continue For
Case "baseaddress"
baseAddress = ParseBaseAddress(name, value, diagnostics)
Continue For
Case "ruleset"
' The ruleset arg has already been processed in a separate pass above.
Continue For
Case "features"
If value Is Nothing Then
features.Clear()
Else
features.Add(value)
End If
Continue For
Case "additionalfile"
If String.IsNullOrEmpty(value) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":")
Continue For
End If
additionalFiles.AddRange(ParseAdditionalFileArgument(value, baseDirectory, diagnostics))
Continue For
End Select
End If
AddDiagnostic(diagnostics, ERRID.WRN_BadSwitch, arg)
Next
Dim specificDiagnosticOptions = New Dictionary(Of String, ReportDiagnostic)(specificDiagnosticOptionsFromRuleSet, CaseInsensitiveComparison.Comparer)
For Each item In specificDiagnosticOptionsFromGeneralArguments
specificDiagnosticOptions(item.Key) = item.Value
Next
For Each item In specificDiagnosticOptionsFromSpecificArguments
specificDiagnosticOptions(item.Key) = item.Value
Next
For Each item In specificDiagnosticOptionsFromNoWarnArguments
specificDiagnosticOptions(item.Key) = item.Value
Next
If Not IsInteractive AndAlso Not hasSourceFiles AndAlso managedResources.IsEmpty() AndAlso outputKind.IsApplication Then
' VB displays help when there is nothing specified on the command line
If flattenedArgs.Any Then
AddDiagnostic(diagnostics, ERRID.ERR_NoSources)
Else
displayHelp = True
End If
End If
' Prepare SDK PATH
If sdkPaths.Count = 0 Then
sdkPaths.Add(RuntimeEnvironment.GetRuntimeDirectory)
End If
' Locate default 'mscorlib.dll' or 'System.Runtime.dll', if any.
Dim defaultCoreLibraryReference As CommandLineReference? = LoadCoreLibraryReference(sdkPaths, baseDirectory)
' If /nostdlib is not specified, load System.dll
' Dev12 does it through combination of CompilerHost::InitStandardLibraryList and CompilerProject::AddStandardLibraries.
If Not noStdLib Then
Dim systemDllPath As String = FindFileInSdkPath(sdkPaths, "System.dll", baseDirectory)
If systemDllPath Is Nothing Then
AddDiagnostic(diagnostics, ERRID.WRN_CannotFindStandardLibrary1, "System.dll")
Else
metadataReferences.Add(
New CommandLineReference(systemDllPath, New MetadataReferenceProperties(MetadataImageKind.Assembly)))
End If
' Dev11 also adds System.Core.dll in VbHostedCompiler::CreateCompilerProject()
End If
' Add reference to 'Microsoft.VisualBasic.dll' if needed
If includeVbRuntimeReference Then
If vbRuntimePath Is Nothing Then
Dim msVbDllPath As String = FindFileInSdkPath(sdkPaths, "Microsoft.VisualBasic.dll", baseDirectory)
If msVbDllPath Is Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_LibNotFound, "Microsoft.VisualBasic.dll")
Else
metadataReferences.Add(
New CommandLineReference(msVbDllPath, New MetadataReferenceProperties(MetadataImageKind.Assembly)))
End If
Else
metadataReferences.Add(New CommandLineReference(vbRuntimePath, New MetadataReferenceProperties(MetadataImageKind.Assembly)))
End If
End If
' add additional reference paths if specified
If Not String.IsNullOrWhiteSpace(additionalReferencePaths) Then
libPaths.AddRange(ParseSeparatedPaths(additionalReferencePaths))
End If
' Build search path
Dim searchPaths As ImmutableArray(Of String) = BuildSearchPaths(baseDirectory, sdkPaths, responsePaths, libPaths)
ValidateWin32Settings(noWin32Manifest, win32ResourceFile, win32IconFile, win32ManifestFile, outputKind, diagnostics)
' Validate root namespace if specified
Debug.Assert(rootNamespace IsNot Nothing)
' NOTE: empty namespace is a valid option
If Not String.Empty.Equals(rootNamespace) Then
rootNamespace = rootNamespace.Unquote()
If String.IsNullOrWhiteSpace(rootNamespace) OrElse Not OptionsValidator.IsValidNamespaceName(rootNamespace) Then
AddDiagnostic(diagnostics, ERRID.ERR_BadNamespaceName1, rootNamespace)
rootNamespace = "" ' To make it pass compilation options' check
End If
End If
' Dev10 searches for the keyfile in the current directory and assembly output directory.
' We always look to base directory and then examine the search paths.
keyFileSearchPaths.Add(baseDirectory)
If baseDirectory <> outputDirectory Then
keyFileSearchPaths.Add(outputDirectory)
End If
Dim compilationName As String = Nothing
GetCompilationAndModuleNames(diagnostics, outputKind, sourceFiles, moduleAssemblyName, outputFileName, moduleName, compilationName)
If Not IsInteractive AndAlso
Not hasSourceFiles AndAlso
Not managedResources.IsEmpty() AndAlso
outputFileName = Nothing AndAlso
Not flattenedArgs.IsEmpty() Then
AddDiagnostic(diagnostics, ERRID.ERR_NoSourcesOut)
End If
Dim parseOptions = New VisualBasicParseOptions(
languageVersion:=languageVersion,
documentationMode:=If(parseDocumentationComments, DocumentationMode.Diagnose, DocumentationMode.None),
kind:=SourceCodeKind.Regular,
preprocessorSymbols:=AddPredefinedPreprocessorSymbols(outputKind, defines.AsImmutableOrEmpty()))
Dim scriptParseOptions = parseOptions.WithKind(SourceCodeKind.Script)
Dim options = New VisualBasicCompilationOptions(
outputKind:=outputKind,
moduleName:=moduleName,
mainTypeName:=mainTypeName,
scriptClassName:=WellKnownMemberNames.DefaultScriptClassName,
globalImports:=globalImports,
rootNamespace:=rootNamespace,
optionStrict:=optionStrict,
optionInfer:=optionInfer,
optionExplicit:=optionExplicit,
optionCompareText:=optionCompareText,
embedVbCoreRuntime:=embedVbCoreRuntime,
checkOverflow:=checkOverflow,
concurrentBuild:=concurrentBuild,
cryptoKeyContainer:=keyContainerSetting,
cryptoKeyFile:=keyFileSetting,
delaySign:=delaySignSetting,
platform:=platform,
generalDiagnosticOption:=generalDiagnosticOption,
specificDiagnosticOptions:=specificDiagnosticOptions,
optimizationLevel:=If(optimize, OptimizationLevel.Release, OptimizationLevel.Debug),
parseOptions:=parseOptions).WithFeatures(features.AsImmutable())
Dim emitOptions = New EmitOptions(
metadataOnly:=False,
debugInformationFormat:=DebugInformationFormat.Pdb,
pdbFilePath:=Nothing, ' to be determined later
outputNameOverride:=Nothing, ' to be determined later
fileAlignment:=fileAlignment,
baseAddress:=baseAddress,
highEntropyVirtualAddressSpace:=highEntropyVA,
subsystemVersion:=ssVersion,
runtimeMetadataVersion:=Nothing)
' add option incompatibility errors if any
diagnostics.AddRange(options.Errors)
If documentationPath Is GenerateFileNameForDocComment Then
documentationPath = PathUtilities.CombineAbsoluteAndRelativePaths(outputDirectory, PathUtilities.RemoveExtension(outputFileName))
documentationPath = documentationPath + ".xml"
End If
Return New VisualBasicCommandLineArguments With
{
.IsInteractive = IsInteractive,
.BaseDirectory = baseDirectory,
.Errors = diagnostics.AsImmutable(),
.Utf8Output = utf8output,
.CompilationName = compilationName,
.OutputFileName = outputFileName,
.OutputDirectory = outputDirectory,
.DocumentationPath = documentationPath,
.SourceFiles = sourceFiles.AsImmutable(),
.Encoding = codepage,
.ChecksumAlgorithm = checksumAlgorithm,
.MetadataReferences = metadataReferences.AsImmutable(),
.AnalyzerReferences = analyzers.AsImmutable(),
.AdditionalFiles = additionalFiles.AsImmutable(),
.ReferencePaths = searchPaths,
.KeyFileSearchPaths = keyFileSearchPaths.AsImmutable(),
.Win32ResourceFile = win32ResourceFile,
.Win32Icon = win32IconFile,
.Win32Manifest = win32ManifestFile,
.NoWin32Manifest = noWin32Manifest,
.DisplayLogo = displayLogo,
.DisplayHelp = displayHelp,
.ManifestResources = managedResources.AsImmutable(),
.CompilationOptions = options,
.ParseOptions = If(IsInteractive, scriptParseOptions, parseOptions),
.EmitOptions = emitOptions,
.ScriptArguments = scriptArgs.AsImmutableOrEmpty(),
.TouchedFilesPath = touchedFilesPath,
.OutputLevel = outputLevel,
.EmitPdb = emitPdb,
.DefaultCoreLibraryReference = defaultCoreLibraryReference,
.PreferredUILang = preferredUILang,
.SqmSessionGuid = sqmsessionguid
}
End Function
Private Function LoadCoreLibraryReference(sdkPaths As List(Of String), baseDirectory As String) As CommandLineReference?
' Load Core library in Dev11:
' Traditionally VB compiler has hard-coded the name of mscorlib.dll. In the Immersive profile the
' library is called System.Runtime.dll. Ideally we should get rid of the dependency on the name and
' identify the core library as the assembly that contains System.Object. At this point in the compiler,
' it is too early though as we haven't loaded any types or assemblies. Changing this now is a deep
' change. So the workaround here is to allow mscorlib or system.runtime and prefer system.runtime if present.
' There is an extra check to only pick an assembly with no other assembly refs. This is so that is an
' user drops a user-defined binary called System.runtime.dll into the fx directory we still want to pick
' mscorlib.
Dim msCorLibPath As String = FindFileInSdkPath(sdkPaths, "mscorlib.dll", baseDirectory)
Dim systemRuntimePath As String = FindFileInSdkPath(sdkPaths, "System.Runtime.dll", baseDirectory)
If systemRuntimePath IsNot Nothing Then
If msCorLibPath Is Nothing Then
Return New CommandLineReference(systemRuntimePath, New MetadataReferenceProperties(MetadataImageKind.Assembly))
End If
' Load System.Runtime.dll and see if it has any references
Try
Using metadata = AssemblyMetadata.CreateFromFile(systemRuntimePath)
' Prefer 'System.Runtime.dll' if it does not have any references
If metadata.GetModules()(0).Module.IsLinkedModule AndAlso
metadata.GetAssembly().AssemblyReferences.Length = 0 Then
Return New CommandLineReference(systemRuntimePath, New MetadataReferenceProperties(MetadataImageKind.Assembly))
End If
End Using
Catch
' If we caught anything, there is something wrong with System.Runtime.dll and we fall back to mscorlib.dll
End Try
' Otherwise prefer 'mscorlib.dll'
Return New CommandLineReference(msCorLibPath, New MetadataReferenceProperties(MetadataImageKind.Assembly))
End If
If msCorLibPath IsNot Nothing Then
' We return a reference to 'mscorlib.dll'
Return New CommandLineReference(msCorLibPath, New MetadataReferenceProperties(MetadataImageKind.Assembly))
End If
Return Nothing
End Function
Private Function FindFileInSdkPath(sdkPaths As List(Of String), fileName As String, baseDirectory As String) As String
For Each path In sdkPaths
Dim absolutePath = FileUtilities.ResolveRelativePath(If(path, RuntimeEnvironment.GetRuntimeDirectory()), baseDirectory)
If absolutePath IsNot Nothing Then
Dim filePath = PathUtilities.CombineAbsoluteAndRelativePaths(absolutePath, fileName)
If File.Exists(filePath) Then
Return filePath
End If
End If
Next
Return Nothing
End Function
Private Function GetWin32Setting(arg As String, value As String, diagnostics As List(Of Diagnostic)) As String
If (value = Nothing) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, arg, ":")
Else
Dim noQuotes As String = RemoveAllQuotes(value)
If (String.IsNullOrWhiteSpace(noQuotes)) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, arg, ":")
Else
Return noQuotes
End If
End If
Return Nothing
End Function
Private Shared Function BuildSearchPaths(baseDirectory As String, sdkPaths As List(Of String), responsePaths As List(Of String), libPaths As List(Of String)) As ImmutableArray(Of String)
Dim builder = ArrayBuilder(Of String).GetInstance()
' Match how Dev11 builds the list of search paths
' see void GetSearchPath(CComBSTR& strSearchPath)
' current folder -- base directory is searched by default by the FileResolver
' SDK path is specified or current runtime directory
AddNormalizedPaths(builder, sdkPaths, baseDirectory)
' Response file path, see the following comment from Dev11:
' // .NET FX 3.5 will have response file in the FX 3.5 directory but SdkPath will still be in 2.0 directory.
' // Therefore we need to make sure the response file directories are also on the search path
' // so response file authors can continue to use relative paths in the response files.
builder.AddRange(responsePaths)
' libpath
AddNormalizedPaths(builder, libPaths, baseDirectory)
Return builder.ToImmutableAndFree()
End Function
Private Shared Sub AddNormalizedPaths(builder As ArrayBuilder(Of String), paths As List(Of String), baseDirectory As String)
For Each path In paths
Dim normalizedPath = FileUtilities.NormalizeRelativePath(path, basePath:=Nothing, baseDirectory:=baseDirectory)
If normalizedPath Is Nothing Then
' just ignore invalid paths, native compiler doesn't report any errors
Continue For
End If
builder.Add(normalizedPath)
Next
End Sub
Private Shared Sub ValidateWin32Settings(noWin32Manifest As Boolean, win32ResSetting As String, win32IconSetting As String, win32ManifestSetting As String, outputKind As OutputKind, diagnostics As List(Of Diagnostic))
If noWin32Manifest AndAlso (win32ManifestSetting IsNot Nothing) Then
AddDiagnostic(diagnostics, ERRID.ERR_ConflictingManifestSwitches)
End If
If win32ResSetting IsNot Nothing Then
If win32IconSetting IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_IconFileAndWin32ResFile)
End If
If win32ManifestSetting IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_CantHaveWin32ResAndManifest)
End If
End If
If win32ManifestSetting IsNot Nothing AndAlso outputKind.IsNetModule() Then
AddDiagnostic(diagnostics, ERRID.WRN_IgnoreModuleManifest)
End If
End Sub
Private Shared Function ParseTarget(optionName As String, value As String, diagnostics As IList(Of Diagnostic)) As OutputKind
Select Case If(value, "").ToLowerInvariant()
Case "exe"
Return OutputKind.ConsoleApplication
Case "winexe"
Return OutputKind.WindowsApplication
Case "library"
Return OutputKind.DynamicallyLinkedLibrary
Case "module"
Return OutputKind.NetModule
Case "appcontainerexe"
Return OutputKind.WindowsRuntimeApplication
Case "winmdobj"
Return OutputKind.WindowsRuntimeMetadata
Case ""
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, optionName, ":exe|winexe|library|module|appcontainerexe|winmdobj")
Return OutputKind.ConsoleApplication
Case Else
AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, value, optionName)
Return OutputKind.ConsoleApplication
End Select
End Function
Private Function ParseAssemblyReferences(name As String, value As String, diagnostics As IList(Of Diagnostic), embedInteropTypes As Boolean) As IEnumerable(Of CommandLineReference)
If String.IsNullOrEmpty(value) Then
' TODO: localize ?
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":")
Return SpecializedCollections.EmptyEnumerable(Of CommandLineReference)()
End If
' TODO: how does VB handle quotes, separators, ...?
' /r:"reference"
' /r:reference;reference
' /r:"path;containing;semicolons"
' /r:"unterminated_quotes
' /r:"quotes"in"the"middle
Return ParseSeparatedPaths(value).
Select(Function(path) New CommandLineReference(path, New MetadataReferenceProperties(MetadataImageKind.Assembly, embedInteropTypes:=embedInteropTypes)))
End Function
Private Function ParseAnalyzers(name As String, value As String, diagnostics As IList(Of Diagnostic)) As IEnumerable(Of CommandLineAnalyzerReference)
If String.IsNullOrEmpty(value) Then
' TODO: localize ?
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":")
Return SpecializedCollections.EmptyEnumerable(Of CommandLineAnalyzerReference)()
End If
Return ParseSeparatedPaths(value).
Select(Function(path)
Return New CommandLineAnalyzerReference(path)
End Function)
End Function
' See ParseCommandLine in vbc.cpp.
Friend Overloads Shared Function ParseResourceDescription(name As String, resourceDesciptor As String, baseDirectory As String, diagnostics As IList(Of Diagnostic), embedded As Boolean) As ResourceDescription
If String.IsNullOrEmpty(resourceDesciptor) Then
AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":")
Return Nothing
End If
' NOTE: these are actually passed to out parameters of .ParseResourceDescription.
Dim filePath As String = Nothing
Dim fullPath As String = Nothing
Dim fileName As String = Nothing
Dim resourceName As String = Nothing
Dim accessibility As String = Nothing
ParseResourceDescription(
resourceDesciptor,
baseDirectory,
True,
filePath,
fullPath,
fileName,
resourceName,
accessibility)
If String.IsNullOrWhiteSpace(filePath) Then
AddInvalidSwitchValueDiagnostic(diagnostics, name, filePath)
Return Nothing
End If
If fullPath Is Nothing OrElse fileName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0 Then
AddDiagnostic(diagnostics, ERRID.FTL_InputFileNameTooLong, filePath)
Return Nothing
End If
Dim isPublic As Boolean
If String.IsNullOrEmpty(accessibility) Then
' If no accessibility is given, we default to "public".
' NOTE: Dev10 treats empty the same as null (the difference being that empty indicates a comma after the resource name).
' NOTE: Dev10 distinguishes between empty and whitespace-only.
isPublic = True
ElseIf String.Equals(accessibility, "public", StringComparison.OrdinalIgnoreCase) Then
isPublic = True
ElseIf String.Equals(accessibility, "private", StringComparison.OrdinalIgnoreCase) Then
isPublic = False
Else
AddInvalidSwitchValueDiagnostic(diagnostics, name, accessibility)
Return Nothing
End If
Dim dataProvider As Func(Of Stream) = Function()
' Use FileShare.ReadWrite because the file could be opened by the current process.
' For example, it Is an XML doc file produced by the build.
Return New FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
End Function
Return New ResourceDescription(resourceName, fileName, dataProvider, isPublic, embedded, checkArgs:=False)
End Function
Private Shared Sub AddInvalidSwitchValueDiagnostic(diagnostics As IList(Of Diagnostic), ByVal name As String, ByVal nullStringText As String)
If String.IsNullOrEmpty(name) Then
' NOTE: "(null)" to match Dev10.
' CONSIDER: should this be a resource string?
name = "(null)"
End If
AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, nullStringText, name)
End Sub
Private Shared Sub ParseGlobalImports(value As String, globalImports As List(Of GlobalImport), errors As List(Of Diagnostic))
Dim importsArray As String() = value.Split({","c}, StringSplitOptions.RemoveEmptyEntries)
For Each importNamespace In importsArray
Dim importDiagnostics As ImmutableArray(Of Diagnostic) = Nothing
Dim import = GlobalImport.Parse(importNamespace, importDiagnostics)
errors.AddRange(importDiagnostics)
globalImports.Add(import)
Next
End Sub
'''
''' Converts a sequence of definitions provided by a caller (public API) into map
''' of definitions used internally.
'''
''' Invalid value provided.
Private Shared Function PublicSymbolsToInternalDefines(symbols As IEnumerable(Of KeyValuePair(Of String, Object)),
parameterName As String) As ImmutableDictionary(Of String, InternalSyntax.CConst)
Dim result = ImmutableDictionary.CreateBuilder(Of String, InternalSyntax.CConst)(CaseInsensitiveComparison.Comparer)
If symbols IsNot Nothing Then
For Each symbol In symbols
Dim constant = InternalSyntax.CConst.TryCreate(symbol.Value)
If constant Is Nothing Then
Throw New ArgumentException(String.Format(ErrorFactory.IdToString(ERRID.IDS_InvalidPreprocessorConstantType, Culture), symbol.Key, symbol.Value.GetType()), parameterName)
End If
result(symbol.Key) = constant
Next
End If
Return result.ToImmutable()
End Function
'''
''' Converts ImmutableDictionary of definitions used internallyinto IReadOnlyDictionary of definitions
''' returned to a caller (of public API)
'''
Private Shared Function InternalDefinesToPublicSymbols(defines As ImmutableDictionary(Of String, InternalSyntax.CConst)) As IReadOnlyDictionary(Of String, Object)
Dim result = ImmutableDictionary.CreateBuilder(Of String, Object)(CaseInsensitiveComparison.Comparer)
For Each kvp In defines
result(kvp.Key) = kvp.Value.ValueAsObject
Next
Return result.ToImmutable()
End Function
'''
''' Parses Conditional Compilations Symbols. Given the string of conditional compilation symbols from the project system, parse them and merge them with an IReadOnlyDictionary
''' ready to be given to the compilation.
'''
'''
''' The conditional compilation string. This takes the form of a comma delimited list
''' of NAME=Value pairs, where Value may be a quoted string or integer.
'''
''' A collection of reported diagnostics during parsing of symbolList, can be empty IEnumerable.
''' A collection representing existing symbols. Symbols parsed from will be merged with this dictionary.
''' Invalid value provided.
Public Shared Function ParseConditionalCompilationSymbols(
symbolList As String,
ByRef diagnostics As IEnumerable(Of Diagnostic),
Optional symbols As IEnumerable(Of KeyValuePair(Of String, Object)) = Nothing
) As IReadOnlyDictionary(Of String, Object)
Dim diagnosticBuilder = ArrayBuilder(Of Diagnostic).GetInstance()
Dim parsedTokensAsString As New StringBuilder
Dim defines As ImmutableDictionary(Of String, InternalSyntax.CConst) = PublicSymbolsToInternalDefines(symbols, "symbols")
' remove quotes around the whole /define argument (incl. nested)
Dim unquotedString As String
Do
unquotedString = symbolList
symbolList = symbolList.Unquote()
Loop While Not String.Equals(symbolList, unquotedString, StringComparison.Ordinal)
' unescape quotes \" -> "
symbolList = symbolList.Replace("\""", """")
Dim trimmedSymbolList As String = symbolList.TrimEnd(Nothing)
If trimmedSymbolList.Length > 0 AndAlso IsConnectorPunctuation(trimmedSymbolList(trimmedSymbolList.Length - 1)) Then
' In case the symbol list ends with '_' we add ',' to the end of the list which in some
' cases will produce an error 30999 to match Dev11 behavior
symbolList = symbolList + ","
End If
' In order to determine our conditional compilation symbols, we must parse the string we get from the
' project system. We take a cue from the legacy language services and use the VB scanner, since this string
' apparently abides by the same tokenization rules
Dim tokenList = SyntaxFactory.ParseTokens(symbolList)
Using tokens = tokenList.GetEnumerator()
If tokens.MoveNext() Then
Do
' This is the beginning of declaration like 'A' or 'A=123' with optional extra
' separators (',' or ':') in the beginning, if this is NOT the first declaration,
' the tokens.Current should be either separator or EOF
If tokens.Current.Position > 0 AndAlso Not IsSeparatorOrEndOfFile(tokens.Current) Then
parsedTokensAsString.Append(" ^^ ^^ ")
' Complete parsedTokensAsString until the next comma or end of stream
While Not IsSeparatorOrEndOfFile(tokens.Current)
parsedTokensAsString.Append(tokens.Current.ToFullString())
tokens.MoveNext()
End While
diagnosticBuilder.Add(
New DiagnosticWithInfo(
ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedEOS),
parsedTokensAsString.ToString),
Location.None))
Exit Do
End If
Dim lastSeparatorToken As SyntaxToken = Nothing
' If we're on a comma, it means there was an empty item in the list (item1,,item2),
' so just eat it and move on...
While tokens.Current.Kind = SyntaxKind.CommaToken OrElse tokens.Current.Kind = SyntaxKind.ColonToken
If lastSeparatorToken.Kind = SyntaxKind.None Then
' accept multiple : or ,
lastSeparatorToken = tokens.Current
ElseIf lastSeparatorToken.Kind <> tokens.Current.Kind Then
' but not mixing them, e.g. ::,,::
GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString, stopTokenKind:=lastSeparatorToken.Kind, includeCurrentToken:=True)
diagnosticBuilder.Add(
New DiagnosticWithInfo(
ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedIdentifier),
parsedTokensAsString.ToString),
Location.None))
End If
parsedTokensAsString.Append(tokens.Current.ToString)
' this can happen when the while loop above consumed all tokens for the diagnostic message
If tokens.Current.Kind <> SyntaxKind.EndOfFileToken Then
Dim moveNextResult = tokens.MoveNext
Debug.Assert(moveNextResult)
End If
End While
parsedTokensAsString.Clear()
' If we're at the end of the list, we're done
If tokens.Current.Kind = SyntaxKind.EndOfFileToken Then
Dim eof = tokens.Current
If eof.FullWidth > 0 Then
If Not eof.LeadingTrivia.All(Function(t) t.Kind = SyntaxKind.WhitespaceTrivia) Then
' This is an invalid line like "'Blah'"
GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString, True)
diagnosticBuilder.Add(
New DiagnosticWithInfo(
ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedIdentifier),
parsedTokensAsString.ToString),
Location.None))
End If
End If
Exit Do
End If
parsedTokensAsString.Append(tokens.Current.ToFullString())
If Not tokens.Current.Kind = SyntaxKind.IdentifierToken Then
GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString)
diagnosticBuilder.Add(
New DiagnosticWithInfo(
ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedIdentifier),
parsedTokensAsString.ToString),
Location.None))
Exit Do
End If
Dim symbolName = tokens.Current.ValueText
' there should at least be a end of file token
Dim moveResult As Boolean = tokens.MoveNext
Debug.Assert(moveResult)
If tokens.Current.Kind = SyntaxKind.EqualsToken Then
parsedTokensAsString.Append(tokens.Current.ToFullString())
' there should at least be a end of file token
moveResult = tokens.MoveNext
Debug.Assert(moveResult)
' Parse expression starting with the offset
Dim offset As Integer = tokens.Current.SpanStart
Dim expression As ExpressionSyntax = ParseConditionalCompilationExpression(symbolList, offset)
Dim parsedEnd As Integer = offset + expression.Span.End
Dim atTheEndOrSeparator As Boolean = IsSeparatorOrEndOfFile(tokens.Current)
' Consume tokens that are supposed to belong to the expression; we loop
' until the token's end position is the end of the expression, but not consume
' the last token as it will be consumed in uppermost While
While tokens.Current.Kind <> SyntaxKind.EndOfFileToken AndAlso tokens.Current.Span.End <= parsedEnd
parsedTokensAsString.Append(tokens.Current.ToFullString())
moveResult = tokens.MoveNext
Debug.Assert(moveResult)
atTheEndOrSeparator = IsSeparatorOrEndOfFile(tokens.Current)
End While
If expression.ContainsDiagnostics Then
' Dev11 reports syntax errors in not consistent way, sometimes errors are not reported by
' command line utility at all; this implementation tries to repro Dev11 when possible
parsedTokensAsString.Append(" ^^ ^^ ")
' Compete parsedTokensAsString until the next comma or end of stream
While Not IsSeparatorOrEndOfFile(tokens.Current)
parsedTokensAsString.Append(tokens.Current.ToFullString())
tokens.MoveNext()
End While
' NOTE: Dev11 reports ERR_ExpectedExpression and ERR_BadCCExpression in different
' cases compared to what ParseConditionalCompilationExpression(...) generates,
' so we have to use different criteria here; if we don't want to match Dev11
' errors we may simplify the code below
Dim errorSkipped As Boolean = False
For Each diag In expression.VbGreen.GetSyntaxErrors
If diag.Code <> ERRID.ERR_ExpectedExpression AndAlso diag.Code <> ERRID.ERR_BadCCExpression Then
diagnosticBuilder.Add(New DiagnosticWithInfo(ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1, diag, parsedTokensAsString.ToString), Location.None))
Else
errorSkipped = True
End If
Next
If errorSkipped Then
diagnosticBuilder.Add(
New DiagnosticWithInfo(
ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
ErrorFactory.ErrorInfo(If(atTheEndOrSeparator, ERRID.ERR_ExpectedExpression, ERRID.ERR_BadCCExpression)),
parsedTokensAsString.ToString),
Location.None))
End If
Exit Do
End If
' Expression parsed successfully --> evaluate it
Dim value As InternalSyntax.CConst =
InternalSyntax.ExpressionEvaluator.EvaluateExpression(
DirectCast(expression.Green, InternalSyntax.ExpressionSyntax), defines)
Dim err As ERRID = value.ErrorId
If err <> 0 Then
GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString)
diagnosticBuilder.Add(
New DiagnosticWithInfo(
ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
ErrorFactory.ErrorInfo(err, value.ErrorArgs),
parsedTokensAsString.ToString),
Location.None))
Exit Do
End If
' Expression evaluated successfully --> add to 'defines'
If defines.ContainsKey(symbolName) Then
defines = defines.Remove(symbolName)
End If
defines = defines.Add(symbolName, value)
ElseIf tokens.Current.Kind = SyntaxKind.CommaToken OrElse
tokens.Current.Kind = SyntaxKind.ColonToken OrElse
tokens.Current.Kind = SyntaxKind.EndOfFileToken Then
' We have no value being assigned, so we'll just assign it to true
If defines.ContainsKey(symbolName) Then
defines = defines.Remove(symbolName)
End If
defines = defines.Add(symbolName, InternalSyntax.CConst.Create(True))
ElseIf tokens.Current.Kind = SyntaxKind.BadToken Then
GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString)
diagnosticBuilder.Add(
New DiagnosticWithInfo(
ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
ErrorFactory.ErrorInfo(ERRID.ERR_IllegalChar),
parsedTokensAsString.ToString),
Location.None))
Exit Do
Else
GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString)
diagnosticBuilder.Add(
New DiagnosticWithInfo(
ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedEOS),
parsedTokensAsString.ToString),
Location.None))
Exit Do
End If
Loop
End If
End Using
diagnostics = diagnosticBuilder.ToArrayAndFree()
Return InternalDefinesToPublicSymbols(defines)
End Function
'''
''' NOTE: implicit line continuation will not be handled here and an error will be generated,
''' but explicit one (like ".... _\r\n ....") should work fine
'''
Private Shared Function ParseConditionalCompilationExpression(symbolList As String, offset As Integer) As ExpressionSyntax
Using p = New InternalSyntax.Parser(SyntaxFactory.MakeSourceText(symbolList, offset), VisualBasicParseOptions.Default)
p.GetNextToken()
Return DirectCast(p.ParseConditionalCompilationExpression().CreateRed(Nothing, 0), ExpressionSyntax)
End Using
End Function
Private Shared Function IsSeparatorOrEndOfFile(token As SyntaxToken) As Boolean
Return token.Kind = SyntaxKind.EndOfFileToken OrElse token.Kind = SyntaxKind.ColonToken OrElse token.Kind = SyntaxKind.CommaToken
End Function
Private Shared Sub GetErrorStringForRemainderOfConditionalCompilation(
tokens As IEnumerator(Of SyntaxToken),
remainderErrorLine As StringBuilder,
Optional includeCurrentToken As Boolean = False,
Optional stopTokenKind As SyntaxKind = SyntaxKind.CommaToken
)
If includeCurrentToken Then
remainderErrorLine.Append(" ^^ ")
If tokens.Current.Kind = SyntaxKind.ColonToken AndAlso tokens.Current.FullWidth = 0 Then
remainderErrorLine.Append(SyntaxFacts.GetText(SyntaxKind.ColonToken))
Else
remainderErrorLine.Append(tokens.Current.ToFullString())
End If
remainderErrorLine.Append(" ^^ ")
Else
remainderErrorLine.Append(" ^^ ^^ ")
End If
While tokens.MoveNext AndAlso Not tokens.Current.Kind = stopTokenKind
remainderErrorLine.Append(tokens.Current.ToFullString())
End While
End Sub
'''
''' Parses the given platform option. Legal strings are "anycpu", "x64", "x86", "itanium", "anycpu32bitpreferred", "arm".
''' In case an invalid value was passed, anycpu is returned.
'''
''' The value for platform.
''' The error bag.
Private Shared Function ParsePlatform(name As String, value As String, errors As List(Of Diagnostic)) As Platform
If value.IsEmpty Then
AddDiagnostic(errors, ERRID.ERR_ArgumentRequired, name, ":")
Else
Select Case value.ToLowerInvariant()
Case "x86"
Return Platform.X86
Case "x64"
Return Platform.X64
Case "itanium"
Return Platform.Itanium
Case "anycpu"
Return Platform.AnyCpu
Case "anycpu32bitpreferred"
Return Platform.AnyCpu32BitPreferred
Case "arm"
Return Platform.Arm
Case Else
AddDiagnostic(errors, ERRID.ERR_InvalidSwitchValue, value, name)
End Select
End If
Return Platform.AnyCpu
End Function
'''
''' Parses the file alignment option.
''' In case an invalid value was passed, nothing is returned.
'''
''' The name of the option.
''' The value for the option.
''' The error bag.
Private Shared Function ParseFileAlignment(name As String, value As String, errors As List(Of Diagnostic)) As Integer
Dim alignment As UShort
If String.IsNullOrEmpty(value) Then
AddDiagnostic(errors, ERRID.ERR_ArgumentRequired, name, ":")
ElseIf Not TryParseUInt16(value, alignment) Then
AddDiagnostic(errors, ERRID.ERR_InvalidSwitchValue, value, name)
ElseIf Not Microsoft.CodeAnalysis.CompilationOptions.IsValidFileAlignment(alignment) Then
AddDiagnostic(errors, ERRID.ERR_InvalidSwitchValue, value, name)
Else
Return alignment
End If
Return 0
End Function
'''
''' Parses the base address option.
''' In case an invalid value was passed, nothing is returned.
'''
''' The name of the option.
''' The value for the option.
''' The error bag.
Private Shared Function ParseBaseAddress(name As String, value As String, errors As List(Of Diagnostic)) As ULong
If String.IsNullOrEmpty(value) Then
AddDiagnostic(errors, ERRID.ERR_ArgumentRequired, name, ":")
Else
Dim baseAddress As ULong
Dim parseValue As String = value
If value.StartsWith("0x", StringComparison.OrdinalIgnoreCase) Then
parseValue = value.Substring(2) ' UInt64.TryParse does not accept hex format strings
End If
' always treat the base address string as being a hex number, regardless of the given format.
' This handling was hardcoded in the command line option parsing of Dev10 and Dev11.
If Not ULong.TryParse(parseValue,
System.Globalization.NumberStyles.HexNumber,
System.Globalization.CultureInfo.InvariantCulture,
baseAddress) Then
AddDiagnostic(errors, ERRID.ERR_InvalidSwitchValue, value.ToString(), name)
Else
Return baseAddress
End If
End If
Return 0
End Function
'''
''' Parses the warning option.
'''
''' The value for the option.
Private Shared Function ParseWarnings(value As String) As IEnumerable(Of String)
Dim values = value.Split(","c)
Dim results = New List(Of String)()
For Each id In values
Dim number As UShort
If UShort.TryParse(id, NumberStyles.Integer, CultureInfo.InvariantCulture, number) AndAlso
(VisualBasic.MessageProvider.Instance.GetSeverity(number) = DiagnosticSeverity.Warning) AndAlso
(VisualBasic.MessageProvider.Instance.GetWarningLevel(number) = 1) Then
' The id refers to a compiler warning.
' Only accept real warnings from the compiler not including the command line warnings.
' Also only accept the numbers that are actually declared in the enum.
results.Add(VisualBasic.MessageProvider.Instance.GetIdForErrorCode(CInt(number)))
Else
' Previous versions of the compiler used to report warnings (BC2026, BC2014)
' whenever unrecognized warning codes were supplied in /nowarn or
' /warnaserror. We no longer generate a warning in such cases.
' Instead we assume that the unrecognized id refers to a custom diagnostic.
results.Add(id)
End If
Next
Return results
End Function
Private Shared Sub AddWarnings(d As IDictionary(Of String, ReportDiagnostic), kind As ReportDiagnostic, items As IEnumerable(Of String))
For Each id In items
Dim existing As ReportDiagnostic
If d.TryGetValue(id, existing) Then
' Rewrite the existing value with the latest one unless it is for /nowarn.
If existing <> ReportDiagnostic.Suppress Then
d(id) = kind
End If
Else
d.Add(id, kind)
End If
Next
End Sub
Private Shared Sub UnimplementedSwitch(diagnostics As IList(Of Diagnostic), switchName As String)
AddDiagnostic(diagnostics, ERRID.WRN_UnimplementedCommandLineSwitch, "/" + switchName)
End Sub
Friend Overrides Sub GenerateErrorForNoFilesFoundInRecurse(path As String, errors As IList(Of Diagnostic))
AddDiagnostic(errors, ERRID.ERR_InvalidSwitchValue, path, "recurse")
End Sub
Private Shared Sub AddDiagnostic(diagnostics As IList(Of Diagnostic), errorCode As ERRID, ParamArray arguments As Object())
diagnostics.Add(Diagnostic.Create(VisualBasic.MessageProvider.Instance, CInt(errorCode), arguments))
End Sub
'''
''' In VB, if the output file name isn't specified explicitly, then it is derived from the name of the
''' first input file.
'''
'''
''' http://msdn.microsoft.com/en-us/library/std9609e(v=vs.110)
''' Specify the full name and extension of the file to create. If you do not, the .exe file takes
''' its name from the source-code file containing the Sub Main procedure, and the .dll file takes
''' its name from the first source-code file.
'''
''' However, vbc.cpp has:
'''
'''
Private Sub GetCompilationAndModuleNames(diagnostics As List(Of Diagnostic),
kind As OutputKind,
sourceFiles As List(Of CommandLineSourceFile),
moduleAssemblyName As String,
ByRef outputFileName As String,
ByRef moduleName As String,
ByRef compilationName As String)
Dim simpleName As String = Nothing
If outputFileName Is Nothing Then
Dim first = sourceFiles.FirstOrDefault()
If first.Path IsNot Nothing Then
simpleName = PathUtilities.RemoveExtension(PathUtilities.GetFileName(first.Path))
outputFileName = simpleName & kind.GetDefaultExtension()
If simpleName.Length = 0 AndAlso Not kind.IsNetModule() Then
AddDiagnostic(diagnostics, ERRID.FTL_InputFileNameTooLong, outputFileName)
simpleName = Nothing
outputFileName = Nothing
End If
End If
Else
Dim ext As String = PathUtilities.GetExtension(outputFileName)
If kind.IsNetModule() Then
If ext.Length = 0 Then
outputFileName = outputFileName & ".netmodule"
End If
Else
Dim defaultExtension As String = kind.GetDefaultExtension()
If Not String.Equals(ext, defaultExtension, StringComparison.OrdinalIgnoreCase) Then
simpleName = outputFileName
outputFileName = outputFileName & defaultExtension
End If
If simpleName Is Nothing Then
simpleName = PathUtilities.RemoveExtension(outputFileName)
' /out:".exe"
' Dev11 emits assembly with an empty name, we don't
If simpleName.Length = 0 Then
AddDiagnostic(diagnostics, ERRID.FTL_InputFileNameTooLong, outputFileName)
simpleName = Nothing
outputFileName = Nothing
End If
End If
End If
End If
If kind.IsNetModule() Then
Debug.Assert(Not IsInteractive)
compilationName = moduleAssemblyName
Else
If moduleAssemblyName IsNot Nothing Then
AddDiagnostic(diagnostics, ERRID.ERR_NeedModule)
End If
compilationName = simpleName
End If
If moduleName Is Nothing Then
moduleName = outputFileName
End If
End Sub
End Class
End Namespace