CommandLineParser.vb 103.0 KB
Newer Older
1
' Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
P
Pilchie 已提交
2 3 4 5 6 7

Imports System.Collections.Immutable
Imports System.Globalization
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Text
8
Imports Microsoft.CodeAnalysis.Emit
9
Imports Microsoft.CodeAnalysis.Text
P
Pilchie 已提交
10 11
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.VisualBasic.SyntaxFacts
12
Imports Roslyn.Utilities
P
Pilchie 已提交
13 14 15

Namespace Microsoft.CodeAnalysis.VisualBasic
    ''' <summary>
A
angocke 已提交
16
    ''' The VisualBasicCommandLineParser class contains members used to perform various Visual Basic command line parsing operations.
P
Pilchie 已提交
17
    ''' </summary>
A
angocke 已提交
18
    Public Class VisualBasicCommandLineParser
P
Pilchie 已提交
19 20 21 22
        Inherits CommandLineParser
        ''' <summary>
        ''' Gets the current command line parser.
        ''' </summary>
A
angocke 已提交
23
        Public Shared ReadOnly [Default] As VisualBasicCommandLineParser = New VisualBasicCommandLineParser()
P
Pilchie 已提交
24 25 26 27

        ''' <summary>
        ''' Gets the current interactive command line parser.
        ''' </summary>
A
angocke 已提交
28
        Public Shared ReadOnly Interactive As VisualBasicCommandLineParser = New VisualBasicCommandLineParser(isInteractive:=True)
P
Pilchie 已提交
29 30 31 32 33 34 35 36 37

        ''' <summary>
        ''' Creates a new command line parser.
        ''' </summary>
        ''' <param name="isInteractive">An optional parameter indicating indicating whether to create a interactive command line parser.</param>
        Public Sub New(Optional isInteractive As Boolean = False)
            MyBase.New(VisualBasic.MessageProvider.Instance, isInteractive)
        End Sub

38 39 40
        Private Const s_win32Manifest As String = "win32manifest"
        Private Const s_win32Icon As String = "win32icon"
        Private Const s_win32Res As String = "win32resource"
P
Pilchie 已提交
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61

        ''' <summary>
        ''' Gets the standard Visual Basic source file extension
        ''' </summary>
        ''' <returns>A string representing the standard Visual Basic source file extension.</returns>
        Protected Overrides ReadOnly Property RegularFileExtension As String
            Get
                Return ".vb"
            End Get
        End Property

        ''' <summary>
        ''' Gets the standard Visual Basic script file extension.
        ''' </summary>
        ''' <returns>A string representing the standard Visual Basic script file extension.</returns>
        Protected Overrides ReadOnly Property ScriptFileExtension As String
            Get
                Return ".vbx"
            End Get
        End Property

62 63
        Friend NotOverridable Overrides Function CommonParse(args As IEnumerable(Of String), baseDirectory As String, sdkDirectory As String, additionalReferenceDirectories As String) As CommandLineArguments
            Return Parse(args, baseDirectory, sdkDirectory, additionalReferenceDirectories)
P
Pilchie 已提交
64 65 66 67 68 69 70
        End Function

        ''' <summary>
        ''' Parses a command line.
        ''' </summary>
        ''' <param name="args">A collection of strings representing the command line arguments.</param>
        ''' <param name="baseDirectory">The base directory used for qualifying file locations.</param>
71 72
        ''' <param name="sdkDirectory">The directory to search for mscorlib.</param>
        ''' <param name="additionalReferenceDirectories">A string representing additional reference paths.</param>
