CommandLineParser.vb 102.2 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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72

        ''' <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

        Private Const c_Win32Manifest As String = "win32manifest"
        Private Const c_Win32Icon As String = "win32icon"
        Private Const c_Win32Res As String = "win32resource"

        ''' <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

        Friend NotOverridable Overrides Function CommonParse(args As IEnumerable(Of String), baseDirectory As String, additionalReferencePaths As String) As CommandLineArguments
            Return Parse(args, baseDirectory, additionalReferencePaths)
        End Function

        ''' <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>
        ''' <param name="additionalReferencePaths">A string representing additional reference paths.</param>
        ''' <returns>A CommandLineArguments object representing the parsed command line.</returns>
A
angocke 已提交
73
        Public Shadows Function Parse(args As IEnumerable(Of String), baseDirectory As String, Optional additionalReferencePaths As String = Nothing) As VisualBasicCommandLineArguments
P
Pilchie 已提交
74 75
            Const GenerateFileNameForDocComment As String = "USE-OUTPUT-NAME"

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 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
            Dim diagnostics As List(Of Diagnostic) = New List(Of Diagnostic)()
            Dim flattenedArgs As List(Of String) = New List(Of String)()
            Dim scriptArgs As List(Of String) = If(IsInteractive, New List(Of String)(), Nothing)

            ' normalized paths to directories containing response files:
            Dim responsePaths As New List(Of String)
            FlattenArgs(args, diagnostics, flattenedArgs, scriptArgs, baseDirectory, responsePaths)

            Dim displayLogo As Boolean = True
            Dim displayHelp As Boolean = False
            Dim outputLevel As OutputLevel = OutputLevel.Normal
            Dim optimize As Boolean = False
            Dim checkOverflow As Boolean = True
            Dim concurrentBuild As Boolean = True
            Dim emitPdb As Boolean = False
            Dim noStdLib As Boolean = False
            Dim utf8output As Boolean = False
            Dim outputFileName As String = Nothing
            Dim outputDirectory As String = baseDirectory
            Dim documentationPath As String = Nothing
            Dim parseDocumentationComments As Boolean = False ' Don't just null check documentationFileName because we want to do this even if the file name is invalid.
            Dim outputKind As OutputKind = OutputKind.ConsoleApplication
            Dim ssVersion As SubsystemVersion = SubsystemVersion.None
            Dim languageVersion As LanguageVersion = LanguageVersion.VisualBasic14
            Dim mainTypeName As String = Nothing
            Dim win32ManifestFile As String = Nothing
            Dim win32ResourceFile As String = Nothing
            Dim win32IconFile As String = Nothing
            Dim noWin32Manifest As Boolean = False
            Dim managedResources = New List(Of ResourceDescription)()
            Dim sourceFiles = New List(Of CommandLineSourceFile)()
            Dim hasSourceFiles = False
            Dim additionalFiles = New List(Of CommandLineSourceFile)()
            Dim codepage As Encoding = Nothing
            Dim checksumAlgorithm = SourceHashAlgorithm.Sha1
            Dim defines As IReadOnlyDictionary(Of String, Object) = Nothing
            Dim metadataReferences = New List(Of CommandLineReference)()
            Dim analyzers = New List(Of CommandLineAnalyzerReference)()
            Dim sdkPaths As New List(Of String)()
            Dim libPaths As New List(Of String)()
            Dim keyFileSearchPaths = New List(Of String)()
            Dim globalImports = New List(Of GlobalImport)
            Dim rootNamespace As String = ""
            Dim optionStrict As OptionStrict = OptionStrict.Off
            Dim optionInfer As Boolean = False ' MSDN says: ...The compiler default for this option is /optioninfer-.
            Dim optionExplicit As Boolean = True
            Dim optionCompareText As Boolean = False
            Dim embedVbCoreRuntime As Boolean = False
            Dim platform As Platform = Platform.AnyCpu
            Dim preferredUILang As CultureInfo = Nothing
            Dim fileAlignment As Integer = 0
            Dim baseAddress As ULong = 0
            Dim highEntropyVA As Boolean = False
            Dim vbRuntimePath As String = Nothing
            Dim includeVbRuntimeReference As Boolean = True
            Dim generalDiagnosticOption As ReportDiagnostic = ReportDiagnostic.Default

            ' Diagnostic ids specified via /nowarn /warnaserror must be processed in case-insensitive fashion.
            Dim specificDiagnosticOptionsFromRuleSet = New Dictionary(Of String, ReportDiagnostic)(CaseInsensitiveComparison.Comparer)
            Dim specificDiagnosticOptionsFromGeneralArguments = New Dictionary(Of String, ReportDiagnostic)(CaseInsensitiveComparison.Comparer)
            Dim specificDiagnosticOptionsFromSpecificArguments = New Dictionary(Of String, ReportDiagnostic)(CaseInsensitiveComparison.Comparer)
            Dim specificDiagnosticOptionsFromNoWarnArguments = New Dictionary(Of String, ReportDiagnostic)(CaseInsensitiveComparison.Comparer)
            Dim keyFileSetting As String = Nothing
            Dim keyContainerSetting As String = Nothing
            Dim delaySignSetting As Boolean? = Nothing
            Dim moduleAssemblyName As String = Nothing
            Dim moduleName As String = Nothing
            Dim sqmsessionguid As Guid = Nothing
            Dim touchedFilesPath As String = Nothing
            Dim features = New List(Of String)()

            ' Process ruleset files first so that diagnostic severity settings specified on the command line via
            ' /nowarn and /warnaserror can override diagnostic severity settings specified in the ruleset file.
            If Not IsInteractive Then
                For Each arg In flattenedArgs
                    Dim name As String = Nothing
                    Dim value As String = Nothing
                    If TryParseOption(arg, name, value) AndAlso (name = "ruleset") Then
                        Dim unquoted = RemoveAllQuotes(value)
                        If String.IsNullOrEmpty(unquoted) Then
                            AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":<file>")
                            Continue For
158
                        End If
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173

                        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
174 175
                End If

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

182
                        displayHelp = True
P
Pilchie 已提交
183 184
                        Continue For

185 186 187 188 189 190 191
                    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 已提交
192

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

202 203 204 205 206
                        Continue For

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

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

230 231
                        optionStrict = VisualBasic.OptionStrict.On
                        Continue For
P
Pilchie 已提交
232

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

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
                        optionStrict = VisualBasic.OptionStrict.Off
                        Continue For

                    Case "optioncompare"
                        If value Is Nothing Then
                            AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "optioncompare", ":binary|text")
                        ElseIf String.Equals(value, "text", StringComparison.OrdinalIgnoreCase) Then
                            optionCompareText = True
                        ElseIf String.Equals(value, "binary", StringComparison.OrdinalIgnoreCase) Then
                            optionCompareText = False
                        Else
                            AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, value, "optioncompare")
                        End If

                        Continue For
P
Pilchie 已提交
254

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

261 262
                        optionExplicit = True
                        Continue For
P
Pilchie 已提交
263

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

270 271
                        optionExplicit = False
                        Continue For
P
Pilchie 已提交
272

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

279 280
                        optionInfer = True
                        Continue For
P
Pilchie 已提交
281

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

288 289
                        optionInfer = False
                        Continue For
P
Pilchie 已提交
290

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

297 298 299 300 301 302 303 304
                        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 已提交
305

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

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

318 319 320 321 322 323
                        checksumAlgorithm = newChecksumAlgorithm
                        Continue For

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

327 328
                        checkOverflow = False
                        Continue For
P
Pilchie 已提交
329

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

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

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

355 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
                        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 已提交
389
                            End If
390
                            Continue For
P
Pilchie 已提交
391

392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
                        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 已提交
409 410 411 412
                            End If

                            Continue For

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

419 420 421 422 423 424 425 426
                            rootNamespace = value
                            Continue For

                        Case "doc"
                            parseDocumentationComments = True
                            If value Is Nothing Then
                                ' Illegal in C#, but works in VB
                                documentationPath = GenerateFileNameForDocComment
427 428
                                Continue For
                            End If
429 430 431 432 433 434 435 436 437 438
                            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
439 440 441

                            Continue For

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

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

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

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

462 463 464
                        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 已提交
465
                            Continue For
466

467 468 469
                        Case "libpath"
                            If String.IsNullOrEmpty(value) Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "libpath", ":<path_list>")
470 471 472
                                Continue For
                            End If

473
                            libPaths.AddRange(ParseSeparatedPaths(RemoveAllQuotes(value)))
474
                            Continue For
P
Pilchie 已提交
475

476 477 478
                        Case "sdkpath"
                            If String.IsNullOrEmpty(value) Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "sdkpath", ":<path>")
P
Pilchie 已提交
479
                                Continue For
480
                            End If
P
Pilchie 已提交
481

482 483 484
                            sdkPaths.Clear()
                            sdkPaths.AddRange(ParseSeparatedPaths(RemoveAllQuotes(value)))
                            Continue For
P
Pilchie 已提交
485

486 487 488
                        Case "recurse"
                            If String.IsNullOrEmpty(value) Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "recurse", ":<wildcard>")
P
Pilchie 已提交
489
                                Continue For
490
                            End If
P
Pilchie 已提交
491

492 493 494 495 496 497
                            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 已提交
498

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

505 506 507 508 509 510 511
                            ' 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 已提交
512

513 514 515
                        Case "l", "link"
                            metadataReferences.AddRange(ParseAssemblyReferences(name, value, diagnostics, embedInteropTypes:=True))
                            Continue For
P
Pilchie 已提交
516

517 518 519
                        Case "win32resource"
                            win32ResourceFile = GetWin32Setting(c_Win32Res, value, diagnostics)
                            Continue For
P
Pilchie 已提交
520

521 522 523
                        Case "win32icon"
                            win32IconFile = GetWin32Setting(c_Win32Icon, value, diagnostics)
                            Continue For
P
Pilchie 已提交
524

525 526 527
                        Case "win32manifest"
                            win32ManifestFile = GetWin32Setting(c_Win32Manifest, value, diagnostics)
                            Continue For
P
Pilchie 已提交
528

529 530 531 532
                        Case "nowin32manifest"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
533

534 535
                            noWin32Manifest = True
                            Continue For
P
Pilchie 已提交
536

537 538 539 540 541 542
                        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 已提交
543

544 545 546 547 548 549
                        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 已提交
550

551
                        Case "debug"
P
Pilchie 已提交
552

553 554
                            ' parse only for backwards compat
                            If value IsNot Nothing Then
P
Pilchie 已提交
555
                                If String.IsNullOrEmpty(value) Then
556 557 558 559
                                    AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "debug", ":pdbonly|full")
                                ElseIf Not String.Equals(value, "full", StringComparison.OrdinalIgnoreCase) AndAlso
                                           Not String.Equals(value, "pdbonly", StringComparison.OrdinalIgnoreCase) Then
                                    AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, value, "debug")
P
Pilchie 已提交
560
                                End If
561
                            End If
P
Pilchie 已提交
562

563 564
                            emitPdb = True
                            Continue For
P
Pilchie 已提交
565

566 567 568 569
                        Case "debug+"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "debug")
                            End If
P
Pilchie 已提交
570

571 572
                            emitPdb = True
                            Continue For
P
Pilchie 已提交
573

574 575 576 577
                        Case "debug-"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "debug")
                            End If
P
Pilchie 已提交
578

579 580
                            emitPdb = False
                            Continue For
P
Pilchie 已提交
581

582 583 584
                        Case "optimize", "optimize+"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optimize")
P
Pilchie 已提交
585
                                Continue For
586
                            End If
P
Pilchie 已提交
587

588 589
                            optimize = True
                            Continue For
P
Pilchie 已提交
590

591 592 593
                        Case "optimize-"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "optimize")
P
Pilchie 已提交
594
                                Continue For
595
                            End If
P
Pilchie 已提交
596

597 598
                            optimize = False
                            Continue For
P
Pilchie 已提交
599

600 601 602
                        Case "parallel", "p"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, name)
P
Pilchie 已提交
603
                                Continue For
604
                            End If
P
Pilchie 已提交
605

606 607
                            concurrentBuild = True
                            Continue For
P
Pilchie 已提交
608

609 610 611
                        Case "parallel+", "p+"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, name.Substring(0, name.Length - 1))
P
Pilchie 已提交
612
                                Continue For
613
                            End If
P
Pilchie 已提交
614

615 616
                            concurrentBuild = True
                            Continue For
P
Pilchie 已提交
617

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

624 625
                            concurrentBuild = False
                            Continue For
P
Pilchie 已提交
626

627 628 629
                        Case "warnaserror", "warnaserror+"
                            If value Is Nothing Then
                                generalDiagnosticOption = ReportDiagnostic.Error
P
Pilchie 已提交
630

631 632 633 634 635 636
                                specificDiagnosticOptionsFromGeneralArguments.Clear()
                                For Each pair In specificDiagnosticOptionsFromRuleSet
                                    If pair.Value = ReportDiagnostic.Warn Then
                                        specificDiagnosticOptionsFromGeneralArguments.Add(pair.Key, ReportDiagnostic.Error)
                                    End If
                                Next
P
Pilchie 已提交
637 638

                                Continue For
639
                            End If
P
Pilchie 已提交
640

641 642
                            AddWarnings(specificDiagnosticOptionsFromSpecificArguments, ReportDiagnostic.Error, ParseWarnings(value))
                            Continue For
P
Pilchie 已提交
643

644 645 646 647
                        Case "warnaserror-"
                            If value Is Nothing Then
                                If generalDiagnosticOption <> ReportDiagnostic.Suppress Then
                                    generalDiagnosticOption = ReportDiagnostic.Default
P
Pilchie 已提交
648 649
                                End If

650
                                specificDiagnosticOptionsFromGeneralArguments.Clear()
P
Pilchie 已提交
651 652

                                Continue For
653
                            End If
P
Pilchie 已提交
654

655 656 657 658 659 660
                            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 已提交
661
                                End If
662
                            Next
P
Pilchie 已提交
663

664
                            Continue For
665

666 667 668
                        Case "nowarn"
                            If value Is Nothing Then
                                generalDiagnosticOption = ReportDiagnostic.Suppress
P
Pilchie 已提交
669

670 671 672 673
                                specificDiagnosticOptionsFromGeneralArguments.Clear()
                                For Each pair In specificDiagnosticOptionsFromRuleSet
                                    If pair.Value <> ReportDiagnostic.Error Then
                                        specificDiagnosticOptionsFromGeneralArguments.Add(pair.Key, ReportDiagnostic.Suppress)
674 675 676
                                    End If
                                Next

P
Pilchie 已提交
677
                                Continue For
678
                            End If
P
Pilchie 已提交
679

680 681
                            AddWarnings(specificDiagnosticOptionsFromNoWarnArguments, ReportDiagnostic.Suppress, ParseWarnings(value))
                            Continue For
P
Pilchie 已提交
682

683 684 685
                        Case "langversion"
                            If value Is Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "langversion", ":<number>")
P
Pilchie 已提交
686
                                Continue For
687
                            End If
P
Pilchie 已提交
688

689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706
                            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
                                        AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, value, "langversion")
                                End Select
                            End If
P
Pilchie 已提交
707

708
                            Continue For
P
Pilchie 已提交
709

710 711 712
                        Case "delaysign", "delaysign+"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "delaysign")
P
Pilchie 已提交
713
                                Continue For
714
                            End If
P
Pilchie 已提交
715

716 717
                            delaySignSetting = True
                            Continue For
P
Pilchie 已提交
718

719 720 721
                        Case "delaysign-"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "delaysign")
P
Pilchie 已提交
722
                                Continue For
723
                            End If
P
Pilchie 已提交
724

725 726
                            delaySignSetting = False
                            Continue For
P
Pilchie 已提交
727

728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
                        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 已提交
746

747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
                        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 已提交
765

766 767 768 769
                        Case "highentropyva", "highentropyva+"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
770

771 772
                            highEntropyVA = True
                            Continue For
P
Pilchie 已提交
773

774 775 776 777
                        Case "highentropyva-"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
778

779 780
                            highEntropyVA = False
                            Continue For
P
Pilchie 已提交
781

782 783 784 785
                        Case "nologo", "nologo+"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
786

787 788
                            displayLogo = False
                            Continue For
P
Pilchie 已提交
789

790 791 792 793
                        Case "nologo-"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
794

795 796
                            displayLogo = True
                            Continue For
P
Pilchie 已提交
797

798 799 800
                        Case "quiet+"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "quiet")
P
Pilchie 已提交
801
                                Continue For
802
                            End If
P
Pilchie 已提交
803

804 805
                            outputLevel = VisualBasic.OutputLevel.Quiet
                            Continue For
P
Pilchie 已提交
806

807 808 809 810
                        Case "quiet"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
811

812 813
                            outputLevel = VisualBasic.OutputLevel.Quiet
                            Continue For
P
Pilchie 已提交
814

815 816 817 818
                        Case "verbose"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
819

820 821
                            outputLevel = VisualBasic.OutputLevel.Verbose
                            Continue For
P
Pilchie 已提交
822

823 824 825
                        Case "verbose+"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "verbose")
P
Pilchie 已提交
826
                                Continue For
827
                            End If
P
Pilchie 已提交
828

829 830
                            outputLevel = VisualBasic.OutputLevel.Verbose
                            Continue For
P
Pilchie 已提交
831

832 833 834
                        Case "quiet-", "verbose-"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, name.Substring(0, name.Length - 1))
P
Pilchie 已提交
835
                                Continue For
836
                            End If
P
Pilchie 已提交
837

838 839
                            outputLevel = VisualBasic.OutputLevel.Normal
                            Continue For
P
Pilchie 已提交
840

841 842 843 844
                        Case "utf8output", "utf8output+"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "utf8output")
                            End If
P
Pilchie 已提交
845

846 847
                            utf8output = True
                            Continue For
P
Pilchie 已提交
848

849 850 851 852
                        Case "utf8output-"
                            If value IsNot Nothing Then
                                AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "utf8output")
                            End If
P
Pilchie 已提交
853

854 855
                            utf8output = False
                            Continue For
P
Pilchie 已提交
856

857 858 859
                        Case "noconfig"
                            ' It is already handled (see CommonCommandLineCompiler.cs).
                            Continue For
P
Pilchie 已提交
860

861 862 863 864 865
                        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 已提交
866

867 868 869 870 871
                            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 已提交
872

873
                            Continue For
P
Pilchie 已提交
874

875 876 877
                        Case "novbruntimeref"
                            ' The switch is no longer supported and for backwards compat ignored.
                            Continue For
P
Pilchie 已提交
878

879 880 881 882 883 884 885
                        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 已提交
886
                                Continue For
887
                            End If
P
Pilchie 已提交
888

889 890
                            mainTypeName = value
                            Continue For
P
Pilchie 已提交
891

892 893 894
                        Case "subsystemversion"
                            If String.IsNullOrEmpty(value) Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":<version>")
P
Pilchie 已提交
895
                                Continue For
896
                            End If
P
Pilchie 已提交
897

898 899 900 901 902 903 904
                            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 已提交
905

906 907 908 909
                        Case "touchedfiles"
                            Dim unquoted = RemoveAllQuotes(value)
                            If (String.IsNullOrEmpty(unquoted)) Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":<touchedfiles>")
P
Pilchie 已提交
910
                                Continue For
911 912 913 914
                            Else
                                touchedFilesPath = unquoted
                            End If
                            Continue For
P
Pilchie 已提交
915

916 917 918
                        Case "fullpaths", "errorendlocation"
                            UnimplementedSwitch(diagnostics, name)
                            Continue For
P
Pilchie 已提交
919

920 921 922 923
                        Case "nostdlib"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
924

925 926
                            noStdLib = True
                            Continue For
P
Pilchie 已提交
927

928 929 930 931
                        Case "vbruntime"
                            If value Is Nothing Then
                                GoTo lVbRuntimePlus
                            End If
P
Pilchie 已提交
932

933 934 935 936 937
                            ' NOTE: that Dev11 does not report errors on empty or invalid file specified
                            vbRuntimePath = RemoveAllQuotes(value)
                            includeVbRuntimeReference = True
                            embedVbCoreRuntime = False
                            Continue For
P
Pilchie 已提交
938

939 940 941 942
                        Case "vbruntime+"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
943 944

lVbRuntimePlus:
945 946 947 948
                            vbRuntimePath = Nothing
                            includeVbRuntimeReference = True
                            embedVbCoreRuntime = False
                            Continue For
P
Pilchie 已提交
949

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

955 956 957 958
                            vbRuntimePath = Nothing
                            includeVbRuntimeReference = False
                            embedVbCoreRuntime = False
                            Continue For
P
Pilchie 已提交
959

960 961 962 963
                        Case "vbruntime*"
                            If value IsNot Nothing Then
                                Exit Select
                            End If
P
Pilchie 已提交
964

965 966 967 968
                            vbRuntimePath = Nothing
                            includeVbRuntimeReference = False
                            embedVbCoreRuntime = True
                            Continue For
P
Pilchie 已提交
969

970 971 972 973 974 975
                        Case "platform"
                            If value IsNot Nothing Then
                                platform = ParsePlatform(name, value, diagnostics)
                            Else
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "platform", ":<string>")
                            End If
P
Pilchie 已提交
976

977
                            Continue For
P
Pilchie 已提交
978

979 980 981
                        Case "filealign"
                            fileAlignment = ParseFileAlignment(name, value, diagnostics)
                            Continue For
P
Pilchie 已提交
982

983 984 985
                        Case "baseaddress"
                            baseAddress = ParseBaseAddress(name, value, diagnostics)
                            Continue For
986

987 988 989
                        Case "ruleset"
                            '  The ruleset arg has already been processed in a separate pass above.
                            Continue For
990

991 992 993 994 995 996 997
                        Case "features"
                            If value Is Nothing Then
                                features.Clear()
                            Else
                                features.Add(value)
                            End If
                            Continue For
998

999 1000 1001
                        Case "additionalfile"
                            If String.IsNullOrEmpty(value) Then
                                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":<file_list>")
1002
                                Continue For
1003
                            End If
P
Pilchie 已提交
1004

1005 1006 1007 1008
                            additionalFiles.AddRange(ParseAdditionalFileArgument(value, baseDirectory, diagnostics))
                            Continue For
                    End Select
                End If
P
Pilchie 已提交
1009

1010 1011
                AddDiagnostic(diagnostics, ERRID.WRN_BadSwitch, arg)
            Next
1012

1013
            Dim specificDiagnosticOptions = New Dictionary(Of String, ReportDiagnostic)(specificDiagnosticOptionsFromRuleSet, CaseInsensitiveComparison.Comparer)
1014

1015 1016 1017
            For Each item In specificDiagnosticOptionsFromGeneralArguments
                specificDiagnosticOptions(item.Key) = item.Value
            Next
1018

1019 1020 1021
            For Each item In specificDiagnosticOptionsFromSpecificArguments
                specificDiagnosticOptions(item.Key) = item.Value
            Next
1022

1023 1024 1025
            For Each item In specificDiagnosticOptionsFromNoWarnArguments
                specificDiagnosticOptions(item.Key) = item.Value
            Next
P
Pilchie 已提交
1026

1027 1028 1029 1030 1031 1032
            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 已提交
1033
                End If
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
            End If

            ' Prepare SDK PATH
            If sdkPaths.Count = 0 Then
                sdkPaths.Add(RuntimeEnvironment.GetRuntimeDirectory)
            End If

            ' Locate default 'mscorlib.dll' or 'System.Runtime.dll', if any.
            Dim defaultCoreLibraryReference As CommandLineReference? = LoadCoreLibraryReference(sdkPaths, baseDirectory)

            ' If /nostdlib is not specified, load System.dll
            ' Dev12 does it through combination of CompilerHost::InitStandardLibraryList and CompilerProject::AddStandardLibraries.
            If Not noStdLib Then
                Dim systemDllPath As String = FindFileInSdkPath(sdkPaths, "System.dll", baseDirectory)
                If systemDllPath Is Nothing Then
                    AddDiagnostic(diagnostics, ERRID.WRN_CannotFindStandardLibrary1, "System.dll")
                Else
                    metadataReferences.Add(
1052
                            New CommandLineReference(systemDllPath, New MetadataReferenceProperties(MetadataImageKind.Assembly)))
P
Pilchie 已提交
1053
                End If
1054 1055 1056 1057 1058 1059 1060 1061 1062
                ' Dev11 also adds System.Core.dll in VbHostedCompiler::CreateCompilerProject()
            End If

            ' Add reference to 'Microsoft.VisualBasic.dll' if needed
            If includeVbRuntimeReference Then
                If vbRuntimePath Is Nothing Then
                    Dim msVbDllPath As String = FindFileInSdkPath(sdkPaths, "Microsoft.VisualBasic.dll", baseDirectory)
                    If msVbDllPath Is Nothing Then
                        AddDiagnostic(diagnostics, ERRID.ERR_LibNotFound, "Microsoft.VisualBasic.dll")
P
Pilchie 已提交
1063
                    Else
1064 1065
                        metadataReferences.Add(
                                New CommandLineReference(msVbDllPath, New MetadataReferenceProperties(MetadataImageKind.Assembly)))
P
Pilchie 已提交
1066
                    End If
1067 1068
                Else
                    metadataReferences.Add(New CommandLineReference(vbRuntimePath, New MetadataReferenceProperties(MetadataImageKind.Assembly)))
P
Pilchie 已提交
1069
                End If
1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
            End If

            ' add additional reference paths if specified
            If Not String.IsNullOrWhiteSpace(additionalReferencePaths) Then
                libPaths.AddRange(ParseSeparatedPaths(additionalReferencePaths))
            End If

            ' Build search path
            Dim searchPaths As ImmutableArray(Of String) = BuildSearchPaths(baseDirectory, sdkPaths, responsePaths, libPaths)

            ValidateWin32Settings(noWin32Manifest, win32ResourceFile, win32IconFile, win32ManifestFile, outputKind, diagnostics)

            ' Validate root namespace if specified
            Debug.Assert(rootNamespace IsNot Nothing)
            ' NOTE: empty namespace is a valid option
            If Not String.Empty.Equals(rootNamespace) Then
                rootNamespace = rootNamespace.Unquote()
                If String.IsNullOrWhiteSpace(rootNamespace) OrElse Not OptionsValidator.IsValidNamespaceName(rootNamespace) Then
                    AddDiagnostic(diagnostics, ERRID.ERR_BadNamespaceName1, rootNamespace)
                    rootNamespace = "" ' To make it pass compilation options' check
P
Pilchie 已提交
1090
                End If
1091
            End If
P
Pilchie 已提交
1092

1093 1094 1095 1096 1097 1098
            ' 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 已提交
1099

1100 1101
            Dim compilationName As String = Nothing
            GetCompilationAndModuleNames(diagnostics, outputKind, sourceFiles, moduleAssemblyName, outputFileName, moduleName, compilationName)
P
Pilchie 已提交
1102

1103
            If Not IsInteractive AndAlso
P
Pilchie 已提交
1104 1105 1106 1107
                    Not hasSourceFiles AndAlso
                    Not managedResources.IsEmpty() AndAlso
                    outputFileName = Nothing AndAlso
                    Not flattenedArgs.IsEmpty() Then
1108 1109
                AddDiagnostic(diagnostics, ERRID.ERR_NoSourcesOut)
            End If
P
Pilchie 已提交
1110

1111
            Dim parseOptions = New VisualBasicParseOptions(
1112
                    languageVersion:=languageVersion,
P
Pilchie 已提交
1113 1114
                    documentationMode:=If(parseDocumentationComments, DocumentationMode.Diagnose, DocumentationMode.None),
                    kind:=SourceCodeKind.Regular,
1115
                    preprocessorSymbols:=AddPredefinedPreprocessorSymbols(outputKind, defines.AsImmutableOrEmpty()))
P
Pilchie 已提交
1116

1117
            Dim scriptParseOptions = parseOptions.WithKind(SourceCodeKind.Script)
P
Pilchie 已提交
1118

1119
            Dim options = New VisualBasicCompilationOptions(
P
Pilchie 已提交
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138
                        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,
1139
                        optimizationLevel:=If(optimize, OptimizationLevel.Release, OptimizationLevel.Debug),
1140
                        parseOptions:=parseOptions).WithFeatures(features.AsImmutable())
P
Pilchie 已提交
1141

1142
            Dim emitOptions = New EmitOptions(
1143 1144 1145
                    metadataOnly:=False,
                    debugInformationFormat:=DebugInformationFormat.Pdb,
                    pdbFilePath:=Nothing, ' to be determined later
1146
                    outputNameOverride:=Nothing,  ' to be determined later
1147 1148 1149 1150 1151 1152
                    fileAlignment:=fileAlignment,
                    baseAddress:=baseAddress,
                    highEntropyVirtualAddressSpace:=highEntropyVA,
                    subsystemVersion:=ssVersion,
                    runtimeMetadataVersion:=Nothing)

1153 1154
            ' add option incompatibility errors if any
            diagnostics.AddRange(options.Errors)
P
Pilchie 已提交
1155

1156 1157 1158 1159
            If documentationPath Is GenerateFileNameForDocComment Then
                documentationPath = PathUtilities.CombineAbsoluteAndRelativePaths(outputDirectory, PathUtilities.RemoveExtension(outputFileName))
                documentationPath = documentationPath + ".xml"
            End If
P
Pilchie 已提交
1160

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

        Private Function LoadCoreLibraryReference(sdkPaths As List(Of String), baseDirectory As String) As CommandLineReference?
            ' Load Core library in Dev11:
            ' Traditionally VB compiler has hard-coded the name of mscorlib.dll. In the Immersive profile the
            ' library is called System.Runtime.dll. Ideally we should get rid of the dependency on the name and
            ' identify the core library as the assembly that contains System.Object. At this point in the compiler,
            ' it is too early though as we haven't loaded any types or assemblies. Changing this now is a deep 
            ' change. So the workaround here is to allow mscorlib or system.runtime and prefer system.runtime if present.
            ' There is an extra check to only pick an assembly with no other assembly refs. This is so that is an 
            ' user drops a user-defined binary called System.runtime.dll into the fx directory we still want to pick 
            ' mscorlib. 
            Dim msCorLibPath As String = FindFileInSdkPath(sdkPaths, "mscorlib.dll", baseDirectory)
            Dim systemRuntimePath As String = FindFileInSdkPath(sdkPaths, "System.Runtime.dll", baseDirectory)

            If systemRuntimePath IsNot Nothing Then
                If msCorLibPath Is Nothing Then
1214
                    Return New CommandLineReference(systemRuntimePath, New MetadataReferenceProperties(MetadataImageKind.Assembly))
P
Pilchie 已提交
1215 1216 1217 1218
                End If

                ' Load System.Runtime.dll and see if it has any references
                Try
1219 1220 1221 1222
                    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
1223
                            Return New CommandLineReference(systemRuntimePath, New MetadataReferenceProperties(MetadataImageKind.Assembly))
P
Pilchie 已提交
1224
                        End If
1225
                    End Using
P
Pilchie 已提交
1226 1227 1228 1229 1230
                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'
1231
                Return New CommandLineReference(msCorLibPath, New MetadataReferenceProperties(MetadataImageKind.Assembly))
P
Pilchie 已提交
1232 1233 1234 1235
            End If

            If msCorLibPath IsNot Nothing Then
                ' We return a reference to 'mscorlib.dll'
1236
                Return New CommandLineReference(msCorLibPath, New MetadataReferenceProperties(MetadataImageKind.Assembly))
P
Pilchie 已提交
1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269
            End If

            Return Nothing
        End Function

        Private Function FindFileInSdkPath(sdkPaths As List(Of String), fileName As String, baseDirectory As String) As String
            For Each path In sdkPaths
                Dim absolutePath = FileUtilities.ResolveRelativePath(If(path, RuntimeEnvironment.GetRuntimeDirectory()), baseDirectory)
                If absolutePath IsNot Nothing Then
                    Dim filePath = PathUtilities.CombineAbsoluteAndRelativePaths(absolutePath, fileName)
                    If File.Exists(filePath) Then
                        Return filePath
                    End If
                End If
            Next
            Return Nothing
        End Function

        Private Function GetWin32Setting(arg As String, value As String, diagnostics As List(Of Diagnostic)) As String
            If (value = Nothing) Then
                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, arg, ":<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

1270
        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 已提交
1271 1272 1273 1274 1275 1276 1277 1278
            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
1279
            AddNormalizedPaths(builder, sdkPaths, baseDirectory)
P
Pilchie 已提交
1280 1281 1282 1283 1284 1285 1286 1287

            ' 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
1288
            AddNormalizedPaths(builder, libPaths, baseDirectory)
P
Pilchie 已提交
1289 1290 1291 1292

            Return builder.ToImmutableAndFree()
        End Function

1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304
        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 已提交
1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 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
        Private Shared Sub ValidateWin32Settings(noWin32Manifest As Boolean, win32ResSetting As String, win32IconSetting As String, win32ManifestSetting As String, outputKind As OutputKind, diagnostics As List(Of Diagnostic))
            If noWin32Manifest AndAlso (win32ManifestSetting IsNot Nothing) Then
                AddDiagnostic(diagnostics, ERRID.ERR_ConflictingManifestSwitches)
            End If

            If win32ResSetting IsNot Nothing Then
                If win32IconSetting IsNot Nothing Then
                    AddDiagnostic(diagnostics, ERRID.ERR_IconFileAndWin32ResFile)
                End If

                If win32ManifestSetting IsNot Nothing Then
                    AddDiagnostic(diagnostics, ERRID.ERR_CantHaveWin32ResAndManifest)
                End If
            End If

            If win32ManifestSetting IsNot Nothing AndAlso outputKind.IsNetModule() Then
                AddDiagnostic(diagnostics, ERRID.WRN_IgnoreModuleManifest)
            End If
        End Sub

        Private Shared Function ParseTarget(optionName As String, value As String, diagnostics As IList(Of Diagnostic)) As OutputKind
            Select Case If(value, "").ToLowerInvariant()
                Case "exe"
                    Return OutputKind.ConsoleApplication
                Case "winexe"
                    Return OutputKind.WindowsApplication
                Case "library"
                    Return OutputKind.DynamicallyLinkedLibrary
                Case "module"
                    Return OutputKind.NetModule
                Case "appcontainerexe"
                    Return OutputKind.WindowsRuntimeApplication
                Case "winmdobj"
                    Return OutputKind.WindowsRuntimeMetadata
                Case ""
                    AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, optionName, ":exe|winexe|library|module|appcontainerexe|winmdobj")
                    Return OutputKind.ConsoleApplication
                Case Else
                    AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, value, optionName)
                    Return OutputKind.ConsoleApplication
            End Select
        End Function

        Private Function ParseAssemblyReferences(name As String, value As String, diagnostics As IList(Of Diagnostic), embedInteropTypes As Boolean) As IEnumerable(Of CommandLineReference)
            If String.IsNullOrEmpty(value) Then
                ' TODO: localize <file_list>?
                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":<file_list>")
1352
                Return SpecializedCollections.EmptyEnumerable(Of CommandLineReference)()
P
Pilchie 已提交
1353 1354 1355 1356 1357 1358 1359 1360 1361 1362
            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).
1363
                   Select(Function(path) New CommandLineReference(path, New MetadataReferenceProperties(MetadataImageKind.Assembly, embedInteropTypes:=embedInteropTypes)))
P
Pilchie 已提交
1364 1365
        End Function

1366
        Private Function ParseAnalyzers(name As String, value As String, diagnostics As IList(Of Diagnostic)) As IEnumerable(Of CommandLineAnalyzerReference)
P
Pilchie 已提交
1367 1368 1369
            If String.IsNullOrEmpty(value) Then
                ' TODO: localize <file_list>?
                AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":<file_list>")
1370
                Return SpecializedCollections.EmptyEnumerable(Of CommandLineAnalyzerReference)()
P
Pilchie 已提交
1371 1372 1373 1374
            End If

            Return ParseSeparatedPaths(value).
                   Select(Function(path)
1375
                              Return New CommandLineAnalyzerReference(path)
P
Pilchie 已提交
1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 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
                          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

1428 1429 1430
            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.
1431
                                                      Return New FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
1432
                                                  End Function
P
Pilchie 已提交
1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457
            Return New ResourceDescription(resourceName, fileName, dataProvider, isPublic, embedded, checkArgs:=False)
        End Function

        Private Shared Sub AddInvalidSwitchValueDiagnostic(diagnostics As IList(Of Diagnostic), ByVal name As String, ByVal nullStringText As String)
            If String.IsNullOrEmpty(name) Then
                ' NOTE: "(null)" to match Dev10.
                ' CONSIDER: should this be a resource string?
                name = "(null)"
            End If

            AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, nullStringText, name)
        End Sub

        Private Shared Sub ParseGlobalImports(value As String, globalImports As List(Of GlobalImport), errors As List(Of Diagnostic))
            Dim importsArray As String() = value.Split({","c}, StringSplitOptions.RemoveEmptyEntries)

            For Each importNamespace In importsArray
                Dim importDiagnostics As ImmutableArray(Of Diagnostic) = Nothing
                Dim import = GlobalImport.Parse(importNamespace, importDiagnostics)
                errors.AddRange(importDiagnostics)
                globalImports.Add(import)
            Next
        End Sub

        ''' <summary>