P
Pilchie 已提交
73
        ''' <returns>A CommandLineArguments object representing the parsed command line.</returns>
74
        Public Shadows Function Parse(args As IEnumerable(Of String), baseDirectory As String, sdkDirectory As String, Optional additionalReferenceDirectories As String = Nothing) As VisualBasicCommandLineArguments
P
Pilchie 已提交
75 76
            Const GenerateFileNameForDocComment As String = "USE-OUTPUT-NAME"

77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
            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
97
            Dim errorLogPath As String = Nothing
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
            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, ":<file>")
                            Continue For
160
                        End If
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175

                        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
176 177
                End If

178 179 180 181 182
                Select Case name
                    Case "?", "help"
                        If value IsNot Nothing Then
                            Exit Select
                        End If
P
Pilchie 已提交
183

184
                        displayHelp = True
P
Pilchie 已提交
185 186
                        Continue For

187 188 189 190 191 192 193
                    Case "r", "reference"
                        metadataReferences.AddRange(ParseAssemblyReferences(name, value, diagnostics, embedInteropTypes:=False))
                        Continue For

                    Case "a", "analyzer"
                        analyzers.AddRange(ParseAnalyzers(name, value, diagnostics))
                        Continue For
P
Pilchie 已提交
194

195 196 197
                    Case "d", "define"
                        If String.IsNullOrEmpty(value) Then
                            AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":<symbol_list>")
P
Pilchie 已提交
198
                            Continue For
199 200 201 202
                        End If
                        Dim conditionalCompilationDiagnostics As IEnumerable(Of Diagnostic) = Nothing
                        defines = ParseConditionalCompilationSymbols(value, conditionalCompilationDiagnostics, defines)
                        diagnostics.AddRange(conditionalCompilationDiagnostics)
P
Pilchie 已提交
203

204 205 206 207 208
                        Continue For

                    Case "imports", "import"
                        If String.IsNullOrEmpty(value) Then
                            AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, If(name = "import", ":<str>", ":<import_list>"))
P
Pilchie 已提交
209
                            Continue For
210
                        End If
P
Pilchie 已提交
211

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
                        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")
P
Pilchie 已提交
229
                            Continue For
230
                        End If
P
Pilchie 已提交
231

232 233
                        optionStrict = VisualBasic.OptionStrict.On
                        Continue For
P
Pilchie 已提交
234

235 236 237
                    Case "optionstrict-"
                        If value IsNot Nothing Then
                            AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optionstrict")
P
Pilchie 已提交
238
                            Continue For
239
                        End If
P
Pilchie 已提交
240

241 242 243 244 245 246 247 248 249 250 251
                        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
252
                            AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, "optioncompare", value)
253 254 255
                        End If

                        Continue For
P
Pilchie 已提交
256

257 258 259
                    Case "optionexplicit", "optionexplicit+"
                        If value IsNot Nothing Then
                            AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optionexplicit")
P
Pilchie 已提交
260
                            Continue For
261
                        End If
P
Pilchie 已提交
262

263 264
                        optionExplicit = True
                        Continue For
P
Pilchie 已提交
265

266 267 268
                    Case "optionexplicit-"
                        If value IsNot Nothing Then
                            AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optionexplicit")
P
Pilchie 已提交
269
                            Continue For
270
                        End If
P
Pilchie 已提交
271

272 273
                        optionExplicit = False
                        Continue For
P
Pilchie 已提交
274

275 276 277
                    Case "optioninfer", "optioninfer+"
                        If value IsNot Nothing Then
                            AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optioninfer")
P
Pilchie 已提交
278
                            Continue For
279
                        End If
P
Pilchie 已提交
280

281 282
                        optionInfer = True
                        Continue For
P
Pilchie 已提交
283

284 285 286
                    Case "optioninfer-"
                        If value IsNot Nothing Then
                            AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optioninfer")
P
Pilchie 已提交
287
                            Continue For
288
                        End If
P
Pilchie 已提交
289

290 291
                        optionInfer = False
                        Continue For
P
Pilchie 已提交
292

293 294 295
                    Case "codepage"
                        If String.IsNullOrEmpty(value) Then
                            AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "codepage", ":<number>")
P
Pilchie 已提交
296
                            Continue For
297
                        End If
P
Pilchie 已提交
298

299 300 301 302 303 304 305 306
                        Dim encoding = TryParseEncodingName(value)
                        If encoding Is Nothing Then
                            AddDiagnostic(diagnostics, ERRID.ERR_BadCodepage, value)
                            Continue For
                        End If

                        codepage = encoding
                        Continue For
P
Pilchie 已提交
307

308 309 310
                    Case "checksumalgorithm"
                        If String.IsNullOrEmpty(value) Then
                            AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "checksumalgorithm", ":<algorithm>")
P
Pilchie 已提交
311
                            Continue For
312
                        End If
P
Pilchie 已提交
313

314 315 316 317 318
                        Dim newChecksumAlgorithm = TryParseHashAlgorithmName(value)
                        If newChecksumAlgorithm = SourceHashAlgorithm.None Then
                            AddDiagnostic(diagnostics, ERRID.ERR_BadChecksumAlgorithm, value)
                            Continue For
                        End If
P
Pilchie 已提交
319

320 321 322 323 324 325
                        checksumAlgorithm = newChecksumAlgorithm
                        Continue For

                    Case "removeintchecks", "removeintchecks+"
                        If value IsNot Nothing Then
                            AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "removeintchecks")
P
Pilchie 已提交
326
                            Continue For
327
                        End If
P
Pilchie 已提交
328

329 330
                        checkOverflow = False
                        Continue For
P
Pilchie 已提交
331

332 333 334
                    Case "removeintchecks-"
                        If value IsNot Nothing Then
                            AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "removeintchecks")
P
Pilchie 已提交
335
                            Continue For
336
                        End If
P
Pilchie 已提交
337

338 339 340 341 342 343 344 345 346
                        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)
P
Pilchie 已提交
347
                            End If
348 349
                        End If
                        Continue For
P
Pilchie 已提交
350

351 352 353
                    Case "preferreduilang"
                        If (String.IsNullOrEmpty(value)) Then
                            AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":<string>")
P
Pilchie 已提交
354
                            Continue For
355
                        End If
P
Pilchie 已提交
356

357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
                        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 ':<file>',
                                ' While the Dev11 VB compiler reports "BC2012 : can't open ' ' for writing,
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":<file>")
                            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)
P
Pilchie 已提交
391
                            End If
392
                            Continue For
P
Pilchie 已提交
393

394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
                        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", ":<string>")
                            ElseIf Not AssemblyIdentity.TryParseDisplayName(value, identity) OrElse
                                       Not MetadataHelpers.IsValidAssemblyOrModuleName(identity.Name) Then
                                AddDiagnostic(diagnostics, ERRID.ERR_InvalidAssemblyName, value, arg)
                            Else
                                moduleAssemblyName = identity.Name
P
Pilchie 已提交
411 412 413 414
                            End If

                            Continue For

415
                        Case "rootnamespace"
416
                            If String.IsNullOrEmpty(value) Then
417
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "rootnamespace", ":<string>")
418 419 420
                                Continue For
                            End If

421 422 423 424 425 426 427 428
                            rootNamespace = value
                            Continue For

                        Case "doc"
                            parseDocumentationComments = True
                            If value Is Nothing Then
                                ' Illegal in C#, but works in VB
                                documentationPath = GenerateFileNameForDocComment
429 430
                                Continue For
                            End If
431 432 433 434 435 436 437 438 439 440
                            Dim unquoted = RemoveAllQuotes(value)
                            If unquoted.Length = 0 Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "doc", ":<file>")
                            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
441 442 443

                            Continue For

444
                        Case "doc+"
P
Pilchie 已提交
445
                            If value IsNot Nothing Then
446
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "doc")
P
Pilchie 已提交
447 448
                            End If

449 450 451
                            ' Seems redundant with default values, but we need to clobber any preceding /doc switches
                            documentationPath = GenerateFileNameForDocComment
                            parseDocumentationComments = True
P
Pilchie 已提交
452 453
                            Continue For

454
                        Case "doc-"
P
Pilchie 已提交
455
                            If value IsNot Nothing Then
456
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "doc")
P
Pilchie 已提交
457 458
                            End If

459 460 461
                            ' Seems redundant with default values, but we need to clobber any preceding /doc switches
                            documentationPath = Nothing
                            parseDocumentationComments = False
P
Pilchie 已提交
462 463
                            Continue For

464 465 466 467 468 469 470 471 472 473
                        Case "errorlog"
                            Dim unquoted = RemoveAllQuotes(value)
                            If String.IsNullOrEmpty(unquoted) Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "errorlog", ":<file>")
                            Else
                                errorLogPath = ParseGenericPathToFile(unquoted, diagnostics, baseDirectory)
                            End If

                            Continue For

474 475 476
                        Case "netcf"
                            ' Do nothing as we no longer have any use for implementing this switch and 
                            ' want to avoid failing with any warnings/errors
P
Pilchie 已提交
477
                            Continue For
478

479 480 481
                        Case "libpath"
                            If String.IsNullOrEmpty(value) Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "libpath", ":<path_list>")
482 483 484
                                Continue For
                            End If

485
                            libPaths.AddRange(ParseSeparatedPaths(RemoveAllQuotes(value)))
486
                            Continue For
P
Pilchie 已提交
487

488 489 490
                        Case "sdkpath"
                            If String.IsNullOrEmpty(value) Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "sdkpath", ":<path>")
P
Pilchie 已提交
491
                                Continue For
492
                            End If
P
Pilchie 已提交
493

494 495 496
                            sdkPaths.Clear()
                            sdkPaths.AddRange(ParseSeparatedPaths(RemoveAllQuotes(value)))
                            Continue For
P
Pilchie 已提交
497

498 499 500
                        Case "recurse"
                            If String.IsNullOrEmpty(value) Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "recurse", ":<wildcard>")
P
Pilchie 已提交
501
                                Continue For
502
                            End If
P
Pilchie 已提交
503

504 505 506 507 508 509
                            Dim before As Integer = sourceFiles.Count
                            sourceFiles.AddRange(ParseRecurseArgument(value, baseDirectory, diagnostics))
                            If sourceFiles.Count > before Then
                                hasSourceFiles = True
                            End If
                            Continue For
P
Pilchie 已提交
510

511 512 513
                        Case "addmodule"
                            If String.IsNullOrEmpty(value) Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "addmodule", ":<file_list>")
P
Pilchie 已提交
514
                                Continue For
515
                            End If
P
Pilchie 已提交
516

517 518 519 520 521 522 523
                            ' 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
P
Pilchie 已提交
524

525 526 527
                        Case "l", "link"
                            metadataReferences.AddRange(ParseAssemblyReferences(name, value, diagnostics, embedInteropTypes:=True))
                            Continue For
P
Pilchie 已提交
528

529
                        Case "win32resource"
530
                            win32ResourceFile = GetWin32Setting(s_win32Res, value, diagnostics)
531
                            Continue For
P
Pilchie 已提交
532

533
                        Case "win32icon"
534
                            win32IconFile = GetWin32Setting(s_win32Icon, value, diagnostics)
535
                            Continue For
P
Pilchie 已提交
536

537
                        Case "win32manifest"
538
                            win32ManifestFile = GetWin32Setting(s_win32Manifest, value, diagnostics)
539
                            Continue For
P
Pilchie 已提交
540

541 542 543 544
                        Case "nowin32manifest"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
545

546 547
                            noWin32Manifest = True
                            Continue For
P
Pilchie 已提交
548

549 550 551 552 553 554
                        Case "res", "resource"
                            Dim embeddedResource = ParseResourceDescription(name, value, baseDirectory, diagnostics, embedded:=True)
                            If embeddedResource IsNot Nothing Then
                                managedResources.Add(embeddedResource)
                            End If
                            Continue For
P
Pilchie 已提交
555

556 557 558 559 560 561
                        Case "linkres", "linkresource"
                            Dim linkedResource = ParseResourceDescription(name, value, baseDirectory, diagnostics, embedded:=False)
                            If linkedResource IsNot Nothing Then
                                managedResources.Add(linkedResource)
                            End If
                            Continue For
P
Pilchie 已提交
562

563
                        Case "debug"
P
Pilchie 已提交
564

565 566
                            ' parse only for backwards compat
                            If value IsNot Nothing Then
P
Pilchie 已提交
567
                                If String.IsNullOrEmpty(value) Then
568 569 570
                                    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
571
                                    AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, "debug", value)
P
Pilchie 已提交
572
                                End If
573
                            End If
P
Pilchie 已提交
574

575 576
                            emitPdb = True
                            Continue For
P
Pilchie 已提交
577

578 579 580 581
                        Case "debug+"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "debug")
                            End If
P
Pilchie 已提交
582

583 584
                            emitPdb = True
                            Continue For
P
Pilchie 已提交
585

586 587 588 589
                        Case "debug-"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "debug")
                            End If
P
Pilchie 已提交
590

591 592
                            emitPdb = False
                            Continue For
P
Pilchie 已提交
593

594 595 596
                        Case "optimize", "optimize+"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optimize")
P
Pilchie 已提交
597
                                Continue For
598
                            End If
P
Pilchie 已提交
599

600 601
                            optimize = True
                            Continue For
P
Pilchie 已提交
602

603 604 605
                        Case "optimize-"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optimize")
P
Pilchie 已提交
606
                                Continue For
607
                            End If
P
Pilchie 已提交
608

609 610
                            optimize = False
                            Continue For
P
Pilchie 已提交
611

612 613 614
                        Case "parallel", "p"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, name)
P
Pilchie 已提交
615
                                Continue For
616
                            End If
P
Pilchie 已提交
617

618 619
                            concurrentBuild = True
                            Continue For
P
Pilchie 已提交
620

621 622 623
                        Case "parallel+", "p+"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, name.Substring(0, name.Length - 1))
P
Pilchie 已提交
624
                                Continue For
625
                            End If
P
Pilchie 已提交
626

627 628
                            concurrentBuild = True
                            Continue For
P
Pilchie 已提交
629

630 631 632
                        Case "parallel-", "p-"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, name.Substring(0, name.Length - 1))
P
Pilchie 已提交
633
                                Continue For
634
                            End If
P
Pilchie 已提交
635

636 637
                            concurrentBuild = False
                            Continue For
P
Pilchie 已提交
638

639 640 641
                        Case "warnaserror", "warnaserror+"
                            If value Is Nothing Then
                                generalDiagnosticOption = ReportDiagnostic.Error
P
Pilchie 已提交
642

643 644 645 646 647 648
                                specificDiagnosticOptionsFromGeneralArguments.Clear()
                                For Each pair In specificDiagnosticOptionsFromRuleSet
                                    If pair.Value = ReportDiagnostic.Warn Then
                                        specificDiagnosticOptionsFromGeneralArguments.Add(pair.Key, ReportDiagnostic.Error)
                                    End If
                                Next
P
Pilchie 已提交
649 650

                                Continue For
651
                            End If
P
Pilchie 已提交
652

653 654
                            AddWarnings(specificDiagnosticOptionsFromSpecificArguments, ReportDiagnostic.Error, ParseWarnings(value))
                            Continue For
P
Pilchie 已提交
655

656 657 658 659
                        Case "warnaserror-"
                            If value Is Nothing Then
                                If generalDiagnosticOption <> ReportDiagnostic.Suppress Then
                                    generalDiagnosticOption = ReportDiagnostic.Default
P
Pilchie 已提交
660 661
                                End If

662
                                specificDiagnosticOptionsFromGeneralArguments.Clear()
P
Pilchie 已提交
663 664

                                Continue For
665
                            End If
P
Pilchie 已提交
666

667 668 669 670 671 672
                            For Each id In ParseWarnings(value)
                                Dim ruleSetValue As ReportDiagnostic
                                If specificDiagnosticOptionsFromRuleSet.TryGetValue(id, ruleSetValue) Then
                                    specificDiagnosticOptionsFromSpecificArguments(id) = ruleSetValue
                                Else
                                    specificDiagnosticOptionsFromSpecificArguments(id) = ReportDiagnostic.Default
P
Pilchie 已提交
673
                                End If
674
                            Next
P
Pilchie 已提交
675

676
                            Continue For
677

678 679 680
                        Case "nowarn"
                            If value Is Nothing Then
                                generalDiagnosticOption = ReportDiagnostic.Suppress
P
Pilchie 已提交
681

682 683 684 685
                                specificDiagnosticOptionsFromGeneralArguments.Clear()
                                For Each pair In specificDiagnosticOptionsFromRuleSet
                                    If pair.Value <> ReportDiagnostic.Error Then
                                        specificDiagnosticOptionsFromGeneralArguments.Add(pair.Key, ReportDiagnostic.Suppress)
686 687 688
                                    End If
                                Next

P
Pilchie 已提交
689
                                Continue For
690
                            End If
P
Pilchie 已提交
691

692 693
                            AddWarnings(specificDiagnosticOptionsFromNoWarnArguments, ReportDiagnostic.Suppress, ParseWarnings(value))
                            Continue For
P
Pilchie 已提交
694

695 696 697
                        Case "langversion"
                            If value Is Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "langversion", ":<number>")
P
Pilchie 已提交
698
                                Continue For
699
                            End If
P
Pilchie 已提交
700

701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
                            If String.IsNullOrEmpty(value) Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "langversion", ":<number>")
                            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
716
                                        AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, "langversion", value)
717 718
                                End Select
                            End If
P
Pilchie 已提交
719

720
                            Continue For
P
Pilchie 已提交
721

722 723 724
                        Case "delaysign", "delaysign+"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "delaysign")
P
Pilchie 已提交
725
                                Continue For
726
                            End If
P
Pilchie 已提交
727

728 729
                            delaySignSetting = True
                            Continue For
P
Pilchie 已提交
730

731 732 733
                        Case "delaysign-"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "delaysign")
P
Pilchie 已提交
734
                                Continue For
735
                            End If
P
Pilchie 已提交
736

737 738
                            delaySignSetting = False
                            Continue For
P
Pilchie 已提交
739

740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757
                        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", ":<string>")
                            Else
                                keyContainerSetting = value
                            End If
                            Continue For
P
Pilchie 已提交
758

759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
                        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", ":<file>")
                            Else
                                keyFileSetting = RemoveAllQuotes(value)
                            End If
                            Continue For
P
Pilchie 已提交
777

778 779 780 781
                        Case "highentropyva", "highentropyva+"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
782

783 784
                            highEntropyVA = True
                            Continue For
P
Pilchie 已提交
785

786 787 788 789
                        Case "highentropyva-"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
790

791 792
                            highEntropyVA = False
                            Continue For
P
Pilchie 已提交
793

794 795 796 797
                        Case "nologo", "nologo+"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
798

799 800
                            displayLogo = False
                            Continue For
P
Pilchie 已提交
801

802 803 804 805
                        Case "nologo-"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
806

807 808
                            displayLogo = True
                            Continue For
P
Pilchie 已提交
809

810 811 812
                        Case "quiet+"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "quiet")
P
Pilchie 已提交
813
                                Continue For
814
                            End If
P
Pilchie 已提交
815

816 817
                            outputLevel = VisualBasic.OutputLevel.Quiet
                            Continue For
P
Pilchie 已提交
818

819 820 821 822
                        Case "quiet"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
823

824 825
                            outputLevel = VisualBasic.OutputLevel.Quiet
                            Continue For
P
Pilchie 已提交
826

827 828 829 830
                        Case "verbose"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
831

832 833
                            outputLevel = VisualBasic.OutputLevel.Verbose
                            Continue For
P
Pilchie 已提交
834

835 836 837
                        Case "verbose+"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "verbose")
P
Pilchie 已提交
838
                                Continue For
839
                            End If
P
Pilchie 已提交
840

841 842
                            outputLevel = VisualBasic.OutputLevel.Verbose
                            Continue For
P
Pilchie 已提交
843

844 845 846
                        Case "quiet-", "verbose-"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, name.Substring(0, name.Length - 1))
P
Pilchie 已提交
847
                                Continue For
848
                            End If
P
Pilchie 已提交
849

850 851
                            outputLevel = VisualBasic.OutputLevel.Normal
                            Continue For
P
Pilchie 已提交
852

853 854 855 856
                        Case "utf8output", "utf8output+"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "utf8output")
                            End If
P
Pilchie 已提交
857

858 859
                            utf8output = True
                            Continue For
P
Pilchie 已提交
860

861 862 863 864
                        Case "utf8output-"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "utf8output")
                            End If
P
Pilchie 已提交
865

866 867
                            utf8output = False
                            Continue For
P
Pilchie 已提交
868

869 870 871
                        Case "noconfig"
                            ' It is already handled (see CommonCommandLineCompiler.cs).
                            Continue For
P
Pilchie 已提交
872

873 874 875 876 877
                        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.
P
Pilchie 已提交
878

879 880 881 882 883
                            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.
P
Pilchie 已提交
884

885
                            Continue For
P
Pilchie 已提交
886

887 888 889
                        Case "novbruntimeref"
                            ' The switch is no longer supported and for backwards compat ignored.
                            Continue For
P
Pilchie 已提交
890

891 892 893 894 895 896 897
                        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, ":<class>")
P
Pilchie 已提交
898
                                Continue For
899
                            End If
P
Pilchie 已提交
900

901 902
                            mainTypeName = value
                            Continue For
P
Pilchie 已提交
903

904 905 906
                        Case "subsystemversion"
                            If String.IsNullOrEmpty(value) Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":<version>")
P
Pilchie 已提交
907
                                Continue For
908
                            End If
P
Pilchie 已提交
909

910 911 912 913 914 915 916
                            Dim version As SubsystemVersion = Nothing
                            If SubsystemVersion.TryParse(value, version) Then
                                ssVersion = version
                            Else
                                AddDiagnostic(diagnostics, ERRID.ERR_InvalidSubsystemVersion, value)
                            End If
                            Continue For
P
Pilchie 已提交
917

918 919 920 921
                        Case "touchedfiles"
                            Dim unquoted = RemoveAllQuotes(value)
                            If (String.IsNullOrEmpty(unquoted)) Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":<touchedfiles>")
P
Pilchie 已提交
922
                                Continue For
923 924 925 926
                            Else
                                touchedFilesPath = unquoted
                            End If
                            Continue For
P
Pilchie 已提交
927

928 929 930
                        Case "fullpaths", "errorendlocation"
                            UnimplementedSwitch(diagnostics, name)
                            Continue For
P
Pilchie 已提交
931

932 933 934 935
                        Case "nostdlib"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
936

937 938
                            noStdLib = True
                            Continue For
P
Pilchie 已提交
939

940 941 942 943
                        Case "vbruntime"
                            If value Is Nothing Then
                                GoTo lVbRuntimePlus
                            End If
P
Pilchie 已提交
944

945 946 947 948 949
                            ' NOTE: that Dev11 does not report errors on empty or invalid file specified
                            vbRuntimePath = RemoveAllQuotes(value)
                            includeVbRuntimeReference = True
                            embedVbCoreRuntime = False
                            Continue For
P
Pilchie 已提交
950

951 952 953 954
                        Case "vbruntime+"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
955 956

lVbRuntimePlus:
957 958 959 960
                            vbRuntimePath = Nothing
                            includeVbRuntimeReference = True
                            embedVbCoreRuntime = False
                            Continue For
P
Pilchie 已提交
961

962 963 964 965
                        Case "vbruntime-"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
966

967 968 969 970
                            vbRuntimePath = Nothing
                            includeVbRuntimeReference = False
                            embedVbCoreRuntime = False
                            Continue For
P
Pilchie 已提交
971

972 973 974 975
                        Case "vbruntime*"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
976

977 978 979 980
                            vbRuntimePath = Nothing
                            includeVbRuntimeReference = False
                            embedVbCoreRuntime = True
                            Continue For
P
Pilchie 已提交
981

982 983 984 985 986 987
                        Case "platform"
                            If value IsNot Nothing Then
                                platform = ParsePlatform(name, value, diagnostics)
                            Else
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "platform", ":<string>")
                            End If
P
Pilchie 已提交
988

989
                            Continue For
P
Pilchie 已提交
990

991 992 993
                        Case "filealign"
                            fileAlignment = ParseFileAlignment(name, value, diagnostics)
                            Continue For
P
Pilchie 已提交
994

995 996 997
                        Case "baseaddress"
                            baseAddress = ParseBaseAddress(name, value, diagnostics)
                            Continue For
998

999 1000 1001
                        Case "ruleset"
                            '  The ruleset arg has already been processed in a separate pass above.
                            Continue For
1002

1003 1004 1005 1006 1007 1008 1009
                        Case "features"
                            If value Is Nothing Then
                                features.Clear()
                            Else
                                features.Add(value)
                            End If
                            Continue For
1010

1011 1012 1013
                        Case "additionalfile"
                            If String.IsNullOrEmpty(value) Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":<file_list>")
1014
                                Continue For
1015
                            End If
P
Pilchie 已提交
1016

1017 1018 1019 1020
                            additionalFiles.AddRange(ParseAdditionalFileArgument(value, baseDirectory, diagnostics))
                            Continue For
                    End Select
                End If
P
Pilchie 已提交
1021

1022 1023
                AddDiagnostic(diagnostics, ERRID.WRN_BadSwitch, arg)
            Next
1024

1025
            Dim specificDiagnosticOptions = New Dictionary(Of String, ReportDiagnostic)(specificDiagnosticOptionsFromRuleSet, CaseInsensitiveComparison.Comparer)
1026

1027 1028 1029
            For Each item In specificDiagnosticOptionsFromGeneralArguments
                specificDiagnosticOptions(item.Key) = item.Value
            Next
1030

1031 1032 1033
            For Each item In specificDiagnosticOptionsFromSpecificArguments
                specificDiagnosticOptions(item.Key) = item.Value
            Next
1034

1035 1036 1037
            For Each item In specificDiagnosticOptionsFromNoWarnArguments
                specificDiagnosticOptions(item.Key) = item.Value
            Next
P
Pilchie 已提交
1038

1039 1040 1041 1042 1043 1044
            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
P
Pilchie 已提交
1045
                End If
1046 1047 1048 1049
            End If

            ' Prepare SDK PATH
            If sdkPaths.Count = 0 Then
1050
                sdkPaths.Add(sdkDirectory)
1051 1052 1053
            End If

            ' Locate default 'mscorlib.dll' or 'System.Runtime.dll', if any.
J
Jared Parsons 已提交
1054
            Dim defaultCoreLibraryReference As CommandLineReference? = LoadCoreLibraryReference(sdkPaths, baseDirectory, sdkDirectory)
1055 1056 1057 1058

            ' If /nostdlib is not specified, load System.dll
            ' Dev12 does it through combination of CompilerHost::InitStandardLibraryList and CompilerProject::AddStandardLibraries.
            If Not noStdLib Then
J
Jared Parsons 已提交
1059
                Dim systemDllPath As String = FindFileInSdkPath(sdkPaths, "System.dll", baseDirectory, sdkDirectory)
1060 1061 1062 1063
                If systemDllPath Is Nothing Then
                    AddDiagnostic(diagnostics, ERRID.WRN_CannotFindStandardLibrary1, "System.dll")
                Else
                    metadataReferences.Add(
1064
                            New CommandLineReference(systemDllPath, New MetadataReferenceProperties(MetadataImageKind.Assembly)))
P
Pilchie 已提交
1065
                End If
1066 1067 1068 1069 1070 1071
                ' 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
J
Jared Parsons 已提交
1072
                    Dim msVbDllPath As String = FindFileInSdkPath(sdkPaths, "Microsoft.VisualBasic.dll", baseDirectory, sdkDirectory)
1073 1074
                    If msVbDllPath Is Nothing Then
                        AddDiagnostic(diagnostics, ERRID.ERR_LibNotFound, "Microsoft.VisualBasic.dll")
P
Pilchie 已提交
1075
                    Else
1076 1077
                        metadataReferences.Add(
                                New CommandLineReference(msVbDllPath, New MetadataReferenceProperties(MetadataImageKind.Assembly)))
P
Pilchie 已提交
1078
                    End If
1079 1080
                Else
                    metadataReferences.Add(New CommandLineReference(vbRuntimePath, New MetadataReferenceProperties(MetadataImageKind.Assembly)))
P
Pilchie 已提交
1081
                End If
1082 1083 1084
            End If

            ' add additional reference paths if specified
1085 1086
            If Not String.IsNullOrWhiteSpace(additionalReferenceDirectories) Then
                libPaths.AddRange(ParseSeparatedPaths(additionalReferenceDirectories))
1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101
            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
P
Pilchie 已提交
1102
                End If
1103
            End If
P
Pilchie 已提交
1104

1105 1106 1107 1108 1109 1110
            ' 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
P
Pilchie 已提交
1111

1112 1113
            Dim compilationName As String = Nothing
            GetCompilationAndModuleNames(diagnostics, outputKind, sourceFiles, moduleAssemblyName, outputFileName, moduleName, compilationName)
P
Pilchie 已提交
1114

1115
            If Not IsInteractive AndAlso
P
Pilchie 已提交
1116 1117 1118 1119
                    Not hasSourceFiles AndAlso
                    Not managedResources.IsEmpty() AndAlso
                    outputFileName = Nothing AndAlso
                    Not flattenedArgs.IsEmpty() Then
1120 1121
                AddDiagnostic(diagnostics, ERRID.ERR_NoSourcesOut)
            End If
P
Pilchie 已提交
1122

1123
            Dim parseOptions = New VisualBasicParseOptions(
1124
                    languageVersion:=languageVersion,
P
Pilchie 已提交
1125 1126
                    documentationMode:=If(parseDocumentationComments, DocumentationMode.Diagnose, DocumentationMode.None),
                    kind:=SourceCodeKind.Regular,
1127
                    preprocessorSymbols:=AddPredefinedPreprocessorSymbols(outputKind, defines.AsImmutableOrEmpty()))
P
Pilchie 已提交
1128

1129
            Dim scriptParseOptions = parseOptions.WithKind(SourceCodeKind.Script)
P
Pilchie 已提交
1130

1131
            Dim options = New VisualBasicCompilationOptions(
P
Pilchie 已提交
1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
                        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,
1151
                        optimizationLevel:=If(optimize, OptimizationLevel.Release, OptimizationLevel.Debug),
1152
                        parseOptions:=parseOptions).WithFeatures(features.AsImmutable())
P
Pilchie 已提交
1153

1154
            Dim emitOptions = New EmitOptions(
1155 1156 1157
                    metadataOnly:=False,
                    debugInformationFormat:=DebugInformationFormat.Pdb,
                    pdbFilePath:=Nothing, ' to be determined later
1158
                    outputNameOverride:=Nothing,  ' to be determined later
1159 1160 1161 1162 1163 1164
                    fileAlignment:=fileAlignment,
                    baseAddress:=baseAddress,
                    highEntropyVirtualAddressSpace:=highEntropyVA,
                    subsystemVersion:=ssVersion,
                    runtimeMetadataVersion:=Nothing)

1165 1166
            ' add option incompatibility errors if any
            diagnostics.AddRange(options.Errors)
P
Pilchie 已提交
1167

1168 1169 1170 1171
            If documentationPath Is GenerateFileNameForDocComment Then
                documentationPath = PathUtilities.CombineAbsoluteAndRelativePaths(outputDirectory, PathUtilities.RemoveExtension(outputFileName))
                documentationPath = documentationPath + ".xml"
            End If
P
Pilchie 已提交
1172

1173
            Return New VisualBasicCommandLineArguments With
P
Pilchie 已提交
1174 1175 1176 1177 1178 1179 1180 1181 1182
                {
                    .IsInteractive = IsInteractive,
                    .BaseDirectory = baseDirectory,
                    .Errors = diagnostics.AsImmutable(),
                    .Utf8Output = utf8output,
                    .CompilationName = compilationName,
                    .OutputFileName = outputFileName,
                    .OutputDirectory = outputDirectory,
                    .DocumentationPath = documentationPath,
1183
                    .ErrorLogPath = errorLogPath,
P
Pilchie 已提交
1184 1185
                    .SourceFiles = sourceFiles.AsImmutable(),
                    .Encoding = codepage,
1186
                    .ChecksumAlgorithm = checksumAlgorithm,
P
Pilchie 已提交
1187
                    .MetadataReferences = metadataReferences.AsImmutable(),
1188
                    .AnalyzerReferences = analyzers.AsImmutable(),
1189
                    .AdditionalFiles = additionalFiles.AsImmutable(),
P
Pilchie 已提交
1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200
                    .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),
1201
                    .EmitOptions = emitOptions,
P
Pilchie 已提交
1202 1203 1204
                    .ScriptArguments = scriptArgs.AsImmutableOrEmpty(),
                    .TouchedFilesPath = touchedFilesPath,
                    .OutputLevel = outputLevel,
1205
                    .EmitPdb = emitPdb,
P
Pilchie 已提交
1206 1207 1208 1209 1210 1211
                    .DefaultCoreLibraryReference = defaultCoreLibraryReference,
                    .PreferredUILang = preferredUILang,
                    .SqmSessionGuid = sqmsessionguid
                }
        End Function

J
Jared Parsons 已提交
1212
        Private Function LoadCoreLibraryReference(sdkPaths As List(Of String), baseDirectory As String, sdkDirectory As String) As CommandLineReference?
P
Pilchie 已提交
1213 1214 1215 1216 1217 1218 1219 1220 1221
            ' 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. 
J
Jared Parsons 已提交
1222 1223
            Dim msCorLibPath As String = FindFileInSdkPath(sdkPaths, "mscorlib.dll", baseDirectory, sdkDirectory)
            Dim systemRuntimePath As String = FindFileInSdkPath(sdkPaths, "System.Runtime.dll", baseDirectory, sdkDirectory)
P
Pilchie 已提交
1224 1225 1226

            If systemRuntimePath IsNot Nothing Then
                If msCorLibPath Is Nothing Then
1227
                    Return New CommandLineReference(systemRuntimePath, New MetadataReferenceProperties(MetadataImageKind.Assembly))
P
Pilchie 已提交
1228 1229 1230 1231
                End If

                ' Load System.Runtime.dll and see if it has any references
                Try
1232 1233 1234 1235
                    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
1236
                            Return New CommandLineReference(systemRuntimePath, New MetadataReferenceProperties(MetadataImageKind.Assembly))
P
Pilchie 已提交
1237
                        End If
1238
                    End Using
P
Pilchie 已提交
1239 1240 1241 1242 1243
                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'
1244
                Return New CommandLineReference(msCorLibPath, New MetadataReferenceProperties(MetadataImageKind.Assembly))
P
Pilchie 已提交
1245 1246 1247 1248
            End If

            If msCorLibPath IsNot Nothing Then
                ' We return a reference to 'mscorlib.dll'
1249
                Return New CommandLineReference(msCorLibPath, New MetadataReferenceProperties(MetadataImageKind.Assembly))
P
Pilchie 已提交
1250 1251 1252 1253 1254
            End If

            Return Nothing
        End Function

J
Jared Parsons 已提交
1255
        Private Function FindFileInSdkPath(sdkPaths As List(Of String), fileName As String, baseDirectory As String, sdkDirectory As String) As String
P
Pilchie 已提交
1256
            For Each path In sdkPaths
J
Jared Parsons 已提交
1257
                Dim absolutePath = FileUtilities.ResolveRelativePath(If(path, sdkDirectory), baseDirectory)
P
Pilchie 已提交
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282
                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, ":<file>")
            Else
                Dim noQuotes As String = RemoveAllQuotes(value)
                If (String.IsNullOrWhiteSpace(noQuotes)) Then
                    AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, arg, ":<file>")
                Else
                    Return noQuotes
                End If
            End If

            Return Nothing
        End Function

1283
        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)
P
Pilchie 已提交
1284 1285 1286 1287 1288 1289 1290 1291
            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
1292
            AddNormalizedPaths(builder, sdkPaths, baseDirectory)
P
Pilchie 已提交
1293 1294 1295 1296 1297 1298 1299 1300

            ' 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
1301
            AddNormalizedPaths(builder, libPaths, baseDirectory)
P
Pilchie 已提交
1302 1303 1304 1305

            Return builder.ToImmutableAndFree()
        End Function

1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317
        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

P
Pilchie 已提交
1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355
        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
1356
                    AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, optionName, value)
P
Pilchie 已提交
1357 1358 1359 1360 1361 1362 1363 1364
                    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 <file_list>?
                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":<file_list>")
1365
                Return SpecializedCollections.EmptyEnumerable(Of CommandLineReference)()
P
Pilchie 已提交
1366 1367 1368 1369 1370 1371 1372 1373 1374 1375
            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).
1376
                   Select(Function(path) New CommandLineReference(path, New MetadataReferenceProperties(MetadataImageKind.Assembly, embedInteropTypes:=embedInteropTypes)))
P
Pilchie 已提交
1377 1378
        End Function

1379
        Private Function ParseAnalyzers(name As String, value As String, diagnostics As IList(Of Diagnostic)) As IEnumerable(Of CommandLineAnalyzerReference)
P
Pilchie 已提交
1380 1381 1382
            If String.IsNullOrEmpty(value) Then
                ' TODO: localize <file_list>?
                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":<file_list>")
1383
                Return SpecializedCollections.EmptyEnumerable(Of CommandLineAnalyzerReference)()
P
Pilchie 已提交
1384 1385 1386 1387
            End If

            Return ParseSeparatedPaths(value).
                   Select(Function(path)
1388
                              Return New CommandLineAnalyzerReference(path)
P
Pilchie 已提交
1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440
                          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, ":<resinfo>")
                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

1441 1442 1443
            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.
1444
                                                      Return New FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
1445
                                                  End Function
P
Pilchie 已提交
1446 1447 1448 1449 1450 1451 1452 1453 1454 1455
            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

1456
            AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, name, nullStringText)
P
Pilchie 已提交
1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470
        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

        ''' <summary>
1471 1472
        ''' Converts a sequence of definitions provided by a caller (public API) into map 
        ''' of definitions used internally.
P
Pilchie 已提交
1473
        ''' </summary>
1474 1475 1476 1477
        ''' <exception cref="ArgumentException">Invalid value provided.</exception>
        Private Shared Function PublicSymbolsToInternalDefines(symbols As IEnumerable(Of KeyValuePair(Of String, Object)),
                                                               parameterName As String) As ImmutableDictionary(Of String, InternalSyntax.CConst)

1478
            Dim result = ImmutableDictionary.CreateBuilder(Of String, InternalSyntax.CConst)(CaseInsensitiveComparison.Comparer)
P
Pilchie 已提交
1479 1480

            If symbols IsNot Nothing Then
1481 1482 1483 1484
                For Each symbol In symbols
                    Dim constant = InternalSyntax.CConst.TryCreate(symbol.Value)

                    If constant Is Nothing Then
1485
                        Throw New ArgumentException(String.Format(ErrorFactory.IdToString(ERRID.IDS_InvalidPreprocessorConstantType, Culture), symbol.Key, symbol.Value.GetType()), parameterName)
1486 1487 1488
                    End If

                    result(symbol.Key) = constant
P
Pilchie 已提交
1489 1490 1491
                Next
            End If