1458 1459
        ''' Converts a sequence of definitions provided by a caller (public API) into map 
        ''' of definitions used internally.
P
Pilchie 已提交
1460
        ''' </summary>
1461 1462 1463 1464
        ''' <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)

1465
            Dim result = ImmutableDictionary.CreateBuilder(Of String, InternalSyntax.CConst)(CaseInsensitiveComparison.Comparer)
P
Pilchie 已提交
1466 1467

            If symbols IsNot Nothing Then
1468 1469 1470 1471
                For Each symbol In symbols
                    Dim constant = InternalSyntax.CConst.TryCreate(symbol.Value)

                    If constant Is Nothing Then
1472
                        Throw New ArgumentException(String.Format(ErrorFactory.IdToString(ERRID.IDS_InvalidPreprocessorConstantType, Culture), symbol.Key, symbol.Value.GetType()), parameterName)
1473 1474 1475
                    End If

                    result(symbol.Key) = constant
P
Pilchie 已提交
1476 1477 1478
                Next
            End If

1479
            Return result.ToImmutable()
P
Pilchie 已提交
1480 1481 1482 1483 1484 1485 1486
        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)
1487
            Dim result = ImmutableDictionary.CreateBuilder(Of String, Object)(CaseInsensitiveComparison.Comparer)