1492
            Return result.ToImmutable()
P
Pilchie 已提交
1493 1494 1495 1496 1497 1498 1499
        End Function

        ''' <summary>
        ''' Converts ImmutableDictionary of definitions used internallyinto IReadOnlyDictionary of definitions 
        ''' returned to a caller (of public API)
        ''' </summary>
        Private Shared Function InternalDefinesToPublicSymbols(defines As ImmutableDictionary(Of String, InternalSyntax.CConst)) As IReadOnlyDictionary(Of String, Object)
1500
            Dim result = ImmutableDictionary.CreateBuilder(Of String, Object)(CaseInsensitiveComparison.Comparer)
P
Pilchie 已提交
1501 1502

            For Each kvp In defines
1503
                result(kvp.Key) = kvp.Value.ValueAsObject
P
Pilchie 已提交
1504 1505
            Next

1506
            Return result.ToImmutable()
P
Pilchie 已提交
1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518
        End Function

        ''' <summary>
        ''' 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.
        ''' </summary>
        ''' <param name="symbolList">
        ''' 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.
        ''' </param>
        ''' <param name="diagnostics">A collection of reported diagnostics during parsing of symbolList, can be empty IEnumerable.</param>
        ''' <param name="symbols">A collection representing existing symbols. Symbols parsed from <paramref name="symbolList"/> will be merged with this dictionary. </param>
1519
        ''' <exception cref="ArgumentException">Invalid value provided.</exception>
P
Pilchie 已提交
1520 1521 1522
        Public Shared Function ParseConditionalCompilationSymbols(
            symbolList As String,
            <Out> ByRef diagnostics As IEnumerable(Of Diagnostic),
1523
            Optional symbols As IEnumerable(Of KeyValuePair(Of String, Object)) = Nothing
P
Pilchie 已提交
1524 1525
        ) As IReadOnlyDictionary(Of String, Object)

1526
            Dim diagnosticBuilder = ArrayBuilder(Of Diagnostic).GetInstance()
P
Pilchie 已提交
1527 1528
            Dim parsedTokensAsString As New StringBuilder

1529
            Dim defines As ImmutableDictionary(Of String, InternalSyntax.CConst) = PublicSymbolsToInternalDefines(symbols, "symbols")
P
Pilchie 已提交
1530 1531 1532 1533 1534

            ' remove quotes around the whole /define argument (incl. nested)
            Dim unquotedString As String
            Do
                unquotedString = symbolList
1535
                symbolList = symbolList.Unquote()
1536
            Loop While Not String.Equals(symbolList, unquotedString, StringComparison.Ordinal)
P
Pilchie 已提交
1537 1538 1539 1540

            ' unescape quotes \" -> "
            symbolList = symbolList.Replace("\""", """")

P
Pharring 已提交
1541
            Dim trimmedSymbolList As String = symbolList.TrimEnd(Nothing)
P
Pilchie 已提交
1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571
            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,
1572
                                        ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedEOS),
P
Pilchie 已提交
1573 1574 1575 1576 1577 1578 1579 1580 1581 1582
                                        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...
1583
                        While tokens.Current.Kind = SyntaxKind.CommaToken OrElse tokens.Current.Kind = SyntaxKind.ColonToken
P
Pilchie 已提交
1584

1585
                            If lastSeparatorToken.Kind = SyntaxKind.None Then
P
Pilchie 已提交
1586 1587 1588
                                ' accept multiple : or ,
                                lastSeparatorToken = tokens.Current

1589
                            ElseIf lastSeparatorToken.Kind <> tokens.Current.Kind Then
P
Pilchie 已提交
1590
                                ' but not mixing them, e.g. ::,,::
1591
                                GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString, stopTokenKind:=lastSeparatorToken.Kind, includeCurrentToken:=True)
P
Pilchie 已提交
1592 1593 1594 1595

                                diagnosticBuilder.Add(
                                    New DiagnosticWithInfo(
                                        ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
1596
                                            ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedIdentifier),
P
Pilchie 已提交
1597 1598 1599 1600 1601 1602 1603
                                            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
1604
                            If tokens.Current.Kind <> SyntaxKind.EndOfFileToken Then
P
Pilchie 已提交
1605 1606 1607 1608 1609 1610 1611 1612
                                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
1613
                        If tokens.Current.Kind = SyntaxKind.EndOfFileToken Then
P
Pilchie 已提交
1614 1615 1616 1617

                            Dim eof = tokens.Current

                            If eof.FullWidth > 0 Then
1618
                                If Not eof.LeadingTrivia.All(Function(t) t.Kind = SyntaxKind.WhitespaceTrivia) Then
P
Pilchie 已提交
1619 1620 1621 1622 1623 1624
                                    ' This is an invalid line like "'Blah'" 
                                    GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString, True)

                                    diagnosticBuilder.Add(
                                        New DiagnosticWithInfo(
                                            ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
1625
                                            ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedIdentifier),
P
Pilchie 已提交
1626 1627 1628 1629 1630 1631 1632 1633 1634 1635
                                            parsedTokensAsString.ToString),
                                        Location.None))
                                End If
                            End If

                            Exit Do
                        End If

                        parsedTokensAsString.Append(tokens.Current.ToFullString())

1636
                        If Not tokens.Current.Kind = SyntaxKind.IdentifierToken Then
P
Pilchie 已提交
1637 1638 1639 1640 1641
                            GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString)

                            diagnosticBuilder.Add(
                                New DiagnosticWithInfo(
                                    ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
1642
                                        ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedIdentifier),
P
Pilchie 已提交
1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653
                                        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)

1654
                        If tokens.Current.Kind = SyntaxKind.EqualsToken Then
P
Pilchie 已提交
1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670
                            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
1671
                            While tokens.Current.Kind <> SyntaxKind.EndOfFileToken AndAlso tokens.Current.Span.End <= parsedEnd
P
Pilchie 已提交
1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695
                                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
1696
                                        diagnosticBuilder.Add(New DiagnosticWithInfo(ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1, diag, parsedTokensAsString.ToString), Location.None))
P
Pilchie 已提交
1697 1698 1699 1700 1701 1702 1703 1704 1705
                                    Else
                                        errorSkipped = True
                                    End If
                                Next

                                If errorSkipped Then
                                    diagnosticBuilder.Add(
                                        New DiagnosticWithInfo(
                                            ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
1706
                                                ErrorFactory.ErrorInfo(If(atTheEndOrSeparator, ERRID.ERR_ExpectedExpression, ERRID.ERR_BadCCExpression)),
P
Pilchie 已提交
1707 1708 1709 1710 1711 1712 1713
                                                parsedTokensAsString.ToString),
                                            Location.None))
                                End If

                                Exit Do
                            End If

1714
                            ' Expression parsed successfully --> evaluate it
P
Pilchie 已提交
1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726

                            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,
1727
                                            ErrorFactory.ErrorInfo(err, value.ErrorArgs),
P
Pilchie 已提交
1728 1729 1730 1731 1732
                                            parsedTokensAsString.ToString),
                                        Location.None))
                                Exit Do
                            End If

1733
                            ' Expression evaluated successfully --> add to 'defines'
P
Pilchie 已提交
1734 1735 1736 1737 1738
                            If defines.ContainsKey(symbolName) Then
                                defines = defines.Remove(symbolName)
                            End If
                            defines = defines.Add(symbolName, value)

1739 1740 1741
                        ElseIf tokens.Current.Kind = SyntaxKind.CommaToken OrElse
                            tokens.Current.Kind = SyntaxKind.ColonToken OrElse
                            tokens.Current.Kind = SyntaxKind.EndOfFileToken Then
P
Pilchie 已提交
1742 1743 1744 1745 1746 1747 1748
                            ' 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))

1749
                        ElseIf tokens.Current.Kind = SyntaxKind.BadToken Then
P
Pilchie 已提交
1750 1751 1752 1753 1754
                            GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString)

                            diagnosticBuilder.Add(
                                New DiagnosticWithInfo(
                                    ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
1755
                                        ErrorFactory.ErrorInfo(ERRID.ERR_IllegalChar),
P
Pilchie 已提交
1756 1757 1758 1759 1760 1761 1762 1763 1764
                                        parsedTokensAsString.ToString),
                                    Location.None))
                            Exit Do
                        Else
                            GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString)

                            diagnosticBuilder.Add(
                                New DiagnosticWithInfo(
                                    ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
1765
                                        ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedEOS),
P
Pilchie 已提交
1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778
                                        parsedTokensAsString.ToString),
                                    Location.None))
                            Exit Do
                        End If
                    Loop
                End If
            End Using

            diagnostics = diagnosticBuilder.ToArrayAndFree()
            Return InternalDefinesToPublicSymbols(defines)
        End Function

        ''' <summary>