P
Pilchie 已提交
1488 1489

            For Each kvp In defines
1490
                result(kvp.Key) = kvp.Value.ValueAsObject
P
Pilchie 已提交
1491 1492
            Next

1493
            Return result.ToImmutable()
P
Pilchie 已提交
1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505
        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>
1506
        ''' <exception cref="ArgumentException">Invalid value provided.</exception>
P
Pilchie 已提交
1507 1508 1509
        Public Shared Function ParseConditionalCompilationSymbols(
            symbolList As String,
            <Out> ByRef diagnostics As IEnumerable(Of Diagnostic),
1510
            Optional symbols As IEnumerable(Of KeyValuePair(Of String, Object)) = Nothing
P
Pilchie 已提交
1511 1512
        ) As IReadOnlyDictionary(Of String, Object)

1513
            Dim diagnosticBuilder = ArrayBuilder(Of Diagnostic).GetInstance()
P
Pilchie 已提交
1514 1515
            Dim parsedTokensAsString As New StringBuilder

1516
            Dim defines As ImmutableDictionary(Of String, InternalSyntax.CConst) = PublicSymbolsToInternalDefines(symbols, "symbols")
P
Pilchie 已提交
1517 1518 1519 1520 1521

            ' remove quotes around the whole /define argument (incl. nested)
            Dim unquotedString As String
            Do
                unquotedString = symbolList
1522
                symbolList = symbolList.Unquote()
1523
            Loop While Not String.Equals(symbolList, unquotedString, StringComparison.Ordinal)
P
Pilchie 已提交
1524 1525 1526 1527

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

P
Pharring 已提交
1528
            Dim trimmedSymbolList As String = symbolList.TrimEnd(Nothing)
P
Pilchie 已提交
1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558
            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,
1559
                                        ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedEOS),
P
Pilchie 已提交
1560 1561 1562 1563 1564 1565 1566 1567 1568 1569
                                        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...
1570
                        While tokens.Current.Kind = SyntaxKind.CommaToken OrElse tokens.Current.Kind = SyntaxKind.ColonToken
P
Pilchie 已提交
1571

1572
                            If lastSeparatorToken.Kind = SyntaxKind.None Then
P
Pilchie 已提交
1573 1574 1575
                                ' accept multiple : or ,
                                lastSeparatorToken = tokens.Current

1576
                            ElseIf lastSeparatorToken.Kind <> tokens.Current.Kind Then
P
Pilchie 已提交
1577
                                ' but not mixing them, e.g. ::,,::
1578
                                GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString, stopTokenKind:=lastSeparatorToken.Kind, includeCurrentToken:=True)
P
Pilchie 已提交
1579 1580 1581 1582

                                diagnosticBuilder.Add(
                                    New DiagnosticWithInfo(
                                        ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
1583
                                            ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedIdentifier),
P
Pilchie 已提交
1584 1585 1586 1587 1588 1589 1590
                                            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
1591
                            If tokens.Current.Kind <> SyntaxKind.EndOfFileToken Then
P
Pilchie 已提交
1592 1593 1594 1595 1596 1597 1598 1599
                                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
1600
                        If tokens.Current.Kind = SyntaxKind.EndOfFileToken Then
P
Pilchie 已提交
1601 1602 1603 1604

                            Dim eof = tokens.Current

                            If eof.FullWidth > 0 Then
1605
                                If Not eof.LeadingTrivia.All(Function(t) t.Kind = SyntaxKind.WhitespaceTrivia) Then
P
Pilchie 已提交
1606 1607 1608 1609 1610 1611
                                    ' This is an invalid line like "'Blah'" 
                                    GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString, True)

                                    diagnosticBuilder.Add(
                                        New DiagnosticWithInfo(
                                            ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
1612
                                            ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedIdentifier),
P
Pilchie 已提交
1613 1614 1615 1616 1617 1618 1619 1620 1621 1622
                                            parsedTokensAsString.ToString),
                                        Location.None))
                                End If
                            End If

                            Exit Do
                        End If

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

1623
                        If Not tokens.Current.Kind = SyntaxKind.IdentifierToken Then
P
Pilchie 已提交
1624 1625 1626 1627 1628
                            GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString)

                            diagnosticBuilder.Add(
                                New DiagnosticWithInfo(
                                    ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
1629
                                        ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedIdentifier),
P
Pilchie 已提交
1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640
                                        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)

1641
                        If tokens.Current.Kind = SyntaxKind.EqualsToken Then
P
Pilchie 已提交
1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657
                            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
1658
                            While tokens.Current.Kind <> SyntaxKind.EndOfFileToken AndAlso tokens.Current.Span.End <= parsedEnd
P
Pilchie 已提交
1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682
                                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
1683
                                        diagnosticBuilder.Add(New DiagnosticWithInfo(ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1, diag, parsedTokensAsString.ToString), Location.None))
P
Pilchie 已提交
1684 1685 1686 1687 1688 1689 1690 1691 1692
                                    Else
                                        errorSkipped = True
                                    End If
                                Next

                                If errorSkipped Then
                                    diagnosticBuilder.Add(
                                        New DiagnosticWithInfo(
                                            ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
1693
                                                ErrorFactory.ErrorInfo(If(atTheEndOrSeparator, ERRID.ERR_ExpectedExpression, ERRID.ERR_BadCCExpression)),
P
Pilchie 已提交
1694 1695 1696 1697 1698 1699 1700
                                                parsedTokensAsString.ToString),
                                            Location.None))
                                End If

                                Exit Do
                            End If

1701
                            ' Expression parsed successfully --> evaluate it
P
Pilchie 已提交
1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713

                            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,
1714
                                            ErrorFactory.ErrorInfo(err, value.ErrorArgs),
P
Pilchie 已提交
1715 1716 1717 1718 1719
                                            parsedTokensAsString.ToString),
                                        Location.None))
                                Exit Do
                            End If

1720
                            ' Expression evaluated successfully --> add to 'defines'
P
Pilchie 已提交
1721 1722 1723 1724 1725
                            If defines.ContainsKey(symbolName) Then
                                defines = defines.Remove(symbolName)
                            End If
                            defines = defines.Add(symbolName, value)

1726 1727 1728
                        ElseIf tokens.Current.Kind = SyntaxKind.CommaToken OrElse
                            tokens.Current.Kind = SyntaxKind.ColonToken OrElse
                            tokens.Current.Kind = SyntaxKind.EndOfFileToken Then
P
Pilchie 已提交
1729 1730 1731 1732 1733 1734 1735
                            ' 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))

1736
                        ElseIf tokens.Current.Kind = SyntaxKind.BadToken Then
P
Pilchie 已提交
1737 1738 1739 1740 1741
                            GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString)

                            diagnosticBuilder.Add(
                                New DiagnosticWithInfo(
                                    ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
1742
                                        ErrorFactory.ErrorInfo(ERRID.ERR_IllegalChar),
P
Pilchie 已提交
1743 1744 1745 1746 1747 1748 1749 1750 1751
                                        parsedTokensAsString.ToString),
                                    Location.None))
                            Exit Do
                        Else
                            GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString)

                            diagnosticBuilder.Add(
                                New DiagnosticWithInfo(
                                    ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1,
1752
                                        ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedEOS),
P
Pilchie 已提交
1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765
                                        parsedTokensAsString.ToString),
                                    Location.None))
                            Exit Do
                        End If
                    Loop
                End If
            End Using

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

        ''' <summary>