P
Pharring 已提交
1779
        ''' NOTE: implicit line continuation will not be handled here and an error will be generated, 
P
Pilchie 已提交
1780 1781 1782
        ''' but explicit one (like ".... _\r\n ....") should work fine
        ''' </summary>
        Private Shared Function ParseConditionalCompilationExpression(symbolList As String, offset As Integer) As ExpressionSyntax
A
angocke 已提交
1783
            Using p = New InternalSyntax.Parser(SyntaxFactory.MakeSourceText(symbolList, offset), VisualBasicParseOptions.Default)
P
Pilchie 已提交
1784 1785 1786 1787 1788 1789
                p.GetNextToken()
                Return DirectCast(p.ParseConditionalCompilationExpression().CreateRed(Nothing, 0), ExpressionSyntax)
            End Using
        End Function

        Private Shared Function IsSeparatorOrEndOfFile(token As SyntaxToken) As Boolean
1790
            Return token.Kind = SyntaxKind.EndOfFileToken OrElse token.Kind = SyntaxKind.ColonToken OrElse token.Kind = SyntaxKind.CommaToken
P
Pilchie 已提交
1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801
        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(" ^^ ")

1802
                If tokens.Current.Kind = SyntaxKind.ColonToken AndAlso tokens.Current.FullWidth = 0 Then
P
Pilchie 已提交
1803 1804 1805 1806 1807 1808 1809 1810 1811 1812
                    remainderErrorLine.Append(SyntaxFacts.GetText(SyntaxKind.ColonToken))
                Else
                    remainderErrorLine.Append(tokens.Current.ToFullString())
                End If

                remainderErrorLine.Append(" ^^ ")
            Else
                remainderErrorLine.Append(" ^^ ^^ ")
            End If

1813
            While tokens.MoveNext AndAlso Not tokens.Current.Kind = stopTokenKind
P
Pilchie 已提交
1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841
                remainderErrorLine.Append(tokens.Current.ToFullString())
            End While
        End Sub

        ''' <summary>
        ''' Parses the given platform option. Legal strings are "anycpu", "x64", "x86", "itanium", "anycpu32bitpreferred", "arm".
        ''' In case an invalid value was passed, anycpu is returned.
        ''' </summary>
        ''' <param name="value">The value for platform.</param>
        ''' <param name="errors">The error bag.</param>
        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, ":<string>")
            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
1842
                        AddDiagnostic(errors, ERRID.ERR_InvalidSwitchValue, name, value)
P
Pilchie 已提交
1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861
                End Select
            End If

            Return Platform.AnyCpu
        End Function

        ''' <summary>
        ''' Parses the file alignment option.
        ''' In case an invalid value was passed, nothing is returned.
        ''' </summary>
        ''' <param name="name">The name of the option.</param>
        ''' <param name="value">The value for the option.</param>
        ''' <param name="errors">The error bag.</param><returns></returns>
        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, ":<number>")
            ElseIf Not TryParseUInt16(value, alignment) Then