P
Pharring 已提交
1766
        ''' NOTE: implicit line continuation will not be handled here and an error will be generated, 
P
Pilchie 已提交
1767 1768 1769
        ''' but explicit one (like ".... _\r\n ....") should work fine
        ''' </summary>
        Private Shared Function ParseConditionalCompilationExpression(symbolList As String, offset As Integer) As ExpressionSyntax
A
angocke 已提交
1770
            Using p = New InternalSyntax.Parser(SyntaxFactory.MakeSourceText(symbolList, offset), VisualBasicParseOptions.Default)
P
Pilchie 已提交
1771 1772 1773 1774 1775 1776
                p.GetNextToken()
                Return DirectCast(p.ParseConditionalCompilationExpression().CreateRed(Nothing, 0), ExpressionSyntax)
            End Using
        End Function

        Private Shared Function IsSeparatorOrEndOfFile(token As SyntaxToken) As Boolean
1777
            Return token.Kind = SyntaxKind.EndOfFileToken OrElse token.Kind = SyntaxKind.ColonToken OrElse token.Kind = SyntaxKind.CommaToken
P
Pilchie 已提交
1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788
        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(" ^^ ")

1789
                If tokens.Current.Kind = SyntaxKind.ColonToken AndAlso tokens.Current.FullWidth = 0 Then
P
Pilchie 已提交
1790 1791 1792 1793 1794 1795 1796 1797 1798 1799
                    remainderErrorLine.Append(SyntaxFacts.GetText(SyntaxKind.ColonToken))
                Else
                    remainderErrorLine.Append(tokens.Current.ToFullString())
                End If

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

1800
            While tokens.MoveNext AndAlso Not tokens.Current.Kind = stopTokenKind
P
Pilchie 已提交
1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 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 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 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 1893 1894 1895 1896
                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
                        AddDiagnostic(errors, ERRID.ERR_InvalidSwitchValue, value, name)
                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
                AddDiagnostic(errors, ERRID.ERR_InvalidSwitchValue, value, name)
            ElseIf Not Microsoft.CodeAnalysis.CompilationOptions.IsValidFileAlignment(alignment) Then
                AddDiagnostic(errors, ERRID.ERR_InvalidSwitchValue, value, name)
            Else
                Return alignment
            End If

            Return 0
        End Function

        ''' <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,
                                      System.Globalization.NumberStyles.HexNumber,
                                      System.Globalization.CultureInfo.InvariantCulture,
                                      baseAddress) Then

                    AddDiagnostic(errors, ERRID.ERR_InvalidSwitchValue, value.ToString(), name)
                Else
                    Return baseAddress
                End If
            End If

            Return 0
        End Function

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

            For Each id In values
1902 1903 1904 1905 1906 1907 1908 1909
                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 已提交
1910
                Else
1911 1912 1913 1914 1915
                    ' 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 已提交
1916 1917 1918 1919 1920 1921
                End If
            Next

            Return results
        End Function

1922 1923
        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 已提交
1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977
                Dim existing As ReportDiagnostic
                If d.TryGetValue(id, existing) Then
                    ' Rewrite the existing value with the latest one unless it is for /nowarn.
                    If existing <> ReportDiagnostic.Suppress Then
                        d(id) = kind
                    End If
                Else
                    d.Add(id, kind)
                End If
            Next
        End Sub

        Private Shared Sub UnimplementedSwitch(diagnostics As IList(Of Diagnostic), switchName As String)
            AddDiagnostic(diagnostics, ERRID.WRN_UnimplementedCommandLineSwitch, "/" + switchName)
        End Sub

        Friend Overrides Sub GenerateErrorForNoFilesFoundInRecurse(path As String, errors As IList(Of Diagnostic))
            AddDiagnostic(errors, ERRID.ERR_InvalidSwitchValue, path, "recurse")
        End Sub

        Private Shared Sub AddDiagnostic(diagnostics As IList(Of Diagnostic), errorCode As ERRID, ParamArray arguments As Object())
            diagnostics.Add(Diagnostic.Create(VisualBasic.MessageProvider.Instance, CInt(errorCode), arguments))
        End Sub

        ''' <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()
1978 1979 1980 1981 1982 1983

                    If simpleName.Length = 0 AndAlso Not kind.IsNetModule() Then
                        AddDiagnostic(diagnostics, ERRID.FTL_InputFileNameTooLong, outputFileName)
                        simpleName = Nothing
                        outputFileName = Nothing
                    End If
P
Pilchie 已提交
1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 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
                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