1862
                AddDiagnostic(errors, ERRID.ERR_InvalidSwitchValue, name, value)
P
Pilchie 已提交
1863
            ElseIf Not Microsoft.CodeAnalysis.CompilationOptions.IsValidFileAlignment(alignment) Then
1864
                AddDiagnostic(errors, ERRID.ERR_InvalidSwitchValue, name, value)
P
Pilchie 已提交
1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892
            Else
                Return alignment
            End If

            Return 0
        End Function

        ''' <summary>
        ''' Parses the base address option.
        ''' In case an invalid value was passed, nothing is returned.
        ''' </summary>
        ''' <param name="name">The name of the option.</param>
        ''' <param name="value">The value for the option.</param>
        ''' <param name="errors">The error bag.</param><returns></returns>
        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, ":<number>")
            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,
1893 1894
                                      NumberStyles.HexNumber,
                                      CultureInfo.InvariantCulture,
P
Pilchie 已提交
1895 1896
                                      baseAddress) Then

1897
                    AddDiagnostic(errors, ERRID.ERR_InvalidSwitchValue, name, value.ToString())
P
Pilchie 已提交
1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909
                Else
                    Return baseAddress
                End If
            End If

            Return 0
        End Function

        ''' <summary>
        ''' Parses the warning option.
        ''' </summary>
        ''' <param name="value">The value for the option.</param>
1910 1911 1912
        Private Shared Function ParseWarnings(value As String) As IEnumerable(Of String)
            Dim values = value.Split(","c)
            Dim results = New List(Of String)()
P
Pilchie 已提交
1913 1914

            For Each id In values
1915 1916 1917 1918 1919 1920 1921 1922
                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)))
P
Pilchie 已提交
1923
                Else
1924 1925 1926 1927 1928
                    ' 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)
P
Pilchie 已提交
1929 1930 1931 1932 1933 1934
                End If
            Next

            Return results
        End Function

1935 1936
        Private Shared Sub AddWarnings(d As IDictionary(Of String, ReportDiagnostic), kind As ReportDiagnostic, items As IEnumerable(Of String))
            For Each id In items
P
Pilchie 已提交
1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953
                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))
1954
            AddDiagnostic(errors, ERRID.ERR_InvalidSwitchValue, "recurse", path)
P
Pilchie 已提交
1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990
        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

        ''' <summary>
        ''' In VB, if the output file name isn't specified explicitly, then it is derived from the name of the
        ''' first input file.
        ''' </summary>
        ''' <remarks>
        ''' 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: 
        ''' <![CDATA[
        '''   // Calculate the output name and directory
        '''   dwCharCount = GetFullPathName(pszOut ? pszOut : g_strFirstFile, &wszFileName);
        ''' ]]>
        ''' </remarks>
        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,
                                                 <Out> 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()
1991 1992 1993 1994 1995 1996

                    If simpleName.Length = 0 AndAlso Not kind.IsNetModule() Then
                        AddDiagnostic(diagnostics, ERRID.FTL_InputFileNameTooLong, outputFileName)
                        simpleName = Nothing
                        outputFileName = Nothing
                    End If
P
Pilchie 已提交
1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044
                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