﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Test.Resources.Proprietary;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Microsoft.DiaSymReader;
using Roslyn.Test.PdbUtilities;
using Roslyn.Test.Utilities;
using Roslyn.Test.Utilities.TestGenerators;
using Roslyn.Utilities;
using Xunit;
using Basic.Reference.Assemblies;
using static Microsoft.CodeAnalysis.CommonDiagnosticAnalyzers;
using static Roslyn.Test.Utilities.SharedResourceHelpers;
using static Roslyn.Test.Utilities.TestMetadata;

namespace Microsoft.CodeAnalysis.CSharp.CommandLine.UnitTests
{
    public class CommandLineTests : CommandLineTestBase
    {
#if NETCOREAPP
        private static readonly string s_CSharpCompilerExecutable;
        private static readonly string s_DotnetCscRun;
#else
        private static readonly string s_CSharpCompilerExecutable = Path.Combine(
            Path.GetDirectoryName(typeof(CommandLineTests).GetTypeInfo().Assembly.Location),
            Path.Combine("dependency", "csc.exe"));
        private static readonly string s_DotnetCscRun = ExecutionConditionUtil.IsMono ? "mono" : string.Empty;
#endif
        private static readonly string s_CSharpScriptExecutable;

        private static readonly string s_compilerVersion = CommonCompiler.GetProductVersion(typeof(CommandLineTests));

        static CommandLineTests()
        {
#if NETCOREAPP
            var cscDllPath = Path.Combine(
                Path.GetDirectoryName(typeof(CommandLineTests).GetTypeInfo().Assembly.Location),
                Path.Combine("dependency", "csc.dll"));
            var dotnetExe = DotNetCoreSdk.ExePath;
            var netStandardDllPath = AppDomain.CurrentDomain.GetAssemblies()
                .FirstOrDefault(assembly => !assembly.IsDynamic && assembly.Location.EndsWith("netstandard.dll")).Location;
            var netStandardDllDir = Path.GetDirectoryName(netStandardDllPath);
            // Since we are using references based on the UnitTest's runtime, we need to use
            // its runtime config when executing out program.
            var runtimeConfigPath = Path.ChangeExtension(Assembly.GetExecutingAssembly().Location, "runtimeconfig.json");

            s_CSharpCompilerExecutable = $@"""{dotnetExe}"" ""{cscDllPath}"" /r:""{netStandardDllPath}"" /r:""{netStandardDllDir}/System.Private.CoreLib.dll"" /r:""{netStandardDllDir}/System.Console.dll"" /r:""{netStandardDllDir}/System.Runtime.dll""";
            s_DotnetCscRun = $@"""{dotnetExe}"" exec --runtimeconfig ""{runtimeConfigPath}""";
            s_CSharpScriptExecutable = s_CSharpCompilerExecutable.Replace("csc.dll", Path.Combine("csi", "csi.dll"));
#else
            s_CSharpScriptExecutable = s_CSharpCompilerExecutable.Replace("csc.exe", Path.Combine("csi", "csi.exe"));
#endif
        }

        private class TestCommandLineParser : CSharpCommandLineParser
        {
            private readonly Dictionary<string, string> _responseFiles;
            private readonly Dictionary<string, string[]> _recursivePatterns;
            private readonly Dictionary<string, string[]> _patterns;

            public TestCommandLineParser(
                Dictionary<string, string> responseFiles = null,
                Dictionary<string, string[]> patterns = null,
                Dictionary<string, string[]> recursivePatterns = null,
                bool isInteractive = false)
                : base(isInteractive)
            {
                _responseFiles = responseFiles;
                _recursivePatterns = recursivePatterns;
                _patterns = patterns;
            }

            internal override IEnumerable<string> EnumerateFiles(string directory,
                                                                 string fileNamePattern,
                                                                 SearchOption searchOption)
            {
                var key = directory + "|" + fileNamePattern;
                if (searchOption == SearchOption.TopDirectoryOnly)
                {
                    return _patterns[key];
                }
                else
                {
                    return _recursivePatterns[key];
                }
            }

            internal override TextReader CreateTextFileReader(string fullPath)
            {
                return new StringReader(_responseFiles[fullPath]);
            }
        }

        private CSharpCommandLineArguments ScriptParse(IEnumerable<string> args, string baseDirectory)
        {
            return CSharpCommandLineParser.Script.Parse(args, baseDirectory, SdkDirectory);
        }

        private CSharpCommandLineArguments FullParse(string commandLine, string baseDirectory, string sdkDirectory = null, string additionalReferenceDirectories = null)
        {
            sdkDirectory = sdkDirectory ?? SdkDirectory;
            var args = CommandLineParser.SplitCommandLineIntoArguments(commandLine, removeHashComments: true);
            return CSharpCommandLineParser.Default.Parse(args, baseDirectory, sdkDirectory, additionalReferenceDirectories);
        }

        [ConditionalFact(typeof(WindowsDesktopOnly))]
        [WorkItem(34101, "https://github.com/dotnet/roslyn/issues/34101")]
        public void SuppressedWarnAsErrorsStillEmit()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
#pragma warning disable 1591

public class P {
    public static void Main() {}
}");
            const string docName = "doc.xml";

            var cmd = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/errorlog:errorlog", $"/doc:{docName}", "/warnaserror", src.Path });

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString());

            string exePath = Path.Combine(dir.Path, "temp.exe");
            Assert.True(File.Exists(exePath));
            var result = ProcessUtilities.Run(exePath, arguments: "");
            Assert.Equal(0, result.ExitCode);
        }

        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.TestExecutionNeedsWindowsTypes)]
        public void XmlMemoryMapped()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText("class C {}");
            const string docName = "doc.xml";

            var cmd = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/t:library", "/preferreduilang:en", $"/doc:{docName}", src.Path });

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString());

            var xmlPath = Path.Combine(dir.Path, docName);
            using (var fileStream = new FileStream(xmlPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            using (var mmf = MemoryMappedFile.CreateFromFile(fileStream, "xmlMap", 0, MemoryMappedFileAccess.Read, HandleInheritability.None, leaveOpen: true))
            {
                exitCode = cmd.Run(outWriter);
                Assert.StartsWith($"error CS0016: Could not write to output file '{xmlPath}' -- ", outWriter.ToString());
                Assert.Equal(1, exitCode);
            }
        }

        [Fact]
        public void SimpleAnalyzerConfig()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText(@"
class C
{
    int _f;
}");
            var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText(@"
[*.cs]
dotnet_diagnostic.cs0169.severity = none");
            var cmd = CreateCSharpCompiler(null, dir.Path, new[] {
                "/nologo",
                "/t:library",
                "/preferreduilang:en",
                "/analyzerconfig:" + analyzerConfig.Path,
                src.Path });

            Assert.Equal(analyzerConfig.Path, Assert.Single(cmd.Arguments.AnalyzerConfigPaths));

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString());

            Assert.Null(cmd.AnalyzerOptions);
        }

        [Fact]
        public void AnalyzerConfigWithOptions()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText(@"
class C
{
    int _f;
}");
            var additionalFile = dir.CreateFile("file.txt");
            var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText(@"
[*.cs]
dotnet_diagnostic.cs0169.severity = none
dotnet_diagnostic.Warning01.severity = none
my_option = my_val

[*.txt]
dotnet_diagnostic.cs0169.severity = none
my_option2 = my_val2");
            var cmd = CreateCSharpCompiler(null, dir.Path, new[] {
                "/nologo",
                "/t:library",
                "/analyzerconfig:" + analyzerConfig.Path,
                "/analyzer:" + Assembly.GetExecutingAssembly().Location,
                "/nowarn:8032",
                "/additionalfile:" + additionalFile.Path,
                src.Path });

            Assert.Equal(analyzerConfig.Path, Assert.Single(cmd.Arguments.AnalyzerConfigPaths));

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal("", outWriter.ToString());
            Assert.Equal(0, exitCode);

            var comp = cmd.Compilation;
            var tree = comp.SyntaxTrees.Single();
            var compilerTreeOptions = comp.Options.SyntaxTreeOptionsProvider;
            Assert.True(compilerTreeOptions.TryGetDiagnosticValue(tree, "cs0169", CancellationToken.None, out var severity));
            Assert.Equal(ReportDiagnostic.Suppress, severity);
            Assert.True(compilerTreeOptions.TryGetDiagnosticValue(tree, "warning01", CancellationToken.None, out severity));
            Assert.Equal(ReportDiagnostic.Suppress, severity);

            var analyzerOptions = cmd.AnalyzerOptions.AnalyzerConfigOptionsProvider;
            var options = analyzerOptions.GetOptions(tree);
            Assert.NotNull(options);
            Assert.True(options.TryGetValue("my_option", out string val));
            Assert.Equal("my_val", val);
            Assert.False(options.TryGetValue("my_option2", out _));
            Assert.False(options.TryGetValue("dotnet_diagnostic.cs0169.severity", out _));

            options = analyzerOptions.GetOptions(cmd.AnalyzerOptions.AdditionalFiles.Single());
            Assert.NotNull(options);
            Assert.True(options.TryGetValue("my_option2", out val));
            Assert.Equal("my_val2", val);
            Assert.False(options.TryGetValue("my_option", out _));
            Assert.False(options.TryGetValue("dotnet_diagnostic.cs0169.severity", out _));
        }

        [Fact]
        public void AnalyzerConfigBadSeverity()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText(@"
class C
{
    int _f;
}");
            var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText(@"
[*.cs]
dotnet_diagnostic.cs0169.severity = garbage");
            var cmd = CreateCSharpCompiler(null, dir.Path, new[] {
                "/nologo",
                "/t:library",
                "/preferreduilang:en",
                "/analyzerconfig:" + analyzerConfig.Path,
                src.Path });

            Assert.Equal(analyzerConfig.Path, Assert.Single(cmd.Arguments.AnalyzerConfigPaths));

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal(
$@"warning InvalidSeverityInAnalyzerConfig: The diagnostic 'cs0169' was given an invalid severity 'garbage' in the analyzer config file at '{analyzerConfig.Path}'.
test.cs(4,9): warning CS0169: The field 'C._f' is never used
", outWriter.ToString());

            Assert.Null(cmd.AnalyzerOptions);
        }

        [Fact]
        public void AnalyzerConfigsInSameDir()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText(@"
class C
{
    int _f;
}");
            var configText = @"
[*.cs]
dotnet_diagnostic.cs0169.severity = suppress";

            var analyzerConfig1 = dir.CreateFile("analyzerconfig1").WriteAllText(configText);
            var analyzerConfig2 = dir.CreateFile("analyzerconfig2").WriteAllText(configText);

            var cmd = CreateCSharpCompiler(null, dir.Path, new[] {
                "/nologo",
                "/t:library",
                "/preferreduilang:en",
                "/analyzerconfig:" + analyzerConfig1.Path,
                "/analyzerconfig:" + analyzerConfig2.Path,
                src.Path
            });

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Equal(
                $"error CS8700: Multiple analyzer config files cannot be in the same directory ('{dir.Path}').",
                outWriter.ToString().TrimEnd());
        }

        // This test should only run when the machine's default encoding is shift-JIS
        [ConditionalFact(typeof(WindowsDesktopOnly), typeof(HasShiftJisDefaultEncoding), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void CompileShiftJisOnShiftJis()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("sjis.cs").WriteAllBytes(TestResources.General.ShiftJisSource);

            var cmd = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", src.Path });

            Assert.Null(cmd.Arguments.Encoding);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString());

            var result = ProcessUtilities.Run(Path.Combine(dir.Path, "sjis.exe"), arguments: "", workingDirectory: dir.Path);
            Assert.Equal(0, result.ExitCode);
            Assert.Equal("星野 八郎太", File.ReadAllText(Path.Combine(dir.Path, "output.txt"), Encoding.GetEncoding(932)));
        }

        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void RunWithShiftJisFile()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("sjis.cs").WriteAllBytes(TestResources.General.ShiftJisSource);

            var cmd = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/codepage:932", src.Path });

            Assert.Equal(932, cmd.Arguments.Encoding?.WindowsCodePage);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString());

            var result = ProcessUtilities.Run(Path.Combine(dir.Path, "sjis.exe"), arguments: "", workingDirectory: dir.Path);
            Assert.Equal(0, result.ExitCode);
            Assert.Equal("星野 八郎太", File.ReadAllText(Path.Combine(dir.Path, "output.txt"), Encoding.GetEncoding(932)));
        }

        [WorkItem(946954, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/946954")]
        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void CompilerBinariesAreAnyCPU()
        {
            Assert.Equal(ProcessorArchitecture.MSIL, AssemblyName.GetAssemblyName(s_CSharpCompilerExecutable).ProcessorArchitecture);
        }

        [Fact]
        public void ResponseFiles1()
        {
            string rsp = Temp.CreateFile().WriteAllText(@"
/r:System.dll
/nostdlib
# this is ignored
System.Console.WriteLine(""*?"");  # this is error
a.cs
").Path;
            var cmd = CreateCSharpCompiler(rsp, WorkingDirectory, new[] { "b.cs" });

            cmd.Arguments.Errors.Verify(
                // error CS2001: Source file 'System.Console.WriteLine(*?);' could not be found
                Diagnostic(ErrorCode.ERR_FileNotFound).WithArguments("System.Console.WriteLine(*?);"));

            AssertEx.Equal(new[] { "System.dll" }, cmd.Arguments.MetadataReferences.Select(r => r.Reference));
            AssertEx.Equal(new[] { Path.Combine(WorkingDirectory, "a.cs"), Path.Combine(WorkingDirectory, "b.cs") }, cmd.Arguments.SourceFiles.Select(file => file.Path));

            CleanupAllGeneratedFiles(rsp);
        }

        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.TestExecutionNeedsWindowsTypes)]
        public void ResponseFiles_RelativePaths()
        {
            var parentDir = Temp.CreateDirectory();
            var baseDir = parentDir.CreateDirectory("temp");
            var dirX = baseDir.CreateDirectory("x");
            var dirAB = baseDir.CreateDirectory("a b");
            var dirSubDir = baseDir.CreateDirectory("subdir");
            var dirGoo = parentDir.CreateDirectory("goo");
            var dirBar = parentDir.CreateDirectory("bar");

            string basePath = baseDir.Path;
            Func<string, string> prependBasePath = fileName => Path.Combine(basePath, fileName);

            var parser = new TestCommandLineParser(responseFiles: new Dictionary<string, string>()
            {
                { prependBasePath(@"a.rsp"), @"
""@subdir\b.rsp""
/r:..\v4.0.30319\System.dll
/r:.\System.Data.dll
a.cs @""..\c.rsp"" @\d.rsp
/libpaths:..\goo;../bar;""a b""
"
                },
                { Path.Combine(dirSubDir.Path, @"b.rsp"), @"
b.cs
"
                },
                { prependBasePath(@"..\c.rsp"), @"
c.cs /lib:x
"
                },
                {  Path.Combine(Path.GetPathRoot(basePath), @"d.rsp"), @"

# comment
d.cs
"
                }
            }, isInteractive: false);

            var args = parser.Parse(new[] { "first.cs", "second.cs", "@a.rsp", "last.cs" }, basePath, SdkDirectory);
            args.Errors.Verify();
            Assert.False(args.IsScriptRunner);

            string[] resolvedSourceFiles = args.SourceFiles.Select(f => f.Path).ToArray();
            string[] references = args.MetadataReferences.Select(r => r.Reference).ToArray();

            AssertEx.Equal(new[] { "first.cs", "second.cs", "b.cs", "a.cs", "c.cs", "d.cs", "last.cs" }.Select(prependBasePath), resolvedSourceFiles);
            AssertEx.Equal(new[] { typeof(object).Assembly.Location, @"..\v4.0.30319\System.dll", @".\System.Data.dll" }, references);
            AssertEx.Equal(new[] { RuntimeEnvironment.GetRuntimeDirectory() }.Concat(new[] { @"x", @"..\goo", @"../bar", @"a b" }.Select(prependBasePath)), args.ReferencePaths.ToArray());
            Assert.Equal(basePath, args.BaseDirectory);
        }

#nullable enable
        [ConditionalFact(typeof(WindowsOnly))]
        public void NullBaseDirectoryNotAddedToKeyFileSearchPaths()
        {
            var parser = CSharpCommandLineParser.Default.Parse(new[] { "c:/test.cs" }, baseDirectory: null, SdkDirectory);
            AssertEx.Equal(ImmutableArray.Create<string>(), parser.KeyFileSearchPaths);
            Assert.Null(parser.OutputDirectory);
            parser.Errors.Verify(
                // error CS8762: Output directory could not be determined
                Diagnostic(ErrorCode.ERR_NoOutputDirectory).WithLocation(1, 1)
                );
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void NullBaseDirectoryWithAdditionalFiles()
        {
            var parser = CSharpCommandLineParser.Default.Parse(new[] { "/additionalfile:web.config", "c:/test.cs" }, baseDirectory: null, SdkDirectory);
            AssertEx.Equal(ImmutableArray.Create<string>(), parser.KeyFileSearchPaths);
            Assert.Null(parser.OutputDirectory);
            parser.Errors.Verify(
                // error CS2021: File name 'web.config' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments("web.config").WithLocation(1, 1),
                // error CS8762: Output directory could not be determined
                Diagnostic(ErrorCode.ERR_NoOutputDirectory).WithLocation(1, 1)
                );
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void NullBaseDirectoryWithAdditionalFiles_Wildcard()
        {
            var parser = CSharpCommandLineParser.Default.Parse(new[] { "/additionalfile:*", "c:/test.cs" }, baseDirectory: null, SdkDirectory);
            AssertEx.Equal(ImmutableArray.Create<string>(), parser.KeyFileSearchPaths);
            Assert.Null(parser.OutputDirectory);
            parser.Errors.Verify(
                // error CS2001: Source file '*' could not be found.
                Diagnostic(ErrorCode.ERR_FileNotFound).WithArguments("*").WithLocation(1, 1),
                // error CS8762: Output directory could not be determined
                Diagnostic(ErrorCode.ERR_NoOutputDirectory).WithLocation(1, 1)
                );
        }
#nullable disable

        [Fact, WorkItem(29252, "https://github.com/dotnet/roslyn/issues/29252")]
        public void NoSdkPath()
        {
            var parentDir = Temp.CreateDirectory();
            var parser = CSharpCommandLineParser.Default.Parse(new[] { "file.cs", $"-out:{parentDir.Path}", "/noSdkPath" }, parentDir.Path, null);
            AssertEx.Equal(ImmutableArray<string>.Empty, parser.ReferencePaths);
        }

        [Fact, WorkItem(29252, "https://github.com/dotnet/roslyn/issues/29252")]
        public void NoSdkPathReferenceSystemDll()
        {
            string source = @"
class C
{
}
";
            var dir = Temp.CreateDirectory();

            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/preferreduilang:en", "/nosdkpath", "/r:System.dll", "a.cs" });
            var exitCode = csc.Run(outWriter);

            Assert.Equal(1, exitCode);
            Assert.Equal("error CS0006: Metadata file 'System.dll' could not be found", outWriter.ToString().Trim());
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void SourceFiles_Patterns()
        {
            var parser = new TestCommandLineParser(
                patterns: new Dictionary<string, string[]>()
                {
                    { @"C:\temp|*.cs", new[] { "a.cs", "b.cs", "c.cs" } }
                },
                recursivePatterns: new Dictionary<string, string[]>()
                {
                    { @"C:\temp\a|*.cs", new[] { @"a\x.cs", @"a\b\b.cs", @"a\c.cs" } },
                });

            var args = parser.Parse(new[] { @"*.cs", @"/recurse:a\*.cs" }, @"C:\temp", SdkDirectory);
            args.Errors.Verify();

            string[] resolvedSourceFiles = args.SourceFiles.Select(f => f.Path).ToArray();

            AssertEx.Equal(new[] { @"C:\temp\a.cs", @"C:\temp\b.cs", @"C:\temp\c.cs", @"C:\temp\a\x.cs", @"C:\temp\a\b\b.cs", @"C:\temp\a\c.cs" }, resolvedSourceFiles);
        }

        [Fact]
        public void ParseQuotedMainType()
        {
            // Verify the main switch are unquoted when used because of the issue with
            // MSBuild quoting some usages and not others. A quote character is not valid in either
            // these names.

            CSharpCommandLineArguments args;
            var folder = Temp.CreateDirectory();
            CreateFile(folder, "a.cs");

            args = DefaultParse(new[] { "/main:Test", "a.cs" }, folder.Path);
            args.Errors.Verify();
            Assert.Equal("Test", args.CompilationOptions.MainTypeName);

            args = DefaultParse(new[] { "/main:\"Test\"", "a.cs" }, folder.Path);
            args.Errors.Verify();
            Assert.Equal("Test", args.CompilationOptions.MainTypeName);

            args = DefaultParse(new[] { "/main:\"Test.Class1\"", "a.cs" }, folder.Path);
            args.Errors.Verify();
            Assert.Equal("Test.Class1", args.CompilationOptions.MainTypeName);

            args = DefaultParse(new[] { "/m:Test", "a.cs" }, folder.Path);
            args.Errors.Verify();
            Assert.Equal("Test", args.CompilationOptions.MainTypeName);

            args = DefaultParse(new[] { "/m:\"Test\"", "a.cs" }, folder.Path);
            args.Errors.Verify();
            Assert.Equal("Test", args.CompilationOptions.MainTypeName);

            args = DefaultParse(new[] { "/m:\"Test.Class1\"", "a.cs" }, folder.Path);
            args.Errors.Verify();
            Assert.Equal("Test.Class1", args.CompilationOptions.MainTypeName);

            // Use of Cyrillic namespace
            args = DefaultParse(new[] { "/m:\"решения.Class1\"", "a.cs" }, folder.Path);
            args.Errors.Verify();
            Assert.Equal("решения.Class1", args.CompilationOptions.MainTypeName);
        }

        [Fact]
        [WorkItem(21508, "https://github.com/dotnet/roslyn/issues/21508")]
        public void ArgumentStartWithDashAndContainingSlash()
        {
            CSharpCommandLineArguments args;
            var folder = Temp.CreateDirectory();

            args = DefaultParse(new[] { "-debug+/debug:portable" }, folder.Path);
            args.Errors.Verify(
                // error CS2007: Unrecognized option: '-debug+/debug:portable'
                Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("-debug+/debug:portable").WithLocation(1, 1),
                // warning CS2008: No source files specified.
                Diagnostic(ErrorCode.WRN_NoSources).WithLocation(1, 1),
                // error CS1562: Outputs without source must have the /out option specified
                Diagnostic(ErrorCode.ERR_OutputNeedsName).WithLocation(1, 1)
                );
        }

        [WorkItem(546009, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546009")]
        [WorkItem(545991, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545991")]
        [ConditionalFact(typeof(WindowsOnly))]
        public void SourceFiles_Patterns2()
        {
            var folder = Temp.CreateDirectory();
            CreateFile(folder, "a.cs");
            CreateFile(folder, "b.vb");
            CreateFile(folder, "c.cpp");

            var folderA = folder.CreateDirectory("A");
            CreateFile(folderA, "A_a.cs");
            CreateFile(folderA, "A_b.cs");
            CreateFile(folderA, "A_c.vb");

            var folderB = folder.CreateDirectory("B");
            CreateFile(folderB, "B_a.cs");
            CreateFile(folderB, "B_b.vb");
            CreateFile(folderB, "B_c.cpx");

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, folder.Path, new[] { "/nologo", "/preferreduilang:en", "/t:library", @"/recurse:.", "/out:abc.dll" }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("warning CS2008: No source files specified.", outWriter.ToString().Trim());

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, folder.Path, new[] { "/nologo", "/preferreduilang:en", "/t:library", @"/recurse:.  ", "/out:abc.dll" }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("warning CS2008: No source files specified.", outWriter.ToString().Trim());

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, folder.Path, new[] { "/nologo", "/preferreduilang:en", "/t:library", @"/recurse:  .  ", "/out:abc.dll" }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("warning CS2008: No source files specified.", outWriter.ToString().Trim());

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, folder.Path, new[] { "/nologo", "/preferreduilang:en", "/t:library", @"/recurse:././.", "/out:abc.dll" }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("warning CS2008: No source files specified.", outWriter.ToString().Trim());

            CSharpCommandLineArguments args;
            string[] resolvedSourceFiles;

            args = DefaultParse(new[] { @"/recurse:*.cp*", @"/recurse:a\*.c*", @"/out:a.dll" }, folder.Path);
            args.Errors.Verify();
            resolvedSourceFiles = args.SourceFiles.Select(f => f.Path).ToArray();
            AssertEx.Equal(new[] { folder.Path + @"\c.cpp", folder.Path + @"\B\B_c.cpx", folder.Path + @"\a\A_a.cs", folder.Path + @"\a\A_b.cs", }, resolvedSourceFiles);

            args = DefaultParse(new[] { @"/recurse:.\\\\\\*.cs", @"/out:a.dll" }, folder.Path);
            args.Errors.Verify();
            resolvedSourceFiles = args.SourceFiles.Select(f => f.Path).ToArray();
            Assert.Equal(4, resolvedSourceFiles.Length);

            args = DefaultParse(new[] { @"/recurse:.////*.cs", @"/out:a.dll" }, folder.Path);
            args.Errors.Verify();
            resolvedSourceFiles = args.SourceFiles.Select(f => f.Path).ToArray();
            Assert.Equal(4, resolvedSourceFiles.Length);
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void SourceFile_BadPath()
        {
            var args = DefaultParse(new[] { @"e:c:\test\test.cs", "/t:library" }, WorkingDirectory);
            Assert.Equal(3, args.Errors.Length);
            Assert.Equal((int)ErrorCode.FTL_InvalidInputFileName, args.Errors[0].Code);
            Assert.Equal((int)ErrorCode.WRN_NoSources, args.Errors[1].Code);
            Assert.Equal((int)ErrorCode.ERR_OutputNeedsName, args.Errors[2].Code);
        }

        private void CreateFile(TempDirectory folder, string file)
        {
            var f = folder.CreateFile(file);
            f.WriteAllText("");
        }

        [Fact, WorkItem(546023, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546023")]
        public void Win32ResourceArguments()
        {
            string[] args = new string[]
            {
                @"/win32manifest:..\here\there\everywhere\nonexistent"
            };

            var parsedArgs = DefaultParse(args, WorkingDirectory);
            var compilation = CreateCompilation(new SyntaxTree[0]);
            IEnumerable<DiagnosticInfo> errors;
            CSharpCompiler.GetWin32ResourcesInternal(StandardFileSystem.Instance, MessageProvider.Instance, parsedArgs, compilation, out errors);
            Assert.Equal(1, errors.Count());
            Assert.Equal((int)ErrorCode.ERR_CantOpenWin32Manifest, errors.First().Code);
            Assert.Equal(2, errors.First().Arguments.Count());

            args = new string[]
            {
                @"/Win32icon:\bogus"
            };

            parsedArgs = DefaultParse(args, WorkingDirectory);

            CSharpCompiler.GetWin32ResourcesInternal(StandardFileSystem.Instance, MessageProvider.Instance, parsedArgs, compilation, out errors);
            Assert.Equal(1, errors.Count());
            Assert.Equal((int)ErrorCode.ERR_CantOpenIcon, errors.First().Code);
            Assert.Equal(2, errors.First().Arguments.Count());

            args = new string[]
            {
                @"/Win32Res:\bogus"
            };

            parsedArgs = DefaultParse(args, WorkingDirectory);
            CSharpCompiler.GetWin32ResourcesInternal(StandardFileSystem.Instance, MessageProvider.Instance, parsedArgs, compilation, out errors);
            Assert.Equal(1, errors.Count());
            Assert.Equal((int)ErrorCode.ERR_CantOpenWin32Res, errors.First().Code);
            Assert.Equal(2, errors.First().Arguments.Count());

            args = new string[]
            {
                @"/Win32Res:goo.win32data:bar.win32data2"
            };

            parsedArgs = DefaultParse(args, WorkingDirectory);
            CSharpCompiler.GetWin32ResourcesInternal(StandardFileSystem.Instance, MessageProvider.Instance, parsedArgs, compilation, out errors);
            Assert.Equal(1, errors.Count());
            Assert.Equal((int)ErrorCode.ERR_CantOpenWin32Res, errors.First().Code);
            Assert.Equal(2, errors.First().Arguments.Count());

            args = new string[]
            {
                @"/Win32icon:goo.win32data:bar.win32data2"
            };

            parsedArgs = DefaultParse(args, WorkingDirectory);
            CSharpCompiler.GetWin32ResourcesInternal(StandardFileSystem.Instance, MessageProvider.Instance, parsedArgs, compilation, out errors);
            Assert.Equal(1, errors.Count());
            Assert.Equal((int)ErrorCode.ERR_CantOpenIcon, errors.First().Code);
            Assert.Equal(2, errors.First().Arguments.Count());

            args = new string[]
            {
                @"/Win32manifest:goo.win32data:bar.win32data2"
            };

            parsedArgs = DefaultParse(args, WorkingDirectory);
            CSharpCompiler.GetWin32ResourcesInternal(StandardFileSystem.Instance, MessageProvider.Instance, parsedArgs, compilation, out errors);
            Assert.Equal(1, errors.Count());
            Assert.Equal((int)ErrorCode.ERR_CantOpenWin32Manifest, errors.First().Code);
            Assert.Equal(2, errors.First().Arguments.Count());
        }

        [Fact]
        public void Win32ResConflicts()
        {
            var parsedArgs = DefaultParse(new[] { "/win32res:goo", "/win32icon:goob", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_CantHaveWin32ResAndIcon, parsedArgs.Errors.First().Code);

            parsedArgs = DefaultParse(new[] { "/win32res:goo", "/win32manifest:goob", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_CantHaveWin32ResAndManifest, parsedArgs.Errors.First().Code);

            parsedArgs = DefaultParse(new[] { "/win32res:", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_NoFileSpec, parsedArgs.Errors.First().Code);
            Assert.Equal(1, parsedArgs.Errors.First().Arguments.Count);

            parsedArgs = DefaultParse(new[] { "/win32Icon: ", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_NoFileSpec, parsedArgs.Errors.First().Code);
            Assert.Equal(1, parsedArgs.Errors.First().Arguments.Count);

            parsedArgs = DefaultParse(new[] { "/win32Manifest:", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_NoFileSpec, parsedArgs.Errors.First().Code);
            Assert.Equal(1, parsedArgs.Errors.First().Arguments.Count);

            parsedArgs = DefaultParse(new[] { "/win32Manifest:goo", "/noWin32Manifest", "a.cs" }, WorkingDirectory);
            Assert.Equal(0, parsedArgs.Errors.Length);
            Assert.True(parsedArgs.NoWin32Manifest);
            Assert.Null(parsedArgs.Win32Manifest);
        }

        [Fact]
        public void Win32ResInvalid()
        {
            var parsedArgs = DefaultParse(new[] { "/win32res", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/win32res"));

            parsedArgs = DefaultParse(new[] { "/win32res+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/win32res+"));

            parsedArgs = DefaultParse(new[] { "/win32icon", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/win32icon"));

            parsedArgs = DefaultParse(new[] { "/win32icon+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/win32icon+"));

            parsedArgs = DefaultParse(new[] { "/win32manifest", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/win32manifest"));

            parsedArgs = DefaultParse(new[] { "/win32manifest+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/win32manifest+"));
        }

        [Fact]
        public void Win32IconContainsGarbage()
        {
            string tmpFileName = Temp.CreateFile().WriteAllBytes(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).Path;

            var parsedArgs = DefaultParse(new[] { "/win32icon:" + tmpFileName, "a.cs" }, WorkingDirectory);
            var compilation = CreateCompilation(new SyntaxTree[0]);
            IEnumerable<DiagnosticInfo> errors;

            CSharpCompiler.GetWin32ResourcesInternal(StandardFileSystem.Instance, MessageProvider.Instance, parsedArgs, compilation, out errors);
            Assert.Equal(1, errors.Count());
            Assert.Equal((int)ErrorCode.ERR_ErrorBuildingWin32Resources, errors.First().Code);
            Assert.Equal(1, errors.First().Arguments.Count());

            CleanupAllGeneratedFiles(tmpFileName);
        }

        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30289")]
        public void Win32ResQuotes()
        {
            string[] responseFile = new string[] {
                @" /win32res:d:\\""abc def""\a""b c""d\a.res",
            };

            CSharpCommandLineArguments args = DefaultParse(CSharpCommandLineParser.ParseResponseLines(responseFile), @"c:\");
            Assert.Equal(@"d:\abc def\ab cd\a.res", args.Win32ResourceFile);

            responseFile = new string[] {
                @" /win32icon:d:\\""abc def""\a""b c""d\a.ico",
            };

            args = DefaultParse(CSharpCommandLineParser.ParseResponseLines(responseFile), @"c:\");
            Assert.Equal(@"d:\abc def\ab cd\a.ico", args.Win32Icon);

            responseFile = new string[] {
                @" /win32manifest:d:\\""abc def""\a""b c""d\a.manifest",
            };

            args = DefaultParse(CSharpCommandLineParser.ParseResponseLines(responseFile), @"c:\");
            Assert.Equal(@"d:\abc def\ab cd\a.manifest", args.Win32Manifest);
        }

        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30289")]
        public void ParseResources()
        {
            var diags = new List<Diagnostic>();

            ResourceDescription desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar", WorkingDirectory, diags, embedded: false);
            Assert.Equal(0, diags.Count);
            Assert.Equal(@"someFile.goo.bar", desc.FileName);
            Assert.Equal("someFile.goo.bar", desc.ResourceName);

            desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,someName", WorkingDirectory, diags, embedded: false);
            Assert.Equal(0, diags.Count);
            Assert.Equal(@"someFile.goo.bar", desc.FileName);
            Assert.Equal("someName", desc.ResourceName);

            desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\s""ome Fil""e.goo.bar,someName", WorkingDirectory, diags, embedded: false);
            Assert.Equal(0, diags.Count);
            Assert.Equal(@"some File.goo.bar", desc.FileName);
            Assert.Equal("someName", desc.ResourceName);

            desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,""some Name"",public", WorkingDirectory, diags, embedded: false);
            Assert.Equal(0, diags.Count);
            Assert.Equal(@"someFile.goo.bar", desc.FileName);
            Assert.Equal("some Name", desc.ResourceName);
            Assert.True(desc.IsPublic);

            // Use file name in place of missing resource name.
            desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,,private", WorkingDirectory, diags, embedded: false);
            Assert.Equal(0, diags.Count);
            Assert.Equal(@"someFile.goo.bar", desc.FileName);
            Assert.Equal("someFile.goo.bar", desc.ResourceName);
            Assert.False(desc.IsPublic);

            // Quoted accessibility is fine.
            desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,,""private""", WorkingDirectory, diags, embedded: false);
            Assert.Equal(0, diags.Count);
            Assert.Equal(@"someFile.goo.bar", desc.FileName);
            Assert.Equal("someFile.goo.bar", desc.ResourceName);
            Assert.False(desc.IsPublic);

            // Leading commas are not ignored...
            desc = CSharpCommandLineParser.ParseResourceDescription("", @",,\somepath\someFile.goo.bar,,private", WorkingDirectory, diags, embedded: false);
            diags.Verify(
                // error CS1906: Invalid option '\somepath\someFile.goo.bar'; Resource visibility must be either 'public' or 'private'
                Diagnostic(ErrorCode.ERR_BadResourceVis).WithArguments(@"\somepath\someFile.goo.bar"));
            diags.Clear();
            Assert.Null(desc);

            // ...even if there's whitespace between them.
            desc = CSharpCommandLineParser.ParseResourceDescription("", @", ,\somepath\someFile.goo.bar,,private", WorkingDirectory, diags, embedded: false);
            diags.Verify(
                // error CS1906: Invalid option '\somepath\someFile.goo.bar'; Resource visibility must be either 'public' or 'private'
                Diagnostic(ErrorCode.ERR_BadResourceVis).WithArguments(@"\somepath\someFile.goo.bar"));
            diags.Clear();
            Assert.Null(desc);

            // Trailing commas are ignored...
            desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,,private", WorkingDirectory, diags, embedded: false);
            diags.Verify();
            diags.Clear();
            Assert.Equal("someFile.goo.bar", desc.FileName);
            Assert.Equal("someFile.goo.bar", desc.ResourceName);
            Assert.False(desc.IsPublic);

            // ...even if there's whitespace between them.
            desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,,private, ,", WorkingDirectory, diags, embedded: false);
            diags.Verify();
            diags.Clear();
            Assert.Equal("someFile.goo.bar", desc.FileName);
            Assert.Equal("someFile.goo.bar", desc.ResourceName);
            Assert.False(desc.IsPublic);

            desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,someName,publi", WorkingDirectory, diags, embedded: false);
            diags.Verify(Diagnostic(ErrorCode.ERR_BadResourceVis).WithArguments("publi"));
            Assert.Null(desc);
            diags.Clear();

            desc = CSharpCommandLineParser.ParseResourceDescription("", @"D:rive\relative\path,someName,public", WorkingDirectory, diags, embedded: false);
            diags.Verify(Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(@"D:rive\relative\path"));
            Assert.Null(desc);
            diags.Clear();

            desc = CSharpCommandLineParser.ParseResourceDescription("", @"inva\l*d?path,someName,public", WorkingDirectory, diags, embedded: false);
            diags.Verify(Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(@"inva\l*d?path"));
            Assert.Null(desc);
            diags.Clear();

            desc = CSharpCommandLineParser.ParseResourceDescription("", (string)null, WorkingDirectory, diags, embedded: false);
            diags.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments(""));
            Assert.Null(desc);
            diags.Clear();

            desc = CSharpCommandLineParser.ParseResourceDescription("", "", WorkingDirectory, diags, embedded: false);
            diags.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments(""));
            Assert.Null(desc);
            diags.Clear();

            desc = CSharpCommandLineParser.ParseResourceDescription("", " ", WorkingDirectory, diags, embedded: false);
            diags.Verify(
                // error CS2005: Missing file specification for '' option
                Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("").WithLocation(1, 1));
            diags.Clear();
            Assert.Null(desc);

            desc = CSharpCommandLineParser.ParseResourceDescription("", " , ", WorkingDirectory, diags, embedded: false);
            diags.Verify(
                // error CS2005: Missing file specification for '' option
                Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("").WithLocation(1, 1));
            diags.Clear();
            Assert.Null(desc);

            desc = CSharpCommandLineParser.ParseResourceDescription("", "path, ", WorkingDirectory, diags, embedded: false);
            diags.Verify();
            diags.Clear();
            Assert.Equal("path", desc.FileName);
            Assert.Equal("path", desc.ResourceName);
            Assert.True(desc.IsPublic);

            desc = CSharpCommandLineParser.ParseResourceDescription("", " ,name", WorkingDirectory, diags, embedded: false);
            diags.Verify(
                // error CS2005: Missing file specification for '' option
                Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("").WithLocation(1, 1));
            diags.Clear();
            Assert.Null(desc);

            desc = CSharpCommandLineParser.ParseResourceDescription("", " , , ", WorkingDirectory, diags, embedded: false);
            diags.Verify(
                // error CS1906: Invalid option ' '; Resource visibility must be either 'public' or 'private'
                Diagnostic(ErrorCode.ERR_BadResourceVis).WithArguments(" "));
            diags.Clear();
            Assert.Null(desc);

            desc = CSharpCommandLineParser.ParseResourceDescription("", "path, , ", WorkingDirectory, diags, embedded: false);
            diags.Verify(
                // error CS1906: Invalid option ' '; Resource visibility must be either 'public' or 'private'
                Diagnostic(ErrorCode.ERR_BadResourceVis).WithArguments(" "));
            diags.Clear();
            Assert.Null(desc);

            desc = CSharpCommandLineParser.ParseResourceDescription("", " ,name, ", WorkingDirectory, diags, embedded: false);
            diags.Verify(
                // error CS1906: Invalid option ' '; Resource visibility must be either 'public' or 'private'
                Diagnostic(ErrorCode.ERR_BadResourceVis).WithArguments(" "));
            diags.Clear();
            Assert.Null(desc);

            desc = CSharpCommandLineParser.ParseResourceDescription("", " , ,private", WorkingDirectory, diags, embedded: false);
            diags.Verify(
                // error CS2005: Missing file specification for '' option
                Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("").WithLocation(1, 1));
            diags.Clear();
            Assert.Null(desc);

            desc = CSharpCommandLineParser.ParseResourceDescription("", "path,name,", WorkingDirectory, diags, embedded: false);
            diags.Verify(
                // CONSIDER: Dev10 actually prints "Invalid option '|'" (note the pipe)
                // error CS1906: Invalid option ''; Resource visibility must be either 'public' or 'private'
                Diagnostic(ErrorCode.ERR_BadResourceVis).WithArguments(""));
            diags.Clear();
            Assert.Null(desc);

            desc = CSharpCommandLineParser.ParseResourceDescription("", "path,name,,", WorkingDirectory, diags, embedded: false);
            diags.Verify(
                // CONSIDER: Dev10 actually prints "Invalid option '|'" (note the pipe)
                // error CS1906: Invalid option ''; Resource visibility must be either 'public' or 'private'
                Diagnostic(ErrorCode.ERR_BadResourceVis).WithArguments(""));
            diags.Clear();
            Assert.Null(desc);

            desc = CSharpCommandLineParser.ParseResourceDescription("", "path,name, ", WorkingDirectory, diags, embedded: false);
            diags.Verify(
                // error CS1906: Invalid option ''; Resource visibility must be either 'public' or 'private'
                Diagnostic(ErrorCode.ERR_BadResourceVis).WithArguments(" "));
            diags.Clear();
            Assert.Null(desc);

            desc = CSharpCommandLineParser.ParseResourceDescription("", "path, ,private", WorkingDirectory, diags, embedded: false);
            diags.Verify();
            diags.Clear();
            Assert.Equal("path", desc.FileName);
            Assert.Equal("path", desc.ResourceName);
            Assert.False(desc.IsPublic);

            desc = CSharpCommandLineParser.ParseResourceDescription("", " ,name,private", WorkingDirectory, diags, embedded: false);
            diags.Verify(
                // error CS2005: Missing file specification for '' option
                Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("").WithLocation(1, 1));
            diags.Clear();
            Assert.Null(desc);

            var longE = new String('e', 1024);

            desc = CSharpCommandLineParser.ParseResourceDescription("", String.Format("path,{0},private", longE), WorkingDirectory, diags, embedded: false);
            diags.Verify(); // Now checked during emit.
            diags.Clear();
            Assert.Equal("path", desc.FileName);
            Assert.Equal(longE, desc.ResourceName);
            Assert.False(desc.IsPublic);

            var longI = new String('i', 260);

            desc = CSharpCommandLineParser.ParseResourceDescription("", String.Format("{0},e,private", longI), WorkingDirectory, diags, embedded: false);
            diags.Verify(
                // error CS2021: File name 'iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments("iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii").WithLocation(1, 1));
        }

        [Fact]
        public void ManagedResourceOptions()
        {
            CSharpCommandLineArguments parsedArgs;
            ResourceDescription resourceDescription;

            parsedArgs = DefaultParse(new[] { "/resource:a", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.DisplayHelp);
            resourceDescription = parsedArgs.ManifestResources.Single();
            Assert.Null(resourceDescription.FileName); // since embedded
            Assert.Equal("a", resourceDescription.ResourceName);

            parsedArgs = DefaultParse(new[] { "/res:b", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.DisplayHelp);
            resourceDescription = parsedArgs.ManifestResources.Single();
            Assert.Null(resourceDescription.FileName); // since embedded
            Assert.Equal("b", resourceDescription.ResourceName);

            parsedArgs = DefaultParse(new[] { "/linkresource:c", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.DisplayHelp);
            resourceDescription = parsedArgs.ManifestResources.Single();
            Assert.Equal("c", resourceDescription.FileName);
            Assert.Equal("c", resourceDescription.ResourceName);

            parsedArgs = DefaultParse(new[] { "/linkres:d", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.DisplayHelp);
            resourceDescription = parsedArgs.ManifestResources.Single();
            Assert.Equal("d", resourceDescription.FileName);
            Assert.Equal("d", resourceDescription.ResourceName);
        }

        [Fact]
        public void ManagedResourceOptions_SimpleErrors()
        {
            var parsedArgs = DefaultParse(new[] { "/resource:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/resource:"));

            parsedArgs = DefaultParse(new[] { "/resource: ", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/resource:"));

            parsedArgs = DefaultParse(new[] { "/res", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/res"));

            parsedArgs = DefaultParse(new[] { "/RES+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/RES+"));

            parsedArgs = DefaultParse(new[] { "/res-:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/res-:"));

            parsedArgs = DefaultParse(new[] { "/linkresource:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/linkresource:"));

            parsedArgs = DefaultParse(new[] { "/linkresource: ", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/linkresource:"));

            parsedArgs = DefaultParse(new[] { "/linkres", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/linkres"));

            parsedArgs = DefaultParse(new[] { "/linkRES+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/linkRES+"));

            parsedArgs = DefaultParse(new[] { "/linkres-:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/linkres-:"));
        }

        [Fact]
        public void Link_SimpleTests()
        {
            var parsedArgs = DefaultParse(new[] { "/link:a", "/link:b,,,,c", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            AssertEx.Equal(new[] { "a", "b", "c" },
                       parsedArgs.MetadataReferences.
                                  Where((res) => res.Properties.EmbedInteropTypes).
                                  Select((res) => res.Reference));

            parsedArgs = DefaultParse(new[] { "/Link: ,,, b ,,", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            AssertEx.Equal(new[] { " b " },
                           parsedArgs.MetadataReferences.
                                      Where((res) => res.Properties.EmbedInteropTypes).
                                      Select((res) => res.Reference));

            parsedArgs = DefaultParse(new[] { "/l:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/l:"));

            parsedArgs = DefaultParse(new[] { "/L", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "/L"));

            parsedArgs = DefaultParse(new[] { "/l+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/l+"));

            parsedArgs = DefaultParse(new[] { "/link-:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/link-:"));
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void Recurse_SimpleTests()
        {
            var dir = Temp.CreateDirectory();
            var file1 = dir.CreateFile("a.cs");
            var file2 = dir.CreateFile("b.cs");
            var file3 = dir.CreateFile("c.txt");
            var file4 = dir.CreateDirectory("d1").CreateFile("d.txt");
            var file5 = dir.CreateDirectory("d2").CreateFile("e.cs");

            file1.WriteAllText("");
            file2.WriteAllText("");
            file3.WriteAllText("");
            file4.WriteAllText("");
            file5.WriteAllText("");

            var parsedArgs = DefaultParse(new[] { "/recurse:" + dir.ToString() + "\\*.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            AssertEx.Equal(new[] { "{DIR}\\a.cs", "{DIR}\\b.cs", "{DIR}\\d2\\e.cs" },
                           parsedArgs.SourceFiles.Select((file) => file.Path.Replace(dir.ToString(), "{DIR}")));

            parsedArgs = DefaultParse(new[] { "*.cs" }, dir.ToString());
            parsedArgs.Errors.Verify();
            AssertEx.Equal(new[] { "{DIR}\\a.cs", "{DIR}\\b.cs" },
                           parsedArgs.SourceFiles.Select((file) => file.Path.Replace(dir.ToString(), "{DIR}")));

            parsedArgs = DefaultParse(new[] { "/reCURSE:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/reCURSE:"));

            parsedArgs = DefaultParse(new[] { "/RECURSE: ", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/RECURSE:"));

            parsedArgs = DefaultParse(new[] { "/recurse", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/recurse"));

            parsedArgs = DefaultParse(new[] { "/recurse+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/recurse+"));

            parsedArgs = DefaultParse(new[] { "/recurse-:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/recurse-:"));

            CleanupAllGeneratedFiles(file1.Path);
            CleanupAllGeneratedFiles(file2.Path);
            CleanupAllGeneratedFiles(file3.Path);
            CleanupAllGeneratedFiles(file4.Path);
            CleanupAllGeneratedFiles(file5.Path);
        }

        [Fact]
        public void Reference_SimpleTests()
        {
            var parsedArgs = DefaultParse(new[] { "/nostdlib", "/r:a", "/REFERENCE:b,,,,c", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            AssertEx.Equal(new[] { "a", "b", "c" },
                           parsedArgs.MetadataReferences.
                                      Where((res) => !res.Properties.EmbedInteropTypes).
                                      Select((res) => res.Reference));

            parsedArgs = DefaultParse(new[] { "/Reference: ,,, b ,,", "/nostdlib", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            AssertEx.Equal(new[] { " b " },
                           parsedArgs.MetadataReferences.
                                      Where((res) => !res.Properties.EmbedInteropTypes).
                                      Select((res) => res.Reference));

            parsedArgs = DefaultParse(new[] { "/Reference:a=b,,,", "/nostdlib", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("a", parsedArgs.MetadataReferences.Single().Properties.Aliases.Single());
            Assert.Equal("b", parsedArgs.MetadataReferences.Single().Reference);

            parsedArgs = DefaultParse(new[] { "/r:a=b,,,c", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_OneAliasPerReference).WithArguments("b,,,c"));

            parsedArgs = DefaultParse(new[] { "/r:1=b", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadExternIdentifier).WithArguments("1"));

            parsedArgs = DefaultParse(new[] { "/r:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/r:"));

            parsedArgs = DefaultParse(new[] { "/R", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "/R"));

            parsedArgs = DefaultParse(new[] { "/reference+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/reference+"));

            parsedArgs = DefaultParse(new[] { "/reference-:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/reference-:"));
        }

        [Fact]
        public void Target_SimpleTests()
        {
            var parsedArgs = DefaultParse(new[] { "/target:exe", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(OutputKind.ConsoleApplication, parsedArgs.CompilationOptions.OutputKind);

            parsedArgs = DefaultParse(new[] { "/t:module", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(OutputKind.NetModule, parsedArgs.CompilationOptions.OutputKind);

            parsedArgs = DefaultParse(new[] { "/target:library", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(OutputKind.DynamicallyLinkedLibrary, parsedArgs.CompilationOptions.OutputKind);

            parsedArgs = DefaultParse(new[] { "/TARGET:winexe", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(OutputKind.WindowsApplication, parsedArgs.CompilationOptions.OutputKind);

            parsedArgs = DefaultParse(new[] { "/target:appcontainerexe", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(OutputKind.WindowsRuntimeApplication, parsedArgs.CompilationOptions.OutputKind);

            parsedArgs = DefaultParse(new[] { "/target:winmdobj", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(OutputKind.WindowsRuntimeMetadata, parsedArgs.CompilationOptions.OutputKind);

            parsedArgs = DefaultParse(new[] { "/target:winexe", "/T:exe", "/target:module", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(OutputKind.NetModule, parsedArgs.CompilationOptions.OutputKind);

            parsedArgs = DefaultParse(new[] { "/t", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/t"));

            parsedArgs = DefaultParse(new[] { "/target:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.FTL_InvalidTarget));

            parsedArgs = DefaultParse(new[] { "/target:xyz", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.FTL_InvalidTarget));

            parsedArgs = DefaultParse(new[] { "/T+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/T+"));

            parsedArgs = DefaultParse(new[] { "/TARGET-:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/TARGET-:"));
        }

        [Fact]
        public void Target_SimpleTestsNoSource()
        {
            var parsedArgs = DefaultParse(new[] { "/target:exe" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // warning CS2008: No source files specified.
                Diagnostic(ErrorCode.WRN_NoSources).WithLocation(1, 1),
                // error CS1562: Outputs without source must have the /out option specified
                Diagnostic(ErrorCode.ERR_OutputNeedsName).WithLocation(1, 1));
            Assert.Equal(OutputKind.ConsoleApplication, parsedArgs.CompilationOptions.OutputKind);

            parsedArgs = DefaultParse(new[] { "/t:module" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // warning CS2008: No source files specified.
                Diagnostic(ErrorCode.WRN_NoSources).WithLocation(1, 1),
                // error CS1562: Outputs without source must have the /out option specified
                Diagnostic(ErrorCode.ERR_OutputNeedsName).WithLocation(1, 1));
            Assert.Equal(OutputKind.NetModule, parsedArgs.CompilationOptions.OutputKind);

            parsedArgs = DefaultParse(new[] { "/target:library" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // warning CS2008: No source files specified.
                Diagnostic(ErrorCode.WRN_NoSources).WithLocation(1, 1),
                // error CS1562: Outputs without source must have the /out option specified
                Diagnostic(ErrorCode.ERR_OutputNeedsName).WithLocation(1, 1));
            Assert.Equal(OutputKind.DynamicallyLinkedLibrary, parsedArgs.CompilationOptions.OutputKind);

            parsedArgs = DefaultParse(new[] { "/TARGET:winexe" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // warning CS2008: No source files specified.
                Diagnostic(ErrorCode.WRN_NoSources).WithLocation(1, 1),
                // error CS1562: Outputs without source must have the /out option specified
                Diagnostic(ErrorCode.ERR_OutputNeedsName).WithLocation(1, 1));
            Assert.Equal(OutputKind.WindowsApplication, parsedArgs.CompilationOptions.OutputKind);

            parsedArgs = DefaultParse(new[] { "/target:appcontainerexe" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // warning CS2008: No source files specified.
                Diagnostic(ErrorCode.WRN_NoSources).WithLocation(1, 1),
                // error CS1562: Outputs without source must have the /out option specified
                Diagnostic(ErrorCode.ERR_OutputNeedsName).WithLocation(1, 1));
            Assert.Equal(OutputKind.WindowsRuntimeApplication, parsedArgs.CompilationOptions.OutputKind);

            parsedArgs = DefaultParse(new[] { "/target:winmdobj" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // warning CS2008: No source files specified.
                Diagnostic(ErrorCode.WRN_NoSources).WithLocation(1, 1),
                // error CS1562: Outputs without source must have the /out option specified
                Diagnostic(ErrorCode.ERR_OutputNeedsName).WithLocation(1, 1));
            Assert.Equal(OutputKind.WindowsRuntimeMetadata, parsedArgs.CompilationOptions.OutputKind);

            parsedArgs = DefaultParse(new[] { "/target:winexe", "/T:exe", "/target:module" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // warning CS2008: No source files specified.
                Diagnostic(ErrorCode.WRN_NoSources).WithLocation(1, 1),
                // error CS1562: Outputs without source must have the /out option specified
                Diagnostic(ErrorCode.ERR_OutputNeedsName).WithLocation(1, 1));
            Assert.Equal(OutputKind.NetModule, parsedArgs.CompilationOptions.OutputKind);

            parsedArgs = DefaultParse(new[] { "/t" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2007: Unrecognized option: '/t'
                Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/t").WithLocation(1, 1),
                // warning CS2008: No source files specified.
                Diagnostic(ErrorCode.WRN_NoSources).WithLocation(1, 1),
                // error CS1562: Outputs without source must have the /out option specified
                Diagnostic(ErrorCode.ERR_OutputNeedsName).WithLocation(1, 1));

            parsedArgs = DefaultParse(new[] { "/target:" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2019: Invalid target type for /target: must specify 'exe', 'winexe', 'library', or 'module'
                Diagnostic(ErrorCode.FTL_InvalidTarget).WithLocation(1, 1),
                // warning CS2008: No source files specified.
                Diagnostic(ErrorCode.WRN_NoSources).WithLocation(1, 1),
                // error CS1562: Outputs without source must have the /out option specified
                Diagnostic(ErrorCode.ERR_OutputNeedsName).WithLocation(1, 1));

            parsedArgs = DefaultParse(new[] { "/target:xyz" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2019: Invalid target type for /target: must specify 'exe', 'winexe', 'library', or 'module'
                Diagnostic(ErrorCode.FTL_InvalidTarget).WithLocation(1, 1),
                // warning CS2008: No source files specified.
                Diagnostic(ErrorCode.WRN_NoSources).WithLocation(1, 1),
                // error CS1562: Outputs without source must have the /out option specified
                Diagnostic(ErrorCode.ERR_OutputNeedsName).WithLocation(1, 1));

            parsedArgs = DefaultParse(new[] { "/T+" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2007: Unrecognized option: '/T+'
                Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/T+").WithLocation(1, 1),
                // warning CS2008: No source files specified.
                Diagnostic(ErrorCode.WRN_NoSources).WithLocation(1, 1),
                // error CS1562: Outputs without source must have the /out option specified
                Diagnostic(ErrorCode.ERR_OutputNeedsName).WithLocation(1, 1));

            parsedArgs = DefaultParse(new[] { "/TARGET-:" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2007: Unrecognized option: '/TARGET-:'
                Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/TARGET-:").WithLocation(1, 1),
                // warning CS2008: No source files specified.
                Diagnostic(ErrorCode.WRN_NoSources).WithLocation(1, 1),
                // error CS1562: Outputs without source must have the /out option specified
                Diagnostic(ErrorCode.ERR_OutputNeedsName).WithLocation(1, 1));
        }

        [Fact]
        public void ModuleManifest()
        {
            CSharpCommandLineArguments args = DefaultParse(new[] { "/win32manifest:blah", "/target:module", "a.cs" }, WorkingDirectory);
            args.Errors.Verify(
                // warning CS1927: Ignoring /win32manifest for module because it only applies to assemblies
                Diagnostic(ErrorCode.WRN_CantHaveManifestForModule));

            // Illegal, but not clobbered.
            Assert.Equal("blah", args.Win32Manifest);
        }

        // The following test is failing in the Linux Debug test leg of CI.
        // This issus is being tracked by https://github.com/dotnet/roslyn/issues/58077
        [ConditionalFact(typeof(WindowsOrMacOSOnly))]
        public void ArgumentParsing()
        {
            var sdkDirectory = SdkDirectory;
            var parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "a + b" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.DisplayHelp);
            Assert.True(parsedArgs.SourceFiles.Any());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "a + b; c" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.DisplayHelp);
            Assert.True(parsedArgs.SourceFiles.Any());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "/help" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.DisplayHelp);
            Assert.False(parsedArgs.SourceFiles.Any());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "/version" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.DisplayVersion);
            Assert.False(parsedArgs.SourceFiles.Any());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "/langversion:?" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.DisplayLangVersions);
            Assert.False(parsedArgs.SourceFiles.Any());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "//langversion:?" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify(
                // error CS2001: Source file '//langversion:?' could not be found.
                Diagnostic(ErrorCode.ERR_FileNotFound).WithArguments("//langversion:?").WithLocation(1, 1)
                );

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "/version", "c.csx" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.DisplayVersion);
            Assert.True(parsedArgs.SourceFiles.Any());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "/version:something" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.DisplayVersion);
            Assert.False(parsedArgs.SourceFiles.Any());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "/?" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.DisplayHelp);
            Assert.False(parsedArgs.SourceFiles.Any());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "c.csx  /langversion:6" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.DisplayHelp);
            Assert.True(parsedArgs.SourceFiles.Any());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "/langversion:-1", "c.csx", }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify(
                // error CS1617: Invalid option '-1' for /langversion. Use '/langversion:?' to list supported values.
                Diagnostic(ErrorCode.ERR_BadCompatMode).WithArguments("-1").WithLocation(1, 1));

            Assert.False(parsedArgs.DisplayHelp);
            Assert.Equal(1, parsedArgs.SourceFiles.Length);

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "c.csx  /r:s=d /r:d.dll" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.DisplayHelp);
            Assert.True(parsedArgs.SourceFiles.Any());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "@roslyn_test_non_existing_file" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify(
                // error CS2011: Error opening response file 'D:\R0\Main\Binaries\Debug\dd'
                Diagnostic(ErrorCode.ERR_OpenResponseFile).WithArguments(Path.Combine(WorkingDirectory, @"roslyn_test_non_existing_file")));

            Assert.False(parsedArgs.DisplayHelp);
            Assert.False(parsedArgs.SourceFiles.Any());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "c /define:DEBUG" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.DisplayHelp);
            Assert.True(parsedArgs.SourceFiles.Any());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "\\" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.DisplayHelp);
            Assert.True(parsedArgs.SourceFiles.Any());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "/r:d.dll", "c.csx" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.DisplayHelp);
            Assert.True(parsedArgs.SourceFiles.Any());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "/define:goo", "c.csx" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify(
                // error CS2007: Unrecognized option: '/define:goo'
                Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/define:goo"));
            Assert.False(parsedArgs.DisplayHelp);
            Assert.True(parsedArgs.SourceFiles.Any());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "\"/r d.dll\"" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.DisplayHelp);
            Assert.True(parsedArgs.SourceFiles.Any());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new[] { "/r: d.dll", "a.cs" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.DisplayHelp);
            Assert.True(parsedArgs.SourceFiles.Any());
        }

        [Theory]
        [InlineData("iso-1", LanguageVersion.CSharp1)]
        [InlineData("iso-2", LanguageVersion.CSharp2)]
        [InlineData("1", LanguageVersion.CSharp1)]
        [InlineData("1.0", LanguageVersion.CSharp1)]
        [InlineData("2", LanguageVersion.CSharp2)]
        [InlineData("2.0", LanguageVersion.CSharp2)]
        [InlineData("3", LanguageVersion.CSharp3)]
        [InlineData("3.0", LanguageVersion.CSharp3)]
        [InlineData("4", LanguageVersion.CSharp4)]
        [InlineData("4.0", LanguageVersion.CSharp4)]
        [InlineData("5", LanguageVersion.CSharp5)]
        [InlineData("5.0", LanguageVersion.CSharp5)]
        [InlineData("6", LanguageVersion.CSharp6)]
        [InlineData("6.0", LanguageVersion.CSharp6)]
        [InlineData("7", LanguageVersion.CSharp7)]
        [InlineData("7.0", LanguageVersion.CSharp7)]
        [InlineData("7.1", LanguageVersion.CSharp7_1)]
        [InlineData("7.2", LanguageVersion.CSharp7_2)]
        [InlineData("7.3", LanguageVersion.CSharp7_3)]
        [InlineData("8", LanguageVersion.CSharp8)]
        [InlineData("8.0", LanguageVersion.CSharp8)]
        [InlineData("9", LanguageVersion.CSharp9)]
        [InlineData("9.0", LanguageVersion.CSharp9)]
        [InlineData("preview", LanguageVersion.Preview)]
        public void LangVersion_CanParseCorrectVersions(string value, LanguageVersion expectedVersion)
        {
            var parsedArgs = DefaultParse(new[] { $"/langversion:{value}", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(expectedVersion, parsedArgs.ParseOptions.LanguageVersion);
            Assert.Equal(expectedVersion, parsedArgs.ParseOptions.SpecifiedLanguageVersion);

            var scriptParsedArgs = ScriptParse(new[] { $"/langversion:{value}" }, WorkingDirectory);
            scriptParsedArgs.Errors.Verify();
            Assert.Equal(expectedVersion, scriptParsedArgs.ParseOptions.LanguageVersion);
            Assert.Equal(expectedVersion, scriptParsedArgs.ParseOptions.SpecifiedLanguageVersion);
        }

        [Theory]
        [InlineData("6", "7", LanguageVersion.CSharp7)]
        [InlineData("7", "6", LanguageVersion.CSharp6)]
        [InlineData("7", "1", LanguageVersion.CSharp1)]
        [InlineData("6", "iso-1", LanguageVersion.CSharp1)]
        [InlineData("6", "iso-2", LanguageVersion.CSharp2)]
        [InlineData("6", "default", LanguageVersion.Default)]
        [InlineData("7", "default", LanguageVersion.Default)]
        [InlineData("iso-2", "6", LanguageVersion.CSharp6)]
        public void LangVersion_LatterVersionOverridesFormerOne(string formerValue, string latterValue, LanguageVersion expectedVersion)
        {
            var parsedArgs = DefaultParse(new[] { $"/langversion:{formerValue}", $"/langversion:{latterValue}", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(expectedVersion, parsedArgs.ParseOptions.SpecifiedLanguageVersion);
        }

        [Fact]
        public void LangVersion_DefaultMapsCorrectly()
        {
            LanguageVersion defaultEffectiveVersion = LanguageVersion.Default.MapSpecifiedToEffectiveVersion();
            Assert.NotEqual(LanguageVersion.Default, defaultEffectiveVersion);

            var parsedArgs = DefaultParse(new[] { "/langversion:default", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            Assert.Equal(LanguageVersion.Default, parsedArgs.ParseOptions.SpecifiedLanguageVersion);
            Assert.Equal(defaultEffectiveVersion, parsedArgs.ParseOptions.LanguageVersion);
        }

        [Fact]
        public void LangVersion_LatestMapsCorrectly()
        {
            LanguageVersion latestEffectiveVersion = LanguageVersion.Latest.MapSpecifiedToEffectiveVersion();
            Assert.NotEqual(LanguageVersion.Latest, latestEffectiveVersion);

            var parsedArgs = DefaultParse(new[] { "/langversion:latest", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            Assert.Equal(LanguageVersion.Latest, parsedArgs.ParseOptions.SpecifiedLanguageVersion);
            Assert.Equal(latestEffectiveVersion, parsedArgs.ParseOptions.LanguageVersion);
        }

        [Fact]
        public void LangVersion_NoValueSpecified()
        {
            var parsedArgs = DefaultParse(new[] { "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(LanguageVersion.Default, parsedArgs.ParseOptions.SpecifiedLanguageVersion);
        }

        [Theory]
        [InlineData("iso-3")]
        [InlineData("iso1")]
        [InlineData("8.1")]
        [InlineData("10.1")]
        [InlineData("11")]
        [InlineData("1000")]
        public void LangVersion_BadVersion(string value)
        {
            DefaultParse(new[] { $"/langversion:{value}", "a.cs" }, WorkingDirectory).Errors.Verify(
                // error CS1617: Invalid option 'XXX' for /langversion. Use '/langversion:?' to list supported values.
                Diagnostic(ErrorCode.ERR_BadCompatMode).WithArguments(value).WithLocation(1, 1)
                );
        }

        [Theory]
        [InlineData("0")]
        [InlineData("05")]
        [InlineData("07")]
        [InlineData("07.1")]
        [InlineData("08")]
        [InlineData("09")]
        public void LangVersion_LeadingZeroes(string value)
        {
            DefaultParse(new[] { $"/langversion:{value}", "a.cs" }, WorkingDirectory).Errors.Verify(
                // error CS8303: Specified language version 'XXX' cannot have leading zeroes
                Diagnostic(ErrorCode.ERR_LanguageVersionCannotHaveLeadingZeroes).WithArguments(value).WithLocation(1, 1));
        }

        [Theory]
        [InlineData("/langversion")]
        [InlineData("/langversion:")]
        [InlineData("/LANGversion:")]
        public void LangVersion_NoVersion(string option)
        {
            DefaultParse(new[] { option, "a.cs" }, WorkingDirectory).Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for '/langversion:' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "/langversion:").WithLocation(1, 1));
        }

        [Fact]
        public void LangVersion_LangVersions()
        {
            var args = DefaultParse(new[] { "/langversion:?" }, WorkingDirectory);
            args.Errors.Verify(
                // warning CS2008: No source files specified.
                Diagnostic(ErrorCode.WRN_NoSources).WithLocation(1, 1),
                // error CS1562: Outputs without source must have the /out option specified
                Diagnostic(ErrorCode.ERR_OutputNeedsName).WithLocation(1, 1)
                );
            Assert.True(args.DisplayLangVersions);
        }

        [Fact]
        public void LanguageVersionAdded_Canary()
        {
            // When a new version is added, this test will break. This list must be checked:
            // - update the "UpgradeProject" codefixer
            // - update all the tests that call this canary
            // - update MaxSupportedLangVersion (a relevant test should break when new version is introduced)
            // - email release management to add to the release notes (see old example: https://github.com/dotnet/core/pull/1454)
            AssertEx.SetEqual(new[] { "default", "1", "2", "3", "4", "5", "6", "7.0", "7.1", "7.2", "7.3", "8.0", "9.0", "10.0", "latest", "latestmajor", "preview" },
                Enum.GetValues(typeof(LanguageVersion)).Cast<LanguageVersion>().Select(v => v.ToDisplayString()));
            // For minor versions and new major versions, the format should be "x.y", such as "7.1"
        }

        [Fact]
        public void LanguageVersion_GetErrorCode()
        {
            var versions = Enum.GetValues(typeof(LanguageVersion))
                .Cast<LanguageVersion>()
                .Except(new[] {
                    LanguageVersion.Default,
                    LanguageVersion.Latest,
                    LanguageVersion.LatestMajor,
                    LanguageVersion.Preview
                })
                .Select(v => v.GetErrorCode());

            var errorCodes = new[]
            {
                ErrorCode.ERR_FeatureNotAvailableInVersion1,
                ErrorCode.ERR_FeatureNotAvailableInVersion2,
                ErrorCode.ERR_FeatureNotAvailableInVersion3,
                ErrorCode.ERR_FeatureNotAvailableInVersion4,
                ErrorCode.ERR_FeatureNotAvailableInVersion5,
                ErrorCode.ERR_FeatureNotAvailableInVersion6,
                ErrorCode.ERR_FeatureNotAvailableInVersion7,
                ErrorCode.ERR_FeatureNotAvailableInVersion7_1,
                ErrorCode.ERR_FeatureNotAvailableInVersion7_2,
                ErrorCode.ERR_FeatureNotAvailableInVersion7_3,
                ErrorCode.ERR_FeatureNotAvailableInVersion8,
                ErrorCode.ERR_FeatureNotAvailableInVersion9,
                ErrorCode.ERR_FeatureNotAvailableInVersion10,
            };

            AssertEx.SetEqual(versions, errorCodes);

            // The canary check is a reminder that this test needs to be updated when a language version is added
            LanguageVersionAdded_Canary();
        }

        [Theory,
            InlineData(LanguageVersion.CSharp1, LanguageVersion.CSharp1),
            InlineData(LanguageVersion.CSharp2, LanguageVersion.CSharp2),
            InlineData(LanguageVersion.CSharp3, LanguageVersion.CSharp3),
            InlineData(LanguageVersion.CSharp4, LanguageVersion.CSharp4),
            InlineData(LanguageVersion.CSharp5, LanguageVersion.CSharp5),
            InlineData(LanguageVersion.CSharp6, LanguageVersion.CSharp6),
            InlineData(LanguageVersion.CSharp7, LanguageVersion.CSharp7),
            InlineData(LanguageVersion.CSharp7_1, LanguageVersion.CSharp7_1),
            InlineData(LanguageVersion.CSharp7_2, LanguageVersion.CSharp7_2),
            InlineData(LanguageVersion.CSharp7_3, LanguageVersion.CSharp7_3),
            InlineData(LanguageVersion.CSharp8, LanguageVersion.CSharp8),
            InlineData(LanguageVersion.CSharp9, LanguageVersion.CSharp9),
            InlineData(LanguageVersion.CSharp10, LanguageVersion.CSharp10),
            InlineData(LanguageVersion.CSharp10, LanguageVersion.LatestMajor),
            InlineData(LanguageVersion.CSharp10, LanguageVersion.Latest),
            InlineData(LanguageVersion.CSharp10, LanguageVersion.Default),
            InlineData(LanguageVersion.Preview, LanguageVersion.Preview),
            ]
        public void LanguageVersion_MapSpecifiedToEffectiveVersion(LanguageVersion expectedMappedVersion, LanguageVersion input)
        {
            Assert.Equal(expectedMappedVersion, input.MapSpecifiedToEffectiveVersion());
            Assert.True(expectedMappedVersion.IsValid());

            // The canary check is a reminder that this test needs to be updated when a language version is added
            LanguageVersionAdded_Canary();
        }

        [Theory,
            InlineData("iso-1", true, LanguageVersion.CSharp1),
            InlineData("ISO-1", true, LanguageVersion.CSharp1),
            InlineData("iso-2", true, LanguageVersion.CSharp2),
            InlineData("1", true, LanguageVersion.CSharp1),
            InlineData("1.0", true, LanguageVersion.CSharp1),
            InlineData("2", true, LanguageVersion.CSharp2),
            InlineData("2.0", true, LanguageVersion.CSharp2),
            InlineData("3", true, LanguageVersion.CSharp3),
            InlineData("3.0", true, LanguageVersion.CSharp3),
            InlineData("4", true, LanguageVersion.CSharp4),
            InlineData("4.0", true, LanguageVersion.CSharp4),
            InlineData("5", true, LanguageVersion.CSharp5),
            InlineData("5.0", true, LanguageVersion.CSharp5),
            InlineData("05", false, LanguageVersion.Default),
            InlineData("6", true, LanguageVersion.CSharp6),
            InlineData("6.0", true, LanguageVersion.CSharp6),
            InlineData("7", true, LanguageVersion.CSharp7),
            InlineData("7.0", true, LanguageVersion.CSharp7),
            InlineData("07", false, LanguageVersion.Default),
            InlineData("7.1", true, LanguageVersion.CSharp7_1),
            InlineData("7.2", true, LanguageVersion.CSharp7_2),
            InlineData("7.3", true, LanguageVersion.CSharp7_3),
            InlineData("8", true, LanguageVersion.CSharp8),
            InlineData("8.0", true, LanguageVersion.CSharp8),
            InlineData("9", true, LanguageVersion.CSharp9),
            InlineData("9.0", true, LanguageVersion.CSharp9),
            InlineData("10", true, LanguageVersion.CSharp10),
            InlineData("10.0", true, LanguageVersion.CSharp10),
            InlineData("08", false, LanguageVersion.Default),
            InlineData("07.1", false, LanguageVersion.Default),
            InlineData("default", true, LanguageVersion.Default),
            InlineData("latest", true, LanguageVersion.Latest),
            InlineData("latestmajor", true, LanguageVersion.LatestMajor),
            InlineData("preview", true, LanguageVersion.Preview),
            InlineData("latestpreview", false, LanguageVersion.Default),
            InlineData(null, true, LanguageVersion.Default),
            InlineData("bad", false, LanguageVersion.Default)]
        public void LanguageVersion_TryParseDisplayString(string input, bool success, LanguageVersion expected)
        {
            Assert.Equal(success, LanguageVersionFacts.TryParse(input, out var version));
            Assert.Equal(expected, version);

            // The canary check is a reminder that this test needs to be updated when a language version is added
            LanguageVersionAdded_Canary();
        }

        [Fact]
        public void LanguageVersion_TryParseTurkishDisplayString()
        {
            var originalCulture = Thread.CurrentThread.CurrentCulture;
            Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR", useUserOverride: false);
            Assert.True(LanguageVersionFacts.TryParse("ISO-1", out var version));
            Assert.Equal(LanguageVersion.CSharp1, version);
            Thread.CurrentThread.CurrentCulture = originalCulture;
        }

        [Fact]
        public void LangVersion_ListLangVersions()
        {
            var dir = Temp.CreateDirectory();
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/langversion:?" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);

            var expected = Enum.GetValues(typeof(LanguageVersion)).Cast<LanguageVersion>()
                .Select(v => v.ToDisplayString());

            var actual = outWriter.ToString();
            var acceptableSurroundingChar = new[] { '\r', '\n', '(', ')', ' ' };
            foreach (var version in expected)
            {
                if (version == "latest")
                    continue;

                var foundIndex = actual.IndexOf(version);
                Assert.True(foundIndex > 0, $"Missing version '{version}'");
                Assert.True(Array.IndexOf(acceptableSurroundingChar, actual[foundIndex - 1]) >= 0);
                Assert.True(Array.IndexOf(acceptableSurroundingChar, actual[foundIndex + version.Length]) >= 0);
            }
        }

        [Fact]
        [WorkItem(546961, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546961")]
        public void Define()
        {
            var parsedArgs = DefaultParse(new[] { "a.cs" }, WorkingDirectory);
            Assert.Equal(0, parsedArgs.ParseOptions.PreprocessorSymbolNames.Count());
            Assert.False(parsedArgs.Errors.Any());

            parsedArgs = DefaultParse(new[] { "/d:GOO", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.ParseOptions.PreprocessorSymbolNames.Count());
            Assert.Contains("GOO", parsedArgs.ParseOptions.PreprocessorSymbolNames);
            Assert.False(parsedArgs.Errors.Any());

            parsedArgs = DefaultParse(new[] { "/d:GOO;BAR,ZIP", "a.cs" }, WorkingDirectory);
            Assert.Equal(3, parsedArgs.ParseOptions.PreprocessorSymbolNames.Count());
            Assert.Contains("GOO", parsedArgs.ParseOptions.PreprocessorSymbolNames);
            Assert.Contains("BAR", parsedArgs.ParseOptions.PreprocessorSymbolNames);
            Assert.Contains("ZIP", parsedArgs.ParseOptions.PreprocessorSymbolNames);
            Assert.False(parsedArgs.Errors.Any());

            parsedArgs = DefaultParse(new[] { "/d:GOO;4X", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.ParseOptions.PreprocessorSymbolNames.Count());
            Assert.Contains("GOO", parsedArgs.ParseOptions.PreprocessorSymbolNames);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.WRN_DefineIdentifierRequired, parsedArgs.Errors.First().Code);
            Assert.Equal("4X", parsedArgs.Errors.First().Arguments[0]);

            IEnumerable<Diagnostic> diagnostics;

            // The docs say /d:def1[;def2]
            string compliant = "def1;def2;def3";
            var expected = new[] { "def1", "def2", "def3" };
            var parsed = CSharpCommandLineParser.ParseConditionalCompilationSymbols(compliant, out diagnostics);
            diagnostics.Verify();
            Assert.Equal<string>(expected, parsed);

            // Bug 17360: Dev11 allows for a terminating semicolon
            var dev11Compliant = "def1;def2;def3;";
            parsed = CSharpCommandLineParser.ParseConditionalCompilationSymbols(dev11Compliant, out diagnostics);
            diagnostics.Verify();
            Assert.Equal<string>(expected, parsed);

            // And comma
            dev11Compliant = "def1,def2,def3,";
            parsed = CSharpCommandLineParser.ParseConditionalCompilationSymbols(dev11Compliant, out diagnostics);
            diagnostics.Verify();
            Assert.Equal<string>(expected, parsed);

            // This breaks everything
            var nonCompliant = "def1;;def2;";
            parsed = CSharpCommandLineParser.ParseConditionalCompilationSymbols(nonCompliant, out diagnostics);
            diagnostics.Verify(
                // warning CS2029: Invalid name for a preprocessing symbol; '' is not a valid identifier
                Diagnostic(ErrorCode.WRN_DefineIdentifierRequired).WithArguments(""));
            Assert.Equal(new[] { "def1", "def2" }, parsed);

            // Bug 17360
            parsedArgs = DefaultParse(new[] { "/d:public1;public2;", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
        }

        [Fact]
        public void Debug()
        {
            var platformPdbKind = PathUtilities.IsUnixLikePlatform ? DebugInformationFormat.PortablePdb : DebugInformationFormat.Pdb;

            var parsedArgs = DefaultParse(new[] { "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.False(parsedArgs.EmitPdb);
            Assert.False(parsedArgs.EmitPdbFile);
            Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, platformPdbKind);

            parsedArgs = DefaultParse(new[] { "/debug-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.False(parsedArgs.EmitPdb);
            Assert.False(parsedArgs.EmitPdbFile);
            Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, platformPdbKind);

            parsedArgs = DefaultParse(new[] { "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.True(parsedArgs.EmitPdb);
            Assert.True(parsedArgs.EmitPdbFile);
            Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, platformPdbKind);

            parsedArgs = DefaultParse(new[] { "/debug+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.True(parsedArgs.EmitPdb);
            Assert.True(parsedArgs.EmitPdbFile);
            Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, platformPdbKind);

            parsedArgs = DefaultParse(new[] { "/debug+", "/debug-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.False(parsedArgs.EmitPdb);
            Assert.False(parsedArgs.EmitPdbFile);
            Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, platformPdbKind);

            parsedArgs = DefaultParse(new[] { "/debug:full", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.True(parsedArgs.EmitPdb);
            Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, platformPdbKind);

            parsedArgs = DefaultParse(new[] { "/debug:FULL", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.True(parsedArgs.EmitPdb);
            Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, platformPdbKind);
            Assert.Equal(Path.Combine(WorkingDirectory, "a.pdb"), parsedArgs.GetPdbFilePath("a.dll"));

            parsedArgs = DefaultParse(new[] { "/debug:pdbonly", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.True(parsedArgs.EmitPdb);
            Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, platformPdbKind);

            parsedArgs = DefaultParse(new[] { "/debug:portable", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.True(parsedArgs.EmitPdb);
            Assert.Equal(DebugInformationFormat.PortablePdb, parsedArgs.EmitOptions.DebugInformationFormat);
            Assert.Equal(Path.Combine(WorkingDirectory, "a.pdb"), parsedArgs.GetPdbFilePath("a.dll"));

            parsedArgs = DefaultParse(new[] { "/debug:embedded", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.True(parsedArgs.EmitPdb);
            Assert.Equal(DebugInformationFormat.Embedded, parsedArgs.EmitOptions.DebugInformationFormat);
            Assert.Equal(Path.Combine(WorkingDirectory, "a.pdb"), parsedArgs.GetPdbFilePath("a.dll"));

            parsedArgs = DefaultParse(new[] { "/debug:PDBONLY", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.True(parsedArgs.EmitPdb);
            Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, platformPdbKind);

            parsedArgs = DefaultParse(new[] { "/debug:full", "/debug:pdbonly", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.True(parsedArgs.EmitPdb);
            Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, platformPdbKind);

            parsedArgs = DefaultParse(new[] { "/debug:pdbonly", "/debug:full", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.True(parsedArgs.EmitPdb);
            Assert.Equal(platformPdbKind, parsedArgs.EmitOptions.DebugInformationFormat);

            parsedArgs = DefaultParse(new[] { "/debug:pdbonly", "/debug-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.False(parsedArgs.EmitPdb);
            Assert.Equal(platformPdbKind, parsedArgs.EmitOptions.DebugInformationFormat);

            parsedArgs = DefaultParse(new[] { "/debug:pdbonly", "/debug-", "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.True(parsedArgs.EmitPdb);
            Assert.Equal(platformPdbKind, parsedArgs.EmitOptions.DebugInformationFormat);

            parsedArgs = DefaultParse(new[] { "/debug:pdbonly", "/debug-", "/debug+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.True(parsedArgs.EmitPdb);
            Assert.Equal(platformPdbKind, parsedArgs.EmitOptions.DebugInformationFormat);

            parsedArgs = DefaultParse(new[] { "/debug:embedded", "/debug-", "/debug+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.True(parsedArgs.EmitPdb);
            Assert.Equal(DebugInformationFormat.Embedded, parsedArgs.EmitOptions.DebugInformationFormat);

            parsedArgs = DefaultParse(new[] { "/debug:embedded", "/debug-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
            Assert.False(parsedArgs.EmitPdb);
            Assert.Equal(DebugInformationFormat.Embedded, parsedArgs.EmitOptions.DebugInformationFormat);

            parsedArgs = DefaultParse(new[] { "/debug:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "debug"));

            parsedArgs = DefaultParse(new[] { "/debug:+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadDebugType).WithArguments("+"));

            parsedArgs = DefaultParse(new[] { "/debug:invalid", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadDebugType).WithArguments("invalid"));

            parsedArgs = DefaultParse(new[] { "/debug-:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/debug-:"));
        }

        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30289")]
        public void Pdb()
        {
            var parsedArgs = DefaultParse(new[] { "/pdb:something", "a.cs" }, WorkingDirectory);
            Assert.Equal(Path.Combine(WorkingDirectory, "something.pdb"), parsedArgs.PdbPath);
            Assert.Equal(Path.Combine(WorkingDirectory, "something.pdb"), parsedArgs.GetPdbFilePath("a.dll"));
            Assert.False(parsedArgs.EmitPdbFile);

            parsedArgs = DefaultParse(new[] { "/pdb:something", "/debug:embedded", "a.cs" }, WorkingDirectory);
            Assert.Equal(Path.Combine(WorkingDirectory, "something.pdb"), parsedArgs.PdbPath);
            Assert.Equal(Path.Combine(WorkingDirectory, "something.pdb"), parsedArgs.GetPdbFilePath("a.dll"));
            Assert.False(parsedArgs.EmitPdbFile);

            parsedArgs = DefaultParse(new[] { "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Null(parsedArgs.PdbPath);
            Assert.True(parsedArgs.EmitPdbFile);
            Assert.Equal(Path.Combine(WorkingDirectory, "a.pdb"), parsedArgs.GetPdbFilePath("a.dll"));

            parsedArgs = DefaultParse(new[] { "/pdb", "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/pdb"));
            Assert.Equal(Path.Combine(WorkingDirectory, "a.pdb"), parsedArgs.GetPdbFilePath("a.dll"));

            parsedArgs = DefaultParse(new[] { "/pdb:", "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/pdb:"));

            parsedArgs = DefaultParse(new[] { "/pdb:something", "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            // temp: path changed
            //parsedArgs = DefaultParse(new[] { "/debug", "/pdb:.x", "a.cs" }, baseDirectory);
            //parsedArgs.Errors.Verify(
            //    // error CS2021: File name '.x' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
            //    Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(".x"));

            parsedArgs = DefaultParse(new[] { @"/pdb:""""", "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2005: Missing file specification for '/pdb:""' option
                Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments(@"/pdb:""""").WithLocation(1, 1));

            parsedArgs = DefaultParse(new[] { "/pdb:C:\\", "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments("C:\\"));

            // Should preserve fully qualified paths
            parsedArgs = DefaultParse(new[] { @"/pdb:C:\MyFolder\MyPdb.pdb", "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(@"C:\MyFolder\MyPdb.pdb", parsedArgs.PdbPath);

            // Should preserve fully qualified paths
            parsedArgs = DefaultParse(new[] { @"/pdb:c:\MyPdb.pdb", "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(@"c:\MyPdb.pdb", parsedArgs.PdbPath);

            parsedArgs = DefaultParse(new[] { @"/pdb:\MyFolder\MyPdb.pdb", "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(Path.Combine(Path.GetPathRoot(WorkingDirectory), @"MyFolder\MyPdb.pdb"), parsedArgs.PdbPath);

            // Should handle quotes
            parsedArgs = DefaultParse(new[] { @"/pdb:""C:\My Folder\MyPdb.pdb""", "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(@"C:\My Folder\MyPdb.pdb", parsedArgs.PdbPath);

            // Should expand partially qualified paths
            parsedArgs = DefaultParse(new[] { @"/pdb:MyPdb.pdb", "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(FileUtilities.ResolveRelativePath("MyPdb.pdb", WorkingDirectory), parsedArgs.PdbPath);

            // Should expand partially qualified paths
            parsedArgs = DefaultParse(new[] { @"/pdb:..\MyPdb.pdb", "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            // Temp: Path info changed
            // Assert.Equal(FileUtilities.ResolveRelativePath("MyPdb.pdb", "..\\", baseDirectory), parsedArgs.PdbPath);

            parsedArgs = DefaultParse(new[] { @"/pdb:\\b", "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name '.x' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(@"\\b"));
            Assert.Null(parsedArgs.PdbPath);

            parsedArgs = DefaultParse(new[] { @"/pdb:\\b\OkFileName.pdb", "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name '.x' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(@"\\b\OkFileName.pdb"));
            Assert.Null(parsedArgs.PdbPath);

            parsedArgs = DefaultParse(new[] { @"/pdb:\\server\share\MyPdb.pdb", "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(@"\\server\share\MyPdb.pdb", parsedArgs.PdbPath);

            // invalid name:
            parsedArgs = DefaultParse(new[] { "/pdb:a.b\0b", "/debug", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments("a.b\0b"));
            Assert.Null(parsedArgs.PdbPath);


            parsedArgs = DefaultParse(new[] { "/pdb:a\uD800b.pdb", "/debug", "a.cs" }, WorkingDirectory);
            //parsedArgs.Errors.Verify(
            //    Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments("a\uD800b.pdb"));
            Assert.Null(parsedArgs.PdbPath);

            // Dev11 reports CS0016: Could not write to output file 'd:\Temp\q\a<>.z'
            parsedArgs = DefaultParse(new[] { @"/pdb:""a<>.pdb""", "a.vb" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name 'a<>.pdb' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments("a<>.pdb"));
            Assert.Null(parsedArgs.PdbPath);

            parsedArgs = DefaultParse(new[] { "/pdb:.x", "/debug", "a.cs" }, WorkingDirectory);
            //parsedArgs.Errors.Verify(
            //    // error CS2021: File name '.x' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
            //    Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(".x"));
            Assert.Null(parsedArgs.PdbPath);
        }

        [Fact]
        public void SourceLink()
        {
            var parsedArgs = DefaultParse(new[] { "/sourcelink:sl.json", "/debug:portable", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(Path.Combine(WorkingDirectory, "sl.json"), parsedArgs.SourceLink);

            parsedArgs = DefaultParse(new[] { "/sourcelink:sl.json", "/debug:embedded", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(Path.Combine(WorkingDirectory, "sl.json"), parsedArgs.SourceLink);

            parsedArgs = DefaultParse(new[] { @"/sourcelink:""s l.json""", "/debug:embedded", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(Path.Combine(WorkingDirectory, "s l.json"), parsedArgs.SourceLink);

            parsedArgs = DefaultParse(new[] { "/sourcelink:sl.json", "/debug:full", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new[] { "/sourcelink:sl.json", "/debug:pdbonly", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new[] { "/sourcelink:sl.json", "/debug-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SourceLinkRequiresPdb));

            parsedArgs = DefaultParse(new[] { "/sourcelink:sl.json", "/debug+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new[] { "/sourcelink:sl.json", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SourceLinkRequiresPdb));
        }

        [Fact]
        public void SourceLink_EndToEnd_EmbeddedPortable()
        {
            var dir = Temp.CreateDirectory();

            var src = dir.CreateFile("a.cs");
            src.WriteAllText(@"class C { public static void Main() {} }");

            var sl = dir.CreateFile("sl.json");
            sl.WriteAllText(@"{ ""documents"" : {} }");

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/debug:embedded", "/sourcelink:sl.json", "a.cs" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);

            var peStream = File.OpenRead(Path.Combine(dir.Path, "a.exe"));

            using (var peReader = new PEReader(peStream))
            {
                var entry = peReader.ReadDebugDirectory().Single(e => e.Type == DebugDirectoryEntryType.EmbeddedPortablePdb);
                using (var mdProvider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(entry))
                {
                    var blob = mdProvider.GetMetadataReader().GetSourceLinkBlob();
                    AssertEx.Equal(File.ReadAllBytes(sl.Path), blob);
                }
            }

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
        }

        [Fact]
        public void SourceLink_EndToEnd_Portable()
        {
            var dir = Temp.CreateDirectory();

            var src = dir.CreateFile("a.cs");
            src.WriteAllText(@"class C { public static void Main() {} }");

            var sl = dir.CreateFile("sl.json");
            sl.WriteAllText(@"{ ""documents"" : {} }");

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/debug:portable", "/sourcelink:sl.json", "a.cs" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);

            var pdbStream = File.OpenRead(Path.Combine(dir.Path, "a.pdb"));

            using (var mdProvider = MetadataReaderProvider.FromPortablePdbStream(pdbStream))
            {
                var blob = mdProvider.GetMetadataReader().GetSourceLinkBlob();
                AssertEx.Equal(File.ReadAllBytes(sl.Path), blob);
            }

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
        }

        [Fact]
        public void SourceLink_EndToEnd_Windows()
        {
            var dir = Temp.CreateDirectory();

            var src = dir.CreateFile("a.cs");
            src.WriteAllText(@"class C { public static void Main() {} }");

            var sl = dir.CreateFile("sl.json");
            byte[] slContent = Encoding.UTF8.GetBytes(@"{ ""documents"" : {} }");
            sl.WriteAllBytes(slContent);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/debug:full", "/sourcelink:sl.json", "a.cs" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);

            var pdbStream = File.OpenRead(Path.Combine(dir.Path, "a.pdb"));
            var actualData = PdbValidation.GetSourceLinkData(pdbStream);
            AssertEx.Equal(slContent, actualData);

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
        }

        [Fact]
        public void Embed()
        {
            var parsedArgs = DefaultParse(new[] { "a.cs " }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Empty(parsedArgs.EmbeddedFiles);

            parsedArgs = DefaultParse(new[] { "/embed", "/debug:portable", "a.cs", "b.cs", "c.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            AssertEx.Equal(parsedArgs.SourceFiles, parsedArgs.EmbeddedFiles);
            AssertEx.Equal(
                new[] { "a.cs", "b.cs", "c.cs" }.Select(f => Path.Combine(WorkingDirectory, f)),
                parsedArgs.EmbeddedFiles.Select(f => f.Path));

            parsedArgs = DefaultParse(new[] { "/embed:a.cs", "/embed:b.cs", "/debug:embedded", "a.cs", "b.cs", "c.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            AssertEx.Equal(
                new[] { "a.cs", "b.cs" }.Select(f => Path.Combine(WorkingDirectory, f)),
                parsedArgs.EmbeddedFiles.Select(f => f.Path));

            parsedArgs = DefaultParse(new[] { "/embed:a.cs;b.cs", "/debug:portable", "a.cs", "b.cs", "c.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            AssertEx.Equal(
                new[] { "a.cs", "b.cs" }.Select(f => Path.Combine(WorkingDirectory, f)),
                parsedArgs.EmbeddedFiles.Select(f => f.Path));

            parsedArgs = DefaultParse(new[] { "/embed:a.cs,b.cs", "/debug:portable", "a.cs", "b.cs", "c.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            AssertEx.Equal(
                new[] { "a.cs", "b.cs" }.Select(f => Path.Combine(WorkingDirectory, f)),
                parsedArgs.EmbeddedFiles.Select(f => f.Path));

            parsedArgs = DefaultParse(new[] { @"/embed:""a,b.cs""", "/debug:portable", "a,b.cs", "c.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            AssertEx.Equal(
                new[] { "a,b.cs" }.Select(f => Path.Combine(WorkingDirectory, f)),
                parsedArgs.EmbeddedFiles.Select(f => f.Path));

            parsedArgs = DefaultParse(new[] { "/embed:a.txt", "/embed", "/debug:portable", "a.cs", "b.cs", "c.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(); ;
            AssertEx.Equal(
                new[] { "a.txt", "a.cs", "b.cs", "c.cs" }.Select(f => Path.Combine(WorkingDirectory, f)),
                parsedArgs.EmbeddedFiles.Select(f => f.Path));

            parsedArgs = DefaultParse(new[] { "/embed", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_CannotEmbedWithoutPdb));

            parsedArgs = DefaultParse(new[] { "/embed:a.txt", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_CannotEmbedWithoutPdb));

            parsedArgs = DefaultParse(new[] { "/embed", "/debug-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_CannotEmbedWithoutPdb));

            parsedArgs = DefaultParse(new[] { "/embed:a.txt", "/debug-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_CannotEmbedWithoutPdb));

            parsedArgs = DefaultParse(new[] { "/embed", "/debug:full", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new[] { "/embed", "/debug:pdbonly", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new[] { "/embed", "/debug+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
        }

        [Theory]
        [InlineData("/debug:portable", "/embed", new[] { "embed.cs", "embed2.cs", "embed.xyz" })]
        [InlineData("/debug:portable", "/embed:embed.cs", new[] { "embed.cs", "embed.xyz" })]
        [InlineData("/debug:portable", "/embed:embed2.cs", new[] { "embed2.cs" })]
        [InlineData("/debug:portable", "/embed:embed.xyz", new[] { "embed.xyz" })]
        [InlineData("/debug:embedded", "/embed", new[] { "embed.cs", "embed2.cs", "embed.xyz" })]
        [InlineData("/debug:embedded", "/embed:embed.cs", new[] { "embed.cs", "embed.xyz" })]
        [InlineData("/debug:embedded", "/embed:embed2.cs", new[] { "embed2.cs" })]
        [InlineData("/debug:embedded", "/embed:embed.xyz", new[] { "embed.xyz" })]
        public void Embed_EndToEnd_Portable(string debugSwitch, string embedSwitch, string[] expectedEmbedded)
        {
            // embed.cs: large enough to compress, has #line directives
            const string embed_cs =
@"///////////////////////////////////////////////////////////////////////////////
class Program {
    static void Main() {
#line 1 ""embed.xyz""
        System.Console.WriteLine(""Hello, World"");

#line 3
        System.Console.WriteLine(""Goodbye, World"");
    }
}
///////////////////////////////////////////////////////////////////////////////";

            // embed2.cs: small enough to not compress, no sequence points
            const string embed2_cs =
@"class C
{
}";
            // target of #line
            const string embed_xyz =
@"print Hello, World

print Goodbye, World";

            Assert.True(embed_cs.Length >= EmbeddedText.CompressionThreshold);
            Assert.True(embed2_cs.Length < EmbeddedText.CompressionThreshold);

            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("embed.cs");
            var src2 = dir.CreateFile("embed2.cs");
            var txt = dir.CreateFile("embed.xyz");

            src.WriteAllText(embed_cs);
            src2.WriteAllText(embed2_cs);
            txt.WriteAllText(embed_xyz);

            var expectedEmbeddedMap = new Dictionary<string, string>();
            if (expectedEmbedded.Contains("embed.cs"))
            {
                expectedEmbeddedMap.Add(src.Path, embed_cs);
            }

            if (expectedEmbedded.Contains("embed2.cs"))
            {
                expectedEmbeddedMap.Add(src2.Path, embed2_cs);
            }

            if (expectedEmbedded.Contains("embed.xyz"))
            {
                expectedEmbeddedMap.Add(txt.Path, embed_xyz);
            }

            var output = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", debugSwitch, embedSwitch, "embed.cs", "embed2.cs" });
            int exitCode = csc.Run(output);
            Assert.Equal("", output.ToString().Trim());
            Assert.Equal(0, exitCode);

            switch (debugSwitch)
            {
                case "/debug:embedded":
                    ValidateEmbeddedSources_Portable(expectedEmbeddedMap, dir, isEmbeddedPdb: true);
                    break;
                case "/debug:portable":
                    ValidateEmbeddedSources_Portable(expectedEmbeddedMap, dir, isEmbeddedPdb: false);
                    break;
                case "/debug:full":
                    ValidateEmbeddedSources_Windows(expectedEmbeddedMap, dir);
                    break;
            }

            Assert.Empty(expectedEmbeddedMap);
            CleanupAllGeneratedFiles(src.Path);
        }

        private static void ValidateEmbeddedSources_Portable(Dictionary<string, string> expectedEmbeddedMap, TempDirectory dir, bool isEmbeddedPdb)
        {
            using (var peReader = new PEReader(File.OpenRead(Path.Combine(dir.Path, "embed.exe"))))
            {
                var entry = peReader.ReadDebugDirectory().SingleOrDefault(e => e.Type == DebugDirectoryEntryType.EmbeddedPortablePdb);
                Assert.Equal(isEmbeddedPdb, entry.DataSize > 0);

                using (var mdProvider = isEmbeddedPdb ?
                    peReader.ReadEmbeddedPortablePdbDebugDirectoryData(entry) :
                    MetadataReaderProvider.FromPortablePdbStream(File.OpenRead(Path.Combine(dir.Path, "embed.pdb"))))
                {
                    var mdReader = mdProvider.GetMetadataReader();

                    foreach (var handle in mdReader.Documents)
                    {
                        var doc = mdReader.GetDocument(handle);
                        var docPath = mdReader.GetString(doc.Name);

                        SourceText embeddedSource = mdReader.GetEmbeddedSource(handle);
                        if (embeddedSource == null)
                        {
                            continue;
                        }

                        Assert.Equal(expectedEmbeddedMap[docPath], embeddedSource.ToString());
                        Assert.True(expectedEmbeddedMap.Remove(docPath));
                    }
                }
            }
        }

        private static void ValidateEmbeddedSources_Windows(Dictionary<string, string> expectedEmbeddedMap, TempDirectory dir)
        {
            ISymUnmanagedReader5 symReader = null;

            try
            {
                symReader = SymReaderFactory.CreateReader(File.OpenRead(Path.Combine(dir.Path, "embed.pdb")));

                foreach (var doc in symReader.GetDocuments())
                {
                    var docPath = doc.GetName();

                    var sourceBlob = doc.GetEmbeddedSource();
                    if (sourceBlob.Array == null)
                    {
                        continue;
                    }

                    var sourceStr = Encoding.UTF8.GetString(sourceBlob.Array, sourceBlob.Offset, sourceBlob.Count);

                    Assert.Equal(expectedEmbeddedMap[docPath], sourceStr);
                    Assert.True(expectedEmbeddedMap.Remove(docPath));
                }
            }
            catch
            {
                symReader?.Dispose();
            }
        }

        private static void ValidateWrittenSources(Dictionary<string, Dictionary<string, string>> expectedFilesMap, Encoding encoding = null)
        {
            foreach ((var dirPath, var fileMap) in expectedFilesMap.ToArray())
            {
                foreach (var file in Directory.GetFiles(dirPath))
                {
                    var name = Path.GetFileName(file);
                    var content = File.ReadAllText(file, encoding ?? Encoding.UTF8);

                    Assert.Equal(fileMap[name], content);
                    Assert.True(fileMap.Remove(name));
                }
                Assert.Empty(fileMap);
                Assert.True(expectedFilesMap.Remove(dirPath));
            }
            Assert.Empty(expectedFilesMap);
        }

        [Fact]
        public void Optimize()
        {
            var parsedArgs = DefaultParse(new[] { "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(new CSharpCompilationOptions(OutputKind.ConsoleApplication).OptimizationLevel, parsedArgs.CompilationOptions.OptimizationLevel);

            parsedArgs = DefaultParse(new[] { "/optimize-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(OptimizationLevel.Debug, parsedArgs.CompilationOptions.OptimizationLevel);

            parsedArgs = DefaultParse(new[] { "/optimize", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(OptimizationLevel.Release, parsedArgs.CompilationOptions.OptimizationLevel);

            parsedArgs = DefaultParse(new[] { "/optimize+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(OptimizationLevel.Release, parsedArgs.CompilationOptions.OptimizationLevel);

            parsedArgs = DefaultParse(new[] { "/optimize+", "/optimize-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(OptimizationLevel.Debug, parsedArgs.CompilationOptions.OptimizationLevel);

            parsedArgs = DefaultParse(new[] { "/optimize:+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/optimize:+"));

            parsedArgs = DefaultParse(new[] { "/optimize:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/optimize:"));

            parsedArgs = DefaultParse(new[] { "/optimize-:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/optimize-:"));

            parsedArgs = DefaultParse(new[] { "/o-", "a.cs" }, WorkingDirectory);
            Assert.Equal(OptimizationLevel.Debug, parsedArgs.CompilationOptions.OptimizationLevel);

            parsedArgs = DefaultParse(new string[] { "/o", "a.cs" }, WorkingDirectory);
            Assert.Equal(OptimizationLevel.Release, parsedArgs.CompilationOptions.OptimizationLevel);

            parsedArgs = DefaultParse(new string[] { "/o+", "a.cs" }, WorkingDirectory);
            Assert.Equal(OptimizationLevel.Release, parsedArgs.CompilationOptions.OptimizationLevel);

            parsedArgs = DefaultParse(new string[] { "/o+", "/optimize-", "a.cs" }, WorkingDirectory);
            Assert.Equal(OptimizationLevel.Debug, parsedArgs.CompilationOptions.OptimizationLevel);

            parsedArgs = DefaultParse(new string[] { "/o:+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/o:+"));

            parsedArgs = DefaultParse(new string[] { "/o:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/o:"));

            parsedArgs = DefaultParse(new string[] { "/o-:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/o-:"));
        }

        [Fact]
        public void Deterministic()
        {
            var parsedArgs = DefaultParse(new[] { "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.Deterministic);

            parsedArgs = DefaultParse(new[] { "/deterministic+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.CompilationOptions.Deterministic);

            parsedArgs = DefaultParse(new[] { "/deterministic", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.CompilationOptions.Deterministic);

            parsedArgs = DefaultParse(new[] { "/deterministic-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.Deterministic);
        }

        [Fact]
        public void ParseReferences()
        {
            var parsedArgs = DefaultParse(new string[] { "/r:goo.dll", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(2, parsedArgs.MetadataReferences.Length);

            parsedArgs = DefaultParse(new string[] { "/r:goo.dll;", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(2, parsedArgs.MetadataReferences.Length);

            Assert.Equal(MscorlibFullPath, parsedArgs.MetadataReferences[0].Reference);
            Assert.Equal(MetadataReferenceProperties.Assembly, parsedArgs.MetadataReferences[0].Properties);

            Assert.Equal("goo.dll", parsedArgs.MetadataReferences[1].Reference);
            Assert.Equal(MetadataReferenceProperties.Assembly, parsedArgs.MetadataReferences[1].Properties);


            parsedArgs = DefaultParse(new string[] { @"/l:goo.dll", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(2, parsedArgs.MetadataReferences.Length);

            Assert.Equal(MscorlibFullPath, parsedArgs.MetadataReferences[0].Reference);
            Assert.Equal(MetadataReferenceProperties.Assembly, parsedArgs.MetadataReferences[0].Properties);

            Assert.Equal("goo.dll", parsedArgs.MetadataReferences[1].Reference);
            Assert.Equal(MetadataReferenceProperties.Assembly.WithEmbedInteropTypes(true), parsedArgs.MetadataReferences[1].Properties);


            parsedArgs = DefaultParse(new string[] { @"/addmodule:goo.dll", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(2, parsedArgs.MetadataReferences.Length);

            Assert.Equal(MscorlibFullPath, parsedArgs.MetadataReferences[0].Reference);
            Assert.Equal(MetadataReferenceProperties.Assembly, parsedArgs.MetadataReferences[0].Properties);

            Assert.Equal("goo.dll", parsedArgs.MetadataReferences[1].Reference);
            Assert.Equal(MetadataReferenceProperties.Module, parsedArgs.MetadataReferences[1].Properties);


            parsedArgs = DefaultParse(new string[] { @"/r:a=goo.dll", "/l:b=bar.dll", "/addmodule:c=mod.dll", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(4, parsedArgs.MetadataReferences.Length);

            Assert.Equal(MscorlibFullPath, parsedArgs.MetadataReferences[0].Reference);
            Assert.Equal(MetadataReferenceProperties.Assembly, parsedArgs.MetadataReferences[0].Properties);

            Assert.Equal("goo.dll", parsedArgs.MetadataReferences[1].Reference);
            Assert.Equal(MetadataReferenceProperties.Assembly.WithAliases(new[] { "a" }), parsedArgs.MetadataReferences[1].Properties);

            Assert.Equal("bar.dll", parsedArgs.MetadataReferences[2].Reference);
            Assert.Equal(MetadataReferenceProperties.Assembly.WithAliases(new[] { "b" }).WithEmbedInteropTypes(true), parsedArgs.MetadataReferences[2].Properties);

            Assert.Equal("c=mod.dll", parsedArgs.MetadataReferences[3].Reference);
            Assert.Equal(MetadataReferenceProperties.Module, parsedArgs.MetadataReferences[3].Properties);

            // TODO: multiple files, quotes, etc.
        }

        [Fact]
        public void ParseAnalyzers()
        {
            var parsedArgs = DefaultParse(new string[] { @"/a:goo.dll", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(1, parsedArgs.AnalyzerReferences.Length);
            Assert.Equal("goo.dll", parsedArgs.AnalyzerReferences[0].FilePath);

            parsedArgs = DefaultParse(new string[] { @"/analyzer:goo.dll", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(1, parsedArgs.AnalyzerReferences.Length);
            Assert.Equal("goo.dll", parsedArgs.AnalyzerReferences[0].FilePath);

            parsedArgs = DefaultParse(new string[] { "/analyzer:\"goo.dll\"", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(1, parsedArgs.AnalyzerReferences.Length);
            Assert.Equal("goo.dll", parsedArgs.AnalyzerReferences[0].FilePath);

            parsedArgs = DefaultParse(new string[] { @"/a:goo.dll;bar.dll", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(2, parsedArgs.AnalyzerReferences.Length);
            Assert.Equal("goo.dll", parsedArgs.AnalyzerReferences[0].FilePath);
            Assert.Equal("bar.dll", parsedArgs.AnalyzerReferences[1].FilePath);

            parsedArgs = DefaultParse(new string[] { @"/a:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/a:"));

            parsedArgs = DefaultParse(new string[] { "/a", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "/a"));
        }

        [Fact]
        public void Analyzers_Missing()
        {
            string source = @"
class C
{
}
";
            var dir = Temp.CreateDirectory();

            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/preferreduilang:en", "/a:missing.dll", "a.cs" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Equal("error CS0006: Metadata file 'missing.dll' could not be found", outWriter.ToString().Trim());

            // Clean up temp files
            CleanupAllGeneratedFiles(file.Path);
        }

        [Fact]
        public void Analyzers_Empty()
        {
            string source = @"
class C
{
}
";
            var dir = Temp.CreateDirectory();

            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/preferreduilang:en", "/t:library", "/a:" + typeof(object).Assembly.Location, "a.cs" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.DoesNotContain("warning", outWriter.ToString());

            CleanupAllGeneratedFiles(file.Path);
        }

        private TempFile CreateRuleSetFile(string source)
        {
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile("a.ruleset");
            file.WriteAllText(source);
            return file;
        }

        [Fact]
        public void RuleSetSwitchPositive()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
    <Rule Id=""CA1013"" Action=""Warning"" />
    <Rule Id=""CA1014"" Action=""None"" />
  </Rules>
</RuleSet>
";
            var file = CreateRuleSetFile(source);
            var parsedArgs = DefaultParse(new string[] { @"/ruleset:" + file.Path, "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(expected: file.Path, actual: parsedArgs.RuleSetPath);
            Assert.True(parsedArgs.CompilationOptions.SpecificDiagnosticOptions.ContainsKey("CA1012"));
            Assert.True(parsedArgs.CompilationOptions.SpecificDiagnosticOptions["CA1012"] == ReportDiagnostic.Error);
            Assert.True(parsedArgs.CompilationOptions.SpecificDiagnosticOptions.ContainsKey("CA1013"));
            Assert.True(parsedArgs.CompilationOptions.SpecificDiagnosticOptions["CA1013"] == ReportDiagnostic.Warn);
            Assert.True(parsedArgs.CompilationOptions.SpecificDiagnosticOptions.ContainsKey("CA1014"));
            Assert.True(parsedArgs.CompilationOptions.SpecificDiagnosticOptions["CA1014"] == ReportDiagnostic.Suppress);
            Assert.True(parsedArgs.CompilationOptions.GeneralDiagnosticOption == ReportDiagnostic.Warn);
        }

        [Fact]
        public void RuleSetSwitchQuoted()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
    <Rule Id=""CA1013"" Action=""Warning"" />
    <Rule Id=""CA1014"" Action=""None"" />
  </Rules>
</RuleSet>
";
            var file = CreateRuleSetFile(source);
            var parsedArgs = DefaultParse(new string[] { @"/ruleset:" + "\"" + file.Path + "\"", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(expected: file.Path, actual: parsedArgs.RuleSetPath);
        }

        [Fact]
        public void RuleSetSwitchParseErrors()
        {
            var parsedArgs = DefaultParse(new string[] { @"/ruleset", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                 Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "ruleset"));
            Assert.Null(parsedArgs.RuleSetPath);

            parsedArgs = DefaultParse(new string[] { @"/ruleset:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "ruleset"));
            Assert.Null(parsedArgs.RuleSetPath);

            parsedArgs = DefaultParse(new string[] { @"/ruleset:blah", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.ERR_CantReadRulesetFile).WithArguments(Path.Combine(TempRoot.Root, "blah"), "File not found."));
            Assert.Equal(expected: Path.Combine(TempRoot.Root, "blah"), actual: parsedArgs.RuleSetPath);

            parsedArgs = DefaultParse(new string[] { @"/ruleset:blah;blah.ruleset", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.ERR_CantReadRulesetFile).WithArguments(Path.Combine(TempRoot.Root, "blah;blah.ruleset"), "File not found."));
            Assert.Equal(expected: Path.Combine(TempRoot.Root, "blah;blah.ruleset"), actual: parsedArgs.RuleSetPath);

            var file = CreateRuleSetFile("Random text");
            parsedArgs = DefaultParse(new string[] { @"/ruleset:" + file.Path, "a.cs" }, WorkingDirectory);
            //parsedArgs.Errors.Verify(
            //    Diagnostic(ErrorCode.ERR_CantReadRulesetFile).WithArguments(file.Path, "Data at the root level is invalid. Line 1, position 1."));
            Assert.Equal(expected: file.Path, actual: parsedArgs.RuleSetPath);
            var err = parsedArgs.Errors.Single();

            Assert.Equal((int)ErrorCode.ERR_CantReadRulesetFile, err.Code);
            Assert.Equal(2, err.Arguments.Count);
            Assert.Equal(file.Path, (string)err.Arguments[0]);
            var currentUICultureName = Thread.CurrentThread.CurrentUICulture.Name;
            if (currentUICultureName.Length == 0 || currentUICultureName.StartsWith("en", StringComparison.OrdinalIgnoreCase))
            {
                Assert.Equal("Data at the root level is invalid. Line 1, position 1.", (string)err.Arguments[1]);
            }
        }

        [WorkItem(892467, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/892467")]
        [Fact]
        public void Analyzers_Found()
        {
            string source = @"
class C
{
}
";
            var dir = Temp.CreateDirectory();

            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            // This assembly has a MockAbstractDiagnosticAnalyzer type which should get run by this compilation.
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/preferreduilang:en", "/t:library", "/a:" + Assembly.GetExecutingAssembly().Location, "a.cs" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            // Diagnostic thrown
            Assert.True(outWriter.ToString().Contains("a.cs(2,7): warning Warning01: Throwing a diagnostic for types declared"));
            // Diagnostic cannot be instantiated
            Assert.True(outWriter.ToString().Contains("warning CS8032"));

            CleanupAllGeneratedFiles(file.Path);
        }

        [Fact]
        public void Analyzers_WithRuleSet()
        {
            string source = @"
class C
{
    int x;
}
";
            var dir = Temp.CreateDirectory();

            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            string rulesetSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""Warning01"" Action=""Error"" />
  </Rules>
</RuleSet>
";
            var ruleSetFile = CreateRuleSetFile(rulesetSource);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            // This assembly has a MockAbstractDiagnosticAnalyzer type which should get run by this compilation.
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/preferreduilang:en", "/t:library", "/a:" + Assembly.GetExecutingAssembly().Location, "a.cs", "/ruleset:" + ruleSetFile.Path });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(1, exitCode);
            // Diagnostic thrown as error.
            Assert.True(outWriter.ToString().Contains("a.cs(2,7): error Warning01: Throwing a diagnostic for types declared"));

            // Clean up temp files
            CleanupAllGeneratedFiles(file.Path);
        }

        [WorkItem(912906, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/912906")]
        [Fact]
        public void Analyzers_CommandLineOverridesRuleset1()
        {
            string source = @"
class C
{
}
";
            var dir = Temp.CreateDirectory();

            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            string rulesetSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"">
  <IncludeAll Action=""Warning"" />
</RuleSet>
";
            var ruleSetFile = CreateRuleSetFile(rulesetSource);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            // This assembly has a MockAbstractDiagnosticAnalyzer type which should get run by this compilation.
            var csc = CreateCSharpCompiler(null, dir.Path,
                new[] {
                    "/nologo", "/preferreduilang:en", "/t:library",
                    "/a:" + Assembly.GetExecutingAssembly().Location, "a.cs",
                    "/ruleset:" + ruleSetFile.Path, "/warnaserror+", "/nowarn:8032" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(1, exitCode);
            // Diagnostic thrown as error: command line always overrides ruleset.
            Assert.Contains("a.cs(2,7): error Warning01: Throwing a diagnostic for types declared", outWriter.ToString(), StringComparison.Ordinal);

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            csc = CreateCSharpCompiler(null, dir.Path,
                new[] {
                    "/nologo", "/preferreduilang:en", "/t:library",
                    "/a:" + Assembly.GetExecutingAssembly().Location, "a.cs",
                    "/warnaserror+", "/ruleset:" + ruleSetFile.Path, "/nowarn:8032" });
            exitCode = csc.Run(outWriter);
            Assert.Equal(1, exitCode);
            // Diagnostic thrown as error: command line always overrides ruleset.
            Assert.Contains("a.cs(2,7): error Warning01: Throwing a diagnostic for types declared", outWriter.ToString(), StringComparison.Ordinal);

            // Clean up temp files
            CleanupAllGeneratedFiles(file.Path);
        }

        [Fact]
        [WorkItem(468, "https://github.com/dotnet/roslyn/issues/468")]
        public void RuleSet_GeneralCommandLineOptionOverridesGeneralRuleSetOption()
        {
            var dir = Temp.CreateDirectory();

            string ruleSetSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"">
  <IncludeAll Action=""Warning"" />
</RuleSet>
";
            var ruleSetFile = dir.CreateFile("Rules.ruleset").WriteAllText(ruleSetSource);

            var arguments = DefaultParse(
                new[]
                {
                    "/nologo",
                    "/t:library",
                    "/ruleset:Rules.ruleset",
                    "/warnaserror+",
                    "a.cs"
                },
                dir.Path);

            var errors = arguments.Errors;
            Assert.Empty(errors);

            Assert.Equal(actual: arguments.CompilationOptions.GeneralDiagnosticOption, expected: ReportDiagnostic.Error);
        }

        [Fact]
        [WorkItem(468, "https://github.com/dotnet/roslyn/issues/468")]
        public void RuleSet_GeneralWarnAsErrorPromotesWarningFromRuleSet()
        {
            var dir = Temp.CreateDirectory();

            string ruleSetSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""Test001"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSetFile = dir.CreateFile("Rules.ruleset").WriteAllText(ruleSetSource);

            var arguments = DefaultParse(
                new[]
                {
                    "/nologo",
                    "/t:library",
                    "/ruleset:Rules.ruleset",
                    "/warnaserror+",
                    "a.cs"
                },
                dir.Path);

            var errors = arguments.Errors;
            Assert.Empty(errors);

            Assert.Equal(actual: arguments.CompilationOptions.GeneralDiagnosticOption, expected: ReportDiagnostic.Error);
            Assert.Equal(actual: arguments.CompilationOptions.SpecificDiagnosticOptions["Test001"], expected: ReportDiagnostic.Error);
        }

        [Fact]
        [WorkItem(468, "https://github.com/dotnet/roslyn/issues/468")]
        public void RuleSet_GeneralWarnAsErrorDoesNotPromoteInfoFromRuleSet()
        {
            var dir = Temp.CreateDirectory();

            string ruleSetSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""Test001"" Action=""Info"" />
  </Rules>
</RuleSet>
";
            var ruleSetFile = dir.CreateFile("Rules.ruleset").WriteAllText(ruleSetSource);

            var arguments = DefaultParse(
                new[]
                {
                    "/nologo",
                    "/t:library",
                    "/ruleset:Rules.ruleset",
                    "/warnaserror+",
                    "a.cs"
                },
                dir.Path);

            var errors = arguments.Errors;
            Assert.Empty(errors);

            Assert.Equal(actual: arguments.CompilationOptions.GeneralDiagnosticOption, expected: ReportDiagnostic.Error);
            Assert.Equal(actual: arguments.CompilationOptions.SpecificDiagnosticOptions["Test001"], expected: ReportDiagnostic.Info);
        }

        [Fact]
        [WorkItem(468, "https://github.com/dotnet/roslyn/issues/468")]
        public void RuleSet_SpecificWarnAsErrorPromotesInfoFromRuleSet()
        {
            var dir = Temp.CreateDirectory();

            string ruleSetSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""Test001"" Action=""Info"" />
  </Rules>
</RuleSet>
";
            var ruleSetFile = dir.CreateFile("Rules.ruleset").WriteAllText(ruleSetSource);

            var arguments = DefaultParse(
                new[]
                {
                    "/nologo",
                    "/t:library",
                    "/ruleset:Rules.ruleset",
                    "/warnaserror+:Test001",
                    "a.cs"
                },
                dir.Path);

            var errors = arguments.Errors;
            Assert.Empty(errors);

            Assert.Equal(actual: arguments.CompilationOptions.GeneralDiagnosticOption, expected: ReportDiagnostic.Default);
            Assert.Equal(actual: arguments.CompilationOptions.SpecificDiagnosticOptions["Test001"], expected: ReportDiagnostic.Error);
        }

        [Fact]
        [WorkItem(468, "https://github.com/dotnet/roslyn/issues/468")]
        public void RuleSet_GeneralWarnAsErrorMinusResetsRules()
        {
            var dir = Temp.CreateDirectory();

            string ruleSetSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""Test001"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSetFile = dir.CreateFile("Rules.ruleset").WriteAllText(ruleSetSource);

            var arguments = DefaultParse(
                new[]
                {
                    "/nologo",
                    "/t:library",
                    "/ruleset:Rules.ruleset",
                    "/warnaserror+",
                    "/warnaserror-",
                    "a.cs"
                },
                dir.Path);

            var errors = arguments.Errors;
            Assert.Empty(errors);

            Assert.Equal(actual: arguments.CompilationOptions.GeneralDiagnosticOption, expected: ReportDiagnostic.Default);
            Assert.Equal(actual: arguments.CompilationOptions.SpecificDiagnosticOptions["Test001"], expected: ReportDiagnostic.Warn);
        }

        [Fact]
        [WorkItem(468, "https://github.com/dotnet/roslyn/issues/468")]
        public void RuleSet_SpecificWarnAsErrorMinusResetsRules()
        {
            var dir = Temp.CreateDirectory();

            string ruleSetSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""Test001"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSetFile = dir.CreateFile("Rules.ruleset").WriteAllText(ruleSetSource);

            var arguments = DefaultParse(
                new[]
                {
                    "/nologo",
                    "/t:library",
                    "/ruleset:Rules.ruleset",
                    "/warnaserror+",
                    "/warnaserror-:Test001",
                    "a.cs"
                },
                dir.Path);

            var errors = arguments.Errors;
            Assert.Empty(errors);

            Assert.Equal(actual: arguments.CompilationOptions.GeneralDiagnosticOption, expected: ReportDiagnostic.Error);
            Assert.Equal(actual: arguments.CompilationOptions.SpecificDiagnosticOptions["Test001"], expected: ReportDiagnostic.Warn);
        }

        [Fact]
        [WorkItem(468, "https://github.com/dotnet/roslyn/issues/468")]
        public void RuleSet_SpecificWarnAsErrorMinusDefaultsRuleNotInRuleSet()
        {
            var dir = Temp.CreateDirectory();

            string ruleSetSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""Test001"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSetFile = dir.CreateFile("Rules.ruleset").WriteAllText(ruleSetSource);

            var arguments = DefaultParse(
                new[]
                {
                    "/nologo",
                    "/t:library",
                    "/ruleset:Rules.ruleset",
                    "/warnaserror+:Test002",
                    "/warnaserror-:Test002",
                    "a.cs"
                },
                dir.Path);

            var errors = arguments.Errors;
            Assert.Empty(errors);

            Assert.Equal(actual: arguments.CompilationOptions.GeneralDiagnosticOption, expected: ReportDiagnostic.Default);
            Assert.Equal(actual: arguments.CompilationOptions.SpecificDiagnosticOptions["Test001"], expected: ReportDiagnostic.Warn);
            Assert.Equal(actual: arguments.CompilationOptions.SpecificDiagnosticOptions["Test002"], expected: ReportDiagnostic.Default);
        }

        [Fact]
        [WorkItem(468, "https://github.com/dotnet/roslyn/issues/468")]
        public void NoWarn_SpecificNoWarnOverridesRuleSet()
        {
            var dir = Temp.CreateDirectory();

            string ruleSetSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""Test001"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSetFile = dir.CreateFile("Rules.ruleset").WriteAllText(ruleSetSource);

            var arguments = DefaultParse(
                new[]
                {
                    "/nologo",
                    "/t:library",
                    "/ruleset:Rules.ruleset",
                    "/nowarn:Test001",
                    "a.cs"
                },
                dir.Path);

            var errors = arguments.Errors;
            Assert.Empty(errors);

            Assert.Equal(expected: ReportDiagnostic.Default, actual: arguments.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(expected: 1, actual: arguments.CompilationOptions.SpecificDiagnosticOptions.Count);
            Assert.Equal(expected: ReportDiagnostic.Suppress, actual: arguments.CompilationOptions.SpecificDiagnosticOptions["Test001"]);
        }

        [Fact]
        [WorkItem(468, "https://github.com/dotnet/roslyn/issues/468")]
        public void NoWarn_SpecificNoWarnOverridesGeneralWarnAsError()
        {
            var dir = Temp.CreateDirectory();

            string ruleSetSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""Test001"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSetFile = dir.CreateFile("Rules.ruleset").WriteAllText(ruleSetSource);

            var arguments = DefaultParse(
                new[]
                {
                    "/nologo",
                    "/t:library",
                    "/ruleset:Rules.ruleset",
                    "/warnaserror+",
                    "/nowarn:Test001",
                    "a.cs"
                },
                dir.Path);

            var errors = arguments.Errors;
            Assert.Empty(errors);

            Assert.Equal(expected: ReportDiagnostic.Error, actual: arguments.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(expected: 1, actual: arguments.CompilationOptions.SpecificDiagnosticOptions.Count);
            Assert.Equal(expected: ReportDiagnostic.Suppress, actual: arguments.CompilationOptions.SpecificDiagnosticOptions["Test001"]);
        }

        [Fact]
        [WorkItem(468, "https://github.com/dotnet/roslyn/issues/468")]
        public void NoWarn_SpecificNoWarnOverridesSpecificWarnAsError()
        {
            var dir = Temp.CreateDirectory();

            string ruleSetSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""Test001"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSetFile = dir.CreateFile("Rules.ruleset").WriteAllText(ruleSetSource);

            var arguments = DefaultParse(
                new[]
                {
                    "/nologo",
                    "/t:library",
                    "/ruleset:Rules.ruleset",
                    "/nowarn:Test001",
                    "/warnaserror+:Test001",
                    "a.cs"
                },
                dir.Path);

            var errors = arguments.Errors;
            Assert.Empty(errors);

            Assert.Equal(expected: ReportDiagnostic.Default, actual: arguments.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(expected: 1, actual: arguments.CompilationOptions.SpecificDiagnosticOptions.Count);
            Assert.Equal(expected: ReportDiagnostic.Suppress, actual: arguments.CompilationOptions.SpecificDiagnosticOptions["Test001"]);
        }

        [Fact]
        [WorkItem(35748, "https://github.com/dotnet/roslyn/issues/35748")]
        public void NoWarn_Nullable()
        {
            var dir = Temp.CreateDirectory();

            var arguments = DefaultParse(
                new[]
                {
                    "/nologo",
                    "/t:library",
                    "/nowarn:nullable",
                    "a.cs"
                },
                dir.Path);

            var errors = arguments.Errors;
            Assert.Empty(errors);

            Assert.Equal(expected: ReportDiagnostic.Default, actual: arguments.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(expected: ErrorFacts.NullableWarnings.Count + 2, actual: arguments.CompilationOptions.SpecificDiagnosticOptions.Count);

            foreach (string warning in ErrorFacts.NullableWarnings)
            {
                Assert.Equal(expected: ReportDiagnostic.Suppress, actual: arguments.CompilationOptions.SpecificDiagnosticOptions[warning]);
            }

            Assert.Equal(expected: ReportDiagnostic.Suppress,
                actual: arguments.CompilationOptions.SpecificDiagnosticOptions[MessageProvider.Instance.GetIdForErrorCode((int)ErrorCode.WRN_MissingNonNullTypesContextForAnnotation)]);
            Assert.Equal(expected: ReportDiagnostic.Suppress,
                actual: arguments.CompilationOptions.SpecificDiagnosticOptions[MessageProvider.Instance.GetIdForErrorCode((int)ErrorCode.WRN_MissingNonNullTypesContextForAnnotationInGeneratedCode)]);
        }

        [Fact]
        [WorkItem(35748, "https://github.com/dotnet/roslyn/issues/35748")]
        public void NoWarn_Nullable_Capitalization()
        {
            var dir = Temp.CreateDirectory();

            var arguments = DefaultParse(
                new[]
                {
                    "/nologo",
                    "/t:library",
                    "/nowarn:NullABLE",
                    "a.cs"
                },
                dir.Path);

            var errors = arguments.Errors;
            Assert.Empty(errors);

            Assert.Equal(expected: ReportDiagnostic.Default, actual: arguments.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(expected: ErrorFacts.NullableWarnings.Count + 2, actual: arguments.CompilationOptions.SpecificDiagnosticOptions.Count);

            foreach (string warning in ErrorFacts.NullableWarnings)
            {
                Assert.Equal(expected: ReportDiagnostic.Suppress, actual: arguments.CompilationOptions.SpecificDiagnosticOptions[warning]);
            }

            Assert.Equal(expected: ReportDiagnostic.Suppress,
                actual: arguments.CompilationOptions.SpecificDiagnosticOptions[MessageProvider.Instance.GetIdForErrorCode((int)ErrorCode.WRN_MissingNonNullTypesContextForAnnotation)]);
            Assert.Equal(expected: ReportDiagnostic.Suppress,
                actual: arguments.CompilationOptions.SpecificDiagnosticOptions[MessageProvider.Instance.GetIdForErrorCode((int)ErrorCode.WRN_MissingNonNullTypesContextForAnnotationInGeneratedCode)]);
        }

        [Fact]
        [WorkItem(35748, "https://github.com/dotnet/roslyn/issues/35748")]
        public void NoWarn_Nullable_MultipleArguments()
        {
            var dir = Temp.CreateDirectory();

            var arguments = DefaultParse(
                new[]
                {
                    "/nologo",
                    "/t:library",
                    "/nowarn:nullable,Test001",
                    "a.cs"
                },
                dir.Path);

            var errors = arguments.Errors;
            Assert.Empty(errors);

            Assert.Equal(expected: ReportDiagnostic.Default, actual: arguments.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(expected: ErrorFacts.NullableWarnings.Count + 3, actual: arguments.CompilationOptions.SpecificDiagnosticOptions.Count);

            foreach (string warning in ErrorFacts.NullableWarnings)
            {
                Assert.Equal(expected: ReportDiagnostic.Suppress, actual: arguments.CompilationOptions.SpecificDiagnosticOptions[warning]);
            }

            Assert.Equal(expected: ReportDiagnostic.Suppress, actual: arguments.CompilationOptions.SpecificDiagnosticOptions["Test001"]);
            Assert.Equal(expected: ReportDiagnostic.Suppress,
                actual: arguments.CompilationOptions.SpecificDiagnosticOptions[MessageProvider.Instance.GetIdForErrorCode((int)ErrorCode.WRN_MissingNonNullTypesContextForAnnotation)]);
            Assert.Equal(expected: ReportDiagnostic.Suppress,
                actual: arguments.CompilationOptions.SpecificDiagnosticOptions[MessageProvider.Instance.GetIdForErrorCode((int)ErrorCode.WRN_MissingNonNullTypesContextForAnnotationInGeneratedCode)]);
        }

        [Fact]
        [WorkItem(35748, "https://github.com/dotnet/roslyn/issues/35748")]
        public void WarnAsError_Nullable()
        {
            var dir = Temp.CreateDirectory();

            var arguments = DefaultParse(
                new[]
                {
                    "/nologo",
                    "/t:library",
                    "/warnaserror:nullable",
                    "a.cs"
                },
                dir.Path);

            var errors = arguments.Errors;
            Assert.Empty(errors);

            Assert.Equal(expected: ReportDiagnostic.Default, actual: arguments.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(expected: ErrorFacts.NullableWarnings.Count + 2, actual: arguments.CompilationOptions.SpecificDiagnosticOptions.Count);

            foreach (string warning in ErrorFacts.NullableWarnings)
            {
                Assert.Equal(expected: ReportDiagnostic.Error, actual: arguments.CompilationOptions.SpecificDiagnosticOptions[warning]);
            }

            Assert.Equal(expected: ReportDiagnostic.Error,
                actual: arguments.CompilationOptions.SpecificDiagnosticOptions[MessageProvider.Instance.GetIdForErrorCode((int)ErrorCode.WRN_MissingNonNullTypesContextForAnnotation)]);
            Assert.Equal(expected: ReportDiagnostic.Error,
                actual: arguments.CompilationOptions.SpecificDiagnosticOptions[MessageProvider.Instance.GetIdForErrorCode((int)ErrorCode.WRN_MissingNonNullTypesContextForAnnotationInGeneratedCode)]);
        }

        [WorkItem(912906, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/912906")]
        [Fact]
        public void Analyzers_CommandLineOverridesRuleset2()
        {
            string source = @"
class C
{
}
";
            var dir = Temp.CreateDirectory();

            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            string rulesetSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""Warning01"" Action=""Error"" />
  </Rules>
</RuleSet>
";
            var ruleSetFile = CreateRuleSetFile(rulesetSource);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            // This assembly has a MockAbstractDiagnosticAnalyzer type which should get run by this compilation.
            var csc = CreateCSharpCompiler(null, dir.Path,
                new[] {
                    "/nologo", "/t:library",
                    "/a:" + Assembly.GetExecutingAssembly().Location, "a.cs",
                    "/ruleset:" + ruleSetFile.Path, "/warn:0" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            // Diagnostic suppressed: commandline always overrides ruleset.
            Assert.DoesNotContain("Warning01", outWriter.ToString(), StringComparison.Ordinal);

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            csc = CreateCSharpCompiler(null, dir.Path,
                new[] {
                    "/nologo", "/t:library",
                    "/a:" + Assembly.GetExecutingAssembly().Location, "a.cs",
                    "/warn:0", "/ruleset:" + ruleSetFile.Path });
            exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            // Diagnostic suppressed: commandline always overrides ruleset.
            Assert.DoesNotContain("Warning01", outWriter.ToString(), StringComparison.Ordinal);

            // Clean up temp files
            CleanupAllGeneratedFiles(file.Path);
        }

        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30289")]
        public void DiagnosticFormatting()
        {
            string source = @"
using System;

class C
{
        public static void Main()
        {
            Goo(0);
#line 10 ""c:\temp\a\1.cs""
            Goo(1);
#line 20 ""C:\a\..\b.cs""
            Goo(2);
#line 30 ""C:\a\../B.cs""
            Goo(3);
#line 40 ""../b.cs""
            Goo(4);
#line 50 ""..\b.cs""
            Goo(5);
#line 60 ""C:\X.cs""
            Goo(6);
#line 70 ""C:\x.cs""
            Goo(7);
#line 90 ""      ""
		    Goo(9);
#line 100 ""C:\*.cs""
		    Goo(10);
#line 110 """"
		    Goo(11);
#line hidden
            Goo(12);
#line default
            Goo(13);
#line 140 ""***""
            Goo(14);
        }
    }
";
            var dir = Temp.CreateDirectory();
            dir.CreateFile("a.cs").WriteAllText(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/preferreduilang:en", "/t:library", "a.cs" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(1, exitCode);

            // with /fullpaths off
            string expected = @"
a.cs(8,13): error CS0103: The name 'Goo' does not exist in the current context
c:\temp\a\1.cs(10,13): error CS0103: The name 'Goo' does not exist in the current context
C:\b.cs(20,13): error CS0103: The name 'Goo' does not exist in the current context
C:\B.cs(30,13): error CS0103: The name 'Goo' does not exist in the current context
" + Path.GetFullPath(Path.Combine(dir.Path, @"..\b.cs")) + @"(40,13): error CS0103: The name 'Goo' does not exist in the current context
" + Path.GetFullPath(Path.Combine(dir.Path, @"..\b.cs")) + @"(50,13): error CS0103: The name 'Goo' does not exist in the current context
C:\X.cs(60,13): error CS0103: The name 'Goo' does not exist in the current context
C:\x.cs(70,13): error CS0103: The name 'Goo' does not exist in the current context
      (90,7): error CS0103: The name 'Goo' does not exist in the current context
C:\*.cs(100,7): error CS0103: The name 'Goo' does not exist in the current context
(110,7): error CS0103: The name 'Goo' does not exist in the current context
(112,13): error CS0103: The name 'Goo' does not exist in the current context
a.cs(32,13): error CS0103: The name 'Goo' does not exist in the current context
***(140,13): error CS0103: The name 'Goo' does not exist in the current context";

            AssertEx.Equal(
                expected.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries),
                outWriter.ToString().Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries),
                itemSeparator: "\r\n");

            // with /fullpaths on
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            csc = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/preferreduilang:en", "/t:library", "/fullpaths", "a.cs" });
            exitCode = csc.Run(outWriter);
            Assert.Equal(1, exitCode);

            expected = @"
" + Path.Combine(dir.Path, @"a.cs") + @"(8,13): error CS0103: The name 'Goo' does not exist in the current context
c:\temp\a\1.cs(10,13): error CS0103: The name 'Goo' does not exist in the current context
C:\b.cs(20,13): error CS0103: The name 'Goo' does not exist in the current context
C:\B.cs(30,13): error CS0103: The name 'Goo' does not exist in the current context
" + Path.GetFullPath(Path.Combine(dir.Path, @"..\b.cs")) + @"(40,13): error CS0103: The name 'Goo' does not exist in the current context
" + Path.GetFullPath(Path.Combine(dir.Path, @"..\b.cs")) + @"(50,13): error CS0103: The name 'Goo' does not exist in the current context
C:\X.cs(60,13): error CS0103: The name 'Goo' does not exist in the current context
C:\x.cs(70,13): error CS0103: The name 'Goo' does not exist in the current context
      (90,7): error CS0103: The name 'Goo' does not exist in the current context
C:\*.cs(100,7): error CS0103: The name 'Goo' does not exist in the current context
(110,7): error CS0103: The name 'Goo' does not exist in the current context
(112,13): error CS0103: The name 'Goo' does not exist in the current context
" + Path.Combine(dir.Path, @"a.cs") + @"(32,13): error CS0103: The name 'Goo' does not exist in the current context
***(140,13): error CS0103: The name 'Goo' does not exist in the current context";

            AssertEx.Equal(
                expected.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries),
                outWriter.ToString().Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries),
                itemSeparator: "\r\n");
        }

        [WorkItem(540891, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540891")]
        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30289")]
        public void ParseOut()
        {
            const string baseDirectory = @"C:\abc\def\baz";

            var parsedArgs = DefaultParse(new[] { @"/out:""""", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name '' contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(""));

            parsedArgs = DefaultParse(new[] { @"/out:", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2005: Missing file specification for '/out:' option
                Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/out:"));

            parsedArgs = DefaultParse(new[] { @"/refout:", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2005: Missing file specification for '/refout:' option
                Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/refout:"));

            parsedArgs = DefaultParse(new[] { @"/refout:ref.dll", "/refonly", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS8301: Do not use refout when using refonly.
                Diagnostic(ErrorCode.ERR_NoRefOutWhenRefOnly).WithLocation(1, 1));

            parsedArgs = DefaultParse(new[] { @"/refout:ref.dll", "/link:b", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new[] { "/refonly", "/link:b", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new[] { "/refonly:incorrect", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2007: Unrecognized option: '/refonly:incorrect'
                Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/refonly:incorrect").WithLocation(1, 1)
                );

            parsedArgs = DefaultParse(new[] { @"/refout:ref.dll", "/target:module", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS8302: Cannot compile net modules when using /refout or /refonly.
                Diagnostic(ErrorCode.ERR_NoNetModuleOutputWhenRefOutOrRefOnly).WithLocation(1, 1)
                );

            parsedArgs = DefaultParse(new[] { @"/refonly", "/target:module", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS8302: Cannot compile net modules when using /refout or /refonly.
                Diagnostic(ErrorCode.ERR_NoNetModuleOutputWhenRefOutOrRefOnly).WithLocation(1, 1)
                );

            // Dev11 reports CS2007: Unrecognized option: '/out'
            parsedArgs = DefaultParse(new[] { @"/out", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2005: Missing file specification for '/out' option
                Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/out"));

            parsedArgs = DefaultParse(new[] { @"/out+", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/out+"));

            // Should preserve fully qualified paths
            parsedArgs = DefaultParse(new[] { @"/out:C:\MyFolder\MyBinary.dll", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("MyBinary", parsedArgs.CompilationName);
            Assert.Equal("MyBinary.dll", parsedArgs.OutputFileName);
            Assert.Equal("MyBinary.dll", parsedArgs.CompilationOptions.ModuleName);
            Assert.Equal(@"C:\MyFolder", parsedArgs.OutputDirectory);
            Assert.Equal(@"C:\MyFolder\MyBinary.dll", parsedArgs.GetOutputFilePath(parsedArgs.OutputFileName));

            // Should handle quotes
            parsedArgs = DefaultParse(new[] { @"/out:""C:\My Folder\MyBinary.dll""", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(@"MyBinary", parsedArgs.CompilationName);
            Assert.Equal("MyBinary.dll", parsedArgs.OutputFileName);
            Assert.Equal("MyBinary.dll", parsedArgs.CompilationOptions.ModuleName);
            Assert.Equal(@"C:\My Folder", parsedArgs.OutputDirectory);

            // Should expand partially qualified paths
            parsedArgs = DefaultParse(new[] { @"/out:MyBinary.dll", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("MyBinary", parsedArgs.CompilationName);
            Assert.Equal("MyBinary.dll", parsedArgs.OutputFileName);
            Assert.Equal("MyBinary.dll", parsedArgs.CompilationOptions.ModuleName);
            Assert.Equal(baseDirectory, parsedArgs.OutputDirectory);
            Assert.Equal(Path.Combine(baseDirectory, "MyBinary.dll"), parsedArgs.GetOutputFilePath(parsedArgs.OutputFileName));

            // Should expand partially qualified paths
            parsedArgs = DefaultParse(new[] { @"/out:..\MyBinary.dll", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("MyBinary", parsedArgs.CompilationName);
            Assert.Equal("MyBinary.dll", parsedArgs.OutputFileName);
            Assert.Equal("MyBinary.dll", parsedArgs.CompilationOptions.ModuleName);
            Assert.Equal(@"C:\abc\def", parsedArgs.OutputDirectory);

            // not specified: exe
            parsedArgs = DefaultParse(new[] { @"a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Null(parsedArgs.CompilationName);
            Assert.Null(parsedArgs.OutputFileName);
            Assert.Null(parsedArgs.CompilationOptions.ModuleName);
            Assert.Equal(baseDirectory, parsedArgs.OutputDirectory);

            // not specified: dll
            parsedArgs = DefaultParse(new[] { @"/target:library", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("a", parsedArgs.CompilationName);
            Assert.Equal("a.dll", parsedArgs.OutputFileName);
            Assert.Equal("a.dll", parsedArgs.CompilationOptions.ModuleName);
            Assert.Equal(baseDirectory, parsedArgs.OutputDirectory);

            // not specified: module
            parsedArgs = DefaultParse(new[] { @"/target:module", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Null(parsedArgs.CompilationName);
            Assert.Equal("a.netmodule", parsedArgs.CompilationOptions.ModuleName);
            Assert.Equal(baseDirectory, parsedArgs.OutputDirectory);

            // not specified: appcontainerexe
            parsedArgs = DefaultParse(new[] { @"/target:appcontainerexe", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Null(parsedArgs.CompilationName);
            Assert.Null(parsedArgs.OutputFileName);
            Assert.Null(parsedArgs.CompilationOptions.ModuleName);
            Assert.Equal(baseDirectory, parsedArgs.OutputDirectory);

            // not specified: winmdobj
            parsedArgs = DefaultParse(new[] { @"/target:winmdobj", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("a", parsedArgs.CompilationName);
            Assert.Equal("a.winmdobj", parsedArgs.OutputFileName);
            Assert.Equal("a.winmdobj", parsedArgs.CompilationOptions.ModuleName);
            Assert.Equal(baseDirectory, parsedArgs.OutputDirectory);

            // drive-relative path:
            char currentDrive = Directory.GetCurrentDirectory()[0];
            parsedArgs = DefaultParse(new[] { currentDrive + @":a.cs", "b.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name 'D:a.cs' is contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(currentDrive + ":a.cs"));

            Assert.Null(parsedArgs.CompilationName);
            Assert.Null(parsedArgs.OutputFileName);
            Assert.Null(parsedArgs.CompilationOptions.ModuleName);
            Assert.Equal(baseDirectory, parsedArgs.OutputDirectory);

            // UNC
            parsedArgs = DefaultParse(new[] { @"/out:\\b", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name '.x' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(@"\\b"));

            Assert.Null(parsedArgs.OutputFileName);
            Assert.Null(parsedArgs.CompilationName);
            Assert.Null(parsedArgs.CompilationOptions.ModuleName);

            parsedArgs = DefaultParse(new[] { @"/out:\\server\share\file.exe", "a.vb" }, baseDirectory);
            parsedArgs.Errors.Verify();

            Assert.Equal(@"\\server\share", parsedArgs.OutputDirectory);
            Assert.Equal("file.exe", parsedArgs.OutputFileName);
            Assert.Equal("file", parsedArgs.CompilationName);
            Assert.Equal("file.exe", parsedArgs.CompilationOptions.ModuleName);

            // invalid name:
            parsedArgs = DefaultParse(new[] { "/out:a.b\0b", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name '.x' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments("a.b\0b"));

            Assert.Null(parsedArgs.OutputFileName);
            Assert.Null(parsedArgs.CompilationName);
            Assert.Null(parsedArgs.CompilationOptions.ModuleName);

            // Temporary skip following scenarios because of the error message changed (path)
            //parsedArgs = DefaultParse(new[] { "/out:a\uD800b.dll", "a.cs" }, baseDirectory);
            //parsedArgs.Errors.Verify(
            //    // error CS2021: File name '.x' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
            //    Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments("a\uD800b.dll"));

            // Dev11 reports CS0016: Could not write to output file 'd:\Temp\q\a<>.z'
            parsedArgs = DefaultParse(new[] { @"/out:""a<>.dll""", "a.vb" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name 'a<>.dll' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments("a<>.dll"));

            Assert.Null(parsedArgs.OutputFileName);
            Assert.Null(parsedArgs.CompilationName);
            Assert.Null(parsedArgs.CompilationOptions.ModuleName);

            parsedArgs = DefaultParse(new[] { @"/out:.exe", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name '.exe' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(".exe")
                );

            Assert.Null(parsedArgs.OutputFileName);
            Assert.Null(parsedArgs.CompilationName);
            Assert.Null(parsedArgs.CompilationOptions.ModuleName);

            parsedArgs = DefaultParse(new[] { @"/t:exe", @"/out:.exe", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name '.exe' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(".exe")
                );

            Assert.Null(parsedArgs.OutputFileName);
            Assert.Null(parsedArgs.CompilationName);
            Assert.Null(parsedArgs.CompilationOptions.ModuleName);

            parsedArgs = DefaultParse(new[] { @"/t:library", @"/out:.dll", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name '.dll' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(".dll")
                );

            Assert.Null(parsedArgs.OutputFileName);
            Assert.Null(parsedArgs.CompilationName);
            Assert.Null(parsedArgs.CompilationOptions.ModuleName);

            parsedArgs = DefaultParse(new[] { @"/t:module", @"/out:.netmodule", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name '.netmodule' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(".netmodule")
                );

            Assert.Null(parsedArgs.OutputFileName);
            Assert.Null(parsedArgs.CompilationName);
            Assert.Null(parsedArgs.CompilationOptions.ModuleName);

            parsedArgs = DefaultParse(new[] { ".cs" }, baseDirectory);
            parsedArgs.Errors.Verify();

            Assert.Null(parsedArgs.OutputFileName);
            Assert.Null(parsedArgs.CompilationName);
            Assert.Null(parsedArgs.CompilationOptions.ModuleName);

            parsedArgs = DefaultParse(new[] { @"/t:exe", ".cs" }, baseDirectory);
            parsedArgs.Errors.Verify();

            Assert.Null(parsedArgs.OutputFileName);
            Assert.Null(parsedArgs.CompilationName);
            Assert.Null(parsedArgs.CompilationOptions.ModuleName);

            parsedArgs = DefaultParse(new[] { @"/t:library", ".cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name '.dll' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(".dll")
                );

            Assert.Null(parsedArgs.OutputFileName);
            Assert.Null(parsedArgs.CompilationName);
            Assert.Null(parsedArgs.CompilationOptions.ModuleName);

            parsedArgs = DefaultParse(new[] { @"/t:module", ".cs" }, baseDirectory);
            parsedArgs.Errors.Verify();

            Assert.Equal(".netmodule", parsedArgs.OutputFileName);
            Assert.Null(parsedArgs.CompilationName);
            Assert.Equal(".netmodule", parsedArgs.CompilationOptions.ModuleName);
        }

        [WorkItem(546012, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546012")]
        [WorkItem(546007, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546007")]
        [Fact]
        public void ParseOut2()
        {
            var parsedArgs = DefaultParse(new[] { "/out:.x", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name '.x' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(".x"));

            Assert.Null(parsedArgs.OutputFileName);
            Assert.Null(parsedArgs.CompilationName);
            Assert.Null(parsedArgs.CompilationOptions.ModuleName);

            parsedArgs = DefaultParse(new[] { "/out:.x", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name '.x' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(".x"));

            Assert.Null(parsedArgs.OutputFileName);
            Assert.Null(parsedArgs.CompilationName);
            Assert.Null(parsedArgs.CompilationOptions.ModuleName);
        }

        [Fact]
        public void ParseInstrumentTestNames()
        {
            var parsedArgs = DefaultParse(SpecializedCollections.EmptyEnumerable<string>(), WorkingDirectory);
            Assert.True(parsedArgs.EmitOptions.InstrumentationKinds.SequenceEqual(ImmutableArray<InstrumentationKind>.Empty));

            parsedArgs = DefaultParse(new[] { @"/instrument", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for 'instrument' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "instrument"));
            Assert.True(parsedArgs.EmitOptions.InstrumentationKinds.SequenceEqual(ImmutableArray<InstrumentationKind>.Empty));

            parsedArgs = DefaultParse(new[] { @"/instrument:""""", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for 'instrument' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "instrument"));
            Assert.True(parsedArgs.EmitOptions.InstrumentationKinds.SequenceEqual(ImmutableArray<InstrumentationKind>.Empty));

            parsedArgs = DefaultParse(new[] { @"/instrument:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for 'instrument' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "instrument"));
            Assert.True(parsedArgs.EmitOptions.InstrumentationKinds.SequenceEqual(ImmutableArray<InstrumentationKind>.Empty));

            parsedArgs = DefaultParse(new[] { "/instrument:", "Test.Flag.Name", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for 'instrument' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "instrument"));
            Assert.True(parsedArgs.EmitOptions.InstrumentationKinds.SequenceEqual(ImmutableArray<InstrumentationKind>.Empty));

            parsedArgs = DefaultParse(new[] { "/instrument:InvalidOption", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.ERR_InvalidInstrumentationKind).WithArguments("InvalidOption"));
            Assert.True(parsedArgs.EmitOptions.InstrumentationKinds.SequenceEqual(ImmutableArray<InstrumentationKind>.Empty));

            parsedArgs = DefaultParse(new[] { "/instrument:None", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.ERR_InvalidInstrumentationKind).WithArguments("None"));
            Assert.True(parsedArgs.EmitOptions.InstrumentationKinds.SequenceEqual(ImmutableArray<InstrumentationKind>.Empty));

            parsedArgs = DefaultParse(new[] { "/instrument:TestCoverage,InvalidOption", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.ERR_InvalidInstrumentationKind).WithArguments("InvalidOption"));
            Assert.True(parsedArgs.EmitOptions.InstrumentationKinds.SequenceEqual(ImmutableArray.Create(InstrumentationKind.TestCoverage)));

            parsedArgs = DefaultParse(new[] { "/instrument:TestCoverage", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.EmitOptions.InstrumentationKinds.SequenceEqual(ImmutableArray.Create(InstrumentationKind.TestCoverage)));

            parsedArgs = DefaultParse(new[] { @"/instrument:""TestCoverage""", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.EmitOptions.InstrumentationKinds.SequenceEqual(ImmutableArray.Create(InstrumentationKind.TestCoverage)));

            parsedArgs = DefaultParse(new[] { @"/instrument:""TESTCOVERAGE""", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.EmitOptions.InstrumentationKinds.SequenceEqual(ImmutableArray.Create(InstrumentationKind.TestCoverage)));

            parsedArgs = DefaultParse(new[] { "/instrument:TestCoverage,TestCoverage", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.EmitOptions.InstrumentationKinds.SequenceEqual(ImmutableArray.Create(InstrumentationKind.TestCoverage)));

            parsedArgs = DefaultParse(new[] { "/instrument:TestCoverage", "/instrument:TestCoverage", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.EmitOptions.InstrumentationKinds.SequenceEqual(ImmutableArray.Create(InstrumentationKind.TestCoverage)));
        }

        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30289")]
        public void ParseDoc()
        {
            const string baseDirectory = @"C:\abc\def\baz";

            var parsedArgs = DefaultParse(new[] { @"/doc:""""", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for '/doc:' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "/doc:"));
            Assert.Null(parsedArgs.DocumentationPath);

            parsedArgs = DefaultParse(new[] { @"/doc:", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for '/doc:' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "/doc:"));
            Assert.Null(parsedArgs.DocumentationPath);

            // NOTE: no colon in error message '/doc'
            parsedArgs = DefaultParse(new[] { @"/doc", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for '/doc' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "/doc"));
            Assert.Null(parsedArgs.DocumentationPath);

            parsedArgs = DefaultParse(new[] { @"/doc+", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/doc+"));
            Assert.Null(parsedArgs.DocumentationPath);

            // Should preserve fully qualified paths
            parsedArgs = DefaultParse(new[] { @"/doc:C:\MyFolder\MyBinary.xml", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(@"C:\MyFolder\MyBinary.xml", parsedArgs.DocumentationPath);
            Assert.Equal(DocumentationMode.Diagnose, parsedArgs.ParseOptions.DocumentationMode);

            // Should handle quotes
            parsedArgs = DefaultParse(new[] { @"/doc:""C:\My Folder\MyBinary.xml""", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(@"C:\My Folder\MyBinary.xml", parsedArgs.DocumentationPath);
            Assert.Equal(DocumentationMode.Diagnose, parsedArgs.ParseOptions.DocumentationMode);

            // Should expand partially qualified paths
            parsedArgs = DefaultParse(new[] { @"/doc:MyBinary.xml", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(Path.Combine(baseDirectory, "MyBinary.xml"), parsedArgs.DocumentationPath);
            Assert.Equal(DocumentationMode.Diagnose, parsedArgs.ParseOptions.DocumentationMode);

            // Should expand partially qualified paths
            parsedArgs = DefaultParse(new[] { @"/doc:..\MyBinary.xml", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(@"C:\abc\def\MyBinary.xml", parsedArgs.DocumentationPath);
            Assert.Equal(DocumentationMode.Diagnose, parsedArgs.ParseOptions.DocumentationMode);

            // drive-relative path:
            char currentDrive = Directory.GetCurrentDirectory()[0];
            parsedArgs = DefaultParse(new[] { "/doc:" + currentDrive + @":a.xml", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name 'D:a.xml' is contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(currentDrive + ":a.xml"));

            Assert.Null(parsedArgs.DocumentationPath);
            Assert.Equal(DocumentationMode.Diagnose, parsedArgs.ParseOptions.DocumentationMode); //Even though the format was incorrect

            // UNC
            parsedArgs = DefaultParse(new[] { @"/doc:\\b", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(@"\\b"));

            Assert.Null(parsedArgs.DocumentationPath);
            Assert.Equal(DocumentationMode.Diagnose, parsedArgs.ParseOptions.DocumentationMode); //Even though the format was incorrect

            parsedArgs = DefaultParse(new[] { @"/doc:\\server\share\file.xml", "a.vb" }, baseDirectory);
            parsedArgs.Errors.Verify();

            Assert.Equal(@"\\server\share\file.xml", parsedArgs.DocumentationPath);
            Assert.Equal(DocumentationMode.Diagnose, parsedArgs.ParseOptions.DocumentationMode);

            // invalid name:
            parsedArgs = DefaultParse(new[] { "/doc:a.b\0b", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments("a.b\0b"));

            Assert.Null(parsedArgs.DocumentationPath);
            Assert.Equal(DocumentationMode.Diagnose, parsedArgs.ParseOptions.DocumentationMode); //Even though the format was incorrect

            // Temp
            // parsedArgs = DefaultParse(new[] { "/doc:a\uD800b.xml", "a.cs" }, baseDirectory);
            // parsedArgs.Errors.Verify(
            //    Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments("a\uD800b.xml"));

            // Assert.Null(parsedArgs.DocumentationPath);
            // Assert.Equal(DocumentationMode.Diagnose, parsedArgs.ParseOptions.DocumentationMode); //Even though the format was incorrect

            parsedArgs = DefaultParse(new[] { @"/doc:""a<>.xml""", "a.vb" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name 'a<>.xml' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments("a<>.xml"));

            Assert.Null(parsedArgs.DocumentationPath);
            Assert.Equal(DocumentationMode.Diagnose, parsedArgs.ParseOptions.DocumentationMode); //Even though the format was incorrect
        }

        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30289")]
        public void ParseErrorLog()
        {
            const string baseDirectory = @"C:\abc\def\baz";

            var parsedArgs = DefaultParse(new[] { @"/errorlog:""""", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<(error log option format>' for '/errorlog:' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments(CSharpCommandLineParser.ErrorLogOptionFormat, "/errorlog:"));
            Assert.Null(parsedArgs.ErrorLogOptions);
            Assert.False(parsedArgs.CompilationOptions.ReportSuppressedDiagnostics);

            parsedArgs = DefaultParse(new[] { @"/errorlog:", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<(error log option format>' for '/errorlog:' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments(CSharpCommandLineParser.ErrorLogOptionFormat, "/errorlog:"));
            Assert.Null(parsedArgs.ErrorLogOptions);
            Assert.False(parsedArgs.CompilationOptions.ReportSuppressedDiagnostics);

            parsedArgs = DefaultParse(new[] { @"/errorlog", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<(error log option format>' for '/errorlog' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments(CSharpCommandLineParser.ErrorLogOptionFormat, "/errorlog"));
            Assert.Null(parsedArgs.ErrorLogOptions);
            Assert.False(parsedArgs.CompilationOptions.ReportSuppressedDiagnostics);

            // Should preserve fully qualified paths
            parsedArgs = DefaultParse(new[] { @"/errorlog:C:\MyFolder\MyBinary.xml", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(@"C:\MyFolder\MyBinary.xml", parsedArgs.ErrorLogOptions.Path);
            Assert.True(parsedArgs.CompilationOptions.ReportSuppressedDiagnostics);

            // Escaped quote in the middle is an error
            parsedArgs = DefaultParse(new[] { @"/errorlog:C:\""My Folder""\MyBinary.xml", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                 Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(@"C:""My Folder\MyBinary.xml").WithLocation(1, 1));

            // Should handle quotes
            parsedArgs = DefaultParse(new[] { @"/errorlog:""C:\My Folder\MyBinary.xml""", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(@"C:\My Folder\MyBinary.xml", parsedArgs.ErrorLogOptions.Path);
            Assert.True(parsedArgs.CompilationOptions.ReportSuppressedDiagnostics);

            // Should expand partially qualified paths
            parsedArgs = DefaultParse(new[] { @"/errorlog:MyBinary.xml", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(Path.Combine(baseDirectory, "MyBinary.xml"), parsedArgs.ErrorLogOptions.Path);
            Assert.True(parsedArgs.CompilationOptions.ReportSuppressedDiagnostics);

            // Should expand partially qualified paths
            parsedArgs = DefaultParse(new[] { @"/errorlog:..\MyBinary.xml", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(@"C:\abc\def\MyBinary.xml", parsedArgs.ErrorLogOptions.Path);
            Assert.True(parsedArgs.CompilationOptions.ReportSuppressedDiagnostics);

            // drive-relative path:
            char currentDrive = Directory.GetCurrentDirectory()[0];
            parsedArgs = DefaultParse(new[] { "/errorlog:" + currentDrive + @":a.xml", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name 'D:a.xml' is contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(currentDrive + ":a.xml"));

            Assert.Null(parsedArgs.ErrorLogOptions);
            Assert.False(parsedArgs.CompilationOptions.ReportSuppressedDiagnostics);

            // UNC
            parsedArgs = DefaultParse(new[] { @"/errorlog:\\b", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(@"\\b"));

            Assert.Null(parsedArgs.ErrorLogOptions);
            Assert.False(parsedArgs.CompilationOptions.ReportSuppressedDiagnostics);

            parsedArgs = DefaultParse(new[] { @"/errorlog:\\server\share\file.xml", "a.vb" }, baseDirectory);
            parsedArgs.Errors.Verify();

            Assert.Equal(@"\\server\share\file.xml", parsedArgs.ErrorLogOptions.Path);

            // invalid name:
            parsedArgs = DefaultParse(new[] { "/errorlog:a.b\0b", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments("a.b\0b"));

            Assert.Null(parsedArgs.ErrorLogOptions);
            Assert.False(parsedArgs.CompilationOptions.ReportSuppressedDiagnostics);

            parsedArgs = DefaultParse(new[] { @"/errorlog:""a<>.xml""", "a.vb" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2021: File name 'a<>.xml' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments("a<>.xml"));

            Assert.Null(parsedArgs.ErrorLogOptions);
            Assert.False(parsedArgs.CompilationOptions.ReportSuppressedDiagnostics);

            // Parses SARIF version.
            parsedArgs = DefaultParse(new[] { @"/errorlog:C:\MyFolder\MyBinary.xml,version=2", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(@"C:\MyFolder\MyBinary.xml", parsedArgs.ErrorLogOptions.Path);
            Assert.Equal(SarifVersion.Sarif2, parsedArgs.ErrorLogOptions.SarifVersion);
            Assert.True(parsedArgs.CompilationOptions.ReportSuppressedDiagnostics);

            // Invalid SARIF version.
            string[] invalidSarifVersions = new string[] { @"C:\MyFolder\MyBinary.xml,version=1.0.0", @"C:\MyFolder\MyBinary.xml,version=2.1.0", @"C:\MyFolder\MyBinary.xml,version=42" };

            foreach (string invalidSarifVersion in invalidSarifVersions)
            {
                parsedArgs = DefaultParse(new[] { $"/errorlog:{invalidSarifVersion}", "a.cs" }, baseDirectory);
                parsedArgs.Errors.Verify(
                    // error CS2046: Command-line syntax error: 'C:\MyFolder\MyBinary.xml,version=42' is not a valid value for the '/errorlog:' option. The value must be of the form '<file>[,version={1|1.0|2|2.1}]'.
                    Diagnostic(ErrorCode.ERR_BadSwitchValue).WithArguments(invalidSarifVersion, "/errorlog:", CSharpCommandLineParser.ErrorLogOptionFormat));
                Assert.Null(parsedArgs.ErrorLogOptions);
                Assert.False(parsedArgs.CompilationOptions.ReportSuppressedDiagnostics);
            }

            // Invalid errorlog qualifier.
            const string InvalidErrorLogQualifier = @"C:\MyFolder\MyBinary.xml,invalid=42";
            parsedArgs = DefaultParse(new[] { $"/errorlog:{InvalidErrorLogQualifier}", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2046: Command-line syntax error: 'C:\MyFolder\MyBinary.xml,invalid=42' is not a valid value for the '/errorlog:' option. The value must be of the form '<file>[,version={1|1.0|2|2.1}]'.
                Diagnostic(ErrorCode.ERR_BadSwitchValue).WithArguments(InvalidErrorLogQualifier, "/errorlog:", CSharpCommandLineParser.ErrorLogOptionFormat));
            Assert.Null(parsedArgs.ErrorLogOptions);
            Assert.False(parsedArgs.CompilationOptions.ReportSuppressedDiagnostics);

            // Too many errorlog qualifiers.
            const string TooManyErrorLogQualifiers = @"C:\MyFolder\MyBinary.xml,version=2,version=2";
            parsedArgs = DefaultParse(new[] { $"/errorlog:{TooManyErrorLogQualifiers}", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2046: Command-line syntax error: 'C:\MyFolder\MyBinary.xml,version=2,version=2' is not a valid value for the '/errorlog:' option. The value must be of the form '<file>[,version={1|1.0|2|2.1}]'.
                Diagnostic(ErrorCode.ERR_BadSwitchValue).WithArguments(TooManyErrorLogQualifiers, "/errorlog:", CSharpCommandLineParser.ErrorLogOptionFormat));
            Assert.Null(parsedArgs.ErrorLogOptions);
            Assert.False(parsedArgs.CompilationOptions.ReportSuppressedDiagnostics);
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void AppConfigParse()
        {
            const string baseDirectory = @"C:\abc\def\baz";

            var parsedArgs = DefaultParse(new[] { @"/appconfig:""""", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing ':<text>' for '/appconfig:' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments(":<text>", "/appconfig:"));
            Assert.Null(parsedArgs.AppConfigPath);

            parsedArgs = DefaultParse(new[] { "/appconfig:", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing ':<text>' for '/appconfig:' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments(":<text>", "/appconfig:"));
            Assert.Null(parsedArgs.AppConfigPath);

            parsedArgs = DefaultParse(new[] { "/appconfig", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing ':<text>' for '/appconfig' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments(":<text>", "/appconfig"));
            Assert.Null(parsedArgs.AppConfigPath);

            parsedArgs = DefaultParse(new[] { "/appconfig:a.exe.config", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(@"C:\abc\def\baz\a.exe.config", parsedArgs.AppConfigPath);

            // If ParseDoc succeeds, all other possible AppConfig paths should succeed as well -- they both call ParseGenericFilePath
        }

        [Fact]
        public void AppConfigBasic()
        {
            var srcFile = Temp.CreateFile().WriteAllText(@"class A { static void Main(string[] args) { } }");
            var srcDirectory = Path.GetDirectoryName(srcFile.Path);
            var appConfigFile = Temp.CreateFile().WriteAllText(
@"<?xml version=""1.0"" encoding=""utf-8"" ?>
<configuration>
  <runtime>
    <assemblyBinding xmlns=""urn:schemas-microsoft-com:asm.v1"">
       <supportPortability PKT=""7cec85d7bea7798e"" enable=""false""/>
    </assemblyBinding>
  </runtime>
</configuration>");

            var silverlight = Temp.CreateFile().WriteAllBytes(ProprietaryTestResources.silverlight_v5_0_5_0.System_v5_0_5_0_silverlight).Path;
            var net4_0dll = Temp.CreateFile().WriteAllBytes(ResourcesNet451.System).Path;

            // Test linking two appconfig dlls with simple src
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = CreateCSharpCompiler(null, srcDirectory,
                new[] { "/nologo",
                        "/r:" + silverlight,
                        "/r:" + net4_0dll,
                        "/appconfig:" + appConfigFile.Path,
                        srcFile.Path }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(srcFile.Path);
            CleanupAllGeneratedFiles(appConfigFile.Path);
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void AppConfigBasicFail()
        {
            var srcFile = Temp.CreateFile().WriteAllText(@"class A { static void Main(string[] args) { } }");
            var srcDirectory = Path.GetDirectoryName(srcFile.Path);
            string root = Path.GetPathRoot(srcDirectory); // Make sure we pick a drive that exists and is plugged in to avoid 'Drive not ready'

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = CreateCSharpCompiler(null, srcDirectory,
                new[] { "/nologo", "/preferreduilang:en",
                        $@"/appconfig:{root}DoesNotExist\NOwhere\bonobo.exe.config" ,
                        srcFile.Path }).Run(outWriter);
            Assert.NotEqual(0, exitCode);
            Assert.Equal($@"error CS7093: Cannot read config file '{root}DoesNotExist\NOwhere\bonobo.exe.config' -- 'Could not find a part of the path '{root}DoesNotExist\NOwhere\bonobo.exe.config'.'", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(srcFile.Path);
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void ParseDocAndOut()
        {
            const string baseDirectory = @"C:\abc\def\baz";

            // Can specify separate directories for binary and XML output.
            var parsedArgs = DefaultParse(new[] { @"/doc:a\b.xml", @"/out:c\d.exe", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();

            Assert.Equal(@"C:\abc\def\baz\a\b.xml", parsedArgs.DocumentationPath);

            Assert.Equal(@"C:\abc\def\baz\c", parsedArgs.OutputDirectory);
            Assert.Equal("d.exe", parsedArgs.OutputFileName);

            // XML does not fall back on output directory.
            parsedArgs = DefaultParse(new[] { @"/doc:b.xml", @"/out:c\d.exe", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();

            Assert.Equal(@"C:\abc\def\baz\b.xml", parsedArgs.DocumentationPath);

            Assert.Equal(@"C:\abc\def\baz\c", parsedArgs.OutputDirectory);
            Assert.Equal("d.exe", parsedArgs.OutputFileName);
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void ParseErrorLogAndOut()
        {
            const string baseDirectory = @"C:\abc\def\baz";

            // Can specify separate directories for binary and error log output.
            var parsedArgs = DefaultParse(new[] { @"/errorlog:a\b.xml", @"/out:c\d.exe", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();

            Assert.Equal(@"C:\abc\def\baz\a\b.xml", parsedArgs.ErrorLogOptions.Path);

            Assert.Equal(@"C:\abc\def\baz\c", parsedArgs.OutputDirectory);
            Assert.Equal("d.exe", parsedArgs.OutputFileName);

            // XML does not fall back on output directory.
            parsedArgs = DefaultParse(new[] { @"/errorlog:b.xml", @"/out:c\d.exe", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();

            Assert.Equal(@"C:\abc\def\baz\b.xml", parsedArgs.ErrorLogOptions.Path);

            Assert.Equal(@"C:\abc\def\baz\c", parsedArgs.OutputDirectory);
            Assert.Equal("d.exe", parsedArgs.OutputFileName);
        }

        [Fact]
        public void ModuleAssemblyName()
        {
            var parsedArgs = DefaultParse(new[] { @"/target:module", "/moduleassemblyname:goo", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("goo", parsedArgs.CompilationName);
            Assert.Equal("a.netmodule", parsedArgs.CompilationOptions.ModuleName);

            parsedArgs = DefaultParse(new[] { @"/target:library", "/moduleassemblyname:goo", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS0734: The /moduleassemblyname option may only be specified when building a target type of 'module'
                Diagnostic(ErrorCode.ERR_AssemblyNameOnNonModule));

            parsedArgs = DefaultParse(new[] { @"/target:exe", "/moduleassemblyname:goo", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS0734: The /moduleassemblyname option may only be specified when building a target type of 'module'
                Diagnostic(ErrorCode.ERR_AssemblyNameOnNonModule));

            parsedArgs = DefaultParse(new[] { @"/target:winexe", "/moduleassemblyname:goo", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS0734: The /moduleassemblyname option may only be specified when building a target type of 'module'
                Diagnostic(ErrorCode.ERR_AssemblyNameOnNonModule));
        }

        [Fact]
        public void ModuleName()
        {
            var parsedArgs = DefaultParse(new[] { @"/target:module", "/modulename:goo", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("goo", parsedArgs.CompilationOptions.ModuleName);

            parsedArgs = DefaultParse(new[] { @"/target:library", "/modulename:bar", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("bar", parsedArgs.CompilationOptions.ModuleName);

            parsedArgs = DefaultParse(new[] { @"/target:exe", "/modulename:CommonLanguageRuntimeLibrary", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("CommonLanguageRuntimeLibrary", parsedArgs.CompilationOptions.ModuleName);

            parsedArgs = DefaultParse(new[] { @"/target:winexe", "/modulename:goo", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("goo", parsedArgs.CompilationOptions.ModuleName);

            parsedArgs = DefaultParse(new[] { @"/target:exe", "/modulename:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for 'modulename' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "modulename").WithLocation(1, 1)
                );
        }

        [Fact]
        public void ModuleName001()
        {
            var dir = Temp.CreateDirectory();

            var file1 = dir.CreateFile("a.cs");
            file1.WriteAllText(@"
                    class c1
                    {
                        public static void Main(){}
                    }
                ");

            var exeName = "aa.exe";
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/modulename:hocusPocus ", "/out:" + exeName + " ", file1.Path });
            int exitCode = csc.Run(outWriter);
            if (exitCode != 0)
            {
                Console.WriteLine(outWriter.ToString());
                Assert.Equal(0, exitCode);
            }


            Assert.Equal(1, Directory.EnumerateFiles(dir.Path, exeName).Count());

            using (var metadata = ModuleMetadata.CreateFromImage(File.ReadAllBytes(Path.Combine(dir.Path, "aa.exe"))))
            {
                var peReader = metadata.Module.GetMetadataReader();

                Assert.True(peReader.IsAssembly);

                Assert.Equal("aa", peReader.GetString(peReader.GetAssemblyDefinition().Name));
                Assert.Equal("hocusPocus", peReader.GetString(peReader.GetModuleDefinition().Name));
            }

            if (System.IO.File.Exists(exeName))
            {
                System.IO.File.Delete(exeName);
            }

            CleanupAllGeneratedFiles(file1.Path);
        }

        [Fact]
        public void ParsePlatform()
        {
            var parsedArgs = DefaultParse(new[] { @"/platform:x64", "a.cs" }, WorkingDirectory);
            Assert.False(parsedArgs.Errors.Any());
            Assert.Equal(Platform.X64, parsedArgs.CompilationOptions.Platform);

            parsedArgs = DefaultParse(new[] { @"/platform:X86", "a.cs" }, WorkingDirectory);
            Assert.False(parsedArgs.Errors.Any());
            Assert.Equal(Platform.X86, parsedArgs.CompilationOptions.Platform);

            parsedArgs = DefaultParse(new[] { @"/platform:itanum", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_BadPlatformType, parsedArgs.Errors.First().Code);
            Assert.Equal(Platform.AnyCpu, parsedArgs.CompilationOptions.Platform);

            parsedArgs = DefaultParse(new[] { "/platform:itanium", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(Platform.Itanium, parsedArgs.CompilationOptions.Platform);

            parsedArgs = DefaultParse(new[] { "/platform:anycpu", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(Platform.AnyCpu, parsedArgs.CompilationOptions.Platform);

            parsedArgs = DefaultParse(new[] { "/platform:anycpu32bitpreferred", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(Platform.AnyCpu32BitPreferred, parsedArgs.CompilationOptions.Platform);

            parsedArgs = DefaultParse(new[] { "/platform:arm", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(Platform.Arm, parsedArgs.CompilationOptions.Platform);

            parsedArgs = DefaultParse(new[] { "/platform", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<string>' for 'platform' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<string>", "/platform"));
            Assert.Equal(Platform.AnyCpu, parsedArgs.CompilationOptions.Platform);  //anycpu is default

            parsedArgs = DefaultParse(new[] { "/platform:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<string>' for 'platform' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<string>", "/platform:"));
            Assert.Equal(Platform.AnyCpu, parsedArgs.CompilationOptions.Platform);  //anycpu is default
        }

        [WorkItem(546016, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546016")]
        [WorkItem(545997, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545997")]
        [WorkItem(546019, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546019")]
        [WorkItem(546029, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546029")]
        [Fact]
        public void ParseBaseAddress()
        {
            var parsedArgs = DefaultParse(new[] { @"/baseaddress:x64", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_BadBaseNumber, parsedArgs.Errors.First().Code);

            parsedArgs = DefaultParse(new[] { @"/platform:x64", @"/baseaddress:0x8000000000011111", "a.cs" }, WorkingDirectory);
            Assert.False(parsedArgs.Errors.Any());
            Assert.Equal(0x8000000000011111ul, parsedArgs.EmitOptions.BaseAddress);

            parsedArgs = DefaultParse(new[] { @"/platform:x86", @"/baseaddress:0x8000000000011111", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_BadBaseNumber, parsedArgs.Errors.First().Code);

            parsedArgs = DefaultParse(new[] { @"/baseaddress:", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_SwitchNeedsNumber, parsedArgs.Errors.First().Code);

            parsedArgs = DefaultParse(new[] { @"/baseaddress:-23", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_BadBaseNumber, parsedArgs.Errors.First().Code);

            parsedArgs = DefaultParse(new[] { @"/platform:x64", @"/baseaddress:01777777777777777777777", "a.cs" }, WorkingDirectory);
            Assert.Equal(ulong.MaxValue, parsedArgs.EmitOptions.BaseAddress);

            parsedArgs = DefaultParse(new[] { @"/platform:x64", @"/baseaddress:0x0000000100000000", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new[] { @"/platform:x64", @"/baseaddress:0xffff8000", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new[] { "test.cs", "/platform:x86", "/baseaddress:0xffffffff" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadBaseNumber).WithArguments("0xFFFFFFFF"));

            parsedArgs = DefaultParse(new[] { "test.cs", "/platform:x86", "/baseaddress:0xffff8000" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadBaseNumber).WithArguments("0xFFFF8000"));

            parsedArgs = DefaultParse(new[] { "test.cs", "/baseaddress:0xffff8000" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadBaseNumber).WithArguments("0xFFFF8000"));

            parsedArgs = DefaultParse(new[] { "C:\\test.cs", "/platform:x86", "/baseaddress:0xffff7fff" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new[] { "C:\\test.cs", "/platform:x64", "/baseaddress:0xffff8000" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new[] { "C:\\test.cs", "/platform:x64", "/baseaddress:0x100000000" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new[] { "test.cs", "/baseaddress:0xFFFF0000FFFF0000" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadBaseNumber).WithArguments("0xFFFF0000FFFF0000"));

            parsedArgs = DefaultParse(new[] { "C:\\test.cs", "/platform:x64", "/baseaddress:0x10000000000000000" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadBaseNumber).WithArguments("0x10000000000000000"));

            parsedArgs = DefaultParse(new[] { "C:\\test.cs", "/baseaddress:0xFFFF0000FFFF0000" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadBaseNumber).WithArguments("0xFFFF0000FFFF0000"));
        }

        [Fact]
        public void ParseFileAlignment()
        {
            var parsedArgs = DefaultParse(new[] { @"/filealign:x64", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2024: Invalid file section alignment number 'x64'
                Diagnostic(ErrorCode.ERR_InvalidFileAlignment).WithArguments("x64"));

            parsedArgs = DefaultParse(new[] { @"/filealign:0x200", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(0x200, parsedArgs.EmitOptions.FileAlignment);

            parsedArgs = DefaultParse(new[] { @"/filealign:512", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(512, parsedArgs.EmitOptions.FileAlignment);

            parsedArgs = DefaultParse(new[] { @"/filealign:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2035: Command-line syntax error: Missing ':<number>' for 'filealign' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsNumber).WithArguments("filealign"));

            parsedArgs = DefaultParse(new[] { @"/filealign:-23", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2024: Invalid file section alignment number '-23'
                Diagnostic(ErrorCode.ERR_InvalidFileAlignment).WithArguments("-23"));

            parsedArgs = DefaultParse(new[] { @"/filealign:020000", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(8192, parsedArgs.EmitOptions.FileAlignment);

            parsedArgs = DefaultParse(new[] { @"/filealign:0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2024: Invalid file section alignment number '0'
                Diagnostic(ErrorCode.ERR_InvalidFileAlignment).WithArguments("0"));

            parsedArgs = DefaultParse(new[] { @"/filealign:123", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2024: Invalid file section alignment number '123'
                Diagnostic(ErrorCode.ERR_InvalidFileAlignment).WithArguments("123"));
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void SdkPathAndLibEnvVariable()
        {
            var dir = Temp.CreateDirectory();
            var lib1 = dir.CreateDirectory("lib1");
            var lib2 = dir.CreateDirectory("lib2");
            var lib3 = dir.CreateDirectory("lib3");

            var sdkDirectory = SdkDirectory;
            var parsedArgs = DefaultParse(new[] { @"/lib:lib1", @"/libpath:lib2", @"/libpaths:lib3", "a.cs" }, dir.Path, sdkDirectory: sdkDirectory);
            AssertEx.Equal(new[]
            {
                sdkDirectory,
                lib1.Path,
                lib2.Path,
                lib3.Path
            }, parsedArgs.ReferencePaths);
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void SdkPathAndLibEnvVariable_Errors()
        {
            var parsedArgs = DefaultParse(new[] { @"/lib:c:lib2", @"/lib:o:\sdk1", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // warning CS1668: Invalid search path 'c:lib2' specified in '/LIB option' -- 'path is too long or invalid'
                Diagnostic(ErrorCode.WRN_InvalidSearchPathDir).WithArguments(@"c:lib2", "/LIB option", "path is too long or invalid"),
                // warning CS1668: Invalid search path 'o:\sdk1' specified in '/LIB option' -- 'directory does not exist'
                Diagnostic(ErrorCode.WRN_InvalidSearchPathDir).WithArguments(@"o:\sdk1", "/LIB option", "directory does not exist"));

            parsedArgs = DefaultParse(new[] { @"/lib:c:\Windows,o:\Windows;e:;", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // warning CS1668: Invalid search path 'o:\Windows' specified in '/LIB option' -- 'directory does not exist'
                Diagnostic(ErrorCode.WRN_InvalidSearchPathDir).WithArguments(@"o:\Windows", "/LIB option", "directory does not exist"),
                // warning CS1668: Invalid search path 'e:' specified in '/LIB option' -- 'path is too long or invalid'
                Diagnostic(ErrorCode.WRN_InvalidSearchPathDir).WithArguments(@"e:", "/LIB option", "path is too long or invalid"));

            parsedArgs = DefaultParse(new[] { @"/lib:c:\Windows,.\Windows;e;", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // warning CS1668: Invalid search path '.\Windows' specified in '/LIB option' -- 'directory does not exist'
                Diagnostic(ErrorCode.WRN_InvalidSearchPathDir).WithArguments(@".\Windows", "/LIB option", "directory does not exist"),
                // warning CS1668: Invalid search path 'e' specified in '/LIB option' -- 'directory does not exist'
                Diagnostic(ErrorCode.WRN_InvalidSearchPathDir).WithArguments(@"e", "/LIB option", "directory does not exist"));

            parsedArgs = DefaultParse(new[] { @"/lib:c:\Windows,o:\Windows;e:; ; ; ; ", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // warning CS1668: Invalid search path 'o:\Windows' specified in '/LIB option' -- 'directory does not exist'
                Diagnostic(ErrorCode.WRN_InvalidSearchPathDir).WithArguments(@"o:\Windows", "/LIB option", "directory does not exist"),
                // warning CS1668: Invalid search path 'e:' specified in '/LIB option' -- 'path is too long or invalid'
                Diagnostic(ErrorCode.WRN_InvalidSearchPathDir).WithArguments("e:", "/LIB option", "path is too long or invalid"),
                // warning CS1668: Invalid search path ' ' specified in '/LIB option' -- 'path is too long or invalid'
                Diagnostic(ErrorCode.WRN_InvalidSearchPathDir).WithArguments(" ", "/LIB option", "path is too long or invalid"),
                // warning CS1668: Invalid search path ' ' specified in '/LIB option' -- 'path is too long or invalid'
                Diagnostic(ErrorCode.WRN_InvalidSearchPathDir).WithArguments(" ", "/LIB option", "path is too long or invalid"),
                // warning CS1668: Invalid search path ' ' specified in '/LIB option' -- 'path is too long or invalid'
                Diagnostic(ErrorCode.WRN_InvalidSearchPathDir).WithArguments(" ", "/LIB option", "path is too long or invalid"));

            parsedArgs = DefaultParse(new[] { @"/lib", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<path list>", "lib"));

            parsedArgs = DefaultParse(new[] { @"/lib:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<path list>", "lib"));

            parsedArgs = DefaultParse(new[] { @"/lib+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/lib+"));

            parsedArgs = DefaultParse(new[] { @"/lib: ", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<path list>", "lib"));
        }

        [Fact, WorkItem(546005, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546005")]
        public void SdkPathAndLibEnvVariable_Relative_csc()
        {
            var tempFolder = Temp.CreateDirectory();
            var baseDirectory = tempFolder.ToString();

            var subFolder = tempFolder.CreateDirectory("temp");
            var subDirectory = subFolder.ToString();

            var src = Temp.CreateFile("a.cs");
            src.WriteAllText("public class C{}");

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, subDirectory, new[] { "/nologo", "/t:library", "/out:abc.xyz", src.ToString() }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString().Trim());

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, baseDirectory, new[] { "/nologo", "/lib:temp", "/r:abc.xyz", "/t:library", src.ToString() }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(src.Path);
        }

        [Fact]
        public void UnableWriteOutput()
        {
            var tempFolder = Temp.CreateDirectory();
            var baseDirectory = tempFolder.ToString();
            var subFolder = tempFolder.CreateDirectory("temp");

            var src = Temp.CreateFile("a.cs");
            src.WriteAllText("public class C{}");

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, baseDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", "/out:" + subFolder.ToString(), src.ToString() }).Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.True(outWriter.ToString().Trim().StartsWith("error CS2012: Cannot open '" + subFolder.ToString() + "' for writing -- '", StringComparison.Ordinal)); // Cannot create a file when that file already exists.

            CleanupAllGeneratedFiles(src.Path);
        }

        [Fact]
        public void ParseHighEntropyVA()
        {
            var parsedArgs = DefaultParse(new[] { @"/highentropyva", "a.cs" }, WorkingDirectory);
            Assert.False(parsedArgs.Errors.Any());
            Assert.True(parsedArgs.EmitOptions.HighEntropyVirtualAddressSpace);
            parsedArgs = DefaultParse(new[] { @"/highentropyva+", "a.cs" }, WorkingDirectory);
            Assert.False(parsedArgs.Errors.Any());
            Assert.True(parsedArgs.EmitOptions.HighEntropyVirtualAddressSpace);
            parsedArgs = DefaultParse(new[] { @"/highentropyva-", "a.cs" }, WorkingDirectory);
            Assert.False(parsedArgs.Errors.Any());
            Assert.False(parsedArgs.EmitOptions.HighEntropyVirtualAddressSpace);
            parsedArgs = DefaultParse(new[] { @"/highentropyva:-", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal(EmitOptions.Default.HighEntropyVirtualAddressSpace, parsedArgs.EmitOptions.HighEntropyVirtualAddressSpace);

            parsedArgs = DefaultParse(new[] { @"/highentropyva:", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal(EmitOptions.Default.HighEntropyVirtualAddressSpace, parsedArgs.EmitOptions.HighEntropyVirtualAddressSpace);

            //last one wins
            parsedArgs = DefaultParse(new[] { @"/highenTROPyva+", @"/HIGHentropyva-", "a.cs" }, WorkingDirectory);
            Assert.False(parsedArgs.Errors.Any());
            Assert.False(parsedArgs.EmitOptions.HighEntropyVirtualAddressSpace);
        }

        [Fact]
        public void Checked()
        {
            var parsedArgs = DefaultParse(new[] { @"/checked+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.CompilationOptions.CheckOverflow);

            parsedArgs = DefaultParse(new[] { @"/checked-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.CheckOverflow);

            parsedArgs = DefaultParse(new[] { @"/checked", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.CompilationOptions.CheckOverflow);

            parsedArgs = DefaultParse(new[] { @"/checked-", @"/checked", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.CompilationOptions.CheckOverflow);

            parsedArgs = DefaultParse(new[] { @"/checked:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/checked:"));
        }

        [Fact]
        public void Nullable()
        {
            var parsedArgs = DefaultParse(new[] { "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable+", "/langversion:7.0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8630: Invalid 'nullable' value: 'Enabled' for C# 7.0. Please use language version '8.0' or greater.
                Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("nullable", "Enable", "7.0", "8.0").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for 'nullable' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "nullable").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:yes", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'yes' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("yes").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:enable", "/langversion:7.0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8630: Invalid 'nullable' value: 'Enable' for C# 7.0. Please use language version '8.0' or greater.
                Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("nullable", "Enable", "7.0", "8.0").WithLocation(1, 1));
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:disable", "/langversion:7.0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:safeonly", "/langversion:7.0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'safeonly' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("safeonly").WithLocation(1, 1));

            parsedArgs = DefaultParse(new[] { @"/nullable+", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable-", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for 'nullable' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "nullable").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:yes", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'yes' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("yes").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:eNable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:disablE", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Safeonly", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'Safeonly' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("Safeonly").WithLocation(1, 1)
                );

            parsedArgs = DefaultParse(new[] { @"/nullable-", @"/nullable-", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable-", @"/nullable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable-", @"/nullable+", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable-", @"/nullable:", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for 'nullable' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "nullable").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable-", @"/nullable:YES", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'YES' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("YES").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable-", @"/nullable:disable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable-", @"/nullable:enable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable-", @"/nullable:safeonly", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'safeonly' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("safeonly").WithLocation(1, 1)
                );

            parsedArgs = DefaultParse(new[] { @"/nullable+", @"/nullable-", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable+", @"/nullable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable+", @"/nullable+", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable+", @"/nullable:", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for 'nullable' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "nullable").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable+", @"/nullable:YES", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'YES' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("YES").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable+", @"/nullable:disable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable+", @"/nullable:enable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable+", @"/nullable:safeonly", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'safeonly' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("safeonly").WithLocation(1, 1)
                );

            parsedArgs = DefaultParse(new[] { @"/nullable:safeonly", @"/nullable-", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'safeonly' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("safeonly").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:safeonly", @"/nullable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'safeonly' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("safeonly").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:safeonly", @"/nullable+", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'safeonly' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("safeonly").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:safeonly", @"/nullable:", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'safeonly' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("safeonly").WithLocation(1, 1),
                // error CS2006: Command-line syntax error: Missing '<text>' for 'nullable' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "nullable").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:safeonly", @"/nullable:YES", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'safeonly' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("safeonly").WithLocation(1, 1),
                // error CS8636: Invalid option 'YES' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("YES").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:safeonly", @"/nullable:disable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'safeonly' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("safeonly").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:safeonly", @"/nullable:enable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'safeonly' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("safeonly").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:safeonly", @"/nullable:safeonly", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'safeonly' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("safeonly").WithLocation(1, 1),
                // error CS8636: Invalid option 'safeonly' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("safeonly").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:", "/langversion:7.3", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for 'nullable' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "nullable").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:yeS", "/langversion:7.0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'yeS' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("yeS").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable+", "/langversion:7.3", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8630: Invalid 'nullable' value: 'Enable' for C# 7.3. Please use language version '8.0' or greater.
                Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("nullable", "Enable", "7.3", "8.0").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable-", "/langversion:7.0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable", "/langversion:7.3", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8630: Invalid 'nullable' value: 'Enabled' for C# 7.3. Please use language version '8.0' or greater.
                Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("nullable", "Enable", "7.3", "8.0").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:enable", "/langversion:7.3", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8630: Invalid 'nullable' value: 'Enabled' for C# 7.3. Please use language version '8.0' or greater.
                Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("nullable", "Enable", "7.3", "8.0").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:disable", "/langversion:7.0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:safeonly", "/langversion:7.3", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'safeonly' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("safeonly").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { "a.cs", "/langversion:8" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { "a.cs", "/langversion:7.3" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:""safeonly""", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'safeonly' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("safeonly").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:\""enable\""", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option '"enable"' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("\"enable\"").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:\\disable\\", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option '\\disable\\' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("\\\\disable\\\\").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:\\""enable\\""", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option '\enable\' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("\\enable\\").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:safeonlywarnings", "/langversion:7.0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'safeonlywarnings' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("safeonlywarnings").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:SafeonlyWarnings", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'SafeonlyWarnings' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("SafeonlyWarnings").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable-", @"/nullable:safeonlyWarnings", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'safeonlyWarnings' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("safeonlyWarnings").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:warnings", "/langversion:7.0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8630: Invalid 'nullable' value: 'Warnings' for C# 7.0. Please use language version '8.0' or greater.
                Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("nullable", "Warnings", "7.0", "8.0").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Warnings, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Warnings, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable-", @"/nullable:Warnings", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Warnings, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable+", @"/nullable:Warnings", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Warnings, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", @"/nullable-", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", @"/nullable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", @"/nullable+", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", @"/nullable:", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for 'nullable' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "nullable").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Warnings, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", @"/nullable:YES", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'YES' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("YES").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Warnings, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", @"/nullable:disable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", @"/nullable:enable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", @"/nullable:Warnings", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Warnings, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Warnings", "/langversion:7.3", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8630: Invalid 'nullable' value: 'Annotations' for C# 7.3. Please use language version '8.0' or greater.
                Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("nullable", "Warnings", "7.3", "8.0").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Warnings, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:annotations", "/langversion:7.0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8630: Invalid 'nullable' value: 'Annotations' for C# 7.0. Please use language version '8.0' or greater.
                Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("nullable", "Annotations", "7.0", "8.0").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Annotations, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Annotations", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Annotations, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable-", @"/nullable:Annotations", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Annotations, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable+", @"/nullable:Annotations", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Annotations, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Annotations", @"/nullable-", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Annotations", @"/nullable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Annotations", @"/nullable+", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Annotations", @"/nullable:", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for 'nullable' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "nullable").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Annotations, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Annotations", @"/nullable:YES", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8636: Invalid option 'YES' for /nullable; must be 'disable', 'enable', 'warnings' or 'annotations'
                Diagnostic(ErrorCode.ERR_BadNullableContextOption).WithArguments("YES").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Annotations, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Annotations", @"/nullable:disable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Disable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Annotations", @"/nullable:enable", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Enable, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Annotations", @"/nullable:Annotations", "/langversion:8", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(NullableContextOptions.Annotations, parsedArgs.CompilationOptions.NullableContextOptions);

            parsedArgs = DefaultParse(new[] { @"/nullable:Annotations", "/langversion:7.3", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS8630: Invalid 'nullable' value: 'Annotations' for C# 7.3. Please use language version '8.0' or greater.
                Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("nullable", "Annotations", "7.3", "8.0").WithLocation(1, 1)
                );
            Assert.Equal(NullableContextOptions.Annotations, parsedArgs.CompilationOptions.NullableContextOptions);
        }

        [Fact]
        public void Usings()
        {
            CSharpCommandLineArguments parsedArgs;

            var sdkDirectory = SdkDirectory;
            parsedArgs = CSharpCommandLineParser.Script.Parse(new string[] { "/u:Goo.Bar" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            AssertEx.Equal(new[] { "Goo.Bar" }, parsedArgs.CompilationOptions.Usings.AsEnumerable());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new string[] { "/u:Goo.Bar;Baz", "/using:System.Core;System" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            AssertEx.Equal(new[] { "Goo.Bar", "Baz", "System.Core", "System" }, parsedArgs.CompilationOptions.Usings.AsEnumerable());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new string[] { "/u:Goo;;Bar" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify();
            AssertEx.Equal(new[] { "Goo", "Bar" }, parsedArgs.CompilationOptions.Usings.AsEnumerable());

            parsedArgs = CSharpCommandLineParser.Script.Parse(new string[] { "/u:" }, WorkingDirectory, sdkDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<namespace>' for '/u:' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<namespace>", "/u:"));
        }

        [Fact]
        public void WarningsErrors()
        {
            var parsedArgs = DefaultParse(new string[] { "/nowarn", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2035: Command-line syntax error: Missing ':<number>' for 'nowarn' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsNumber).WithArguments("nowarn"));

            parsedArgs = DefaultParse(new string[] { "/nowarn:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2035: Command-line syntax error: Missing ':<number>' for 'nowarn' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsNumber).WithArguments("nowarn"));

            // Previous versions of the compiler used to report a warning (CS1691)
            // whenever an unrecognized warning code was supplied via /nowarn or /warnaserror.
            // We no longer generate a warning in such cases.
            parsedArgs = DefaultParse(new string[] { "/nowarn:-1", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new string[] { "/nowarn:abc", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new string[] { "/warnaserror:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2035: Command-line syntax error: Missing ':<number>' for 'warnaserror' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsNumber).WithArguments("warnaserror"));

            parsedArgs = DefaultParse(new string[] { "/warnaserror:-1", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new string[] { "/warnaserror:70000", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new string[] { "/warnaserror:abc", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new string[] { "/warnaserror+:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2035: Command-line syntax error: Missing ':<number>' for '/warnaserror+:' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsNumber).WithArguments("warnaserror+"));

            parsedArgs = DefaultParse(new string[] { "/warnaserror-:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2035: Command-line syntax error: Missing ':<number>' for '/warnaserror-:' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsNumber).WithArguments("warnaserror-"));

            parsedArgs = DefaultParse(new string[] { "/w", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2035: Command-line syntax error: Missing ':<number>' for '/w' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsNumber).WithArguments("w"));

            parsedArgs = DefaultParse(new string[] { "/w:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2035: Command-line syntax error: Missing ':<number>' for '/w:' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsNumber).WithArguments("w"));

            parsedArgs = DefaultParse(new string[] { "/warn:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2035: Command-line syntax error: Missing ':<number>' for '/warn:' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsNumber).WithArguments("warn"));

            parsedArgs = DefaultParse(new string[] { "/w:-1", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS1900: Warning level must be zero or greater
                Diagnostic(ErrorCode.ERR_BadWarningLevel).WithArguments("w"));

            parsedArgs = DefaultParse(new string[] { "/w:5", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new string[] { "/warn:-1", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS1900: Warning level must be zero or greater
                Diagnostic(ErrorCode.ERR_BadWarningLevel).WithArguments("warn"));

            parsedArgs = DefaultParse(new string[] { "/warn:5", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            // Previous versions of the compiler used to report a warning (CS1691)
            // whenever an unrecognized warning code was supplied via /nowarn or /warnaserror.
            // We no longer generate a warning in such cases.
            parsedArgs = DefaultParse(new string[] { "/warnaserror:1,2,3", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new string[] { "/nowarn:1,2,3", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new string[] { "/nowarn:1;2;;3", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
        }

        private static void AssertSpecificDiagnostics(int[] expectedCodes, ReportDiagnostic[] expectedOptions, CSharpCommandLineArguments args)
        {
            var actualOrdered = args.CompilationOptions.SpecificDiagnosticOptions.OrderBy(entry => entry.Key);

            AssertEx.Equal(
                expectedCodes.Select(i => MessageProvider.Instance.GetIdForErrorCode(i)),
                actualOrdered.Select(entry => entry.Key));

            AssertEx.Equal(expectedOptions, actualOrdered.Select(entry => entry.Value));
        }

        [Fact]
        public void WarningsParse()
        {
            var parsedArgs = DefaultParse(new string[] { "/warnaserror", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Error, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(4, parsedArgs.CompilationOptions.WarningLevel);
            Assert.Equal(0, parsedArgs.CompilationOptions.SpecificDiagnosticOptions.Count);

            parsedArgs = DefaultParse(new string[] { "/warnaserror:1062,1066,1734", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Default, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(4, parsedArgs.CompilationOptions.WarningLevel);
            AssertSpecificDiagnostics(new[] { 1062, 1066, 1734 }, new[] { ReportDiagnostic.Error, ReportDiagnostic.Error, ReportDiagnostic.Error }, parsedArgs);

            parsedArgs = DefaultParse(new string[] { "/warnaserror:+1062,+1066,+1734", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Default, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(4, parsedArgs.CompilationOptions.WarningLevel);
            AssertSpecificDiagnostics(new[] { 1062, 1066, 1734 }, new[] { ReportDiagnostic.Error, ReportDiagnostic.Error, ReportDiagnostic.Error }, parsedArgs);

            parsedArgs = DefaultParse(new string[] { "/warnaserror+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Error, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(4, parsedArgs.CompilationOptions.WarningLevel);
            AssertSpecificDiagnostics(new int[0], new ReportDiagnostic[0], parsedArgs);

            parsedArgs = DefaultParse(new string[] { "/warnaserror+:1062,1066,1734", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Default, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(4, parsedArgs.CompilationOptions.WarningLevel);
            AssertSpecificDiagnostics(new[] { 1062, 1066, 1734 }, new[] { ReportDiagnostic.Error, ReportDiagnostic.Error, ReportDiagnostic.Error }, parsedArgs);

            parsedArgs = DefaultParse(new string[] { "/warnaserror-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Default, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(4, parsedArgs.CompilationOptions.WarningLevel);
            AssertSpecificDiagnostics(new int[0], new ReportDiagnostic[0], parsedArgs);

            parsedArgs = DefaultParse(new string[] { "/warnaserror-:1062,1066,1734", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Default, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(4, parsedArgs.CompilationOptions.WarningLevel);
            AssertSpecificDiagnostics(new[] { 1062, 1066, 1734 }, new[] { ReportDiagnostic.Default, ReportDiagnostic.Default, ReportDiagnostic.Default }, parsedArgs);

            parsedArgs = DefaultParse(new string[] { "/warnaserror+:1062,1066,1734", "/warnaserror-:1762,1974", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Default, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(4, parsedArgs.CompilationOptions.WarningLevel);
            AssertSpecificDiagnostics(
                new[] { 1062, 1066, 1734, 1762, 1974 },
                new[] { ReportDiagnostic.Error, ReportDiagnostic.Error, ReportDiagnostic.Error, ReportDiagnostic.Default, ReportDiagnostic.Default },
                parsedArgs);

            parsedArgs = DefaultParse(new string[] { "/warnaserror+:1062,1066,1734", "/warnaserror-:1062,1974", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Default, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(4, parsedArgs.CompilationOptions.WarningLevel);
            Assert.Equal(4, parsedArgs.CompilationOptions.SpecificDiagnosticOptions.Count);
            AssertSpecificDiagnostics(new[] { 1062, 1066, 1734, 1974 }, new[] { ReportDiagnostic.Default, ReportDiagnostic.Error, ReportDiagnostic.Error, ReportDiagnostic.Default }, parsedArgs);

            parsedArgs = DefaultParse(new string[] { "/warnaserror-:1062,1066,1734", "/warnaserror+:1062,1974", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Default, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(4, parsedArgs.CompilationOptions.WarningLevel);
            AssertSpecificDiagnostics(new[] { 1062, 1066, 1734, 1974 }, new[] { ReportDiagnostic.Error, ReportDiagnostic.Default, ReportDiagnostic.Default, ReportDiagnostic.Error }, parsedArgs);

            parsedArgs = DefaultParse(new string[] { "/w:1", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Default, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(1, parsedArgs.CompilationOptions.WarningLevel);
            AssertSpecificDiagnostics(new int[0], new ReportDiagnostic[0], parsedArgs);

            parsedArgs = DefaultParse(new string[] { "/warn:1", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Default, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(1, parsedArgs.CompilationOptions.WarningLevel);
            AssertSpecificDiagnostics(new int[0], new ReportDiagnostic[0], parsedArgs);

            parsedArgs = DefaultParse(new string[] { "/warn:1", "/warnaserror+:1062,1974", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Default, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(1, parsedArgs.CompilationOptions.WarningLevel);
            AssertSpecificDiagnostics(new[] { 1062, 1974 }, new[] { ReportDiagnostic.Error, ReportDiagnostic.Error }, parsedArgs);

            parsedArgs = DefaultParse(new string[] { "/nowarn:1062,1066,1734", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Default, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(4, parsedArgs.CompilationOptions.WarningLevel);
            AssertSpecificDiagnostics(new[] { 1062, 1066, 1734 }, new[] { ReportDiagnostic.Suppress, ReportDiagnostic.Suppress, ReportDiagnostic.Suppress }, parsedArgs);

            parsedArgs = DefaultParse(new string[] { @"/nowarn:""1062 1066 1734""", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Default, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(4, parsedArgs.CompilationOptions.WarningLevel);
            AssertSpecificDiagnostics(new[] { 1062, 1066, 1734 }, new[] { ReportDiagnostic.Suppress, ReportDiagnostic.Suppress, ReportDiagnostic.Suppress }, parsedArgs);

            parsedArgs = DefaultParse(new string[] { "/nowarn:1062,1066,1734", "/warnaserror:1066,1762", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Default, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(4, parsedArgs.CompilationOptions.WarningLevel);
            AssertSpecificDiagnostics(new[] { 1062, 1066, 1734, 1762 }, new[] { ReportDiagnostic.Suppress, ReportDiagnostic.Suppress, ReportDiagnostic.Suppress, ReportDiagnostic.Error }, parsedArgs);

            parsedArgs = DefaultParse(new string[] { "/warnaserror:1066,1762", "/nowarn:1062,1066,1734", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ReportDiagnostic.Default, parsedArgs.CompilationOptions.GeneralDiagnosticOption);
            Assert.Equal(4, parsedArgs.CompilationOptions.WarningLevel);
            AssertSpecificDiagnostics(new[] { 1062, 1066, 1734, 1762 }, new[] { ReportDiagnostic.Suppress, ReportDiagnostic.Suppress, ReportDiagnostic.Suppress, ReportDiagnostic.Error }, parsedArgs);
        }

        [Fact]
        public void AllowUnsafe()
        {
            CSharpCommandLineArguments parsedArgs = DefaultParse(new[] { "/unsafe", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.CompilationOptions.AllowUnsafe);

            parsedArgs = DefaultParse(new[] { "/unsafe+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.CompilationOptions.AllowUnsafe);

            parsedArgs = DefaultParse(new[] { "/UNSAFE-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.AllowUnsafe);

            parsedArgs = DefaultParse(new[] { "/unsafe-", "/unsafe+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.CompilationOptions.AllowUnsafe);

            parsedArgs = DefaultParse(new[] { "a.cs" }, WorkingDirectory); // default
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.AllowUnsafe);

            parsedArgs = DefaultParse(new[] { "/unsafe:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/unsafe:"));

            parsedArgs = DefaultParse(new[] { "/unsafe:+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/unsafe:+"));

            parsedArgs = DefaultParse(new[] { "/unsafe-:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/unsafe-:"));
        }

        [Fact]
        public void DelaySign()
        {
            CSharpCommandLineArguments parsedArgs;

            parsedArgs = DefaultParse(new[] { "/delaysign", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.NotNull(parsedArgs.CompilationOptions.DelaySign);
            Assert.True((bool)parsedArgs.CompilationOptions.DelaySign);

            parsedArgs = DefaultParse(new[] { "/delaysign+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.NotNull(parsedArgs.CompilationOptions.DelaySign);
            Assert.True((bool)parsedArgs.CompilationOptions.DelaySign);

            parsedArgs = DefaultParse(new[] { "/DELAYsign-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.NotNull(parsedArgs.CompilationOptions.DelaySign);
            Assert.False((bool)parsedArgs.CompilationOptions.DelaySign);

            parsedArgs = DefaultParse(new[] { "/delaysign:-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2007: Unrecognized option: '/delaysign:-'
                Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/delaysign:-"));

            Assert.Null(parsedArgs.CompilationOptions.DelaySign);
        }

        [Fact]
        public void PublicSign()
        {
            var parsedArgs = DefaultParse(new[] { "/publicsign", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.CompilationOptions.PublicSign);

            parsedArgs = DefaultParse(new[] { "/publicsign+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.CompilationOptions.PublicSign);

            parsedArgs = DefaultParse(new[] { "/PUBLICsign-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.CompilationOptions.PublicSign);

            parsedArgs = DefaultParse(new[] { "/publicsign:-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2007: Unrecognized option: '/publicsign:-'
                Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/publicsign:-").WithLocation(1, 1));

            Assert.False(parsedArgs.CompilationOptions.PublicSign);
        }

        [WorkItem(8360, "https://github.com/dotnet/roslyn/issues/8360")]
        [Fact]
        public void PublicSign_KeyFileRelativePath()
        {
            var parsedArgs = DefaultParse(new[] { "/publicsign", "/keyfile:test.snk", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(Path.Combine(WorkingDirectory, "test.snk"), parsedArgs.CompilationOptions.CryptoKeyFile);
        }

        [Fact]
        [WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")]
        public void PublicSignWithEmptyKeyPath()
        {
            DefaultParse(new[] { "/publicsign", "/keyfile:", "a.cs" }, WorkingDirectory).Errors.Verify(
                // error CS2005: Missing file specification for 'keyfile' option
                Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("keyfile").WithLocation(1, 1));
        }

        [Fact]
        [WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")]
        public void PublicSignWithEmptyKeyPath2()
        {
            DefaultParse(new[] { "/publicsign", "/keyfile:\"\"", "a.cs" }, WorkingDirectory).Errors.Verify(
                // error CS2005: Missing file specification for 'keyfile' option
                Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("keyfile").WithLocation(1, 1));
        }

        [WorkItem(546301, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546301")]
        [Fact]
        public void SubsystemVersionTests()
        {
            CSharpCommandLineArguments parsedArgs = DefaultParse(new[] { "/subsystemversion:4.0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(SubsystemVersion.Create(4, 0), parsedArgs.EmitOptions.SubsystemVersion);

            // wrongly supported subsystem version. CompilationOptions data will be faithful to the user input.
            // It is normalized at the time of emit.
            parsedArgs = DefaultParse(new[] { "/subsystemversion:0.0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(); // no error in Dev11
            Assert.Equal(SubsystemVersion.Create(0, 0), parsedArgs.EmitOptions.SubsystemVersion);

            parsedArgs = DefaultParse(new[] { "/subsystemversion:0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(); // no error in Dev11
            Assert.Equal(SubsystemVersion.Create(0, 0), parsedArgs.EmitOptions.SubsystemVersion);

            parsedArgs = DefaultParse(new[] { "/subsystemversion:3.99", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(); // no error in Dev11
            Assert.Equal(SubsystemVersion.Create(3, 99), parsedArgs.EmitOptions.SubsystemVersion);

            parsedArgs = DefaultParse(new[] { "/subsystemversion:4.0", "/SUBsystemversion:5.333", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(SubsystemVersion.Create(5, 333), parsedArgs.EmitOptions.SubsystemVersion);

            parsedArgs = DefaultParse(new[] { "/subsystemversion:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "subsystemversion"));

            parsedArgs = DefaultParse(new[] { "/subsystemversion", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "subsystemversion"));

            parsedArgs = DefaultParse(new[] { "/subsystemversion-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/subsystemversion-"));

            parsedArgs = DefaultParse(new[] { "/subsystemversion: ", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "subsystemversion"));

            parsedArgs = DefaultParse(new[] { "/subsystemversion: 4.1", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_InvalidSubsystemVersion).WithArguments(" 4.1"));

            parsedArgs = DefaultParse(new[] { "/subsystemversion:4 .0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_InvalidSubsystemVersion).WithArguments("4 .0"));

            parsedArgs = DefaultParse(new[] { "/subsystemversion:4. 0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_InvalidSubsystemVersion).WithArguments("4. 0"));

            parsedArgs = DefaultParse(new[] { "/subsystemversion:.", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_InvalidSubsystemVersion).WithArguments("."));

            parsedArgs = DefaultParse(new[] { "/subsystemversion:4.", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_InvalidSubsystemVersion).WithArguments("4."));

            parsedArgs = DefaultParse(new[] { "/subsystemversion:.0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_InvalidSubsystemVersion).WithArguments(".0"));

            parsedArgs = DefaultParse(new[] { "/subsystemversion:4.2 ", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new[] { "/subsystemversion:4.65536", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_InvalidSubsystemVersion).WithArguments("4.65536"));

            parsedArgs = DefaultParse(new[] { "/subsystemversion:65536.0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_InvalidSubsystemVersion).WithArguments("65536.0"));

            parsedArgs = DefaultParse(new[] { "/subsystemversion:-4.0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_InvalidSubsystemVersion).WithArguments("-4.0"));

            // TODO: incompatibilities: versions lower than '6.2' and 'arm', 'winmdobj', 'appcontainer'
        }

        [Fact]
        public void MainType()
        {
            CSharpCommandLineArguments parsedArgs = DefaultParse(new[] { "/m:A.B.C", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("A.B.C", parsedArgs.CompilationOptions.MainTypeName);

            parsedArgs = DefaultParse(new[] { "/m: ", "a.cs" }, WorkingDirectory); // Mimicking Dev11
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "m"));
            Assert.Null(parsedArgs.CompilationOptions.MainTypeName);

            //  overriding the value
            parsedArgs = DefaultParse(new[] { "/m:A.B.C", "/MAIN:X.Y.Z", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("X.Y.Z", parsedArgs.CompilationOptions.MainTypeName);

            //  error
            parsedArgs = DefaultParse(new[] { "/maiN:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "main"));

            parsedArgs = DefaultParse(new[] { "/MAIN+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/MAIN+"));

            parsedArgs = DefaultParse(new[] { "/M", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "m"));

            //  incompatible values /main && /target
            parsedArgs = DefaultParse(new[] { "/main:a", "/t:library", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoMainOnDLL));

            parsedArgs = DefaultParse(new[] { "/main:a", "/t:module", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoMainOnDLL));
        }

        [Fact]
        public void Codepage()
        {
            CSharpCommandLineArguments parsedArgs = DefaultParse(new[] { "/CodePage:1200", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("Unicode", parsedArgs.Encoding.EncodingName);

            parsedArgs = DefaultParse(new[] { "/CodePage:1200", "/codePAGE:65001", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("Unicode (UTF-8)", parsedArgs.Encoding.EncodingName);

            //  error
            parsedArgs = DefaultParse(new[] { "/codepage:0", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.FTL_BadCodepage).WithArguments("0"));

            parsedArgs = DefaultParse(new[] { "/codepage:abc", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.FTL_BadCodepage).WithArguments("abc"));

            parsedArgs = DefaultParse(new[] { "/codepage:-5", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.FTL_BadCodepage).WithArguments("-5"));

            parsedArgs = DefaultParse(new[] { "/codepage: ", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.FTL_BadCodepage).WithArguments(""));

            parsedArgs = DefaultParse(new[] { "/codepage:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.FTL_BadCodepage).WithArguments(""));

            parsedArgs = DefaultParse(new[] { "/codepage", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "codepage"));

            parsedArgs = DefaultParse(new[] { "/codepage+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/codepage+"));
        }

        [Fact, WorkItem(24735, "https://github.com/dotnet/roslyn/issues/24735")]
        public void ChecksumAlgorithm()
        {
            CSharpCommandLineArguments parsedArgs = DefaultParse(new[] { "/checksumAlgorithm:sHa1", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(SourceHashAlgorithm.Sha1, parsedArgs.ChecksumAlgorithm);
            Assert.Equal(HashAlgorithmName.SHA256, parsedArgs.EmitOptions.PdbChecksumAlgorithm);

            parsedArgs = DefaultParse(new[] { "/checksumAlgorithm:sha256", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(SourceHashAlgorithm.Sha256, parsedArgs.ChecksumAlgorithm);
            Assert.Equal(HashAlgorithmName.SHA256, parsedArgs.EmitOptions.PdbChecksumAlgorithm);

            parsedArgs = DefaultParse(new[] { "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            Assert.Equal(SourceHashAlgorithm.Sha256, parsedArgs.ChecksumAlgorithm);
            Assert.Equal(HashAlgorithmName.SHA256, parsedArgs.EmitOptions.PdbChecksumAlgorithm);

            //  error
            parsedArgs = DefaultParse(new[] { "/checksumAlgorithm:256", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.FTL_BadChecksumAlgorithm).WithArguments("256"));

            parsedArgs = DefaultParse(new[] { "/checksumAlgorithm:sha-1", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.FTL_BadChecksumAlgorithm).WithArguments("sha-1"));

            parsedArgs = DefaultParse(new[] { "/checksumAlgorithm:sha", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.FTL_BadChecksumAlgorithm).WithArguments("sha"));

            parsedArgs = DefaultParse(new[] { "/checksumAlgorithm: ", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "checksumalgorithm"));

            parsedArgs = DefaultParse(new[] { "/checksumAlgorithm:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "checksumalgorithm"));

            parsedArgs = DefaultParse(new[] { "/checksumAlgorithm", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "checksumalgorithm"));

            parsedArgs = DefaultParse(new[] { "/checksumAlgorithm+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/checksumAlgorithm+"));
        }

        [Fact]
        public void AddModule()
        {
            CSharpCommandLineArguments parsedArgs = DefaultParse(new[] { "/noconfig", "/nostdlib", "/addmodule:abc.netmodule", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(1, parsedArgs.MetadataReferences.Length);
            Assert.Equal("abc.netmodule", parsedArgs.MetadataReferences[0].Reference);
            Assert.Equal(MetadataImageKind.Module, parsedArgs.MetadataReferences[0].Properties.Kind);

            parsedArgs = DefaultParse(new[] { "/noconfig", "/nostdlib", "/aDDmodule:c:\\abc;c:\\abc;d:\\xyz", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(3, parsedArgs.MetadataReferences.Length);
            Assert.Equal("c:\\abc", parsedArgs.MetadataReferences[0].Reference);
            Assert.Equal(MetadataImageKind.Module, parsedArgs.MetadataReferences[0].Properties.Kind);
            Assert.Equal("c:\\abc", parsedArgs.MetadataReferences[1].Reference);
            Assert.Equal(MetadataImageKind.Module, parsedArgs.MetadataReferences[1].Properties.Kind);
            Assert.Equal("d:\\xyz", parsedArgs.MetadataReferences[2].Reference);
            Assert.Equal(MetadataImageKind.Module, parsedArgs.MetadataReferences[2].Properties.Kind);

            //  error
            parsedArgs = DefaultParse(new[] { "/ADDMODULE", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "/addmodule:"));

            parsedArgs = DefaultParse(new[] { "/ADDMODULE+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/ADDMODULE+"));

            parsedArgs = DefaultParse(new[] { "/ADDMODULE:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/ADDMODULE:"));
        }

        [Fact, WorkItem(530751, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530751")]
        public void CS7061fromCS0647_ModuleWithCompilationRelaxations()
        {
            string source1 = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"
using System.Runtime.CompilerServices;
[assembly: CompilationRelaxations(CompilationRelaxations.NoStringInterning)]
public class Mod { }").Path;

            string source2 = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"
using System.Runtime.CompilerServices;
[assembly: CompilationRelaxations(4)]
public class Mod { }").Path;

            string source = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"
using System.Runtime.CompilerServices;
[assembly: CompilationRelaxations(CompilationRelaxations.NoStringInterning)]
class Test { static void Main() {} }").Path;

            var baseDir = Path.GetDirectoryName(source);
            // === Scenario 1 ===
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, baseDir, new[] { "/nologo", "/t:module", source1 }).Run(outWriter);
            Assert.Equal(0, exitCode);

            var modfile = source1.Substring(0, source1.Length - 2) + "netmodule";
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var parsedArgs = DefaultParse(new[] { "/nologo", "/addmodule:" + modfile, source }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            exitCode = CreateCSharpCompiler(null, baseDir, new[] { "/nologo", "/addmodule:" + modfile, source }).Run(outWriter);
            Assert.Empty(outWriter.ToString());

            // === Scenario 2 ===
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, baseDir, new[] { "/nologo", "/t:module", source2 }).Run(outWriter);
            Assert.Equal(0, exitCode);

            modfile = source2.Substring(0, source2.Length - 2) + "netmodule";
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            parsedArgs = DefaultParse(new[] { "/nologo", "/addmodule:" + modfile, source }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            exitCode = CreateCSharpCompiler(null, baseDir, new[] { "/nologo", "/preferreduilang:en", "/addmodule:" + modfile, source }).Run(outWriter);
            Assert.Equal(1, exitCode);
            // Dev11: CS0647 (Emit)
            Assert.Contains("error CS7061: Duplicate 'CompilationRelaxationsAttribute' attribute in", outWriter.ToString(), StringComparison.Ordinal);

            CleanupAllGeneratedFiles(source1);
            CleanupAllGeneratedFiles(source2);
            CleanupAllGeneratedFiles(source);
        }

        [Fact, WorkItem(530780, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530780")]
        public void AddModuleWithExtensionMethod()
        {
            string source1 = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"public static class Extensions { public static bool EB(this bool b) { return b; } }").Path;
            string source2 = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"class C { static void Main() {} }").Path;
            var baseDir = Path.GetDirectoryName(source2);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, baseDir, new[] { "/nologo", "/t:module", source1 }).Run(outWriter);
            Assert.Equal(0, exitCode);

            var modfile = source1.Substring(0, source1.Length - 2) + "netmodule";
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, baseDir, new[] { "/nologo", "/addmodule:" + modfile, source2 }).Run(outWriter);
            Assert.Equal(0, exitCode);

            CleanupAllGeneratedFiles(source1);
            CleanupAllGeneratedFiles(source2);
        }

        [Fact, WorkItem(546297, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546297")]
        public void OLDCS0013FTL_MetadataEmitFailureSameModAndRes()
        {
            string source1 = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"class Mod { }").Path;
            string source2 = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"class C { static void Main() {} }").Path;
            var baseDir = Path.GetDirectoryName(source2);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, baseDir, new[] { "/nologo", "/t:module", source1 }).Run(outWriter);
            Assert.Equal(0, exitCode);

            var modfile = source1.Substring(0, source1.Length - 2) + "netmodule";
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, baseDir, new[] { "/nologo", "/preferreduilang:en", "/addmodule:" + modfile, "/linkres:" + modfile, source2 }).Run(outWriter);
            Assert.Equal(1, exitCode);
            // Native gives CS0013 at emit stage
            Assert.Equal("error CS7041: Each linked resource and module must have a unique filename. Filename '" + Path.GetFileName(modfile) + "' is specified more than once in this assembly", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(source1);
            CleanupAllGeneratedFiles(source2);
        }

        [Fact]
        public void Utf8Output()
        {
            CSharpCommandLineArguments parsedArgs = DefaultParse(new[] { "/utf8output", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True((bool)parsedArgs.Utf8Output);

            parsedArgs = DefaultParse(new[] { "/utf8output", "/utf8output", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True((bool)parsedArgs.Utf8Output);

            parsedArgs = DefaultParse(new[] { "/utf8output:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/utf8output:"));
        }

        [Fact]
        public void CscCompile_WithSourceCodeRedirectedViaStandardInput_ProducesRunnableProgram()
        {
            string tempDir = Temp.CreateDirectory().Path;
            ProcessResult result = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
                ProcessUtilities.Run("cmd", $@"/C echo  ^
class A                                                 ^
{{                                                      ^
    public static void Main() =^^^>                     ^
        System.Console.WriteLine(""Hello World!"");     ^
}} | {s_CSharpCompilerExecutable} /nologo /t:exe -"
    .Replace(Environment.NewLine, string.Empty), workingDirectory: tempDir) :
                ProcessUtilities.Run("/usr/bin/env", $@"sh -c ""echo  \
class A                                                               \
{{                                                                    \
    public static void Main\(\) =\>                                   \
        System.Console.WriteLine\(\\\""Hello World\!\\\""\)\;         \
}} | {s_CSharpCompilerExecutable} /nologo /t:exe -""", workingDirectory: tempDir,
                    // we are testing shell's piped/redirected stdin behavior explicitly
                    // instead of using Process.StandardInput.Write(), so we set
                    // redirectStandardInput to true, which implies that isatty of child
                    // process is false and thereby Console.IsInputRedirected will return
                    // true in csc code.
                    redirectStandardInput: true);

            Assert.False(result.ContainsErrors, $"Compilation error(s) occurred: {result.Output} {result.Errors}");

            string output = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
                ProcessUtilities.RunAndGetOutput("cmd.exe", $@"/C ""{s_DotnetCscRun} -.exe""", expectedRetCode: 0, startFolder: tempDir) :
                ProcessUtilities.RunAndGetOutput("sh", $@"-c ""{s_DotnetCscRun} -.exe""", expectedRetCode: 0, startFolder: tempDir);

            Assert.Equal("Hello World!", output.Trim());
        }

        [Fact]
        public void CscCompile_WithSourceCodeRedirectedViaStandardInput_ProducesLibrary()
        {
            var name = Guid.NewGuid().ToString() + ".dll";
            string tempDir = Temp.CreateDirectory().Path;
            ProcessResult result = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
                ProcessUtilities.Run("cmd", $@"/C echo  ^
class A                                                 ^
{{                                                      ^
    public A Get() =^^^> default;                       ^
}} | {s_CSharpCompilerExecutable} /nologo /t:library /out:{name} -"
    .Replace(Environment.NewLine, string.Empty), workingDirectory: tempDir) :
                ProcessUtilities.Run("/usr/bin/env", $@"sh -c ""echo  \
class A                                                               \
{{                                                                    \
    public A Get\(\) =\> default\;                                    \
}} | {s_CSharpCompilerExecutable} /nologo /t:library /out:{name} -""", workingDirectory: tempDir,
                    // we are testing shell's piped/redirected stdin behavior explicitly
                    // instead of using Process.StandardInput.Write(), so we set
                    // redirectStandardInput to true, which implies that isatty of child
                    // process is false and thereby Console.IsInputRedirected will return
                    // true in csc code.
                    redirectStandardInput: true);

            Assert.False(result.ContainsErrors, $"Compilation error(s) occurred: {result.Output} {result.Errors}");

            var assemblyName = AssemblyName.GetAssemblyName(Path.Combine(tempDir, name));
            Assert.Equal(name.Replace(".dll", ", Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"),
                assemblyName.ToString());
        }

        [Fact(Skip = "https://github.com/dotnet/roslyn/issues/55727")]
        public void CsiScript_WithSourceCodeRedirectedViaStandardInput_ExecutesNonInteractively()
        {
            string tempDir = Temp.CreateDirectory().Path;
            ProcessResult result = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
                ProcessUtilities.Run("cmd", $@"/C echo Console.WriteLine(""Hello World!"") | {s_CSharpScriptExecutable} -") :
                ProcessUtilities.Run("/usr/bin/env", $@"sh -c ""echo Console.WriteLine\(\\\""Hello World\!\\\""\) | {s_CSharpScriptExecutable} -""",
                workingDirectory: tempDir,
                    // we are testing shell's piped/redirected stdin behavior explicitly
                    // instead of using Process.StandardInput.Write(), so we set
                    // redirectStandardInput to true, which implies that isatty of child
                    // process is false and thereby Console.IsInputRedirected will return
                    // true in csc code.
                    redirectStandardInput: true);

            Assert.False(result.ContainsErrors, $"Compilation error(s) occurred: {result.Output} {result.Errors}");
            Assert.Equal("Hello World!", result.Output.Trim());
        }

        [Fact]
        public void CscCompile_WithRedirectedInputIndicatorAndStandardInputNotRedirected_ReportsCS8782()
        {
            if (Console.IsInputRedirected)
            {
                // [applicable to both Windows and Unix]
                // if our parent (xunit) process itself has input redirected, we cannot test this
                // error case because our child process will inherit it and we cannot achieve what
                // we are aiming for: isatty(0):true and thereby Console.IsInputerRedirected:false in
                // child. running this case will make StreamReader to hang (waiting for input, that
                // we do not propagate: parent.In->child.In).
                //
                // note: in Unix we can "close" fd0 by appending `0>&-` in the `sh -c` command below,
                // but that will also not impact the result of isatty(), and in turn causes a different
                // compiler error.
                return;
            }

            string tempDir = Temp.CreateDirectory().Path;
            ProcessResult result = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
                ProcessUtilities.Run("cmd", $@"/C ""{s_CSharpCompilerExecutable} /nologo /t:exe -""", workingDirectory: tempDir) :
                ProcessUtilities.Run("/usr/bin/env", $@"sh -c ""{s_CSharpCompilerExecutable} /nologo /t:exe -""", workingDirectory: tempDir);

            Assert.True(result.ContainsErrors);
            Assert.Contains(((int)ErrorCode.ERR_StdInOptionProvidedButConsoleInputIsNotRedirected).ToString(), result.Output);
        }

        [Fact]
        public void CscCompile_WithMultipleStdInOperators_WarnsCS2002()
        {
            string tempDir = Temp.CreateDirectory().Path;
            ProcessResult result = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
                ProcessUtilities.Run("cmd", $@"/C echo  ^
class A                                                 ^
{{                                                      ^
    public static void Main() =^^^>                     ^
        System.Console.WriteLine(""Hello World!"");     ^
}} | {s_CSharpCompilerExecutable} /nologo - /t:exe -"
    .Replace(Environment.NewLine, string.Empty)) :
                ProcessUtilities.Run("/usr/bin/env", $@"sh -c ""echo  \
class A                                                               \
{{                                                                    \
    public static void Main\(\) =\>                                   \
        System.Console.WriteLine\(\\\""Hello World\!\\\""\)\;         \
}} | {s_CSharpCompilerExecutable} /nologo - /t:exe -""", workingDirectory: tempDir,
                    // we are testing shell's piped/redirected stdin behavior explicitly
                    // instead of using Process.StandardInput.Write(), so we set
                    // redirectStandardInput to true, which implies that isatty of child
                    // process is false and thereby Console.IsInputRedirected will return
                    // true in csc code.
                    redirectStandardInput: true);

            Assert.Contains(((int)ErrorCode.WRN_FileAlreadyIncluded).ToString(), result.Output);
        }

        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void CscUtf8Output_WithRedirecting_Off()
        {
            var srcFile = Temp.CreateFile().WriteAllText("\u265A").Path;

            var tempOut = Temp.CreateFile();

            var output = ProcessUtilities.RunAndGetOutput("cmd", "/C \"" + s_CSharpCompilerExecutable + "\" /nologo /preferreduilang:en /t:library " + srcFile + " > " + tempOut.Path, expectedRetCode: 1);
            Assert.Equal("", output.Trim());
            Assert.Equal("SRC.CS(1,1): error CS1056: Unexpected character '?'", tempOut.ReadAllText().Trim().Replace(srcFile, "SRC.CS"));

            CleanupAllGeneratedFiles(srcFile);
            CleanupAllGeneratedFiles(tempOut.Path);
        }

        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void CscUtf8Output_WithRedirecting_On()
        {
            var srcFile = Temp.CreateFile().WriteAllText("\u265A").Path;

            var tempOut = Temp.CreateFile();

            var output = ProcessUtilities.RunAndGetOutput("cmd", "/C \"" + s_CSharpCompilerExecutable + "\" /utf8output /nologo /preferreduilang:en /t:library " + srcFile + " > " + tempOut.Path, expectedRetCode: 1);
            Assert.Equal("", output.Trim());
            Assert.Equal("SRC.CS(1,1): error CS1056: Unexpected character '♚'", tempOut.ReadAllText().Trim().Replace(srcFile, "SRC.CS"));

            CleanupAllGeneratedFiles(srcFile);
            CleanupAllGeneratedFiles(tempOut.Path);
        }

        [WorkItem(546653, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546653")]
        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void NoSourcesWithModule()
        {
            var folder = Temp.CreateDirectory();
            var aCs = folder.CreateFile("a.cs");
            aCs.WriteAllText("public class C {}");

            var output = ProcessUtilities.RunAndGetOutput(s_CSharpCompilerExecutable, $"/nologo /t:module /out:a.netmodule \"{aCs}\"", startFolder: folder.ToString());
            Assert.Equal("", output.Trim());

            output = ProcessUtilities.RunAndGetOutput(s_CSharpCompilerExecutable, "/nologo /t:library /out:b.dll /addmodule:a.netmodule ", startFolder: folder.ToString());
            Assert.Equal("", output.Trim());

            output = ProcessUtilities.RunAndGetOutput(s_CSharpCompilerExecutable, "/nologo /preferreduilang:en /t:module /out:b.dll /addmodule:a.netmodule ", startFolder: folder.ToString());
            Assert.Equal("warning CS2008: No source files specified.", output.Trim());

            CleanupAllGeneratedFiles(aCs.Path);
        }

        [WorkItem(546653, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546653")]
        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void NoSourcesWithResource()
        {
            var folder = Temp.CreateDirectory();
            var aCs = folder.CreateFile("a.cs");
            aCs.WriteAllText("public class C {}");

            var output = ProcessUtilities.RunAndGetOutput(s_CSharpCompilerExecutable, "/nologo /t:library /out:b.dll /resource:a.cs", startFolder: folder.ToString());
            Assert.Equal("", output.Trim());

            CleanupAllGeneratedFiles(aCs.Path);
        }

        [WorkItem(546653, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546653")]
        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void NoSourcesWithLinkResource()
        {
            var folder = Temp.CreateDirectory();
            var aCs = folder.CreateFile("a.cs");
            aCs.WriteAllText("public class C {}");

            var output = ProcessUtilities.RunAndGetOutput(s_CSharpCompilerExecutable, "/nologo /t:library /out:b.dll /linkresource:a.cs", startFolder: folder.ToString());
            Assert.Equal("", output.Trim());

            CleanupAllGeneratedFiles(aCs.Path);
        }

        [Fact]
        public void KeyContainerAndKeyFile()
        {
            // KEYCONTAINER
            CSharpCommandLineArguments parsedArgs = DefaultParse(new[] { "/keycontainer:RIPAdamYauch", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("RIPAdamYauch", parsedArgs.CompilationOptions.CryptoKeyContainer);

            parsedArgs = DefaultParse(new[] { "/keycontainer", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for 'keycontainer' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "keycontainer"));
            Assert.Null(parsedArgs.CompilationOptions.CryptoKeyContainer);

            parsedArgs = DefaultParse(new[] { "/keycontainer-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2007: Unrecognized option: '/keycontainer-'
                Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/keycontainer-"));
            Assert.Null(parsedArgs.CompilationOptions.CryptoKeyContainer);

            parsedArgs = DefaultParse(new[] { "/keycontainer:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for 'keycontainer' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "keycontainer"));
            Assert.Null(parsedArgs.CompilationOptions.CryptoKeyContainer);

            parsedArgs = DefaultParse(new[] { "/keycontainer: ", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "keycontainer"));
            Assert.Null(parsedArgs.CompilationOptions.CryptoKeyContainer);

            // KEYFILE
            parsedArgs = DefaultParse(new[] { @"/keyfile:\somepath\s""ome Fil""e.goo.bar", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            //EDMAURER let's not set the option in the event that there was an error.
            //Assert.Equal(@"\somepath\some File.goo.bar", parsedArgs.CompilationOptions.CryptoKeyFile);

            parsedArgs = DefaultParse(new[] { "/keyFile", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2005: Missing file specification for 'keyfile' option
                Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("keyfile"));
            Assert.Null(parsedArgs.CompilationOptions.CryptoKeyFile);

            parsedArgs = DefaultParse(new[] { "/keyFile: ", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("keyfile"));
            Assert.Null(parsedArgs.CompilationOptions.CryptoKeyFile);

            parsedArgs = DefaultParse(new[] { "/keyfile-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS2007: Unrecognized option: '/keyfile-'
                Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/keyfile-"));
            Assert.Null(parsedArgs.CompilationOptions.CryptoKeyFile);

            // DEFAULTS
            parsedArgs = DefaultParse(new[] { "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Null(parsedArgs.CompilationOptions.CryptoKeyFile);
            Assert.Null(parsedArgs.CompilationOptions.CryptoKeyContainer);

            // KEYFILE | KEYCONTAINER conflicts
            parsedArgs = DefaultParse(new[] { "/keyFile:a", "/keyContainer:b", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("a", parsedArgs.CompilationOptions.CryptoKeyFile);
            Assert.Equal("b", parsedArgs.CompilationOptions.CryptoKeyContainer);

            parsedArgs = DefaultParse(new[] { "/keyContainer:b", "/keyFile:a", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("a", parsedArgs.CompilationOptions.CryptoKeyFile);
            Assert.Equal("b", parsedArgs.CompilationOptions.CryptoKeyContainer);
        }

        [Fact, WorkItem(554551, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/554551")]
        public void CS1698WRN_AssumedMatchThis()
        {
            // compile with: /target:library /keyfile:mykey.snk
            var text1 = @"[assembly:System.Reflection.AssemblyVersion(""2"")]
public class CS1698_a {}
";
            // compile with: /target:library /reference:CS1698_a.dll /keyfile:mykey.snk
            var text2 = @"public class CS1698_b : CS1698_a {}
";
            //compile with: /target:library /out:cs1698_a.dll /reference:cs1698_b.dll /keyfile:mykey.snk
            var text = @"[assembly:System.Reflection.AssemblyVersion(""3"")]
public class CS1698_c : CS1698_b {}
public class CS1698_a {}
";

            var folder = Temp.CreateDirectory();
            var cs1698a = folder.CreateFile("CS1698a.cs");
            cs1698a.WriteAllText(text1);

            var cs1698b = folder.CreateFile("CS1698b.cs");
            cs1698b.WriteAllText(text2);

            var cs1698 = folder.CreateFile("CS1698.cs");
            cs1698.WriteAllText(text);

            var snkFile = Temp.CreateFile().WriteAllBytes(TestResources.General.snKey);
            var kfile = "/keyfile:" + snkFile.Path;

            CSharpCommandLineArguments parsedArgs = DefaultParse(new[] { "/t:library", kfile, "CS1698a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new[] { "/t:library", kfile, "/r:" + cs1698a.Path, "CS1698b.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();

            parsedArgs = DefaultParse(new[] { "/t:library", kfile, "/r:" + cs1698b.Path, "/out:" + cs1698a.Path, "CS1698.cs" }, WorkingDirectory);

            // Roslyn no longer generates a warning for this...since this was only a warning, we're not really
            // saving anyone...does not provide high value to implement...

            // warning CS1698: Circular assembly reference 'CS1698a, Version=2.0.0.0, Culture=neutral,PublicKeyToken = 9e9d6755e7bb4c10'
            // does not match the output assembly name 'CS1698a, Version = 3.0.0.0, Culture = neutral, PublicKeyToken = 9e9d6755e7bb4c10'.
            // Try adding a reference to 'CS1698a, Version = 2.0.0.0, Culture = neutral, PublicKeyToken = 9e9d6755e7bb4c10' or changing the output assembly name to match.
            parsedArgs.Errors.Verify();

            CleanupAllGeneratedFiles(snkFile.Path);
            CleanupAllGeneratedFiles(cs1698a.Path);
            CleanupAllGeneratedFiles(cs1698b.Path);
            CleanupAllGeneratedFiles(cs1698.Path);
        }

        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30926")]
        public void BinaryFileErrorTest()
        {
            var binaryPath = Temp.CreateFile().WriteAllBytes(ResourcesNet451.mscorlib).Path;
            var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", binaryPath });
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = csc.Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Equal(
                "error CS2015: '" + binaryPath + "' is a binary file instead of a text file",
                outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(binaryPath);
        }

#if !NETCOREAPP
        [WorkItem(530221, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530221")]
        [WorkItem(5660, "https://github.com/dotnet/roslyn/issues/5660")]
        [ConditionalFact(typeof(WindowsOnly), typeof(IsEnglishLocal))]
        public void Bug15538()
        {
            // Several Jenkins VMs are still running with local systems permissions.  This suite won't run properly
            // in that environment.  Removing this check is being tracked by issue #79.
            using (var identity = System.Security.Principal.WindowsIdentity.GetCurrent())
            {
                if (identity.IsSystem)
                {
                    return;
                }

                // The icacls command fails on our Helix machines and it appears to be related to the use of the $ in
                // the username.
                // https://github.com/dotnet/roslyn/issues/28836
                if (StringComparer.OrdinalIgnoreCase.Equals(Environment.UserDomainName, "WORKGROUP"))
                {
                    return;
                }
            }

            var folder = Temp.CreateDirectory();
            var source = folder.CreateFile("src.vb").WriteAllText("").Path;
            var _ref = folder.CreateFile("ref.dll").WriteAllText("").Path;
            try
            {
                var output = ProcessUtilities.RunAndGetOutput("cmd", "/C icacls " + _ref + " /inheritance:r /Q");
                Assert.Equal("Successfully processed 1 files; Failed processing 0 files", output.Trim());

                output = ProcessUtilities.RunAndGetOutput("cmd", "/C icacls " + _ref + @" /deny %USERDOMAIN%\%USERNAME%:(r,WDAC) /Q");
                Assert.Equal("Successfully processed 1 files; Failed processing 0 files", output.Trim());

                output = ProcessUtilities.RunAndGetOutput("cmd", "/C \"" + s_CSharpCompilerExecutable + "\" /nologo /preferreduilang:en /r:" + _ref + " /t:library " + source, expectedRetCode: 1);
                Assert.Equal("error CS0009: Metadata file '" + _ref + "' could not be opened -- Access to the path '" + _ref + "' is denied.", output.Trim());
            }
            finally
            {
                var output = ProcessUtilities.RunAndGetOutput("cmd", "/C icacls " + _ref + " /reset /Q");
                Assert.Equal("Successfully processed 1 files; Failed processing 0 files", output.Trim());
                File.Delete(_ref);
            }

            CleanupAllGeneratedFiles(source);
        }
#endif

        [WorkItem(545832, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545832")]
        [Fact]
        public void ResponseFilesWithEmptyAliasReference()
        {
            string source = Temp.CreateFile("a.cs").WriteAllText(@"
// <Area> ExternAlias - command line alias</Area>
// <Title>
// negative test cases: empty file name ("""")
// </Title>
// <Description>
// </Description>
// <RelatedBugs></RelatedBugs>

//<Expects Status=error>CS1680:.*myAlias=</Expects>

// <Code>
class myClass
{
    static int Main()
    {
        return 1;
    }
}
// </Code>
").Path;

            string rsp = Temp.CreateFile().WriteAllText(@"
/nologo
/r:myAlias=""""
").Path;

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            // csc errors_whitespace_008.cs @errors_whitespace_008.cs.rsp
            var csc = CreateCSharpCompiler(rsp, WorkingDirectory, new[] { source, "/preferreduilang:en" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Equal("error CS1680: Invalid reference alias option: 'myAlias=' -- missing filename", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(source);
            CleanupAllGeneratedFiles(rsp);
        }

        [Fact]
        public void ResponseFileOrdering()
        {
            var rspFilePath1 = Temp.CreateFile().WriteAllText(@"
/b
/c
").Path;

            assertOrder(
                new[] { "/a", "/b", "/c", "/d" },
                new[] { "/a", @$"@""{rspFilePath1}""", "/d" });

            var rspFilePath2 = Temp.CreateFile().WriteAllText(@"
/c
/d
").Path;

            rspFilePath1 = Temp.CreateFile().WriteAllText(@$"
/b
@""{rspFilePath2}""
").Path;

            assertOrder(
                new[] { "/a", "/b", "/c", "/d", "/e" },
                new[] { "/a", @$"@""{rspFilePath1}""", "/e" });

            rspFilePath1 = Temp.CreateFile().WriteAllText(@$"
/b
").Path;

            rspFilePath2 = Temp.CreateFile().WriteAllText(@"
# this will be ignored
/c
/d
").Path;

            assertOrder(
                new[] { "/a", "/b", "/c", "/d", "/e" },
                new[] { "/a", @$"@""{rspFilePath1}""", $@"@""{rspFilePath2}""", "/e" });

            void assertOrder(string[] expected, string[] args)
            {
                var flattenedArgs = ArrayBuilder<string>.GetInstance();
                var diagnostics = new List<Diagnostic>();
                CSharpCommandLineParser.Default.FlattenArgs(
                    args,
                    diagnostics,
                    flattenedArgs,
                    scriptArgsOpt: null,
                    baseDirectory: Path.DirectorySeparatorChar == '\\' ? @"c:\" : "/");

                Assert.Empty(diagnostics);
                Assert.Equal(expected, flattenedArgs);
                flattenedArgs.Free();
            }
        }

        [WorkItem(545832, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545832")]
        [Fact]
        public void ResponseFilesWithEmptyAliasReference2()
        {
            string source = Temp.CreateFile("a.cs").WriteAllText(@"
// <Area> ExternAlias - command line alias</Area>
// <Title>
// negative test cases: empty file name ("""")
// </Title>
// <Description>
// </Description>
// <RelatedBugs></RelatedBugs>

//<Expects Status=error>CS1680:.*myAlias=</Expects>

// <Code>
class myClass
{
    static int Main()
    {
        return 1;
    }
}
// </Code>
").Path;

            string rsp = Temp.CreateFile().WriteAllText(@"
/nologo
/r:myAlias=""  ""
").Path;

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            // csc errors_whitespace_008.cs @errors_whitespace_008.cs.rsp
            var csc = CreateCSharpCompiler(rsp, WorkingDirectory, new[] { source, "/preferreduilang:en" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Equal("error CS1680: Invalid reference alias option: 'myAlias=' -- missing filename", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(source);
            CleanupAllGeneratedFiles(rsp);
        }

        [WorkItem(1784, "https://github.com/dotnet/roslyn/issues/1784")]
        [Fact]
        public void QuotedDefineInRespFile()
        {
            string source = Temp.CreateFile("a.cs").WriteAllText(@"
#if NN
class myClass
{
#endif
    static int Main()
#if DD
    {
        return 1;
#endif

#if AA
    }
#endif

#if BB
}
#endif

").Path;

            string rsp = Temp.CreateFile().WriteAllText(@"
/d:""DD""
/d:""AA;BB""
/d:""N""N
").Path;

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            // csc errors_whitespace_008.cs @errors_whitespace_008.cs.rsp
            var csc = CreateCSharpCompiler(rsp, WorkingDirectory, new[] { source, "/preferreduilang:en" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);

            CleanupAllGeneratedFiles(source);
            CleanupAllGeneratedFiles(rsp);
        }

        [WorkItem(1784, "https://github.com/dotnet/roslyn/issues/1784")]
        [Fact]
        public void QuotedDefineInRespFileErr()
        {
            string source = Temp.CreateFile("a.cs").WriteAllText(@"
#if NN
class myClass
{
#endif
    static int Main()
#if DD
    {
        return 1;
#endif

#if AA
    }
#endif

#if BB
}
#endif

").Path;

            string rsp = Temp.CreateFile().WriteAllText(@"
/d:""DD""""
/d:""AA;BB""
/d:""N"" ""N
").Path;

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            // csc errors_whitespace_008.cs @errors_whitespace_008.cs.rsp
            var csc = CreateCSharpCompiler(rsp, WorkingDirectory, new[] { source, "/preferreduilang:en" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(1, exitCode);

            CleanupAllGeneratedFiles(source);
            CleanupAllGeneratedFiles(rsp);
        }

        [Fact]
        public void ResponseFileSplitting()
        {
            string[] responseFile;

            responseFile = new string[] {
                @"a.cs b.cs ""c.cs e.cs""",
                @"hello world # this is a comment"
            };

            IEnumerable<string> args = CSharpCommandLineParser.ParseResponseLines(responseFile);
            AssertEx.Equal(new[] { "a.cs", "b.cs", @"c.cs e.cs", "hello", "world" }, args);

            // Check comment handling; comment character only counts at beginning of argument
            responseFile = new string[] {
                @"   # ignore this",
                @"   # ignore that ""hello""",
                @"  a.cs #3.cs",
                @"  b#.cs c#d.cs #e.cs",
                @"  ""#f.cs""",
                @"  ""#g.cs #h.cs"""
            };

            args = CSharpCommandLineParser.ParseResponseLines(responseFile);
            AssertEx.Equal(new[] { "a.cs", "b#.cs", "c#d.cs", "#f.cs", "#g.cs #h.cs" }, args);

            // Check backslash escaping
            responseFile = new string[] {
                @"a\b\c d\\e\\f\\ \\\g\\\h\\\i \\\\ \\\\\k\\\\\",
            };
            args = CSharpCommandLineParser.ParseResponseLines(responseFile);
            AssertEx.Equal(new[] { @"a\b\c", @"d\\e\\f\\", @"\\\g\\\h\\\i", @"\\\\", @"\\\\\k\\\\\" }, args);

            // More backslash escaping and quoting
            responseFile = new string[] {
                @"a\""a b\\""b c\\\""c d\\\\""d e\\\\\""e f"" g""",
            };
            args = CSharpCommandLineParser.ParseResponseLines(responseFile);
            AssertEx.Equal(new[] { @"a\""a", @"b\\""b c\\\""c d\\\\""d", @"e\\\\\""e", @"f"" g""" }, args);

            // Quoting inside argument is valid.
            responseFile = new string[] {
                @"  /o:""goo.cs"" /o:""abc def""\baz ""/o:baz bar""bing",
            };
            args = CSharpCommandLineParser.ParseResponseLines(responseFile);
            AssertEx.Equal(new[] { @"/o:""goo.cs""", @"/o:""abc def""\baz", @"""/o:baz bar""bing" }, args);
        }

        [ConditionalFact(typeof(WindowsOnly))]
        private void SourceFileQuoting()
        {
            string[] responseFile = new string[] {
                @"d:\\""abc def""\baz.cs ab""c d""e.cs",
            };

            CSharpCommandLineArguments args = DefaultParse(CSharpCommandLineParser.ParseResponseLines(responseFile), @"c:\");
            AssertEx.Equal(new[] { @"d:\abc def\baz.cs", @"c:\abc de.cs" }, args.SourceFiles.Select(file => file.Path));
        }

        [WorkItem(544441, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544441")]
        [Fact]
        public void OutputFileName1()
        {
            string source1 = @"
class A
{
}
";
            string source2 = @"
class B
{
    static void Main() { }
}
";
            // Name comes from first input (file, not class) name, since DLL.
            CheckOutputFileName(
                source1, source2,
                inputName1: "p.cs", inputName2: "q.cs",
                commandLineArguments: new[] { "/target:library" },
                expectedOutputName: "p.dll");
        }

        [WorkItem(544441, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544441")]
        [Fact]
        public void OutputFileName2()
        {
            string source1 = @"
class A
{
}
";
            string source2 = @"
class B
{
    static void Main() { }
}
";
            // Name comes from command-line option.
            CheckOutputFileName(
                source1, source2,
                inputName1: "p.cs", inputName2: "q.cs",
                commandLineArguments: new[] { "/target:library", "/out:r.dll" },
                expectedOutputName: "r.dll");
        }

        [WorkItem(544441, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544441")]
        [Fact]
        public void OutputFileName3()
        {
            string source1 = @"
class A
{
}
";
            string source2 = @"
class B
{
    static void Main() { }
}
";
            // Name comes from name of file containing entrypoint, since EXE.
            CheckOutputFileName(
                source1, source2,
                inputName1: "p.cs", inputName2: "q.cs",
                commandLineArguments: new[] { "/target:exe" },
                expectedOutputName: "q.exe");
        }

        [WorkItem(544441, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544441")]
        [Fact]
        public void OutputFileName4()
        {
            string source1 = @"
class A
{
}
";
            string source2 = @"
class B
{
    static void Main() { }
}
";
            // Name comes from command-line option.
            CheckOutputFileName(
                source1, source2,
                inputName1: "p.cs", inputName2: "q.cs",
                commandLineArguments: new[] { "/target:exe", "/out:r.exe" },
                expectedOutputName: "r.exe");
        }

        [WorkItem(544441, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544441")]
        [Fact]
        public void OutputFileName5()
        {
            string source1 = @"
class A
{
    static void Main() { }
}
";
            string source2 = @"
class B
{
    static void Main() { }
}
";
            // Name comes from name of file containing entrypoint - affected by /main, since EXE.
            CheckOutputFileName(
                source1, source2,
                inputName1: "p.cs", inputName2: "q.cs",
                commandLineArguments: new[] { "/target:exe", "/main:A" },
                expectedOutputName: "p.exe");
        }

        [WorkItem(544441, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544441")]
        [Fact]
        public void OutputFileName6()
        {
            string source1 = @"
class A
{
    static void Main() { }
}
";
            string source2 = @"
class B
{
    static void Main() { }
}
";
            // Name comes from name of file containing entrypoint - affected by /main, since EXE.
            CheckOutputFileName(
                source1, source2,
                inputName1: "p.cs", inputName2: "q.cs",
                commandLineArguments: new[] { "/target:exe", "/main:B" },
                expectedOutputName: "q.exe");
        }

        [WorkItem(544441, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544441")]
        [Fact]
        public void OutputFileName7()
        {
            string source1 = @"
partial class A
{
    static partial void Main() { }
}
";
            string source2 = @"
partial class A
{
    static partial void Main();
}
";
            // Name comes from name of file containing entrypoint, since EXE.
            CheckOutputFileName(
                source1, source2,
                inputName1: "p.cs", inputName2: "q.cs",
                commandLineArguments: new[] { "/target:exe" },
                expectedOutputName: "p.exe");
        }

        [WorkItem(544441, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544441")]
        [Fact]
        public void OutputFileName8()
        {
            string source1 = @"
partial class A
{
    static partial void Main();
}
";
            string source2 = @"
partial class A
{
    static partial void Main() { }
}
";
            // Name comes from name of file containing entrypoint, since EXE.
            CheckOutputFileName(
                source1, source2,
                inputName1: "p.cs", inputName2: "q.cs",
                commandLineArguments: new[] { "/target:exe" },
                expectedOutputName: "q.exe");
        }

        [Fact]
        public void OutputFileName9()
        {
            string source1 = @"
class A
{
}
";
            string source2 = @"
class B
{
    static void Main() { }
}
";
            // Name comes from first input (file, not class) name, since winmdobj.
            CheckOutputFileName(
                source1, source2,
                inputName1: "p.cs", inputName2: "q.cs",
                commandLineArguments: new[] { "/target:winmdobj" },
                expectedOutputName: "p.winmdobj");
        }

        [Fact]
        public void OutputFileName10()
        {
            string source1 = @"
class A
{
}
";
            string source2 = @"
class B
{
    static void Main() { }
}
";
            // Name comes from name of file containing entrypoint, since appcontainerexe.
            CheckOutputFileName(
                source1, source2,
                inputName1: "p.cs", inputName2: "q.cs",
                commandLineArguments: new[] { "/target:appcontainerexe" },
                expectedOutputName: "q.exe");
        }

        [Fact]
        public void OutputFileName_Switch()
        {
            string source1 = @"
class A
{
}
";
            string source2 = @"
class B
{
    static void Main() { }
}
";
            // Name comes from name of file containing entrypoint, since EXE.
            CheckOutputFileName(
                source1, source2,
                inputName1: "p.cs", inputName2: "q.cs",
                commandLineArguments: new[] { "/target:exe", "/out:r.exe" },
                expectedOutputName: "r.exe");
        }

        [Fact]
        public void OutputFileName_NoEntryPoint()
        {
            string source = @"
class C
{
}
";
            var dir = Temp.CreateDirectory();

            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/preferreduilang:en", "/target:exe", "a.cs" });
            int exitCode = csc.Run(outWriter);
            Assert.NotEqual(0, exitCode);
            Assert.Equal("error CS5001: Program does not contain a static 'Main' method suitable for an entry point", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(file.Path);
        }

        [Fact, WorkItem(1093063, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1093063")]
        public void VerifyDiagnosticSeverityNotLocalized()
        {
            string source = @"
class C
{
}
";
            var dir = Temp.CreateDirectory();

            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/target:exe", "a.cs" });
            int exitCode = csc.Run(outWriter);
            Assert.NotEqual(0, exitCode);

            // If "error" was localized, below assert will fail on PLOC builds. The output would be something like: "!pTCvB!vbc : !FLxft!error 表! CS5001:"
            Assert.Contains("error CS5001:", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(file.Path);
        }

        [Fact]
        public void NoLogo_1()
        {
            string source = @"
class C
{
}
";
            var dir = Temp.CreateDirectory();

            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/target:library", "a.cs" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal(@"",
                outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(file.Path);
        }

        [Fact]
        public void NoLogo_2()
        {
            string source = @"
class C
{
}
";
            var dir = Temp.CreateDirectory();

            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/target:library", "/preferreduilang:en", "a.cs" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);

            var patched = Regex.Replace(outWriter.ToString().Trim(), "version \\d+\\.\\d+\\.\\d+(-[\\w\\d]+)*", "version A.B.C-d");
            patched = ReplaceCommitHash(patched);
            Assert.Equal(@"
Microsoft (R) Visual C# Compiler version A.B.C-d (HASH)
Copyright (C) Microsoft Corporation. All rights reserved.".Trim(),
                patched);

            CleanupAllGeneratedFiles(file.Path);
        }

        [Theory,
            InlineData("Microsoft (R) Visual C# Compiler version A.B.C-d (<developer build>)",
                "Microsoft (R) Visual C# Compiler version A.B.C-d (HASH)"),
            InlineData("Microsoft (R) Visual C# Compiler version A.B.C-d (ABCDEF01)",
                "Microsoft (R) Visual C# Compiler version A.B.C-d (HASH)"),
            InlineData("Microsoft (R) Visual C# Compiler version A.B.C-d (abcdef90)",
                "Microsoft (R) Visual C# Compiler version A.B.C-d (HASH)"),
            InlineData("Microsoft (R) Visual C# Compiler version A.B.C-d (12345678)",
                "Microsoft (R) Visual C# Compiler version A.B.C-d (HASH)")]
        public void TestReplaceCommitHash(string orig, string expected)
        {
            Assert.Equal(expected, ReplaceCommitHash(orig));
        }

        private static string ReplaceCommitHash(string s)
        {
            // open paren, followed by either <developer build> or 8 hex, followed by close paren
            return Regex.Replace(s, "(\\((<developer build>|[a-fA-F0-9]{8})\\))", "(HASH)");
        }

        [Fact]
        public void ExtractShortCommitHash()
        {
            Assert.Null(CommonCompiler.ExtractShortCommitHash(null));
            Assert.Equal("", CommonCompiler.ExtractShortCommitHash(""));
            Assert.Equal("<", CommonCompiler.ExtractShortCommitHash("<"));
            Assert.Equal("<developer build>", CommonCompiler.ExtractShortCommitHash("<developer build>"));
            Assert.Equal("1", CommonCompiler.ExtractShortCommitHash("1"));
            Assert.Equal("1234567", CommonCompiler.ExtractShortCommitHash("1234567"));
            Assert.Equal("12345678", CommonCompiler.ExtractShortCommitHash("12345678"));
            Assert.Equal("12345678", CommonCompiler.ExtractShortCommitHash("123456789"));
        }

        private void CheckOutputFileName(string source1, string source2, string inputName1, string inputName2, string[] commandLineArguments, string expectedOutputName)
        {
            var dir = Temp.CreateDirectory();

            var file1 = dir.CreateFile(inputName1);
            file1.WriteAllText(source1);

            var file2 = dir.CreateFile(inputName2);
            file2.WriteAllText(source2);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, commandLineArguments.Concat(new[] { inputName1, inputName2 }).ToArray());
            int exitCode = csc.Run(outWriter);
            if (exitCode != 0)
            {
                Console.WriteLine(outWriter.ToString());
                Assert.Equal(0, exitCode);
            }

            Assert.Equal(1, Directory.EnumerateFiles(dir.Path, "*" + PathUtilities.GetExtension(expectedOutputName)).Count());
            Assert.Equal(1, Directory.EnumerateFiles(dir.Path, expectedOutputName).Count());

            using (var metadata = ModuleMetadata.CreateFromImage(File.ReadAllBytes(Path.Combine(dir.Path, expectedOutputName))))
            {
                var peReader = metadata.Module.GetMetadataReader();

                Assert.True(peReader.IsAssembly);

                Assert.Equal(PathUtilities.RemoveExtension(expectedOutputName), peReader.GetString(peReader.GetAssemblyDefinition().Name));
                Assert.Equal(expectedOutputName, peReader.GetString(peReader.GetModuleDefinition().Name));
            }

            if (System.IO.File.Exists(expectedOutputName))
            {
                System.IO.File.Delete(expectedOutputName);
            }

            CleanupAllGeneratedFiles(file1.Path);
            CleanupAllGeneratedFiles(file2.Path);
        }

        [Fact]
        public void MissingReference()
        {
            string source = @"
class C
{
}
";
            var dir = Temp.CreateDirectory();

            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "/preferreduilang:en", "/r:missing.dll", "a.cs" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Equal("error CS0006: Metadata file 'missing.dll' could not be found", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(file.Path);
        }

        [WorkItem(545025, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545025")]
        [ConditionalFact(typeof(WindowsOnly))]
        public void CompilationWithWarnAsError_01()
        {
            string source = @"
public class C
{
    public static void Main()
    {
    }
}";

            // Baseline without warning options (expect success)
            int exitCode = GetExitCode(source, "a.cs", new String[] { });
            Assert.Equal(0, exitCode);

            // The case with /warnaserror (expect to be success, since there will be no warning)
            exitCode = GetExitCode(source, "b.cs", new[] { "/warnaserror" });
            Assert.Equal(0, exitCode);

            // The case with /warnaserror and /nowarn:1 (expect success)
            // Note that even though the command line option has a warning, it is not going to become an error
            // in order to avoid the halt of compilation.
            exitCode = GetExitCode(source, "c.cs", new[] { "/warnaserror", "/nowarn:1" });
            Assert.Equal(0, exitCode);
        }

        [WorkItem(545025, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545025")]
        [ConditionalFact(typeof(WindowsOnly))]
        public void CompilationWithWarnAsError_02()
        {
            string source = @"
public class C
{
    public static void Main()
    {
        int x; // CS0168
    }
}";

            // Baseline without warning options (expect success)
            int exitCode = GetExitCode(source, "a.cs", new String[] { });
            Assert.Equal(0, exitCode);

            // The case with /warnaserror (expect failure)
            exitCode = GetExitCode(source, "b.cs", new[] { "/warnaserror" });
            Assert.NotEqual(0, exitCode);

            // The case with /warnaserror:168 (expect failure)
            exitCode = GetExitCode(source, "c.cs", new[] { "/warnaserror:168" });
            Assert.NotEqual(0, exitCode);

            // The case with /warnaserror:219 (expect success)
            exitCode = GetExitCode(source, "c.cs", new[] { "/warnaserror:219" });
            Assert.Equal(0, exitCode);

            // The case with /warnaserror and /nowarn:168 (expect success)
            exitCode = GetExitCode(source, "d.cs", new[] { "/warnaserror", "/nowarn:168" });
            Assert.Equal(0, exitCode);
        }

        private int GetExitCode(string source, string fileName, string[] commandLineArguments)
        {
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile(fileName);
            file.WriteAllText(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, commandLineArguments.Concat(new[] { fileName }).ToArray());
            int exitCode = csc.Run(outWriter);

            return exitCode;
        }

        [WorkItem(545247, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545247")]
        [ConditionalFact(typeof(WindowsOnly))]
        public void CompilationWithNonExistingOutPath()
        {
            string source = @"
public class C
{
    public static void Main()
    {
    }
}";

            var fileName = "a.cs";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile(fileName);
            file.WriteAllText(source);
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { fileName, "/preferreduilang:en", "/target:exe", "/out:sub\\a.exe" });
            int exitCode = csc.Run(outWriter);

            Assert.Equal(1, exitCode);
            Assert.Contains("error CS2012: Cannot open '" + dir.Path + "\\sub\\a.exe' for writing", outWriter.ToString(), StringComparison.Ordinal);

            CleanupAllGeneratedFiles(file.Path);
        }

        [WorkItem(545247, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545247")]
        [Fact]
        public void CompilationWithWrongOutPath_01()
        {
            string source = @"
public class C
{
    public static void Main()
    {
    }
}";

            var fileName = "a.cs";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile(fileName);
            file.WriteAllText(source);
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { fileName, "/preferreduilang:en", "/target:exe", "/out:sub\\" });
            int exitCode = csc.Run(outWriter);

            Assert.Equal(1, exitCode);
            var message = outWriter.ToString();
            Assert.Contains("error CS2021: File name", message, StringComparison.Ordinal);
            Assert.Contains("sub", message, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(file.Path);
        }

        [WorkItem(545247, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545247")]
        [Fact]
        public void CompilationWithWrongOutPath_02()
        {
            string source = @"
public class C
{
    public static void Main()
    {
    }
}";

            var fileName = "a.cs";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile(fileName);
            file.WriteAllText(source);
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { fileName, "/preferreduilang:en", "/target:exe", "/out:sub\\ " });
            int exitCode = csc.Run(outWriter);

            Assert.Equal(1, exitCode);
            var message = outWriter.ToString();
            Assert.Contains("error CS2021: File name", message, StringComparison.Ordinal);
            Assert.Contains("sub", message, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(file.Path);
        }

        [WorkItem(545247, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545247")]
        [ConditionalFact(typeof(WindowsDesktopOnly))]
        public void CompilationWithWrongOutPath_03()
        {
            string source = @"
public class C
{
    public static void Main()
    {
    }
}";

            var fileName = "a.cs";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile(fileName);
            file.WriteAllText(source);
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { fileName, "/preferreduilang:en", "/target:exe", "/out:aaa:\\a.exe" });
            int exitCode = csc.Run(outWriter);

            Assert.Equal(1, exitCode);
            Assert.Contains(@"error CS2021: File name 'aaa:\a.exe' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long", outWriter.ToString(), StringComparison.Ordinal);

            CleanupAllGeneratedFiles(file.Path);
        }

        [WorkItem(545247, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545247")]
        [Fact]
        public void CompilationWithWrongOutPath_04()
        {
            string source = @"
public class C
{
    public static void Main()
    {
    }
}";

            var fileName = "a.cs";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile(fileName);
            file.WriteAllText(source);
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path, new[] { fileName, "/preferreduilang:en", "/target:exe", "/out: " });
            int exitCode = csc.Run(outWriter);

            Assert.Equal(1, exitCode);
            Assert.Contains("error CS2005: Missing file specification for '/out:' option", outWriter.ToString(), StringComparison.Ordinal);

            CleanupAllGeneratedFiles(file.Path);
        }

        [Fact]
        public void EmittedSubsystemVersion()
        {
            var compilation = CSharpCompilation.Create("a.dll", references: new[] { MscorlibRef }, options: TestOptions.ReleaseDll);
            var peHeaders = new PEHeaders(compilation.EmitToStream(options: new EmitOptions(subsystemVersion: SubsystemVersion.Create(5, 1))));
            Assert.Equal(5, peHeaders.PEHeader.MajorSubsystemVersion);
            Assert.Equal(1, peHeaders.PEHeader.MinorSubsystemVersion);
        }

        [Fact]
        public void CreateCompilationWithKeyFile()
        {
            string source = @"
public class C
{
    public static void Main()
    {
    }
}";

            var fileName = "a.cs";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile(fileName);
            file.WriteAllText(source);

            var cmd = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "a.cs", "/keyfile:key.snk", });
            var comp = cmd.CreateCompilation(TextWriter.Null, new TouchedFileLogger(), NullErrorLogger.Instance);

            Assert.IsType<DesktopStrongNameProvider>(comp.Options.StrongNameProvider);
        }

        [Fact]
        public void CreateCompilationWithKeyContainer()
        {
            string source = @"
public class C
{
    public static void Main()
    {
    }
}";

            var fileName = "a.cs";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile(fileName);
            file.WriteAllText(source);

            var cmd = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "a.cs", "/keycontainer:bbb", });
            var comp = cmd.CreateCompilation(TextWriter.Null, new TouchedFileLogger(), NullErrorLogger.Instance);

            Assert.Equal(typeof(DesktopStrongNameProvider), comp.Options.StrongNameProvider.GetType());
        }

        [Fact]
        public void CreateCompilationFallbackCommand()
        {
            string source = @"
public class C
{
    public static void Main()
    {
    }
}";

            var fileName = "a.cs";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile(fileName);
            file.WriteAllText(source);

            var cmd = CreateCSharpCompiler(null, dir.Path, new[] { "/nologo", "a.cs", "/keyFile:key.snk", "/features:UseLegacyStrongNameProvider" });
            var comp = cmd.CreateCompilation(TextWriter.Null, new TouchedFileLogger(), NullErrorLogger.Instance);

            Assert.Equal(typeof(DesktopStrongNameProvider), comp.Options.StrongNameProvider.GetType());
        }

        [Fact]
        public void CreateCompilation_MainAndTargetIncompatibilities()
        {
            string source = @"
public class C
{
    public static void Main()
    {
    }
}";

            var fileName = "a.cs";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile(fileName);
            file.WriteAllText(source);

            var compilation = CSharpCompilation.Create("a.dll", options: TestOptions.ReleaseDll);

            var options = compilation.Options;

            Assert.Equal(0, options.Errors.Length);

            options = options.WithMainTypeName("a");

            options.Errors.Verify(
    // error CS2017: Cannot specify /main if building a module or library
    Diagnostic(ErrorCode.ERR_NoMainOnDLL)
                );

            var comp = CSharpCompilation.Create("a.dll", options: options);

            comp.GetDiagnostics().Verify(
    // error CS2017: Cannot specify /main if building a module or library
    Diagnostic(ErrorCode.ERR_NoMainOnDLL)
                );

            options = options.WithOutputKind(OutputKind.WindowsApplication);
            options.Errors.Verify();

            comp = CSharpCompilation.Create("a.dll", options: options);
            comp.GetDiagnostics().Verify(
    // error CS1555: Could not find 'a' specified for Main method
    Diagnostic(ErrorCode.ERR_MainClassNotFound).WithArguments("a")
                );

            options = options.WithOutputKind(OutputKind.NetModule);
            options.Errors.Verify(
    // error CS2017: Cannot specify /main if building a module or library
    Diagnostic(ErrorCode.ERR_NoMainOnDLL)
                );

            comp = CSharpCompilation.Create("a.dll", options: options);
            comp.GetDiagnostics().Verify(
    // error CS2017: Cannot specify /main if building a module or library
    Diagnostic(ErrorCode.ERR_NoMainOnDLL)
                );

            options = options.WithMainTypeName(null);
            options.Errors.Verify();

            comp = CSharpCompilation.Create("a.dll", options: options);
            comp.GetDiagnostics().Verify();

            CleanupAllGeneratedFiles(file.Path);
        }

        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30328")]
        public void SpecifyProperCodePage()
        {
            byte[] source = {
                                0x63, // c
                                0x6c, // l
                                0x61, // a
                                0x73, // s
                                0x73, // s
                                0x20, //
                                0xd0, 0x96, // Utf-8 Cyrillic character
                                0x7b, // {
                                0x7d, // }
                            };

            var fileName = "a.cs";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile(fileName);
            file.WriteAllBytes(source);

            var output = ProcessUtilities.RunAndGetOutput(s_CSharpCompilerExecutable, $"/nologo /t:library \"{file}\"", startFolder: dir.Path);
            Assert.Equal("", output); // Autodetected UTF8, NO ERROR

            output = ProcessUtilities.RunAndGetOutput(s_CSharpCompilerExecutable, $"/nologo /preferreduilang:en /t:library /codepage:20127 \"{file}\"", expectedRetCode: 1, startFolder: dir.Path); // 20127: US-ASCII
            // 0xd0, 0x96 ==> ERROR
            Assert.Equal(@"
a.cs(1,7): error CS1001: Identifier expected
a.cs(1,7): error CS1514: { expected
a.cs(1,7): error CS1513: } expected
a.cs(1,7): error CS8803: Top-level statements must precede namespace and type declarations.
a.cs(1,7): error CS1525: Invalid expression term '??'
a.cs(1,9): error CS1525: Invalid expression term '{'
a.cs(1,9): error CS1002: ; expected
".Trim(),
                Regex.Replace(output, "^.*a.cs", "a.cs", RegexOptions.Multiline).Trim());

            CleanupAllGeneratedFiles(file.Path);
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void DefaultWin32ResForExe()
        {
            var source = @"
class C
{
    static void Main() { }
}
";

            CheckManifestString(source, OutputKind.ConsoleApplication, explicitManifest: null, expectedManifest:
@"<?xml version=""1.0"" encoding=""utf-16""?>
<ManifestResource Size=""490"">
  <Contents><![CDATA[<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>

<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">
  <assemblyIdentity version=""1.0.0.0"" name=""MyApplication.app""/>
  <trustInfo xmlns=""urn:schemas-microsoft-com:asm.v2"">
    <security>
      <requestedPrivileges xmlns=""urn:schemas-microsoft-com:asm.v3"">
        <requestedExecutionLevel level=""asInvoker"" uiAccess=""false""/>
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>]]></Contents>
</ManifestResource>");
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void DefaultManifestForDll()
        {
            var source = @"
class C
{
}
";

            CheckManifestString(source, OutputKind.DynamicallyLinkedLibrary, explicitManifest: null, expectedManifest: null);
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void DefaultManifestForWinExe()
        {
            var source = @"
class C
{
    static void Main() { }
}
";

            CheckManifestString(source, OutputKind.WindowsApplication, explicitManifest: null, expectedManifest:
@"<?xml version=""1.0"" encoding=""utf-16""?>
<ManifestResource Size=""490"">
  <Contents><![CDATA[<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>

<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">
  <assemblyIdentity version=""1.0.0.0"" name=""MyApplication.app""/>
  <trustInfo xmlns=""urn:schemas-microsoft-com:asm.v2"">
    <security>
      <requestedPrivileges xmlns=""urn:schemas-microsoft-com:asm.v3"">
        <requestedExecutionLevel level=""asInvoker"" uiAccess=""false""/>
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>]]></Contents>
</ManifestResource>");
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void DefaultManifestForAppContainerExe()
        {
            var source = @"
class C
{
    static void Main() { }
}
";

            CheckManifestString(source, OutputKind.WindowsRuntimeApplication, explicitManifest: null, expectedManifest:
@"<?xml version=""1.0"" encoding=""utf-16""?>
<ManifestResource Size=""490"">
  <Contents><![CDATA[<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>

<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">
  <assemblyIdentity version=""1.0.0.0"" name=""MyApplication.app""/>
  <trustInfo xmlns=""urn:schemas-microsoft-com:asm.v2"">
    <security>
      <requestedPrivileges xmlns=""urn:schemas-microsoft-com:asm.v3"">
        <requestedExecutionLevel level=""asInvoker"" uiAccess=""false""/>
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>]]></Contents>
</ManifestResource>");
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void DefaultManifestForWinMD()
        {
            var source = @"
class C
{
}
";

            CheckManifestString(source, OutputKind.WindowsRuntimeMetadata, explicitManifest: null, expectedManifest: null);
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void DefaultWin32ResForModule()
        {
            var source = @"
class C
{
}
";

            CheckManifestString(source, OutputKind.NetModule, explicitManifest: null, expectedManifest: null);
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void ExplicitWin32ResForExe()
        {
            var source = @"
class C
{
    static void Main() { }
}
";

            var explicitManifest =
@"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>
<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">
  <assemblyIdentity version=""1.0.0.0"" name=""Test.app""/>
  <trustInfo xmlns=""urn:schemas-microsoft-com:asm.v2"">
    <security>
      <requestedPrivileges xmlns=""urn:schemas-microsoft-com:asm.v3"">
        <requestedExecutionLevel level=""asInvoker"" uiAccess=""false""/>
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>";

            var explicitManifestStream = new MemoryStream(Encoding.UTF8.GetBytes(explicitManifest));

            var expectedManifest =
@"<?xml version=""1.0"" encoding=""utf-16""?>
<ManifestResource Size=""476"">
  <Contents><![CDATA[" +
explicitManifest +
@"]]></Contents>
</ManifestResource>";

            CheckManifestString(source, OutputKind.ConsoleApplication, explicitManifest, expectedManifest);
        }

        // DLLs don't get the default manifest, but they do respect explicitly set manifests.
        [ConditionalFact(typeof(WindowsOnly))]
        public void ExplicitWin32ResForDll()
        {
            var source = @"
class C
{
    static void Main() { }
}
";

            var explicitManifest =
@"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>
<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">
  <assemblyIdentity version=""1.0.0.0"" name=""Test.app""/>
  <trustInfo xmlns=""urn:schemas-microsoft-com:asm.v2"">
    <security>
      <requestedPrivileges xmlns=""urn:schemas-microsoft-com:asm.v3"">
        <requestedExecutionLevel level=""asInvoker"" uiAccess=""false""/>
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>";


            var expectedManifest =
@"<?xml version=""1.0"" encoding=""utf-16""?>
<ManifestResource Size=""476"">
  <Contents><![CDATA[" +
explicitManifest +
@"]]></Contents>
</ManifestResource>";

            CheckManifestString(source, OutputKind.DynamicallyLinkedLibrary, explicitManifest, expectedManifest);
        }

        // Modules don't have manifests, even if one is explicitly specified.
        [ConditionalFact(typeof(WindowsOnly))]
        public void ExplicitWin32ResForModule()
        {
            var source = @"
class C
{
}
";

            var explicitManifest =
@"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>
<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">
  <assemblyIdentity version=""1.0.0.0"" name=""Test.app""/>
  <trustInfo xmlns=""urn:schemas-microsoft-com:asm.v2"">
    <security>
      <requestedPrivileges xmlns=""urn:schemas-microsoft-com:asm.v3"">
        <requestedExecutionLevel level=""asInvoker"" uiAccess=""false""/>
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>";

            CheckManifestString(source, OutputKind.NetModule, explicitManifest, expectedManifest: null);
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool FreeLibrary([In] IntPtr hFile);

        private void CheckManifestString(string source, OutputKind outputKind, string explicitManifest, string expectedManifest)
        {
            var dir = Temp.CreateDirectory();
            var sourceFile = dir.CreateFile("Test.cs").WriteAllText(source);

            string outputFileName;
            string target;
            switch (outputKind)
            {
                case OutputKind.ConsoleApplication:
                    outputFileName = "Test.exe";
                    target = "exe";
                    break;
                case OutputKind.WindowsApplication:
                    outputFileName = "Test.exe";
                    target = "winexe";
                    break;
                case OutputKind.DynamicallyLinkedLibrary:
                    outputFileName = "Test.dll";
                    target = "library";
                    break;
                case OutputKind.NetModule:
                    outputFileName = "Test.netmodule";
                    target = "module";
                    break;
                case OutputKind.WindowsRuntimeMetadata:
                    outputFileName = "Test.winmdobj";
                    target = "winmdobj";
                    break;
                case OutputKind.WindowsRuntimeApplication:
                    outputFileName = "Test.exe";
                    target = "appcontainerexe";
                    break;
                default:
                    throw TestExceptionUtilities.UnexpectedValue(outputKind);
            }

            MockCSharpCompiler csc;
            if (explicitManifest == null)
            {
                csc = CreateCSharpCompiler(null, dir.Path, new[]
                {
                    string.Format("/target:{0}", target),
                    string.Format("/out:{0}", outputFileName),
                    Path.GetFileName(sourceFile.Path),
                });
            }
            else
            {
                var manifestFile = dir.CreateFile("Test.config").WriteAllText(explicitManifest);
                csc = CreateCSharpCompiler(null, dir.Path, new[]
                {
                    string.Format("/target:{0}", target),
                    string.Format("/out:{0}", outputFileName),
                    string.Format("/win32manifest:{0}", Path.GetFileName(manifestFile.Path)),
                    Path.GetFileName(sourceFile.Path),
                });
            }

            int actualExitCode = csc.Run(new StringWriter(CultureInfo.InvariantCulture));

            Assert.Equal(0, actualExitCode);

            //Open as data
            IntPtr lib = LoadLibraryEx(Path.Combine(dir.Path, outputFileName), IntPtr.Zero, 0x00000002);
            if (lib == IntPtr.Zero)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            const string resourceType = "#24";
            var resourceId = outputKind == OutputKind.DynamicallyLinkedLibrary ? "#2" : "#1";

            uint manifestSize;
            if (expectedManifest == null)
            {
                Assert.Throws<Win32Exception>(() => Win32Res.GetResource(lib, resourceId, resourceType, out manifestSize));
            }
            else
            {
                IntPtr manifestResourcePointer = Win32Res.GetResource(lib, resourceId, resourceType, out manifestSize);
                string actualManifest = Win32Res.ManifestResourceToXml(manifestResourcePointer, manifestSize);
                Assert.Equal(expectedManifest, actualManifest);
            }

            FreeLibrary(lib);
        }

        [WorkItem(544926, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544926")]
        [ConditionalFact(typeof(WindowsOnly), Reason = "https://github.com/dotnet/roslyn/issues/30289")]
        public void ResponseFilesWithNoconfig_01()
        {
            string source = Temp.CreateFile("a.cs").WriteAllText(@"
public class C
{
    public static void Main()
    {
        int x; // CS0168
    }
}").Path;

            string rsp = Temp.CreateFile().WriteAllText(@"
/warnaserror
").Path;
            // Checks the base case without /noconfig (expect to see error)
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(rsp, WorkingDirectory, new[] { source, "/preferreduilang:en" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Contains("error CS0168: The variable 'x' is declared but never used\r\n", outWriter.ToString(), StringComparison.Ordinal);

            // Checks the case with /noconfig (expect to see warning, instead of error)
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            csc = CreateCSharpCompiler(rsp, WorkingDirectory, new[] { source, "/noconfig", "/preferreduilang:en" });
            exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Contains("warning CS0168: The variable 'x' is declared but never used\r\n", outWriter.ToString(), StringComparison.Ordinal);

            // Checks the case with /NOCONFIG (expect to see warning, instead of error)
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            csc = CreateCSharpCompiler(rsp, WorkingDirectory, new[] { source, "/NOCONFIG", "/preferreduilang:en" });
            exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Contains("warning CS0168: The variable 'x' is declared but never used\r\n", outWriter.ToString(), StringComparison.Ordinal);

            // Checks the case with -noconfig (expect to see warning, instead of error)
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            csc = CreateCSharpCompiler(rsp, WorkingDirectory, new[] { source, "-noconfig", "/preferreduilang:en" });
            exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Contains("warning CS0168: The variable 'x' is declared but never used\r\n", outWriter.ToString(), StringComparison.Ordinal);

            CleanupAllGeneratedFiles(source);
            CleanupAllGeneratedFiles(rsp);
        }

        [WorkItem(544926, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544926")]
        [ConditionalFact(typeof(WindowsOnly))]
        public void ResponseFilesWithNoconfig_02()
        {
            string source = Temp.CreateFile("a.cs").WriteAllText(@"
public class C
{
    public static void Main()
    {
    }
}").Path;

            string rsp = Temp.CreateFile().WriteAllText(@"
/noconfig
").Path;
            // Checks the case with /noconfig inside the response file (expect to see warning)
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(rsp, WorkingDirectory, new[] { source, "/preferreduilang:en" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Contains("warning CS2023: Ignoring /noconfig option because it was specified in a response file\r\n", outWriter.ToString(), StringComparison.Ordinal);

            // Checks the case with /noconfig inside the response file as along with /nowarn (expect to see warning)
            // to verify that this warning is not suppressed by the /nowarn option (See MSDN).
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            csc = CreateCSharpCompiler(rsp, WorkingDirectory, new[] { source, "/preferreduilang:en", "/nowarn:2023" });
            exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Contains("warning CS2023: Ignoring /noconfig option because it was specified in a response file\r\n", outWriter.ToString(), StringComparison.Ordinal);

            CleanupAllGeneratedFiles(source);
            CleanupAllGeneratedFiles(rsp);
        }

        [WorkItem(544926, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544926")]
        [ConditionalFact(typeof(WindowsOnly), Reason = "https://github.com/dotnet/roslyn/issues/30289")]
        public void ResponseFilesWithNoconfig_03()
        {
            string source = Temp.CreateFile("a.cs").WriteAllText(@"
public class C
{
    public static void Main()
    {
    }
}").Path;

            string rsp = Temp.CreateFile().WriteAllText(@"
/NOCONFIG
").Path;
            // Checks the case with /noconfig inside the response file (expect to see warning)
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(rsp, WorkingDirectory, new[] { source, "/preferreduilang:en" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Contains("warning CS2023: Ignoring /noconfig option because it was specified in a response file\r\n", outWriter.ToString(), StringComparison.Ordinal);

            // Checks the case with /NOCONFIG inside the response file as along with /nowarn (expect to see warning)
            // to verify that this warning is not suppressed by the /nowarn option (See MSDN).
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            csc = CreateCSharpCompiler(rsp, WorkingDirectory, new[] { source, "/preferreduilang:en", "/nowarn:2023" });
            exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Contains("warning CS2023: Ignoring /noconfig option because it was specified in a response file\r\n", outWriter.ToString(), StringComparison.Ordinal);

            CleanupAllGeneratedFiles(source);
            CleanupAllGeneratedFiles(rsp);
        }

        [WorkItem(544926, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544926")]
        [ConditionalFact(typeof(WindowsOnly))]
        public void ResponseFilesWithNoconfig_04()
        {
            string source = Temp.CreateFile("a.cs").WriteAllText(@"
public class C
{
    public static void Main()
    {
    }
}").Path;

            string rsp = Temp.CreateFile().WriteAllText(@"
-noconfig
").Path;
            // Checks the case with /noconfig inside the response file (expect to see warning)
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(rsp, WorkingDirectory, new[] { source, "/preferreduilang:en" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Contains("warning CS2023: Ignoring /noconfig option because it was specified in a response file\r\n", outWriter.ToString(), StringComparison.Ordinal);

            // Checks the case with -noconfig inside the response file as along with /nowarn (expect to see warning)
            // to verify that this warning is not suppressed by the /nowarn option (See MSDN).
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            csc = CreateCSharpCompiler(rsp, WorkingDirectory, new[] { source, "/preferreduilang:en", "/nowarn:2023" });
            exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Contains("warning CS2023: Ignoring /noconfig option because it was specified in a response file\r\n", outWriter.ToString(), StringComparison.Ordinal);

            CleanupAllGeneratedFiles(source);
            CleanupAllGeneratedFiles(rsp);
        }

        [Fact, WorkItem(530024, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530024")]
        public void NoStdLib()
        {
            var src = Temp.CreateFile("a.cs");

            src.WriteAllText("public class C{}");

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/t:library", src.ToString() }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString().Trim());

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", "/nostdlib", "/t:library", src.ToString() }).Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Equal("{FILE}(1,14): error CS0518: Predefined type 'System.Object' is not defined or imported",
                         outWriter.ToString().Replace(Path.GetFileName(src.Path), "{FILE}").Trim());

            // Bug#15021: breaking change - empty source no error with /nostdlib
            src.WriteAllText("namespace System { }");
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/nostdlib", "/t:library", "/runtimemetadataversion:v4.0.30319", "/langversion:8", src.ToString() }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(src.Path);
        }

        private string GetDefaultResponseFilePath()
        {
            var cscRsp = global::TestResources.ResourceLoader.GetResourceBlob("csc.rsp");
            return Temp.CreateFile().WriteAllBytes(cscRsp).Path;
        }

        [Fact, WorkItem(530359, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530359")]
        public void NoStdLib02()
        {
            #region "source"
            var source = @"
// <Title>A collection initializer can be declared with a user-defined IEnumerable that is declared in a user-defined System.Collections</Title>
using System.Collections;

class O<T> where T : new()
{
    public T list = new T();
}

class C
{
    static StructCollection sc = new StructCollection { 1 };
    public static int Main()
    {
        ClassCollection cc = new ClassCollection { 2 };
        var o1 = new O<ClassCollection> { list = { 5 } };
        var o2 = new O<StructCollection> { list = sc };
        return 0;
    }
}

struct StructCollection : IEnumerable
{
    public int added;
#region IEnumerable Members
    public void Add(int t)
    {
        added = t;
    }
#endregion
}

class ClassCollection : IEnumerable
{
    public int added;
#region IEnumerable Members
    public void Add(int t)
    {
        added = t;
    }
#endregion
}

namespace System.Collections
{
    public interface IEnumerable
    {
        void Add(int t);
    }
}
";
            #endregion

            #region "mslib"
            var mslib = @"
namespace System
{
    public class Object {}
    public struct Byte { }
    public struct Int16 { }
    public struct Int32 { }
    public struct Int64 { }
    public struct Single { }
    public struct Double { }
    public struct SByte { }
    public struct UInt32 { }
    public struct UInt64 { }
    public struct Char { }
    public struct Boolean { }
    public struct UInt16 { }
    public struct UIntPtr { }
    public struct IntPtr { }
    public class Delegate { }
    public class String {
        public int Length    {    get { return 10; }    }
    }
    public class MulticastDelegate { }
    public class Array { }
    public class Exception { public Exception(string s){} }
    public class Type { }
    public class ValueType { }
    public class Enum { }
    public interface IEnumerable { }
    public interface IDisposable { }
    public class Attribute { }
    public class ParamArrayAttribute { }
    public struct Void { }
    public struct RuntimeFieldHandle { }
    public struct RuntimeTypeHandle { }
    public class Activator
    {
         public static T CreateInstance<T>(){return default(T);}
    }

    namespace Collections
    {
        public interface IEnumerator { }
    }

    namespace Runtime
    {
        namespace InteropServices
        {
            public class OutAttribute { }
        }

        namespace CompilerServices
        {
            public class RuntimeHelpers { }
        }
    }

    namespace Reflection
    {
        public class DefaultMemberAttribute { }
    }
}
";
            #endregion

            var src = Temp.CreateFile("NoStdLib02.cs");
            src.WriteAllText(source + mslib);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/noconfig", "/nostdlib", "/runtimemetadataversion:v4.0.30319", "/nowarn:8625", src.ToString() }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString().Trim());

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/nostdlib", "/runtimemetadataversion:v4.0.30319", "/nowarn:8625", src.ToString() }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString().Trim());
            string OriginalSource = src.Path;

            src = Temp.CreateFile("NoStdLib02b.cs");
            src.WriteAllText(mslib);
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(GetDefaultResponseFilePath(), WorkingDirectory, new[] { "/nologo", "/noconfig", "/nostdlib", "/t:library", "/runtimemetadataversion:v4.0.30319", "/nowarn:8625", src.ToString() }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(OriginalSource);
            CleanupAllGeneratedFiles(src.Path);
        }

        [Fact, WorkItem(546018, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546018"), WorkItem(546020, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546020"), WorkItem(546024, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546024"), WorkItem(546049, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546049")]
        public void InvalidDefineSwitch()
        {
            var src = Temp.CreateFile("a.cs");

            src.WriteAllText("public class C{}");

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", src.ToString(), "/define" }).Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Equal("error CS2006: Command-line syntax error: Missing '<text>' for '/define' option", outWriter.ToString().Trim());

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", src.ToString(), @"/define:""""" }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("warning CS2029: Invalid name for a preprocessing symbol; '' is not a valid identifier", outWriter.ToString().Trim());

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", src.ToString(), "/define: " }).Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Equal("error CS2006: Command-line syntax error: Missing '<text>' for '/define:' option", outWriter.ToString().Trim());

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", src.ToString(), "/define:" }).Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Equal("error CS2006: Command-line syntax error: Missing '<text>' for '/define:' option", outWriter.ToString().Trim());

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", src.ToString(), "/define:,,," }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("warning CS2029: Invalid name for a preprocessing symbol; '' is not a valid identifier", outWriter.ToString().Trim());

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", src.ToString(), "/define:,blah,Blah" }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("warning CS2029: Invalid name for a preprocessing symbol; '' is not a valid identifier", outWriter.ToString().Trim());

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", src.ToString(), "/define:a;;b@" }).Run(outWriter);
            Assert.Equal(0, exitCode);
            var errorLines = outWriter.ToString().Trim().Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
            Assert.Equal("warning CS2029: Invalid name for a preprocessing symbol; '' is not a valid identifier", errorLines[0]);
            Assert.Equal("warning CS2029: Invalid name for a preprocessing symbol; 'b@' is not a valid identifier", errorLines[1]);

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", src.ToString(), "/define:a,b@;" }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("warning CS2029: Invalid name for a preprocessing symbol; 'b@' is not a valid identifier", outWriter.ToString().Trim());

            //Bug 531612 - Native would normally not give the 2nd warning
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", src.ToString(), @"/define:OE_WIN32=-1:LANG_HOST_EN=-1:LANG_OE_EN=-1:LANG_PRJ_EN=-1:HOST_COM20SDKEVERETT=-1:EXEMODE=-1:OE_NT5=-1:Win32=-1", @"/d:TRACE=TRUE,DEBUG=TRUE" }).Run(outWriter);
            Assert.Equal(0, exitCode);
            errorLines = outWriter.ToString().Trim().Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
            Assert.Equal(@"warning CS2029: Invalid name for a preprocessing symbol; 'OE_WIN32=-1:LANG_HOST_EN=-1:LANG_OE_EN=-1:LANG_PRJ_EN=-1:HOST_COM20SDKEVERETT=-1:EXEMODE=-1:OE_NT5=-1:Win32=-1' is not a valid identifier", errorLines[0]);
            Assert.Equal(@"warning CS2029: Invalid name for a preprocessing symbol; 'TRACE=TRUE' is not a valid identifier", errorLines[1]);

            CleanupAllGeneratedFiles(src.Path);
        }

        [WorkItem(733242, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/733242")]
        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void Bug733242()
        {
            var dir = Temp.CreateDirectory();

            var src = dir.CreateFile("a.cs");
            src.WriteAllText(
@"
/// <summary>ABC...XYZ</summary>
class C {} ");

            var xml = dir.CreateFile("a.xml");
            xml.WriteAllText("EMPTY");

            using (var xmlFileHandle = File.Open(xml.ToString(), FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.ReadWrite))
            {
                var output = ProcessUtilities.RunAndGetOutput(s_CSharpCompilerExecutable, String.Format("/nologo /t:library /doc:\"{1}\" \"{0}\"", src.ToString(), xml.ToString()), startFolder: dir.ToString());
                Assert.Equal("", output.Trim());

                Assert.True(File.Exists(Path.Combine(dir.ToString(), "a.xml")));

                using (var reader = new StreamReader(xmlFileHandle))
                {
                    var content = reader.ReadToEnd();
                    Assert.Equal(
@"<?xml version=""1.0""?>
<doc>
    <assembly>
        <name>a</name>
    </assembly>
    <members>
        <member name=""T:C"">
            <summary>ABC...XYZ</summary>
        </member>
    </members>
</doc>".Trim(), content.Trim());
                }
            }

            CleanupAllGeneratedFiles(src.Path);
            CleanupAllGeneratedFiles(xml.Path);
        }

        [WorkItem(768605, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/768605")]
        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void Bug768605()
        {
            var dir = Temp.CreateDirectory();

            var src = dir.CreateFile("a.cs");
            src.WriteAllText(
@"
/// <summary>ABC</summary>
class C {}
/// <summary>XYZ</summary>
class E {}
");

            var xml = dir.CreateFile("a.xml");
            xml.WriteAllText("EMPTY");

            var output = ProcessUtilities.RunAndGetOutput(s_CSharpCompilerExecutable, String.Format("/nologo /t:library /doc:\"{1}\" \"{0}\"", src.ToString(), xml.ToString()), startFolder: dir.ToString());
            Assert.Equal("", output.Trim());

            using (var reader = new StreamReader(xml.ToString()))
            {
                var content = reader.ReadToEnd();
                Assert.Equal(
@"<?xml version=""1.0""?>
<doc>
    <assembly>
        <name>a</name>
    </assembly>
    <members>
        <member name=""T:C"">
            <summary>ABC</summary>
        </member>
        <member name=""T:E"">
            <summary>XYZ</summary>
        </member>
    </members>
</doc>".Trim(), content.Trim());
            }

            src.WriteAllText(
@"
/// <summary>ABC</summary>
class C {}
");

            output = ProcessUtilities.RunAndGetOutput(s_CSharpCompilerExecutable, String.Format("/nologo /t:library /doc:\"{1}\" \"{0}\"", src.ToString(), xml.ToString()), startFolder: dir.ToString());
            Assert.Equal("", output.Trim());

            using (var reader = new StreamReader(xml.ToString()))
            {
                var content = reader.ReadToEnd();
                Assert.Equal(
@"<?xml version=""1.0""?>
<doc>
    <assembly>
        <name>a</name>
    </assembly>
    <members>
        <member name=""T:C"">
            <summary>ABC</summary>
        </member>
    </members>
</doc>".Trim(), content.Trim());
            }

            CleanupAllGeneratedFiles(src.Path);
            CleanupAllGeneratedFiles(xml.Path);
        }

        [Fact]
        public void ParseFullpaths()
        {
            var parsedArgs = DefaultParse(new[] { "a.cs" }, WorkingDirectory);
            Assert.False(parsedArgs.PrintFullPaths);

            parsedArgs = DefaultParse(new[] { "a.cs", "/fullpaths" }, WorkingDirectory);
            Assert.True(parsedArgs.PrintFullPaths);

            parsedArgs = DefaultParse(new[] { "a.cs", "/fullpaths:" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_BadSwitch, parsedArgs.Errors.First().Code);

            parsedArgs = DefaultParse(new[] { "a.cs", "/fullpaths: " }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_BadSwitch, parsedArgs.Errors.First().Code);

            parsedArgs = DefaultParse(new[] { "a.cs", "/fullpaths+" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_BadSwitch, parsedArgs.Errors.First().Code);

            parsedArgs = DefaultParse(new[] { "a.cs", "/fullpaths+:" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_BadSwitch, parsedArgs.Errors.First().Code);
        }

        [Fact]
        public void CheckFullpaths()
        {
            string source = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"
public class C
{
    public static void Main()
    {
        string x;
    }
}").Path;

            var baseDir = Path.GetDirectoryName(source);
            var fileName = Path.GetFileName(source);

            // Checks the base case without /fullpaths (expect to see relative path name)
            //      c:\temp> csc.exe c:\temp\a.cs
            //      a.cs(6,16): warning CS0168: The variable 'x' is declared but never used
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, baseDir, new[] { source, "/preferreduilang:en" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Contains(fileName + "(6,16): warning CS0168: The variable 'x' is declared but never used", outWriter.ToString(), StringComparison.Ordinal);

            // Checks the base case without /fullpaths when the file is located in the sub-folder (expect to see relative path name)
            //      c:\temp> csc.exe c:\temp\example\a.cs
            //      example\a.cs(6,16): warning CS0168: The variable 'x' is declared but never used
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            csc = CreateCSharpCompiler(null, Directory.GetParent(baseDir).FullName, new[] { source, "/preferreduilang:en" });
            exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Contains(fileName + "(6,16): warning CS0168: The variable 'x' is declared but never used", outWriter.ToString(), StringComparison.Ordinal);
            Assert.DoesNotContain(source, outWriter.ToString(), StringComparison.Ordinal);

            // Checks the base case without /fullpaths when the file is not located under the base directory (expect to see the full path name)
            //      c:\temp> csc.exe c:\test\a.cs
            //      c:\test\a.cs(6,16): warning CS0168: The variable 'x' is declared but never used
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            csc = CreateCSharpCompiler(null, Temp.CreateDirectory().Path, new[] { source, "/preferreduilang:en" });
            exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Contains(source + "(6,16): warning CS0168: The variable 'x' is declared but never used", outWriter.ToString(), StringComparison.Ordinal);

            // Checks the case with /fullpaths (expect to see the full paths)
            //      c:\temp> csc.exe c:\temp\a.cs /fullpaths
            //      c:\temp\a.cs(6,16): warning CS0168: The variable 'x' is declared but never used
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            csc = CreateCSharpCompiler(null, baseDir, new[] { source, "/fullpaths", "/preferreduilang:en" });
            exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Contains(source + @"(6,16): warning CS0168: The variable 'x' is declared but never used", outWriter.ToString(), StringComparison.Ordinal);

            // Checks the base case without /fullpaths when the file is located in the sub-folder (expect to see the full path name)
            //      c:\temp> csc.exe c:\temp\example\a.cs /fullpaths
            //      c:\temp\example\a.cs(6,16): warning CS0168: The variable 'x' is declared but never used
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            csc = CreateCSharpCompiler(null, Directory.GetParent(baseDir).FullName, new[] { source, "/preferreduilang:en", "/fullpaths" });
            exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Contains(source + "(6,16): warning CS0168: The variable 'x' is declared but never used", outWriter.ToString(), StringComparison.Ordinal);

            // Checks the base case without /fullpaths when the file is not located under the base directory (expect to see the full path name)
            //      c:\temp> csc.exe c:\test\a.cs /fullpaths
            //      c:\test\a.cs(6,16): warning CS0168: The variable 'x' is declared but never used
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            csc = CreateCSharpCompiler(null, Temp.CreateDirectory().Path, new[] { source, "/preferreduilang:en", "/fullpaths" });
            exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Contains(source + "(6,16): warning CS0168: The variable 'x' is declared but never used", outWriter.ToString(), StringComparison.Ordinal);

            CleanupAllGeneratedFiles(source);
            CleanupAllGeneratedFiles(Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(source)), Path.GetFileName(source)));
        }

        [Fact]
        public void DefaultResponseFile()
        {
            var sdkDirectory = SdkDirectory;
            MockCSharpCompiler csc = new MockCSharpCompiler(
                GetDefaultResponseFilePath(),
                RuntimeUtilities.CreateBuildPaths(WorkingDirectory, sdkDirectory),
                new string[0]);
            AssertEx.Equal(csc.Arguments.MetadataReferences.Select(r => r.Reference), new string[]
            {
                MscorlibFullPath,
                "Accessibility.dll",
                "Microsoft.CSharp.dll",
                "System.Configuration.dll",
                "System.Configuration.Install.dll",
                "System.Core.dll",
                "System.Data.dll",
                "System.Data.DataSetExtensions.dll",
                "System.Data.Linq.dll",
                "System.Data.OracleClient.dll",
                "System.Deployment.dll",
                "System.Design.dll",
                "System.DirectoryServices.dll",
                "System.dll",
                "System.Drawing.Design.dll",
                "System.Drawing.dll",
                "System.EnterpriseServices.dll",
                "System.Management.dll",
                "System.Messaging.dll",
                "System.Runtime.Remoting.dll",
                "System.Runtime.Serialization.dll",
                "System.Runtime.Serialization.Formatters.Soap.dll",
                "System.Security.dll",
                "System.ServiceModel.dll",
                "System.ServiceModel.Web.dll",
                "System.ServiceProcess.dll",
                "System.Transactions.dll",
                "System.Web.dll",
                "System.Web.Extensions.Design.dll",
                "System.Web.Extensions.dll",
                "System.Web.Mobile.dll",
                "System.Web.RegularExpressions.dll",
                "System.Web.Services.dll",
                "System.Windows.Forms.dll",
                "System.Workflow.Activities.dll",
                "System.Workflow.ComponentModel.dll",
                "System.Workflow.Runtime.dll",
                "System.Xml.dll",
                "System.Xml.Linq.dll",
            }, StringComparer.OrdinalIgnoreCase);
        }

        [Fact]
        public void DefaultResponseFileNoConfig()
        {
            MockCSharpCompiler csc = CreateCSharpCompiler(GetDefaultResponseFilePath(), WorkingDirectory, new[] { "/noconfig" });
            Assert.Equal(csc.Arguments.MetadataReferences.Select(r => r.Reference), new string[]
            {
                MscorlibFullPath,
            }, StringComparer.OrdinalIgnoreCase);
        }

        [Fact, WorkItem(545954, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545954")]
        public void TestFilterParseDiagnostics()
        {
            string source = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"
#pragma warning disable 440
using global = A; // CS0440
class A
{
static void Main() {
#pragma warning suppress 440
}
}").Path;

            var baseDir = Path.GetDirectoryName(source);
            var fileName = Path.GetFileName(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, baseDir, new[] { "/nologo", "/preferreduilang:en", source.ToString() }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal(Path.GetFileName(source) + "(7,17): warning CS1634: Expected 'disable' or 'restore'", outWriter.ToString().Trim());

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, baseDir, new[] { "/nologo", "/nowarn:1634", source.ToString() }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString().Trim());

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, baseDir, new[] { "/nologo", "/preferreduilang:en", Path.Combine(baseDir, "nonexistent.cs"), source.ToString() }).Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Equal("error CS2001: Source file '" + Path.Combine(baseDir, "nonexistent.cs") + "' could not be found.", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(source);
        }

        [Fact, WorkItem(546058, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546058")]
        public void TestNoWarnParseDiagnostics()
        {
            string source = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"
class Test
{
 static void Main()
 {
  //Generates warning CS1522: Empty switch block
  switch (1)   { }

  //Generates warning CS0642: Possible mistaken empty statement
  while (false) ;
  {  }
 }
}
").Path;

            var baseDir = Path.GetDirectoryName(source);
            var fileName = Path.GetFileName(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/nowarn:1522,642", source.ToString() }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(source);
        }

        [Fact, WorkItem(41610, "https://github.com/dotnet/roslyn/issues/41610")]
        public void TestWarnAsError_CS8632()
        {
            string source = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"
public class C
{
    public string? field;
    public static void Main()
    {
    }
}
").Path;

            var baseDir = Path.GetDirectoryName(source);
            var fileName = Path.GetFileName(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, baseDir, new[] { "/nologo", "/preferreduilang:en", "/warn:3", "/warnaserror:nullable", source.ToString() }).Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Equal(
$@"{fileName}(4,18): error CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(source);
        }

        [Fact, WorkItem(546076, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546076")]
        public void TestWarnAsError_CS1522()
        {
            string source = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"
public class Test
{
    // CS0169 (level 3)
    private int x;
    // CS0109 (level 4)
    public new void Method() { }
    public static int Main()
    {
        int i = 5;
        // CS1522 (level 1)
        switch (i) { }
        return 0;
        // CS0162 (level 2)
        i = 6;
    }
}
").Path;

            var baseDir = Path.GetDirectoryName(source);
            var fileName = Path.GetFileName(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, baseDir, new[] { "/nologo", "/preferreduilang:en", "/warn:3", "/warnaserror", source.ToString() }).Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Equal(
$@"{fileName}(12,20): error CS1522: Empty switch block
{fileName}(15,9): error CS0162: Unreachable code detected
{fileName}(5,17): error CS0169: The field 'Test.x' is never used", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(source);
        }

        [Fact(), WorkItem(546025, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546025")]
        public void TestWin32ResWithBadResFile_CS1583ERR_BadWin32Res_01()
        {
            string source = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"class Test { static void Main() {} }").Path;
            string badres = Temp.CreateFile().WriteAllBytes(TestResources.DiagnosticTests.badresfile).Path;

            var baseDir = Path.GetDirectoryName(source);
            var fileName = Path.GetFileName(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, baseDir, new[]
            {
                "/nologo",
                "/preferreduilang:en",
                "/win32res:" + badres,
                source
            }).Run(outWriter);

            Assert.Equal(1, exitCode);
            Assert.Equal("error CS1583: Error reading Win32 resources -- Image is too small.", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(source);
            CleanupAllGeneratedFiles(badres);
        }

        [Fact(), WorkItem(217718, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=217718")]
        public void TestWin32ResWithBadResFile_CS1583ERR_BadWin32Res_02()
        {
            string source = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"class Test { static void Main() {} }").Path;
            string badres = Temp.CreateFile().WriteAllBytes(new byte[] { 0, 0 }).Path;

            var baseDir = Path.GetDirectoryName(source);
            var fileName = Path.GetFileName(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, baseDir, new[]
            {
                "/nologo",
                "/preferreduilang:en",
                "/win32res:" + badres,
                source
            }).Run(outWriter);

            Assert.Equal(1, exitCode);
            Assert.Equal("error CS1583: Error reading Win32 resources -- Unrecognized resource file format.", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(source);
            CleanupAllGeneratedFiles(badres);
        }

        [Fact, WorkItem(546114, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546114")]
        public void TestFilterCommandLineDiagnostics()
        {
            string source = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"
class A
{
static void Main() { }
}").Path;
            var baseDir = Path.GetDirectoryName(source);
            var fileName = Path.GetFileName(source);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/target:library", "/out:goo.dll", "/nowarn:2008" }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString().Trim());

            System.IO.File.Delete(System.IO.Path.Combine(baseDir, "goo.dll"));
            CleanupAllGeneratedFiles(source);
        }

        [Fact, WorkItem(546452, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546452")]
        public void CS1691WRN_BadWarningNumber_Bug15905()
        {
            string source = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"
class Program
{
#pragma warning disable 1998
        public static void Main() { }
#pragma warning restore 1998
} ").Path;
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);

            // Repro case 1
            int exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/warnaserror", source.ToString() }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString().Trim());

            // Repro case 2
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/nowarn:1998", source.ToString() }).Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(source);
        }

        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)]
        public void ExistingPdb()
        {
            var dir = Temp.CreateDirectory();

            var source1 = dir.CreateFile("program1.cs").WriteAllText(@"
class " + new string('a', 10000) + @"
{
    public static void Main()
    {
    }
}");
            var source2 = dir.CreateFile("program2.cs").WriteAllText(@"
class Program2
{
        public static void Main() { }
}");
            var source3 = dir.CreateFile("program3.cs").WriteAllText(@"
class Program3
{
        public static void Main() { }
}");

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);

            int oldSize = 16 * 1024;

            var exe = dir.CreateFile("Program.exe");
            using (var stream = File.OpenWrite(exe.Path))
            {
                byte[] buffer = new byte[oldSize];
                stream.Write(buffer, 0, buffer.Length);
            }

            var pdb = dir.CreateFile("Program.pdb");
            using (var stream = File.OpenWrite(pdb.Path))
            {
                byte[] buffer = new byte[oldSize];
                stream.Write(buffer, 0, buffer.Length);
            }

            int exitCode1 = CreateCSharpCompiler(null, dir.Path, new[] { "/debug:full", "/out:Program.exe", source1.Path }).Run(outWriter);
            Assert.NotEqual(0, exitCode1);

            ValidateZeroes(exe.Path, oldSize);
            ValidateZeroes(pdb.Path, oldSize);

            int exitCode2 = CreateCSharpCompiler(null, dir.Path, new[] { "/debug:full", "/out:Program.exe", source2.Path }).Run(outWriter);
            Assert.Equal(0, exitCode2);

            using (var peFile = File.OpenRead(exe.Path))
            {
                PdbValidation.ValidateDebugDirectory(peFile, null, pdb.Path, hashAlgorithm: default, hasEmbeddedPdb: false, isDeterministic: false);
            }

            Assert.True(new FileInfo(exe.Path).Length < oldSize);
            Assert.True(new FileInfo(pdb.Path).Length < oldSize);

            int exitCode3 = CreateCSharpCompiler(null, dir.Path, new[] { "/debug:full", "/out:Program.exe", source3.Path }).Run(outWriter);
            Assert.Equal(0, exitCode3);

            using (var peFile = File.OpenRead(exe.Path))
            {
                PdbValidation.ValidateDebugDirectory(peFile, null, pdb.Path, hashAlgorithm: default, hasEmbeddedPdb: false, isDeterministic: false);
            }
        }

        private static void ValidateZeroes(string path, int count)
        {
            using (var stream = File.OpenRead(path))
            {
                byte[] buffer = new byte[count];
                stream.Read(buffer, 0, buffer.Length);

                for (int i = 0; i < buffer.Length; i++)
                {
                    if (buffer[i] != 0)
                    {
                        Assert.True(false);
                    }
                }
            }
        }

        /// <summary>
        /// When the output file is open with <see cref="FileShare.Read"/> | <see cref="FileShare.Delete"/>
        /// the compiler should delete the file to unblock build while allowing the reader to continue
        /// reading the previous snapshot of the file content.
        ///
        /// On Windows we can read the original data directly from the stream without creating a memory map.
        /// </summary>
        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)]
        public void FileShareDeleteCompatibility_Windows()
        {
            var dir = Temp.CreateDirectory();
            var libSrc = dir.CreateFile("Lib.cs").WriteAllText("class C { }");
            var libDll = dir.CreateFile("Lib.dll").WriteAllText("DLL");
            var libPdb = dir.CreateFile("Lib.pdb").WriteAllText("PDB");

            var fsDll = new FileStream(libDll.Path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);
            var fsPdb = new FileStream(libPdb.Path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, dir.Path, new[] { "/target:library", "/debug:full", libSrc.Path }).Run(outWriter);
            if (exitCode != 0)
            {
                AssertEx.AssertEqualToleratingWhitespaceDifferences("", outWriter.ToString());
            }

            Assert.Equal(0, exitCode);

            AssertEx.Equal(new byte[] { 0x4D, 0x5A }, ReadBytes(libDll.Path, 2));
            AssertEx.Equal(new[] { (byte)'D', (byte)'L', (byte)'L' }, ReadBytes(fsDll, 3));

            AssertEx.Equal(new byte[] { 0x4D, 0x69 }, ReadBytes(libPdb.Path, 2));
            AssertEx.Equal(new[] { (byte)'P', (byte)'D', (byte)'B' }, ReadBytes(fsPdb, 3));

            fsDll.Dispose();
            fsPdb.Dispose();

            AssertEx.Equal(new[] { "Lib.cs", "Lib.dll", "Lib.pdb" }, Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)).Order());
        }

        /// <summary>
        /// On Linux/Mac <see cref="FileShare.Delete"/> on its own doesn't do anything.
        /// We need to create the actual memory map. This works on Windows as well.
        /// </summary>
        [WorkItem(8896, "https://github.com/dotnet/roslyn/issues/8896")]
        [ConditionalFact(typeof(WindowsDesktopOnly), typeof(IsEnglishLocal), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void FileShareDeleteCompatibility_Xplat()
        {
            var bytes = TestResources.MetadataTests.InterfaceAndClass.CSClasses01;
            var mvid = ReadMvid(new MemoryStream(bytes));

            var dir = Temp.CreateDirectory();
            var libSrc = dir.CreateFile("Lib.cs").WriteAllText("class C { }");
            var libDll = dir.CreateFile("Lib.dll").WriteAllBytes(bytes);
            var libPdb = dir.CreateFile("Lib.pdb").WriteAllBytes(bytes);

            var fsDll = new FileStream(libDll.Path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);
            var fsPdb = new FileStream(libPdb.Path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);

            var peDll = new PEReader(fsDll);
            var pePdb = new PEReader(fsPdb);

            // creates memory map view:
            var imageDll = peDll.GetEntireImage();
            var imagePdb = pePdb.GetEntireImage();

            var output = ProcessUtilities.RunAndGetOutput(s_CSharpCompilerExecutable, $"/target:library /debug:portable \"{libSrc.Path}\"", startFolder: dir.ToString());
            AssertEx.AssertEqualToleratingWhitespaceDifferences($@"
Microsoft (R) Visual C# Compiler version {s_compilerVersion}
Copyright (C) Microsoft Corporation. All rights reserved.", output);

            // reading original content from the memory map:
            Assert.Equal(mvid, ReadMvid(new MemoryStream(imageDll.GetContent().ToArray())));
            Assert.Equal(mvid, ReadMvid(new MemoryStream(imagePdb.GetContent().ToArray())));

            // reading original content directly from the streams:
            fsDll.Position = 0;
            fsPdb.Position = 0;
            Assert.Equal(mvid, ReadMvid(fsDll));
            Assert.Equal(mvid, ReadMvid(fsPdb));

            // reading new content from the file:
            using (var fsNewDll = File.OpenRead(libDll.Path))
            {
                Assert.NotEqual(mvid, ReadMvid(fsNewDll));
            }

            // Portable PDB metadata signature:
            AssertEx.Equal(new[] { (byte)'B', (byte)'S', (byte)'J', (byte)'B' }, ReadBytes(libPdb.Path, 4));

            // dispose PEReaders (they dispose the underlying file streams)
            peDll.Dispose();
            pePdb.Dispose();

            AssertEx.Equal(new[] { "Lib.cs", "Lib.dll", "Lib.pdb" }, Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)).Order());

            // files can be deleted now:
            File.Delete(libSrc.Path);
            File.Delete(libDll.Path);
            File.Delete(libPdb.Path);

            // directory can be deleted (should be empty):
            Directory.Delete(dir.Path, recursive: false);
        }

        private static Guid ReadMvid(Stream stream)
        {
            using (var peReader = new PEReader(stream, PEStreamOptions.LeaveOpen))
            {
                var mdReader = peReader.GetMetadataReader();
                return mdReader.GetGuid(mdReader.GetModuleDefinition().Mvid);
            }
        }

        // Seems like File.SetAttributes(libDll.Path, FileAttributes.ReadOnly) doesn't restrict access to the file on Mac (Linux passes).
        [ConditionalFact(typeof(WindowsOnly)), WorkItem(8939, "https://github.com/dotnet/roslyn/issues/8939")]
        public void FileShareDeleteCompatibility_ReadOnlyFiles()
        {
            var dir = Temp.CreateDirectory();
            var libSrc = dir.CreateFile("Lib.cs").WriteAllText("class C { }");
            var libDll = dir.CreateFile("Lib.dll").WriteAllText("DLL");

            File.SetAttributes(libDll.Path, FileAttributes.ReadOnly);

            var fsDll = new FileStream(libDll.Path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, dir.Path, new[] { "/target:library", "/preferreduilang:en", libSrc.Path }).Run(outWriter);
            Assert.Contains($"error CS2012: Cannot open '{libDll.Path}' for writing", outWriter.ToString());

            AssertEx.Equal(new[] { (byte)'D', (byte)'L', (byte)'L' }, ReadBytes(libDll.Path, 3));
            AssertEx.Equal(new[] { (byte)'D', (byte)'L', (byte)'L' }, ReadBytes(fsDll, 3));

            fsDll.Dispose();

            AssertEx.Equal(new[] { "Lib.cs", "Lib.dll" }, Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)).Order());
        }

        [Fact]
        public void FileShareDeleteCompatibility_ExistingDirectory()
        {
            var dir = Temp.CreateDirectory();
            var libSrc = dir.CreateFile("Lib.cs").WriteAllText("class C { }");
            var libDll = dir.CreateDirectory("Lib.dll");

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, dir.Path, new[] { "/target:library", "/preferreduilang:en", libSrc.Path }).Run(outWriter);
            Assert.Contains($"error CS2012: Cannot open '{libDll.Path}' for writing", outWriter.ToString());
        }

        private byte[] ReadBytes(Stream stream, int count)
        {
            var buffer = new byte[count];
            stream.Read(buffer, 0, count);
            return buffer;
        }

        private byte[] ReadBytes(string path, int count)
        {
            using (var stream = File.OpenRead(path))
            {
                return ReadBytes(stream, count);
            }
        }

        [Fact]
        public void IOFailure_DisposeOutputFile()
        {
            var srcPath = MakeTrivialExe(Temp.CreateDirectory().Path);
            var exePath = Path.Combine(Path.GetDirectoryName(srcPath), "test.exe");
            var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", $"/out:{exePath}", srcPath });
            csc.FileSystem = TestableFileSystem.CreateForStandard(openFileFunc: (file, mode, access, share) =>
            {
                if (file == exePath)
                {
                    return new TestStream(backingStream: new MemoryStream(),
                        dispose: () => { throw new IOException("Fake IOException"); });
                }

                return File.Open(file, mode, access, share);
            });

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            Assert.Equal(1, csc.Run(outWriter));
            Assert.Contains($"error CS0016: Could not write to output file '{exePath}' -- 'Fake IOException'{Environment.NewLine}", outWriter.ToString());
        }

        [Fact]
        public void IOFailure_DisposePdbFile()
        {
            var srcPath = MakeTrivialExe(Temp.CreateDirectory().Path);
            var exePath = Path.Combine(Path.GetDirectoryName(srcPath), "test.exe");
            var pdbPath = Path.ChangeExtension(exePath, "pdb");
            var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", "/debug", $"/out:{exePath}", srcPath });
            csc.FileSystem = TestableFileSystem.CreateForStandard(openFileFunc: (file, mode, access, share) =>
            {
                if (file == pdbPath)
                {
                    return new TestStream(backingStream: new MemoryStream(),
                        dispose: () => { throw new IOException("Fake IOException"); });
                }

                return File.Open(file, mode, access, share);
            });

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            Assert.Equal(1, csc.Run(outWriter));
            Assert.Contains($"error CS0016: Could not write to output file '{pdbPath}' -- 'Fake IOException'{Environment.NewLine}", outWriter.ToString());
        }

        [Fact]
        public void IOFailure_DisposeXmlFile()
        {
            var srcPath = MakeTrivialExe(Temp.CreateDirectory().Path);
            var xmlPath = Path.Combine(Path.GetDirectoryName(srcPath), "test.xml");
            var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", $"/doc:{xmlPath}", srcPath });
            csc.FileSystem = TestableFileSystem.CreateForStandard(openFileFunc: (file, mode, access, share) =>
            {
                if (file == xmlPath)
                {
                    return new TestStream(backingStream: new MemoryStream(),
                        dispose: () => { throw new IOException("Fake IOException"); });
                }

                return File.Open(file, mode, access, share);
            });

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            Assert.Equal(1, csc.Run(outWriter));
            Assert.Equal($"error CS0016: Could not write to output file '{xmlPath}' -- 'Fake IOException'{Environment.NewLine}", outWriter.ToString());
        }

        [Theory]
        [InlineData("portable")]
        [InlineData("full")]
        public void IOFailure_DisposeSourceLinkFile(string format)
        {
            var srcPath = MakeTrivialExe(Temp.CreateDirectory().Path);
            var sourceLinkPath = Path.Combine(Path.GetDirectoryName(srcPath), "test.json");
            var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", "/debug:" + format, $"/sourcelink:{sourceLinkPath}", srcPath });
            csc.FileSystem = TestableFileSystem.CreateForStandard(openFileFunc: (file, mode, access, share) =>
            {
                if (file == sourceLinkPath)
                {
                    return new TestStream(backingStream: new MemoryStream(Encoding.UTF8.GetBytes(@"
{
  ""documents"": {
     ""f:/build/*"" : ""https://raw.githubusercontent.com/my-org/my-project/1111111111111111111111111111111111111111/*""
  }
}
")),
                        dispose: () => { throw new IOException("Fake IOException"); });
                }

                return File.Open(file, mode, access, share);
            });

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            Assert.Equal(1, csc.Run(outWriter));
            Assert.Equal($"error CS0016: Could not write to output file '{sourceLinkPath}' -- 'Fake IOException'{Environment.NewLine}", outWriter.ToString());
        }

        [Fact]
        public void IOFailure_OpenOutputFile()
        {
            string sourcePath = MakeTrivialExe();
            string exePath = Path.Combine(Path.GetDirectoryName(sourcePath), "test.exe");
            var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", $"/out:{exePath}", sourcePath });
            csc.FileSystem = TestableFileSystem.CreateForStandard(openFileFunc: (file, mode, access, share) =>
            {
                if (file == exePath)
                {
                    throw new IOException();
                }

                return File.Open(file, mode, access, share);
            });

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            Assert.Equal(1, csc.Run(outWriter));
            Assert.Contains($"error CS2012: Cannot open '{exePath}' for writing", outWriter.ToString());

            System.IO.File.Delete(sourcePath);
            System.IO.File.Delete(exePath);
            CleanupAllGeneratedFiles(sourcePath);
        }

        [Fact]
        public void IOFailure_OpenPdbFileNotCalled()
        {
            string sourcePath = MakeTrivialExe();
            string exePath = Path.Combine(Path.GetDirectoryName(sourcePath), "test.exe");
            string pdbPath = Path.ChangeExtension(exePath, ".pdb");
            var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/debug-", $"/out:{exePath}", sourcePath });
            csc.FileSystem = TestableFileSystem.CreateForStandard(openFileFunc: (file, mode, access, share) =>
            {
                if (file == pdbPath)
                {
                    throw new IOException();
                }

                return File.Open(file, (FileMode)mode, (FileAccess)access, (FileShare)share);
            });

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            Assert.Equal(0, csc.Run(outWriter));

            System.IO.File.Delete(sourcePath);
            System.IO.File.Delete(exePath);
            System.IO.File.Delete(pdbPath);
            CleanupAllGeneratedFiles(sourcePath);
        }

        [Fact]
        public void IOFailure_OpenXmlFinal()
        {
            string sourcePath = MakeTrivialExe();
            string xmlPath = Path.Combine(WorkingDirectory, "Test.xml");
            var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/preferreduilang:en", "/doc:" + xmlPath, sourcePath });
            csc.FileSystem = TestableFileSystem.CreateForStandard(openFileFunc: (file, mode, access, share) =>
            {
                if (file == xmlPath)
                {
                    throw new IOException();
                }
                else
                {
                    return File.Open(file, (FileMode)mode, (FileAccess)access, (FileShare)share);
                }
            });

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = csc.Run(outWriter);

            var expectedOutput = string.Format("error CS0016: Could not write to output file '{0}' -- 'I/O error occurred.'", xmlPath);
            Assert.Equal(expectedOutput, outWriter.ToString().Trim());

            Assert.NotEqual(0, exitCode);

            System.IO.File.Delete(xmlPath);
            System.IO.File.Delete(sourcePath);
            CleanupAllGeneratedFiles(sourcePath);
        }

        private string MakeTrivialExe(string directory = null)
        {
            return Temp.CreateFile(directory: directory, prefix: "", extension: ".cs").WriteAllText(@"
class Program
{
    public static void Main() { }
} ").Path;
        }

        [Fact, WorkItem(546452, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546452")]
        public void CS1691WRN_BadWarningNumber_AllErrorCodes()
        {
            const int jump = 200;
            for (int i = 0; i < 8000; i += (8000 / jump))
            {
                int startErrorCode = (int)i * jump;
                int endErrorCode = startErrorCode + jump;
                string source = ComputeSourceText(startErrorCode, endErrorCode);

                // Previous versions of the compiler used to report a warning (CS1691)
                // whenever an unrecognized warning code was supplied in a #pragma directive
                // (or via /nowarn /warnaserror flags on the command line).
                // Going forward, we won't generate any warning in such cases. This will make
                // maintenance of backwards compatibility easier (we no longer need to worry
                // about breaking existing projects / command lines if we deprecate / remove
                // an old warning code).
                Test(source, startErrorCode, endErrorCode);
            }
        }

        private static string ComputeSourceText(int startErrorCode, int endErrorCode)
        {
            string pragmaDisableWarnings = String.Empty;

            for (int errorCode = startErrorCode; errorCode < endErrorCode; errorCode++)
            {
                string pragmaDisableStr = @"#pragma warning disable " + errorCode.ToString() + @"
";
                pragmaDisableWarnings += pragmaDisableStr;
            }

            return pragmaDisableWarnings + @"
public class C
{
    public static void Main() { }
}";
        }

        private void Test(string source, int startErrorCode, int endErrorCode)
        {
            string sourcePath = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(source).Path;

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", sourcePath }).Run(outWriter);
            Assert.Equal(0, exitCode);
            var cscOutput = outWriter.ToString().Trim();

            for (int errorCode = startErrorCode; errorCode < endErrorCode; errorCode++)
            {
                Assert.True(cscOutput == string.Empty, "Failed at error code: " + errorCode);
            }

            CleanupAllGeneratedFiles(sourcePath);
        }

        [Fact]
        public void WriteXml()
        {
            var source = @"
/// <summary>
/// A subtype of <see cref=""object""/>.
/// </summary>
public class C { }
";

            var sourcePath = Temp.CreateFile(directory: WorkingDirectory, extension: ".cs").WriteAllText(source).Path;
            string xmlPath = Path.Combine(WorkingDirectory, "Test.xml");
            var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/target:library", "/out:Test.dll", "/doc:" + xmlPath, sourcePath });

            var writer = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = csc.Run(writer);
            if (exitCode != 0)
            {
                Console.WriteLine(writer.ToString());
                Assert.Equal(0, exitCode);
            }

            var bytes = File.ReadAllBytes(xmlPath);
            var actual = new string(Encoding.UTF8.GetChars(bytes));
            var expected = @"
<?xml version=""1.0""?>
<doc>
    <assembly>
        <name>Test</name>
    </assembly>
    <members>
        <member name=""T:C"">
            <summary>
            A subtype of <see cref=""T:System.Object""/>.
            </summary>
        </member>
    </members>
</doc>
";
            Assert.Equal(expected.Trim(), actual.Trim());

            System.IO.File.Delete(xmlPath);
            System.IO.File.Delete(sourcePath);

            CleanupAllGeneratedFiles(sourcePath);
            CleanupAllGeneratedFiles(xmlPath);
        }

        [WorkItem(546468, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546468")]
        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30289")]
        public void CS2002WRN_FileAlreadyIncluded()
        {
            const string cs2002 = @"warning CS2002: Source file '{0}' specified multiple times";

            TempDirectory tempParentDir = Temp.CreateDirectory();
            TempDirectory tempDir = tempParentDir.CreateDirectory("tmpDir");
            TempFile tempFile = tempDir.CreateFile("a.cs").WriteAllText(@"public class A { }");

            // Simple case
            var commandLineArgs = new[] { "a.cs", "a.cs" };
            // warning CS2002: Source file 'a.cs' specified multiple times
            string aWrnString = String.Format(cs2002, "a.cs");
            TestCS2002(commandLineArgs, tempDir.Path, 0, aWrnString);

            // Multiple duplicates
            commandLineArgs = new[] { "a.cs", "a.cs", "a.cs" };
            // warning CS2002: Source file 'a.cs' specified multiple times
            var warnings = new[] { aWrnString };
            TestCS2002(commandLineArgs, tempDir.Path, 0, warnings);

            // Case-insensitive
            commandLineArgs = new[] { "a.cs", "A.cs" };
            // warning CS2002: Source file 'A.cs' specified multiple times
            string AWrnString = String.Format(cs2002, "A.cs");
            TestCS2002(commandLineArgs, tempDir.Path, 0, AWrnString);

            // Different extensions
            tempDir.CreateFile("a.csx");
            commandLineArgs = new[] { "a.cs", "a.csx" };
            // No errors or warnings
            TestCS2002(commandLineArgs, tempDir.Path, 0, String.Empty);

            // Absolute vs Relative
            commandLineArgs = new[] { @"tmpDir\a.cs", tempFile.Path };
            // warning CS2002: Source file 'tmpDir\a.cs' specified multiple times
            string tmpDiraString = String.Format(cs2002, @"tmpDir\a.cs");
            TestCS2002(commandLineArgs, tempParentDir.Path, 0, tmpDiraString);

            // Both relative
            commandLineArgs = new[] { @"tmpDir\..\tmpDir\a.cs", @"tmpDir\a.cs" };
            // warning CS2002: Source file 'tmpDir\a.cs' specified multiple times
            TestCS2002(commandLineArgs, tempParentDir.Path, 0, tmpDiraString);

            // With wild cards
            commandLineArgs = new[] { tempFile.Path, @"tmpDir\*.cs" };
            // warning CS2002: Source file 'tmpDir\a.cs' specified multiple times
            TestCS2002(commandLineArgs, tempParentDir.Path, 0, tmpDiraString);

            // "/recurse" scenarios
            commandLineArgs = new[] { @"/recurse:a.cs", @"tmpDir\a.cs" };
            // warning CS2002: Source file 'tmpDir\a.cs' specified multiple times
            TestCS2002(commandLineArgs, tempParentDir.Path, 0, tmpDiraString);

            commandLineArgs = new[] { @"/recurse:a.cs", @"/recurse:tmpDir\..\tmpDir\*.cs" };
            // warning CS2002: Source file 'tmpDir\a.cs' specified multiple times
            TestCS2002(commandLineArgs, tempParentDir.Path, 0, tmpDiraString);

            // Invalid file/path characters
            const string cs1504 = @"error CS1504: Source file '{0}' could not be opened -- {1}";
            commandLineArgs = new[] { "/preferreduilang:en", tempFile.Path, "tmpDir\a.cs" };
            // error CS1504: Source file '{0}' could not be opened: Illegal characters in path.
            var formattedcs1504Str = String.Format(cs1504, PathUtilities.CombineAbsoluteAndRelativePaths(tempParentDir.Path, "tmpDir\a.cs"), "Illegal characters in path.");
            TestCS2002(commandLineArgs, tempParentDir.Path, 1, formattedcs1504Str);

            commandLineArgs = new[] { tempFile.Path, @"tmpDi\r*a?.cs" };
            var parseDiags = new[] {
                // error CS2021: File name 'tmpDi\r*a?.cs' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(@"tmpDi\r*a?.cs"),
                // error CS2001: Source file 'tmpDi\r*a?.cs' could not be found.
                Diagnostic(ErrorCode.ERR_FileNotFound).WithArguments(@"tmpDi\r*a?.cs")};
            TestCS2002(commandLineArgs, tempParentDir.Path, 1, (string[])null, parseDiags);

            char currentDrive = Directory.GetCurrentDirectory()[0];
            commandLineArgs = new[] { tempFile.Path, currentDrive + @":a.cs" };
            parseDiags = new[] {
                // error CS2021: File name 'e:a.cs' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
                Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(currentDrive + @":a.cs")};
            TestCS2002(commandLineArgs, tempParentDir.Path, 1, (string[])null, parseDiags);

            commandLineArgs = new[] { "/preferreduilang:en", tempFile.Path, @":a.cs" };
            // error CS1504: Source file '{0}' could not be opened: {1}
            var formattedcs1504 = String.Format(cs1504, PathUtilities.CombineAbsoluteAndRelativePaths(tempParentDir.Path, @":a.cs"), @"The given path's format is not supported.");
            TestCS2002(commandLineArgs, tempParentDir.Path, 1, formattedcs1504);

            CleanupAllGeneratedFiles(tempFile.Path);
            System.IO.Directory.Delete(tempParentDir.Path, true);
        }

        private void TestCS2002(string[] commandLineArgs, string baseDirectory, int expectedExitCode, string compileDiagnostic, params DiagnosticDescription[] parseDiagnostics)
        {
            TestCS2002(commandLineArgs, baseDirectory, expectedExitCode, new[] { compileDiagnostic }, parseDiagnostics);
        }

        private void TestCS2002(string[] commandLineArgs, string baseDirectory, int expectedExitCode, string[] compileDiagnostics, params DiagnosticDescription[] parseDiagnostics)
        {
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var allCommandLineArgs = new[] { "/nologo", "/preferreduilang:en", "/t:library" }.Concat(commandLineArgs).ToArray();

            // Verify command line parser diagnostics.
            DefaultParse(allCommandLineArgs, baseDirectory).Errors.Verify(parseDiagnostics);

            // Verify compile.
            int exitCode = CreateCSharpCompiler(null, baseDirectory, allCommandLineArgs).Run(outWriter);
            Assert.Equal(expectedExitCode, exitCode);

            if (parseDiagnostics.IsEmpty())
            {
                // Verify compile diagnostics.
                string outString = String.Empty;
                for (int i = 0; i < compileDiagnostics.Length; i++)
                {
                    if (i != 0)
                    {
                        outString += @"
";
                    }

                    outString += compileDiagnostics[i];
                }

                Assert.Equal(outString, outWriter.ToString().Trim());
            }
            else
            {
                Assert.Null(compileDiagnostics);
            }
        }

        [Fact]
        public void ErrorLineEnd()
        {
            var tree = SyntaxFactory.ParseSyntaxTree("class C public { }", path: "goo");

            var comp = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/errorendlocation" });
            var loc = new SourceLocation(tree.GetCompilationUnitRoot().FindToken(6));
            var diag = new CSDiagnostic(new DiagnosticInfo(MessageProvider.Instance, (int)ErrorCode.ERR_MetadataNameTooLong), loc);
            var text = comp.DiagnosticFormatter.Format(diag);

            string stringStart = "goo(1,7,1,8)";

            Assert.Equal(stringStart, text.Substring(0, stringStart.Length));
        }

        [Fact]
        public void ReportAnalyzer()
        {
            var parsedArgs1 = DefaultParse(new[] { "a.cs", "/reportanalyzer" }, WorkingDirectory);
            Assert.True(parsedArgs1.ReportAnalyzer);

            var parsedArgs2 = DefaultParse(new[] { "a.cs", "" }, WorkingDirectory);
            Assert.False(parsedArgs2.ReportAnalyzer);
        }

        [Fact]
        public void ReportAnalyzerOutput()
        {
            var srcFile = Temp.CreateFile().WriteAllText(@"class C {}");
            var srcDirectory = Path.GetDirectoryName(srcFile.Path);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, srcDirectory, new[] { "/reportanalyzer", "/t:library", "/a:" + Assembly.GetExecutingAssembly().Location, srcFile.Path });
            var exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            var output = outWriter.ToString();
            Assert.Contains(CodeAnalysisResources.AnalyzerExecutionTimeColumnHeader, output, StringComparison.Ordinal);
            Assert.Contains("WarningDiagnosticAnalyzer (Warning01)", output, StringComparison.Ordinal);
            CleanupAllGeneratedFiles(srcFile.Path);
        }

        [Fact]
        [WorkItem(40926, "https://github.com/dotnet/roslyn/issues/40926")]
        public void SkipAnalyzersParse()
        {
            var parsedArgs = DefaultParse(new[] { "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.SkipAnalyzers);

            parsedArgs = DefaultParse(new[] { "/skipanalyzers+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.SkipAnalyzers);

            parsedArgs = DefaultParse(new[] { "/skipanalyzers", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.SkipAnalyzers);

            parsedArgs = DefaultParse(new[] { "/SKIPANALYZERS+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.SkipAnalyzers);

            parsedArgs = DefaultParse(new[] { "/skipanalyzers-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.SkipAnalyzers);

            parsedArgs = DefaultParse(new[] { "/skipanalyzers-", "/skipanalyzers+", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.True(parsedArgs.SkipAnalyzers);

            parsedArgs = DefaultParse(new[] { "/skipanalyzers", "/skipanalyzers-", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.False(parsedArgs.SkipAnalyzers);
        }

        [Theory, CombinatorialData]
        [WorkItem(40926, "https://github.com/dotnet/roslyn/issues/40926")]
        public void SkipAnalyzersSemantics(bool skipAnalyzers)
        {
            var srcFile = Temp.CreateFile().WriteAllText(@"class C {}");
            var srcDirectory = Path.GetDirectoryName(srcFile.Path);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var skipAnalyzersFlag = "/skipanalyzers" + (skipAnalyzers ? "+" : "-");
            var csc = CreateCSharpCompiler(null, srcDirectory, new[] { skipAnalyzersFlag, "/reportanalyzer", "/t:library", "/a:" + Assembly.GetExecutingAssembly().Location, srcFile.Path });
            var exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            var output = outWriter.ToString();
            if (skipAnalyzers)
            {
                Assert.DoesNotContain(CodeAnalysisResources.AnalyzerExecutionTimeColumnHeader, output, StringComparison.Ordinal);
                Assert.DoesNotContain(new WarningDiagnosticAnalyzer().ToString(), output, StringComparison.Ordinal);
            }
            else
            {
                Assert.Contains(CodeAnalysisResources.AnalyzerExecutionTimeColumnHeader, output, StringComparison.Ordinal);
                Assert.Contains(new WarningDiagnosticAnalyzer().ToString(), output, StringComparison.Ordinal);
            }

            CleanupAllGeneratedFiles(srcFile.Path);
        }

        [Fact]
        [WorkItem(24835, "https://github.com/dotnet/roslyn/issues/24835")]
        public void TestCompilationSuccessIfOnlySuppressedDiagnostics()
        {
            var srcFile = Temp.CreateFile().WriteAllText(@"
#pragma warning disable Warning01
class C { }
");

            var errorLog = Temp.CreateFile();
            var csc = CreateCSharpCompiler(
                null,
                workingDirectory: Path.GetDirectoryName(srcFile.Path),
                args: new[] { "/errorlog:" + errorLog.Path, "/warnaserror+", "/nologo", "/t:library", srcFile.Path },
                analyzers: ImmutableArray.Create<DiagnosticAnalyzer>(new WarningDiagnosticAnalyzer()));

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = csc.Run(outWriter);

            // Previously, the compiler would return error code 1 without printing any diagnostics
            Assert.Empty(outWriter.ToString());
            Assert.Equal(0, exitCode);

            CleanupAllGeneratedFiles(srcFile.Path);
            CleanupAllGeneratedFiles(errorLog.Path);
        }

        [Fact]
        [WorkItem(1759, "https://github.com/dotnet/roslyn/issues/1759")]
        public void AnalyzerDiagnosticThrowsInGetMessage()
        {
            var srcFile = Temp.CreateFile().WriteAllText(@"class C {}");
            var srcDirectory = Path.GetDirectoryName(srcFile.Path);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/t:library", srcFile.Path },
               analyzers: ImmutableArray.Create<DiagnosticAnalyzer>(new AnalyzerThatThrowsInGetMessage()));

            var exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            var output = outWriter.ToString();

            // Verify that the diagnostic reported by AnalyzerThatThrowsInGetMessage is reported, though it doesn't have the message.
            Assert.Contains(AnalyzerThatThrowsInGetMessage.Rule.Id, output, StringComparison.Ordinal);

            // Verify that the analyzer exception diagnostic for the exception throw in AnalyzerThatThrowsInGetMessage is also reported.
            Assert.Contains(AnalyzerExecutor.AnalyzerExceptionDiagnosticId, output, StringComparison.Ordinal);
            Assert.Contains(nameof(NotImplementedException), output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(srcFile.Path);
        }

        [Fact]
        [WorkItem(3707, "https://github.com/dotnet/roslyn/issues/3707")]
        public void AnalyzerExceptionDiagnosticCanBeConfigured()
        {
            var srcFile = Temp.CreateFile().WriteAllText(@"class C {}");
            var srcDirectory = Path.GetDirectoryName(srcFile.Path);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/t:library", $"/warnaserror:{AnalyzerExecutor.AnalyzerExceptionDiagnosticId}", srcFile.Path },
               analyzers: ImmutableArray.Create<DiagnosticAnalyzer>(new AnalyzerThatThrowsInGetMessage()));

            var exitCode = csc.Run(outWriter);
            Assert.NotEqual(0, exitCode);
            var output = outWriter.ToString();

            // Verify that the analyzer exception diagnostic for the exception throw in AnalyzerThatThrowsInGetMessage is also reported.
            Assert.Contains(AnalyzerExecutor.AnalyzerExceptionDiagnosticId, output, StringComparison.Ordinal);
            Assert.Contains(nameof(NotImplementedException), output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(srcFile.Path);
        }

        [Fact]
        [WorkItem(4589, "https://github.com/dotnet/roslyn/issues/4589")]
        public void AnalyzerReportsMisformattedDiagnostic()
        {
            var srcFile = Temp.CreateFile().WriteAllText(@"class C {}");
            var srcDirectory = Path.GetDirectoryName(srcFile.Path);

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/t:library", srcFile.Path },
               analyzers: ImmutableArray.Create<DiagnosticAnalyzer>(new AnalyzerReportingMisformattedDiagnostic()));

            var exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            var output = outWriter.ToString();

            // Verify that the diagnostic reported by AnalyzerReportingMisformattedDiagnostic is reported with the message format string, instead of the formatted message.
            Assert.Contains(AnalyzerThatThrowsInGetMessage.Rule.Id, output, StringComparison.Ordinal);
            Assert.Contains(AnalyzerThatThrowsInGetMessage.Rule.MessageFormat.ToString(CultureInfo.InvariantCulture), output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(srcFile.Path);
        }

        [Fact]
        public void ErrorPathsFromLineDirectives()
        {
            string sampleProgram = @"
#line 10 "".."" //relative path
using System*
";
            var syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram, path: "filename.cs");
            var comp = CreateCSharpCompiler(null, WorkingDirectory, new string[] { });
            var text = comp.DiagnosticFormatter.Format(syntaxTree.GetDiagnostics().First());
            //Pull off the last segment of the current directory.
            var expectedPath = Path.GetDirectoryName(WorkingDirectory);
            //the end of the diagnostic's "file" portion should be signaled with the '(' of the line/col info.
            Assert.Equal('(', text[expectedPath.Length]);

            sampleProgram = @"
#line 10 "".>"" //invalid path character
using System*
";
            syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram, path: "filename.cs");
            text = comp.DiagnosticFormatter.Format(syntaxTree.GetDiagnostics().First());
            Assert.True(text.StartsWith(".>", StringComparison.Ordinal));

            sampleProgram = @"
#line 10 ""http://goo.bar/baz.aspx"" //URI
using System*
";
            syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram, path: "filename.cs");
            text = comp.DiagnosticFormatter.Format(syntaxTree.GetDiagnostics().First());
            Assert.True(text.StartsWith("http://goo.bar/baz.aspx", StringComparison.Ordinal));
        }

        [WorkItem(1119609, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1119609")]
        [Fact]
        public void PreferredUILang()
        {
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/preferreduilang" }).Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Contains("CS2006", outWriter.ToString(), StringComparison.Ordinal);

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/preferreduilang:" }).Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Contains("CS2006", outWriter.ToString(), StringComparison.Ordinal);

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/preferreduilang:zz" }).Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Contains("CS2038", outWriter.ToString(), StringComparison.Ordinal);

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/preferreduilang:en-zz" }).Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Contains("CS2038", outWriter.ToString(), StringComparison.Ordinal);

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/preferreduilang:en-US" }).Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.DoesNotContain("CS2038", outWriter.ToString(), StringComparison.Ordinal);

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/preferreduilang:de" }).Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.DoesNotContain("CS2038", outWriter.ToString(), StringComparison.Ordinal);

            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/preferreduilang:de-AT" }).Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.DoesNotContain("CS2038", outWriter.ToString(), StringComparison.Ordinal);
        }

        [WorkItem(531263, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/531263")]
        [Fact]
        public void EmptyFileName()
        {
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = CreateCSharpCompiler(null, WorkingDirectory, new[] { "" }).Run(outWriter);
            Assert.NotEqual(0, exitCode);

            // error CS2021: File name '' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long
            Assert.Contains("CS2021", outWriter.ToString(), StringComparison.Ordinal);
        }

        [WorkItem(747219, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/747219")]
        [Fact]
        public void NoInfoDiagnostics()
        {
            string filePath = Temp.CreateFile().WriteAllText(@"
using System.Diagnostics; // Unused.
").Path;
            var cmd = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/nologo", "/target:library", filePath });
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString().Trim());

            CleanupAllGeneratedFiles(filePath);
        }

        [Fact]
        public void RuntimeMetadataVersion()
        {
            var parsedArgs = DefaultParse(new[] { "a.cs", "/runtimemetadataversion" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_SwitchNeedsString, parsedArgs.Errors.First().Code);

            parsedArgs = DefaultParse(new[] { "a.cs", "/runtimemetadataversion:" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_SwitchNeedsString, parsedArgs.Errors.First().Code);

            parsedArgs = DefaultParse(new[] { "a.cs", "/runtimemetadataversion:  " }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Length);
            Assert.Equal((int)ErrorCode.ERR_SwitchNeedsString, parsedArgs.Errors.First().Code);

            parsedArgs = DefaultParse(new[] { "a.cs", "/runtimemetadataversion:v4.0.30319" }, WorkingDirectory);
            Assert.Equal(0, parsedArgs.Errors.Length);
            Assert.Equal("v4.0.30319", parsedArgs.EmitOptions.RuntimeMetadataVersion);

            parsedArgs = DefaultParse(new[] { "a.cs", "/runtimemetadataversion:-_+@%#*^" }, WorkingDirectory);
            Assert.Equal(0, parsedArgs.Errors.Length);
            Assert.Equal("-_+@%#*^", parsedArgs.EmitOptions.RuntimeMetadataVersion);

            var comp = CreateEmptyCompilation(string.Empty);
            Assert.Equal("v4.0.30319", ModuleMetadata.CreateFromImage(comp.EmitToArray(new EmitOptions(runtimeMetadataVersion: "v4.0.30319"))).Module.MetadataVersion);

            comp = CreateEmptyCompilation(string.Empty);
            Assert.Equal("_+@%#*^", ModuleMetadata.CreateFromImage(comp.EmitToArray(new EmitOptions(runtimeMetadataVersion: "_+@%#*^"))).Module.MetadataVersion);
        }

        [WorkItem(715339, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/715339")]
        [ConditionalFact(typeof(WindowsOnly))]
        public void WRN_InvalidSearchPathDir()
        {
            var baseDir = Temp.CreateDirectory();
            var sourceFile = baseDir.CreateFile("Source.cs");

            var invalidPath = "::";
            var nonExistentPath = "DoesNotExist";

            // lib switch
            DefaultParse(new[] { "/lib:" + invalidPath, sourceFile.Path }, WorkingDirectory).Errors.Verify(
                // warning CS1668: Invalid search path '::' specified in '/LIB option' -- 'path is too long or invalid'
                Diagnostic(ErrorCode.WRN_InvalidSearchPathDir).WithArguments("::", "/LIB option", "path is too long or invalid"));
            DefaultParse(new[] { "/lib:" + nonExistentPath, sourceFile.Path }, WorkingDirectory).Errors.Verify(
                // warning CS1668: Invalid search path 'DoesNotExist' specified in '/LIB option' -- 'directory does not exist'
                Diagnostic(ErrorCode.WRN_InvalidSearchPathDir).WithArguments("DoesNotExist", "/LIB option", "directory does not exist"));

            // LIB environment variable
            DefaultParse(new[] { sourceFile.Path }, WorkingDirectory, additionalReferenceDirectories: invalidPath).Errors.Verify(
                // warning CS1668: Invalid search path '::' specified in 'LIB environment variable' -- 'path is too long or invalid'
                Diagnostic(ErrorCode.WRN_InvalidSearchPathDir).WithArguments("::", "LIB environment variable", "path is too long or invalid"));
            DefaultParse(new[] { sourceFile.Path }, WorkingDirectory, additionalReferenceDirectories: nonExistentPath).Errors.Verify(
                // warning CS1668: Invalid search path 'DoesNotExist' specified in 'LIB environment variable' -- 'directory does not exist'
                Diagnostic(ErrorCode.WRN_InvalidSearchPathDir).WithArguments("DoesNotExist", "LIB environment variable", "directory does not exist"));

            CleanupAllGeneratedFiles(sourceFile.Path);
        }

        [WorkItem(650083, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/650083")]
        [InlineData("a.cs /t:library /appconfig:.\\aux.config")]
        [InlineData("a.cs /out:com1.dll")]
        [InlineData("a.cs /doc:..\\lpt2.xml")]
        [InlineData("a.cs /pdb:..\\prn.pdb")]
        [Theory]
        public void ReservedDeviceNameAsFileName(string commandLine)
        {
            var parsedArgs = DefaultParse(commandLine.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries), WorkingDirectory);
            if (ExecutionConditionUtil.OperatingSystemRestrictsFileNames)
            {
                Assert.Equal(1, parsedArgs.Errors.Length);
                Assert.Equal((int)ErrorCode.FTL_InvalidInputFileName, parsedArgs.Errors.First().Code);
            }
            else
            {
                Assert.Equal(0, parsedArgs.Errors.Length);
            }
        }

        [Fact]
        public void ReservedDeviceNameAsFileName2()
        {
            string filePath = Temp.CreateFile().WriteAllText(@"class C {}").Path;
            // make sure reserved device names don't
            var cmd = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/r:com2.dll", "/target:library", "/preferreduilang:en", filePath });
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Contains("error CS0006: Metadata file 'com2.dll' could not be found", outWriter.ToString(), StringComparison.Ordinal);

            cmd = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/link:..\\lpt8.dll", "/target:library", "/preferreduilang:en", filePath });
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = cmd.Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Contains("error CS0006: Metadata file '..\\lpt8.dll' could not be found", outWriter.ToString(), StringComparison.Ordinal);

            cmd = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/lib:aux", "/preferreduilang:en", filePath });
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = cmd.Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Contains("warning CS1668: Invalid search path 'aux' specified in '/LIB option' -- 'directory does not exist'", outWriter.ToString(), StringComparison.Ordinal);

            CleanupAllGeneratedFiles(filePath);
        }

        [Fact]
        public void ParseFeatures()
        {
            var args = DefaultParse(new[] { "/features:Test", "a.vb" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.Equal("Test", args.ParseOptions.Features.Single().Key);

            args = DefaultParse(new[] { "/features:Test", "a.vb", "/Features:Experiment" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.Equal(2, args.ParseOptions.Features.Count);
            Assert.True(args.ParseOptions.Features.ContainsKey("Test"));
            Assert.True(args.ParseOptions.Features.ContainsKey("Experiment"));

            args = DefaultParse(new[] { "/features:Test=false,Key=value", "a.vb" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.True(args.ParseOptions.Features.SetEquals(new Dictionary<string, string> { { "Test", "false" }, { "Key", "value" } }));

            args = DefaultParse(new[] { "/features:Test,", "a.vb" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.True(args.ParseOptions.Features.SetEquals(new Dictionary<string, string> { { "Test", "true" } }));
        }

        [ConditionalFact(typeof(WindowsOnly), Reason = "https://github.com/dotnet/roslyn/issues/30289")]
        public void ParseAdditionalFile()
        {
            var args = DefaultParse(new[] { "/additionalfile:web.config", "a.cs" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.Equal(Path.Combine(WorkingDirectory, "web.config"), args.AdditionalFiles.Single().Path);

            args = DefaultParse(new[] { "/additionalfile:web.config", "a.cs", "/additionalfile:app.manifest" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.Equal(2, args.AdditionalFiles.Length);
            Assert.Equal(Path.Combine(WorkingDirectory, "web.config"), args.AdditionalFiles[0].Path);
            Assert.Equal(Path.Combine(WorkingDirectory, "app.manifest"), args.AdditionalFiles[1].Path);

            args = DefaultParse(new[] { "/additionalfile:web.config", "a.cs", "/additionalfile:web.config" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.Equal(2, args.AdditionalFiles.Length);
            Assert.Equal(Path.Combine(WorkingDirectory, "web.config"), args.AdditionalFiles[0].Path);
            Assert.Equal(Path.Combine(WorkingDirectory, "web.config"), args.AdditionalFiles[1].Path);

            args = DefaultParse(new[] { "/additionalfile:..\\web.config", "a.cs" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.Equal(Path.Combine(WorkingDirectory, "..\\web.config"), args.AdditionalFiles.Single().Path);

            var baseDir = Temp.CreateDirectory();
            baseDir.CreateFile("web1.config");
            baseDir.CreateFile("web2.config");
            baseDir.CreateFile("web3.config");

            args = DefaultParse(new[] { "/additionalfile:web*.config", "a.cs" }, baseDir.Path);
            args.Errors.Verify();
            Assert.Equal(3, args.AdditionalFiles.Length);
            Assert.Equal(Path.Combine(baseDir.Path, "web1.config"), args.AdditionalFiles[0].Path);
            Assert.Equal(Path.Combine(baseDir.Path, "web2.config"), args.AdditionalFiles[1].Path);
            Assert.Equal(Path.Combine(baseDir.Path, "web3.config"), args.AdditionalFiles[2].Path);

            args = DefaultParse(new[] { "/additionalfile:web.config;app.manifest", "a.cs" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.Equal(2, args.AdditionalFiles.Length);
            Assert.Equal(Path.Combine(WorkingDirectory, "web.config"), args.AdditionalFiles[0].Path);
            Assert.Equal(Path.Combine(WorkingDirectory, "app.manifest"), args.AdditionalFiles[1].Path);

            args = DefaultParse(new[] { "/additionalfile:web.config,app.manifest", "a.cs" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.Equal(2, args.AdditionalFiles.Length);
            Assert.Equal(Path.Combine(WorkingDirectory, "web.config"), args.AdditionalFiles[0].Path);
            Assert.Equal(Path.Combine(WorkingDirectory, "app.manifest"), args.AdditionalFiles[1].Path);

            args = DefaultParse(new[] { "/additionalfile:web.config,app.manifest", "a.cs" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.Equal(2, args.AdditionalFiles.Length);
            Assert.Equal(Path.Combine(WorkingDirectory, "web.config"), args.AdditionalFiles[0].Path);
            Assert.Equal(Path.Combine(WorkingDirectory, "app.manifest"), args.AdditionalFiles[1].Path);

            args = DefaultParse(new[] { @"/additionalfile:""web.config,app.manifest""", "a.cs" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.Equal(1, args.AdditionalFiles.Length);
            Assert.Equal(Path.Combine(WorkingDirectory, "web.config,app.manifest"), args.AdditionalFiles[0].Path);

            args = DefaultParse(new[] { "/additionalfile:web.config:app.manifest", "a.cs" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.Equal(1, args.AdditionalFiles.Length);
            Assert.Equal(Path.Combine(WorkingDirectory, "web.config:app.manifest"), args.AdditionalFiles[0].Path);

            args = DefaultParse(new[] { "/additionalfile", "a.cs" }, WorkingDirectory);
            args.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<file list>", "additionalfile"));
            Assert.Equal(0, args.AdditionalFiles.Length);

            args = DefaultParse(new[] { "/additionalfile:", "a.cs" }, WorkingDirectory);
            args.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<file list>", "additionalfile"));
            Assert.Equal(0, args.AdditionalFiles.Length);
        }

        [Fact]
        public void ParseEditorConfig()
        {
            var args = DefaultParse(new[] { "/analyzerconfig:.editorconfig", "a.cs" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.Equal(Path.Combine(WorkingDirectory, ".editorconfig"), args.AnalyzerConfigPaths.Single());

            args = DefaultParse(new[] { "/analyzerconfig:.editorconfig", "a.cs", "/analyzerconfig:subdir\\.editorconfig" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.Equal(2, args.AnalyzerConfigPaths.Length);
            Assert.Equal(Path.Combine(WorkingDirectory, ".editorconfig"), args.AnalyzerConfigPaths[0]);
            Assert.Equal(Path.Combine(WorkingDirectory, "subdir\\.editorconfig"), args.AnalyzerConfigPaths[1]);

            args = DefaultParse(new[] { "/analyzerconfig:.editorconfig", "a.cs", "/analyzerconfig:.editorconfig" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.Equal(2, args.AnalyzerConfigPaths.Length);
            Assert.Equal(Path.Combine(WorkingDirectory, ".editorconfig"), args.AnalyzerConfigPaths[0]);
            Assert.Equal(Path.Combine(WorkingDirectory, ".editorconfig"), args.AnalyzerConfigPaths[1]);

            args = DefaultParse(new[] { "/analyzerconfig:..\\.editorconfig", "a.cs" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.Equal(Path.Combine(WorkingDirectory, "..\\.editorconfig"), args.AnalyzerConfigPaths.Single());

            args = DefaultParse(new[] { "/analyzerconfig:.editorconfig;subdir\\.editorconfig", "a.cs" }, WorkingDirectory);
            args.Errors.Verify();
            Assert.Equal(2, args.AnalyzerConfigPaths.Length);
            Assert.Equal(Path.Combine(WorkingDirectory, ".editorconfig"), args.AnalyzerConfigPaths[0]);
            Assert.Equal(Path.Combine(WorkingDirectory, "subdir\\.editorconfig"), args.AnalyzerConfigPaths[1]);

            args = DefaultParse(new[] { "/analyzerconfig", "a.cs" }, WorkingDirectory);
            args.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<file list>' for 'analyzerconfig' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<file list>", "analyzerconfig").WithLocation(1, 1));
            Assert.Equal(0, args.AnalyzerConfigPaths.Length);

            args = DefaultParse(new[] { "/analyzerconfig:", "a.cs" }, WorkingDirectory);
            args.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<file list>' for 'analyzerconfig' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<file list>", "analyzerconfig").WithLocation(1, 1));
            Assert.Equal(0, args.AnalyzerConfigPaths.Length);
        }

        [Fact]
        public void NullablePublicOnly()
        {
            string source =
@"namespace System.Runtime.CompilerServices
{
    public sealed class NullableAttribute : Attribute { } // missing constructor
}
public class Program
{
    private object? F = null;
}";
            string errorMessage = "error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor'";

            string filePath = Temp.CreateFile().WriteAllText(source).Path;
            int exitCode;
            string output;

            // No /feature
            (exitCode, output) = compileAndRun(featureOpt: null);
            Assert.Equal(1, exitCode);
            Assert.Contains(errorMessage, output, StringComparison.Ordinal);

            // /features:nullablePublicOnly
            (exitCode, output) = compileAndRun("/features:nullablePublicOnly");
            Assert.Equal(0, exitCode);
            Assert.DoesNotContain(errorMessage, output, StringComparison.Ordinal);

            // /features:nullablePublicOnly=true
            (exitCode, output) = compileAndRun("/features:nullablePublicOnly=true");
            Assert.Equal(0, exitCode);
            Assert.DoesNotContain(errorMessage, output, StringComparison.Ordinal);

            // /features:nullablePublicOnly=false (the value is ignored)
            (exitCode, output) = compileAndRun("/features:nullablePublicOnly=false");
            Assert.Equal(0, exitCode);
            Assert.DoesNotContain(errorMessage, output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(filePath);

            (int, string) compileAndRun(string featureOpt)
            {
                var args = new[] { "/target:library", "/preferreduilang:en", "/langversion:8", "/nullable+", filePath };
                if (featureOpt != null) args = args.Concat(featureOpt).ToArray();
                var compiler = CreateCSharpCompiler(null, WorkingDirectory, args);
                var outWriter = new StringWriter(CultureInfo.InvariantCulture);
                int exitCode = compiler.Run(outWriter);
                return (exitCode, outWriter.ToString());
            };
        }

        // See also NullableContextTests.NullableAnalysisFlags_01().
        [Fact]
        public void NullableAnalysisFlags()
        {
            string source =
@"class Program
{
#nullable enable
    static object F1() => null;
#nullable disable
    static object F2() => null;
}";

            string filePath = Temp.CreateFile().WriteAllText(source).Path;
            string fileName = Path.GetFileName(filePath);

            string[] expectedWarningsAll = new[] { fileName + "(4,27): warning CS8603: Possible null reference return." };
            string[] expectedWarningsNone = Array.Empty<string>();

            AssertEx.Equal(expectedWarningsAll, compileAndRun(featureOpt: null));
            AssertEx.Equal(expectedWarningsAll, compileAndRun("/features:run-nullable-analysis"));
            AssertEx.Equal(expectedWarningsAll, compileAndRun("/features:run-nullable-analysis=always"));
            AssertEx.Equal(expectedWarningsNone, compileAndRun("/features:run-nullable-analysis=never"));
            AssertEx.Equal(expectedWarningsAll, compileAndRun("/features:run-nullable-analysis=ALWAYS")); // unrecognized value (incorrect case) ignored
            AssertEx.Equal(expectedWarningsAll, compileAndRun("/features:run-nullable-analysis=NEVER")); // unrecognized value (incorrect case) ignored
            AssertEx.Equal(expectedWarningsAll, compileAndRun("/features:run-nullable-analysis=true")); // unrecognized value ignored
            AssertEx.Equal(expectedWarningsAll, compileAndRun("/features:run-nullable-analysis=false")); // unrecognized value ignored
            AssertEx.Equal(expectedWarningsAll, compileAndRun("/features:run-nullable-analysis=unknown")); // unrecognized value ignored

            CleanupAllGeneratedFiles(filePath);

            string[] compileAndRun(string featureOpt)
            {
                var args = new[] { "/target:library", "/preferreduilang:en", "/nologo", filePath };
                if (featureOpt != null) args = args.Concat(featureOpt).ToArray();
                var compiler = CreateCSharpCompiler(null, WorkingDirectory, args);
                var outWriter = new StringWriter(CultureInfo.InvariantCulture);
                int exitCode = compiler.Run(outWriter);
                return outWriter.ToString().Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
            };
        }

        [Fact]
        public void Compiler_Uses_DriverCache()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            int sourceCallbackCount = 0;
            var generator = new PipelineCallbackGenerator((ctx) =>
            {
                ctx.RegisterSourceOutput(ctx.ParseOptionsProvider, (spc, po) =>
                {
                    sourceCallbackCount++;
                });
            });

            // with no cache, we'll see the callback execute multiple times
            RunWithNoCache();
            Assert.Equal(1, sourceCallbackCount);

            RunWithNoCache();
            Assert.Equal(2, sourceCallbackCount);

            RunWithNoCache();
            Assert.Equal(3, sourceCallbackCount);

            // now re-run with a cache
            GeneratorDriverCache cache = new GeneratorDriverCache();
            sourceCallbackCount = 0;

            RunWithCache();
            Assert.Equal(1, sourceCallbackCount);

            RunWithCache();
            Assert.Equal(1, sourceCallbackCount);

            RunWithCache();
            Assert.Equal(1, sourceCallbackCount);

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);

            void RunWithNoCache() => VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/langversion:preview" }, generators: new[] { generator.AsSourceGenerator() }, analyzers: null);

            void RunWithCache() => VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/langversion:preview", "/features:enable-generator-cache" }, generators: new[] { generator.AsSourceGenerator() }, driverCache: cache, analyzers: null);
        }

        [Fact]
        public void Compiler_Doesnt_Use_Cache_From_Other_Compilation()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            int sourceCallbackCount = 0;
            var generator = new PipelineCallbackGenerator((ctx) =>
            {
                ctx.RegisterSourceOutput(ctx.ParseOptionsProvider, (spc, po) =>
                {
                    sourceCallbackCount++;
                });
            });

            // now re-run with a cache
            GeneratorDriverCache cache = new GeneratorDriverCache();
            sourceCallbackCount = 0;

            RunWithCache("1.dll");
            Assert.Equal(1, sourceCallbackCount);

            RunWithCache("1.dll");
            Assert.Equal(1, sourceCallbackCount);

            // now emulate a new compilation, and check we were invoked, but only once
            RunWithCache("2.dll");
            Assert.Equal(2, sourceCallbackCount);

            RunWithCache("2.dll");
            Assert.Equal(2, sourceCallbackCount);

            // now re-run our first compilation
            RunWithCache("1.dll");
            Assert.Equal(2, sourceCallbackCount);

            // a new one
            RunWithCache("3.dll");
            Assert.Equal(3, sourceCallbackCount);

            // and another old one
            RunWithCache("2.dll");
            Assert.Equal(3, sourceCallbackCount);

            RunWithCache("1.dll");
            Assert.Equal(3, sourceCallbackCount);

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);

            void RunWithCache(string outputPath) => VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/langversion:preview", "/out:" + outputPath, "/features:enable-generator-cache" }, generators: new[] { generator.AsSourceGenerator() }, driverCache: cache, analyzers: null);
        }

        [Fact]
        public void Compiler_Can_Enable_DriverCache()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            int sourceCallbackCount = 0;
            var generator = new PipelineCallbackGenerator((ctx) =>
            {
                ctx.RegisterSourceOutput(ctx.ParseOptionsProvider, (spc, po) =>
                {
                    sourceCallbackCount++;
                });
            });

            // run with the cache
            GeneratorDriverCache cache = new GeneratorDriverCache();
            sourceCallbackCount = 0;

            RunWithCache();
            Assert.Equal(1, sourceCallbackCount);

            RunWithCache();
            Assert.Equal(1, sourceCallbackCount);

            RunWithCache();
            Assert.Equal(1, sourceCallbackCount);

            // now re-run with the cache disabled
            sourceCallbackCount = 0;

            RunWithCacheDisabled();
            Assert.Equal(1, sourceCallbackCount);

            RunWithCacheDisabled();
            Assert.Equal(2, sourceCallbackCount);

            RunWithCacheDisabled();
            Assert.Equal(3, sourceCallbackCount);

            // now clear the cache as well as disabling, and verify we don't put any entries into it either
            cache = new GeneratorDriverCache();
            sourceCallbackCount = 0;

            RunWithCacheDisabled();
            Assert.Equal(1, sourceCallbackCount);
            Assert.Equal(0, cache.CacheSize);

            RunWithCacheDisabled();
            Assert.Equal(2, sourceCallbackCount);
            Assert.Equal(0, cache.CacheSize);

            RunWithCacheDisabled();
            Assert.Equal(3, sourceCallbackCount);
            Assert.Equal(0, cache.CacheSize);

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);

            void RunWithCache() => VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/langversion:preview", "/features:enable-generator-cache" }, generators: new[] { generator.AsSourceGenerator() }, driverCache: cache, analyzers: null);

            void RunWithCacheDisabled() => VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/langversion:preview" }, generators: new[] { generator.AsSourceGenerator() }, driverCache: cache, analyzers: null);
        }

        [Fact]
        public void Adding_Or_Removing_A_Generator_Invalidates_Cache()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            int sourceCallbackCount = 0;
            int sourceCallbackCount2 = 0;
            var generator = new PipelineCallbackGenerator((ctx) =>
            {
                ctx.RegisterSourceOutput(ctx.ParseOptionsProvider, (spc, po) =>
                {
                    sourceCallbackCount++;
                });
            });

            var generator2 = new PipelineCallbackGenerator2((ctx) =>
            {
                ctx.RegisterSourceOutput(ctx.ParseOptionsProvider, (spc, po) =>
                {
                    sourceCallbackCount2++;
                });
            });

            // run with the cache
            GeneratorDriverCache cache = new GeneratorDriverCache();

            RunWithOneGenerator();
            Assert.Equal(1, sourceCallbackCount);
            Assert.Equal(0, sourceCallbackCount2);

            RunWithOneGenerator();
            Assert.Equal(1, sourceCallbackCount);
            Assert.Equal(0, sourceCallbackCount2);

            RunWithTwoGenerators();
            Assert.Equal(2, sourceCallbackCount);
            Assert.Equal(1, sourceCallbackCount2);

            RunWithTwoGenerators();
            Assert.Equal(2, sourceCallbackCount);
            Assert.Equal(1, sourceCallbackCount2);

            // this seems counterintuitive, but when the only thing to change is the generator, we end up back at the state of the project when 
            // we just ran a single generator. Thus we already have an entry in the cache we can use (the one created by the original call to
            // RunWithOneGenerator above) meaning we can use the previously cached results and not run.
            RunWithOneGenerator();
            Assert.Equal(2, sourceCallbackCount);
            Assert.Equal(1, sourceCallbackCount2);

            void RunWithOneGenerator() => VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/langversion:preview", "/features:enable-generator-cache" }, generators: new[] { generator.AsSourceGenerator() }, driverCache: cache, analyzers: null);

            void RunWithTwoGenerators() => VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/langversion:preview", "/features:enable-generator-cache" }, generators: new[] { generator.AsSourceGenerator(), generator2.AsSourceGenerator() }, driverCache: cache, analyzers: null);
        }

        [Fact]
        public void Compiler_Updates_Cached_Driver_AdditionalTexts()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText("class C { }");
            var additionalFile = dir.CreateFile("additionalFile.txt").WriteAllText("some text");

            int sourceCallbackCount = 0;
            int additionalFileCallbackCount = 0;
            var generator = new PipelineCallbackGenerator((ctx) =>
            {
                ctx.RegisterSourceOutput(ctx.ParseOptionsProvider, (spc, po) =>
                {
                    sourceCallbackCount++;
                });

                ctx.RegisterSourceOutput(ctx.AdditionalTextsProvider, (spc, po) =>
                {
                    additionalFileCallbackCount++;
                });

            });

            GeneratorDriverCache cache = new GeneratorDriverCache();

            RunWithCache();
            Assert.Equal(1, sourceCallbackCount);
            Assert.Equal(1, additionalFileCallbackCount);

            RunWithCache();
            Assert.Equal(1, sourceCallbackCount);
            Assert.Equal(1, additionalFileCallbackCount);

            additionalFile.WriteAllText("some new content");

            RunWithCache();
            Assert.Equal(1, sourceCallbackCount);
            Assert.Equal(2, additionalFileCallbackCount); // additional file was updated

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);

            void RunWithCache() => VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/langversion:preview", "/features:enable-generator-cache", "/additionalFile:" + additionalFile.Path }, generators: new[] { generator.AsSourceGenerator() }, driverCache: cache, analyzers: null);
        }

        [Fact]
        public void Compiler_DoesNotCache_Driver_ConfigProvider()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText("class C { }");
            var editorconfig = dir.CreateFile(".editorconfig").WriteAllText(@"
[temp.cs]
a = localA
");

            var globalconfig = dir.CreateFile(".globalconfig").WriteAllText(@"
is_global = true
a = globalA");

            int sourceCallbackCount = 0;
            int configOptionsCallbackCount = 0;
            int filteredGlobalCallbackCount = 0;
            int filteredLocalCallbackCount = 0;
            string globalA = "";
            string localA = "";
            var generator = new PipelineCallbackGenerator((ctx) =>
            {
                ctx.RegisterSourceOutput(ctx.ParseOptionsProvider, (spc, po) =>
                {
                    sourceCallbackCount++;
                });

                ctx.RegisterSourceOutput(ctx.AnalyzerConfigOptionsProvider, (spc, po) =>
                {
                    configOptionsCallbackCount++;
                    po.GlobalOptions.TryGetValue("a", out globalA);
                });

                ctx.RegisterSourceOutput(ctx.AnalyzerConfigOptionsProvider.Select((p, _) => { p.GlobalOptions.TryGetValue("a", out var value); return value; }), (spc, value) =>
                {
                    filteredGlobalCallbackCount++;
                    globalA = value;
                });

                var syntaxTreeInput = ctx.CompilationProvider.Select((c, _) => c.SyntaxTrees.First());
                ctx.RegisterSourceOutput(ctx.AnalyzerConfigOptionsProvider.Combine(syntaxTreeInput).Select((p, _) => { p.Left.GetOptions(p.Right).TryGetValue("a", out var value); return value; }), (spc, value) =>
                {
                    filteredLocalCallbackCount++;
                    localA = value;
                });
            });

            GeneratorDriverCache cache = new GeneratorDriverCache();

            RunWithCache();
            Assert.Equal(1, sourceCallbackCount);
            Assert.Equal(1, configOptionsCallbackCount);
            Assert.Equal(1, filteredGlobalCallbackCount);
            Assert.Equal(1, filteredLocalCallbackCount);
            Assert.Equal("globalA", globalA);
            Assert.Equal("localA", localA);


            RunWithCache();
            Assert.Equal(1, sourceCallbackCount);
            Assert.Equal(2, configOptionsCallbackCount); // we can't compare the provider directly, so we consider it modified
            Assert.Equal(1, filteredGlobalCallbackCount); // however, the values in it will cache out correctly.
            Assert.Equal(1, filteredLocalCallbackCount);

            editorconfig.WriteAllText(@"
[temp.cs]
a = diffLocalA
");

            RunWithCache();
            Assert.Equal(1, sourceCallbackCount);
            Assert.Equal(3, configOptionsCallbackCount);
            Assert.Equal(1, filteredGlobalCallbackCount); // the provider changed, but only the local value changed
            Assert.Equal(2, filteredLocalCallbackCount);
            Assert.Equal("globalA", globalA);
            Assert.Equal("diffLocalA", localA);


            globalconfig.WriteAllText(@"
is_global = true
a = diffGlobalA
");

            RunWithCache();
            Assert.Equal(1, sourceCallbackCount);
            Assert.Equal(4, configOptionsCallbackCount);
            Assert.Equal(2, filteredGlobalCallbackCount); // only the global value was changed
            Assert.Equal(2, filteredLocalCallbackCount);
            Assert.Equal("diffGlobalA", globalA);
            Assert.Equal("diffLocalA", localA);

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);

            void RunWithCache() => VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/langversion:preview", "/features:enable-generator-cache", "/analyzerConfig:" + editorconfig.Path, "/analyzerConfig:" + globalconfig.Path }, generators: new[] { generator.AsSourceGenerator() }, driverCache: cache, analyzers: null);
        }

        [Fact]
        public void Compiler_DoesNotCache_Compilation()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            int sourceCallbackCount = 0;
            var generator = new PipelineCallbackGenerator((ctx) =>
            {
                ctx.RegisterSourceOutput(ctx.CompilationProvider, (spc, po) =>
                {
                    sourceCallbackCount++;
                });
            });

            // now re-run with a cache
            GeneratorDriverCache cache = new GeneratorDriverCache();

            RunWithCache();
            Assert.Equal(1, sourceCallbackCount);

            RunWithCache();
            Assert.Equal(2, sourceCallbackCount);

            RunWithCache();
            Assert.Equal(3, sourceCallbackCount);

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);

            void RunWithCache() => VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/langversion:preview", "/features:enable-generator-cache" }, generators: new[] { generator.AsSourceGenerator() }, driverCache: cache, analyzers: null);
        }

        private static int OccurrenceCount(string source, string word)
        {
            var n = 0;
            var index = source.IndexOf(word, StringComparison.Ordinal);
            while (index >= 0)
            {
                ++n;
                index = source.IndexOf(word, index + word.Length, StringComparison.Ordinal);
            }
            return n;
        }

        private string VerifyOutput(TempDirectory sourceDir, TempFile sourceFile,
                                           bool includeCurrentAssemblyAsAnalyzerReference = true,
                                           string[] additionalFlags = null,
                                           int expectedInfoCount = 0,
                                           int expectedWarningCount = 0,
                                           int expectedErrorCount = 0,
                                           int? expectedExitCode = null,
                                           bool errorlog = false,
                                           bool skipAnalyzers = false,
                                           IEnumerable<ISourceGenerator> generators = null,
                                           GeneratorDriverCache driverCache = null,
                                           params DiagnosticAnalyzer[] analyzers)
        {
            var args = new[] {
                                "/nologo", "/preferreduilang:en", "/t:library",
                                sourceFile.Path
                             };
            if (includeCurrentAssemblyAsAnalyzerReference)
            {
                args = args.Append("/a:" + Assembly.GetExecutingAssembly().Location);
            }

            if (errorlog)
            {
                args = args.Append("/errorlog:errorlog");
            }

            if (skipAnalyzers)
            {
                args = args.Append("/skipAnalyzers");
            }

            if (additionalFlags != null)
            {
                args = args.Append(additionalFlags);
            }

            var csc = CreateCSharpCompiler(null, sourceDir.Path, args, analyzers: analyzers.ToImmutableArrayOrEmpty(), generators: generators.ToImmutableArrayOrEmpty(), driverCache: driverCache);
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = csc.Run(outWriter);
            var output = outWriter.ToString();

            expectedExitCode ??= expectedErrorCount > 0 ? 1 : 0;
            Assert.True(
                expectedExitCode == exitCode,
                string.Format("Expected exit code to be '{0}' was '{1}'.{2} Output:{3}{4}",
                expectedExitCode, exitCode, Environment.NewLine, Environment.NewLine, output));

            Assert.DoesNotContain("hidden", output, StringComparison.Ordinal);

            if (expectedInfoCount == 0)
            {
                Assert.DoesNotContain("info", output, StringComparison.Ordinal);
            }
            else
            {
                // Info diagnostics are only logged with /errorlog.
                Assert.True(errorlog);
                Assert.Equal(expectedInfoCount, OccurrenceCount(output, "info"));
            }

            if (expectedWarningCount == 0)
            {
                Assert.DoesNotContain("warning", output, StringComparison.Ordinal);
            }
            else
            {
                Assert.Equal(expectedWarningCount, OccurrenceCount(output, "warning"));
            }

            if (expectedErrorCount == 0)
            {
                Assert.DoesNotContain("error", output, StringComparison.Ordinal);
            }
            else
            {
                Assert.Equal(expectedErrorCount, OccurrenceCount(output, "error"));
            }

            return output;
        }

        [WorkItem(899050, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/899050")]
        [Fact]
        public void NoWarnAndWarnAsError_AnalyzerDriverWarnings()
        {
            // This assembly has an abstract MockAbstractDiagnosticAnalyzer type which should cause
            // compiler warning CS8032 to be produced when compilations created in this test try to load it.
            string source = @"using System;";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            var output = VerifyOutput(dir, file, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that compiler warning CS8032 can be suppressed via /warn:0.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warn:0" });
            Assert.True(string.IsNullOrEmpty(output));

            // TEST: Verify that compiler warning CS8032 can be individually suppressed via /nowarn:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/nowarn:CS8032" });
            Assert.True(string.IsNullOrEmpty(output));

            // TEST: Verify that compiler warning CS8032 can be promoted to an error via /warnaserror.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror" }, expectedErrorCount: 1);
            Assert.Contains("error CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that compiler warning CS8032 can be individually promoted to an error via /warnaserror:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror:8032" }, expectedErrorCount: 1);
            Assert.Contains("error CS8032", output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(file.Path);
        }

        [WorkItem(899050, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/899050")]
        [WorkItem(981677, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/981677")]
        [WorkItem(1021115, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1021115")]
        [Fact]
        public void NoWarnAndWarnAsError_HiddenDiagnostic()
        {
            // This assembly has a HiddenDiagnosticAnalyzer type which should produce custom hidden
            // diagnostics for #region directives present in the compilations created in this test.
            var source = @"using System;
#region Region
#endregion";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            var output = VerifyOutput(dir, file, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /warn:0 has no impact on custom hidden diagnostic Hidden01.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warn:0" });
            Assert.True(string.IsNullOrEmpty(output));

            // TEST: Verify that /nowarn: has no impact on custom hidden diagnostic Hidden01.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/nowarn:Hidden01" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /warnaserror+ has no impact on custom hidden diagnostic Hidden01.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror+", "/nowarn:8032" });
            Assert.True(string.IsNullOrEmpty(output));

            // TEST: Verify that /warnaserror- has no impact on custom hidden diagnostic Hidden01.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /warnaserror: promotes custom hidden diagnostic Hidden01 to an error.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror:Hidden01" }, expectedWarningCount: 1, expectedErrorCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(2,1): error Hidden01: Throwing a diagnostic for #region", output, StringComparison.Ordinal);

            // TEST: Verify that /warnaserror-: has no impact on custom hidden diagnostic Hidden01.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-:Hidden01" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify /nowarn: overrides /warnaserror:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror:Hidden01", "/nowarn:Hidden01" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify /nowarn: overrides /warnaserror:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/nowarn:Hidden01", "/warnaserror:Hidden01" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify /nowarn: overrides /warnaserror-:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-:Hidden01", "/nowarn:Hidden01" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify /nowarn: overrides /warnaserror-:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/nowarn:Hidden01", "/warnaserror-:Hidden01" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /warn:0 has no impact on custom hidden diagnostic Hidden01.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warn:0", "/warnaserror:Hidden01" });
            Assert.True(string.IsNullOrEmpty(output));

            // TEST: Verify that /warn:0 has no impact on custom hidden diagnostic Hidden01.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror:Hidden01", "/warn:0" });
            Assert.True(string.IsNullOrEmpty(output));

            // TEST: Verify that last /warnaserror[+/-]: flag on command line wins.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror+:Hidden01", "/warnaserror-:Hidden01" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that last /warnaserror[+/-]: flag on command line wins.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-:Hidden01", "/warnaserror+:Hidden01" }, expectedWarningCount: 1, expectedErrorCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(2,1): error Hidden01: Throwing a diagnostic for #region", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-] and /warnaserror[+/-]:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-", "/warnaserror+:Hidden01" }, expectedWarningCount: 1, expectedErrorCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(2,1): error Hidden01: Throwing a diagnostic for #region", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-]: and /warnaserror[+/-].
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-:Hidden01", "/warnaserror+" }, expectedErrorCount: 1);
            Assert.Contains("error CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-] and /warnaserror[+/-]:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror+", "/warnaserror+:Hidden01", "/nowarn:8032" }, expectedErrorCount: 1);
            Assert.Contains("a.cs(2,1): error Hidden01: Throwing a diagnostic for #region", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-]: and /warnaserror[+/-].
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror+:Hidden01", "/warnaserror+", "/nowarn:8032" });
            Assert.True(string.IsNullOrEmpty(output));

            // TEST: Verify that last one wins between /warnaserror[+/-]: and /warnaserror[+/-].
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror+:Hidden01", "/warnaserror-" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-] and /warnaserror[+/-]:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror+", "/warnaserror-:Hidden01", "/nowarn:8032" });
            Assert.True(string.IsNullOrEmpty(output));

            // TEST: Verify that last one wins between /warnaserror[+/-]: and /warnaserror[+/-].
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-:Hidden01", "/warnaserror-" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-] and /warnaserror[+/-]:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-", "/warnaserror-:Hidden01" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(file.Path);
        }

        [WorkItem(899050, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/899050")]
        [WorkItem(981677, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/981677")]
        [WorkItem(1021115, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1021115")]
        [WorkItem(42166, "https://github.com/dotnet/roslyn/issues/42166")]
        [CombinatorialData, Theory]
        public void NoWarnAndWarnAsError_InfoDiagnostic(bool errorlog)
        {
            // NOTE: Info diagnostics are only logged on command line when /errorlog is specified. See https://github.com/dotnet/roslyn/issues/42166 for details.

            // This assembly has an InfoDiagnosticAnalyzer type which should produce custom info
            // diagnostics for the #pragma warning restore directives present in the compilations created in this test.
            var source = @"using System;
#pragma warning restore";
            var name = "a.cs";
            string output;
            output = GetOutput(name, source, expectedWarningCount: 1, expectedInfoCount: errorlog ? 1 : 0, errorlog: errorlog);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            if (errorlog)
                Assert.Contains("a.cs(2,1): info Info01: Throwing a diagnostic for #pragma restore", output, StringComparison.Ordinal);

            // TEST: Verify that /warn:0 suppresses custom info diagnostic Info01.
            output = GetOutput(name, source, additionalFlags: new[] { "/warn:0" }, errorlog: errorlog);

            // TEST: Verify that custom info diagnostic Info01 can be individually suppressed via /nowarn:.
            output = GetOutput(name, source, additionalFlags: new[] { "/nowarn:Info01" }, expectedWarningCount: 1, errorlog: errorlog);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that custom info diagnostic Info01 can never be promoted to an error via /warnaserror+.
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror+", "/nowarn:8032" }, expectedInfoCount: errorlog ? 1 : 0, errorlog: errorlog);
            if (errorlog)
                Assert.Contains("a.cs(2,1): info Info01: Throwing a diagnostic for #pragma restore", output, StringComparison.Ordinal);

            // TEST: Verify that custom info diagnostic Info01 is still reported as an info when /warnaserror- is used.
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror-" }, expectedWarningCount: 1, expectedInfoCount: errorlog ? 1 : 0, errorlog: errorlog);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            if (errorlog)
                Assert.Contains("a.cs(2,1): info Info01: Throwing a diagnostic for #pragma restore", output, StringComparison.Ordinal);

            // TEST: Verify that custom info diagnostic Info01 can be individually promoted to an error via /warnaserror:.
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror:Info01" }, expectedWarningCount: 1, expectedErrorCount: 1, errorlog: errorlog);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(2,1): error Info01: Throwing a diagnostic for #pragma restore", output, StringComparison.Ordinal);

            // TEST: Verify that custom info diagnostic Info01 is still reported as an info when passed to /warnaserror-:.
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror-:Info01" }, expectedWarningCount: 1, expectedInfoCount: errorlog ? 1 : 0, errorlog: errorlog);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            if (errorlog)
                Assert.Contains("a.cs(2,1): info Info01: Throwing a diagnostic for #pragma restore", output, StringComparison.Ordinal);

            // TEST: Verify /nowarn overrides /warnaserror.
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror:Info01", "/nowarn:Info01" }, expectedWarningCount: 1, errorlog: errorlog);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify /nowarn overrides /warnaserror.
            output = GetOutput(name, source, additionalFlags: new[] { "/nowarn:Info01", "/warnaserror:Info01" }, expectedWarningCount: 1, errorlog: errorlog);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify /nowarn overrides /warnaserror-.
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror-:Info01", "/nowarn:Info01" }, expectedWarningCount: 1, errorlog: errorlog);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify /nowarn overrides /warnaserror-.
            output = GetOutput(name, source, additionalFlags: new[] { "/nowarn:Info01", "/warnaserror-:Info01" }, expectedWarningCount: 1, errorlog: errorlog);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /warn:0 has no impact on custom info diagnostic Info01.
            output = GetOutput(name, source, additionalFlags: new[] { "/warn:0", "/warnaserror:Info01" }, errorlog: errorlog);

            // TEST: Verify that /warn:0 has no impact on custom info diagnostic Info01.
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror:Info01", "/warn:0" });

            // TEST: Verify that last /warnaserror[+/-]: flag on command line wins.
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror+:Info01", "/warnaserror-:Info01" }, expectedWarningCount: 1, expectedInfoCount: errorlog ? 1 : 0, errorlog: errorlog);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            if (errorlog)
                Assert.Contains("a.cs(2,1): info Info01: Throwing a diagnostic for #pragma restore", output, StringComparison.Ordinal);

            // TEST: Verify that last /warnaserror[+/-]: flag on command line wins.
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror-:Info01", "/warnaserror+:Info01" }, expectedWarningCount: 1, expectedErrorCount: 1, errorlog: errorlog);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(2,1): error Info01: Throwing a diagnostic for #pragma restore", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-] and /warnaserror[+/-]:.
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror-", "/warnaserror+:Info01" }, expectedWarningCount: 1, expectedErrorCount: 1, errorlog: errorlog);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(2,1): error Info01: Throwing a diagnostic for #pragma restore", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-]: and /warnaserror[+/-].
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror-:Info01", "/warnaserror+", "/nowarn:8032" }, expectedInfoCount: errorlog ? 1 : 0, errorlog: errorlog);
            if (errorlog)
                Assert.Contains("a.cs(2,1): info Info01: Throwing a diagnostic for #pragma restore", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-]: and /warnaserror[+/-].
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror+:Info01", "/warnaserror+", "/nowarn:8032" }, expectedInfoCount: errorlog ? 1 : 0, errorlog: errorlog);
            if (errorlog)
                Assert.Contains("a.cs(2,1): info Info01: Throwing a diagnostic for #pragma restore", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-] and /warnaserror[+/-]:.
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror+", "/warnaserror+:Info01", "/nowarn:8032" }, expectedErrorCount: 1, errorlog: errorlog);
            Assert.Contains("a.cs(2,1): error Info01: Throwing a diagnostic for #pragma restore", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-]: and /warnaserror[+/-].
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror+:Info01", "/warnaserror-" }, expectedWarningCount: 1, expectedInfoCount: errorlog ? 1 : 0, errorlog: errorlog);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            if (errorlog)
                Assert.Contains("a.cs(2,1): info Info01: Throwing a diagnostic for #pragma restore", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-] and /warnaserror[+/-]:.
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror+", "/warnaserror-:Info01", "/nowarn:8032" }, expectedInfoCount: errorlog ? 1 : 0, errorlog: errorlog);
            if (errorlog)
                Assert.Contains("a.cs(2,1): info Info01: Throwing a diagnostic for #pragma restore", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-]: and /warnaserror[+/-].
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror-:Info01", "/warnaserror-" }, expectedWarningCount: 1, expectedInfoCount: errorlog ? 1 : 0, errorlog: errorlog);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            if (errorlog)
                Assert.Contains("a.cs(2,1): info Info01: Throwing a diagnostic for #pragma restore", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-] and /warnaserror[+/-]:.
            output = GetOutput(name, source, additionalFlags: new[] { "/warnaserror-", "/warnaserror-:Info01" }, expectedWarningCount: 1, expectedInfoCount: errorlog ? 1 : 0, errorlog: errorlog);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            if (errorlog)
                Assert.Contains("a.cs(2,1): info Info01: Throwing a diagnostic for #pragma restore", output, StringComparison.Ordinal);
        }

        private string GetOutput(
            string name,
            string source,
            bool includeCurrentAssemblyAsAnalyzerReference = true,
            string[] additionalFlags = null,
            int expectedInfoCount = 0,
            int expectedWarningCount = 0,
            int expectedErrorCount = 0,
            bool errorlog = false)
        {
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile(name);
            file.WriteAllText(source);

            var output = VerifyOutput(dir, file, includeCurrentAssemblyAsAnalyzerReference, additionalFlags, expectedInfoCount, expectedWarningCount, expectedErrorCount, null, errorlog);
            CleanupAllGeneratedFiles(file.Path);
            return output;
        }

        [WorkItem(11368, "https://github.com/dotnet/roslyn/issues/11368")]
        [WorkItem(899050, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/899050")]
        [WorkItem(981677, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/981677")]
        [WorkItem(998069, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/998069")]
        [WorkItem(998724, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/998724")]
        [WorkItem(1021115, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1021115")]
        [Fact]
        public void NoWarnAndWarnAsError_WarningDiagnostic()
        {
            // This assembly has a WarningDiagnosticAnalyzer type which should produce custom warning
            // diagnostics for source types present in the compilations created in this test.
            string source = @"
class C
{
    static void Main()
    {
        int i;
    }
}
";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            var output = VerifyOutput(dir, file, expectedWarningCount: 3);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(2,7): warning Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): warning CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);

            // TEST: Verify that compiler warning CS0168 as well as custom warning diagnostic Warning01 can be suppressed via /warn:0.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warn:0" });
            Assert.True(string.IsNullOrEmpty(output));

            // TEST: Verify that compiler warning CS0168 as well as custom warning diagnostic Warning01 can be individually suppressed via /nowarn:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/nowarn:0168,Warning01,58000" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that diagnostic ids are processed in case-sensitive fashion inside /nowarn:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/nowarn:cs0168,warning01,700000" }, expectedWarningCount: 3);
            Assert.Contains("a.cs(2,7): warning Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): warning CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that compiler warning CS0168 as well as custom warning diagnostic Warning01 can be promoted to errors via /warnaserror.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror", "/nowarn:8032" }, expectedErrorCount: 2);
            Assert.Contains("a.cs(2,7): error Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): error CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);

            // TEST: Verify that compiler warning CS0168 as well as custom warning diagnostic Warning01 can be promoted to errors via /warnaserror+.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror+", "/nowarn:8032" }, expectedErrorCount: 2);
            Assert.Contains("a.cs(2,7): error Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): error CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);

            // TEST: Verify that /warnaserror- keeps compiler warning CS0168 as well as custom warning diagnostic Warning01 as warnings.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-" }, expectedWarningCount: 3);
            Assert.Contains("a.cs(2,7): warning Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): warning CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that custom warning diagnostic Warning01 can be individually promoted to an error via /warnaserror:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror:Something,Warning01" }, expectedWarningCount: 2, expectedErrorCount: 1);
            Assert.Contains("a.cs(2,7): error Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): warning CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that compiler warning CS0168 can be individually promoted to an error via /warnaserror+:.
            // This doesn't work correctly currently - promoting compiler warning CS0168 to an error causes us to no longer report any custom warning diagnostics as errors (Bug 998069).
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror+:CS0168" }, expectedWarningCount: 2, expectedErrorCount: 1);
            Assert.Contains("a.cs(2,7): warning Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): error CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that diagnostic ids are processed in case-sensitive fashion inside /warnaserror.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror:cs0168,warning01,58000" }, expectedWarningCount: 3);
            Assert.Contains("a.cs(2,7): warning Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): warning CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that custom warning diagnostic Warning01 as well as compiler warning CS0168 can be promoted to errors via /warnaserror:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror:CS0168,Warning01" }, expectedWarningCount: 1, expectedErrorCount: 2);
            Assert.Contains("a.cs(2,7): error Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): error CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /warn:0 overrides /warnaserror+.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warn:0", "/warnaserror+" });

            // TEST: Verify that /warn:0 overrides /warnaserror.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror", "/warn:0" });

            // TEST: Verify that /warn:0 overrides /warnaserror-.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-", "/warn:0" });

            // TEST: Verify that /warn:0 overrides /warnaserror-.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warn:0", "/warnaserror-" });

            // TEST: Verify that /nowarn: overrides /warnaserror:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror:Something,CS0168,Warning01", "/nowarn:0168,Warning01,58000" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /nowarn: overrides /warnaserror:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/nowarn:0168,Warning01,58000", "/warnaserror:Something,CS0168,Warning01" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /nowarn: overrides /warnaserror-:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-:Something,CS0168,Warning01", "/nowarn:0168,Warning01,58000" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /nowarn: overrides /warnaserror-:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/nowarn:0168,Warning01,58000", "/warnaserror-:Something,CS0168,Warning01" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /nowarn: overrides /warnaserror+.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror+", "/nowarn:0168,Warning01,58000,8032" });

            // TEST: Verify that /nowarn: overrides /warnaserror+.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/nowarn:0168,Warning01,58000,8032", "/warnaserror+" });

            // TEST: Verify that /nowarn: overrides /warnaserror-.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-", "/nowarn:0168,Warning01,58000,8032" });

            // TEST: Verify that /nowarn: overrides /warnaserror-.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/nowarn:0168,Warning01,58000,8032", "/warnaserror-" });

            // TEST: Verify that /warn:0 overrides /warnaserror:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror:Something,CS0168,Warning01", "/warn:0" });

            // TEST: Verify that /warn:0 overrides /warnaserror:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warn:0", "/warnaserror:Something,CS0168,Warning01" });

            // TEST: Verify that last /warnaserror[+/-] flag on command line wins.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-", "/warnaserror+" }, expectedErrorCount: 1);
            Assert.Contains("error CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that last /warnaserror[+/-] flag on command line wins.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror", "/warnaserror-" }, expectedWarningCount: 3);
            Assert.Contains("a.cs(2,7): warning Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): warning CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that last /warnaserror[+/-]: flag on command line wins.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-:Warning01", "/warnaserror+:Warning01" }, expectedWarningCount: 2, expectedErrorCount: 1);
            Assert.Contains("a.cs(2,7): error Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): warning CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that last /warnaserror[+/-]: flag on command line wins.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror+:Warning01", "/warnaserror-:Warning01" }, expectedWarningCount: 3);
            Assert.Contains("a.cs(2,7): warning Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): warning CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-]: and /warnaserror[+/-].
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-:Warning01,CS0168,58000,8032", "/warnaserror+" }, expectedErrorCount: 1);
            Assert.Contains("error CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-] and /warnaserror[+/-]:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror", "/warnaserror-:Warning01,CS0168,58000,8032" }, expectedWarningCount: 3);
            Assert.Contains("a.cs(2,7): warning Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): warning CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-]: and /warnaserror[+/-].
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror:Warning01,58000,8032", "/warnaserror-" }, expectedWarningCount: 3);
            Assert.Contains("a.cs(2,7): warning Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): warning CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-] and /warnaserror[+/-]:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-", "/warnaserror+:Warning01" }, expectedWarningCount: 2, expectedErrorCount: 1);
            Assert.Contains("a.cs(2,7): error Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): warning CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-]: and /warnaserror[+/-].
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror:Warning01,CS0168,58000", "/warnaserror+" }, expectedErrorCount: 1);
            Assert.Contains("error CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-] and /warnaserror[+/-]:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror", "/warnaserror+:Warning01,CS0168,58000" }, expectedErrorCount: 1);
            Assert.Contains("error CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-]: and /warnaserror[+/-].
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-:Warning01,58000,8032", "/warnaserror-" }, expectedWarningCount: 3);
            Assert.Contains("a.cs(2,7): warning Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): warning CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that last one wins between /warnaserror[+/-] and /warnaserror[+/-]:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-", "/warnaserror-:Warning01,58000,8032" }, expectedWarningCount: 3);
            Assert.Contains("a.cs(2,7): warning Warning01: Throwing a diagnostic for types declared", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(6,13): warning CS0168: The variable 'i' is declared but never used", output, StringComparison.Ordinal);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(file.Path);
        }

        [WorkItem(899050, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/899050")]
        [WorkItem(981677, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/981677")]
        [Fact]
        public void NoWarnAndWarnAsError_ErrorDiagnostic()
        {
            // This assembly has an ErrorDiagnosticAnalyzer type which should produce custom error
            // diagnostics for #pragma warning disable directives present in the compilations created in this test.
            string source = @"using System;
#pragma warning disable";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            var output = VerifyOutput(dir, file, expectedErrorCount: 1, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(2,1): error Error01: Throwing a diagnostic for #pragma disable", output, StringComparison.Ordinal);

            // TEST: Verify that custom error diagnostic Error01 can't be suppressed via /warn:0.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warn:0" }, expectedErrorCount: 1);
            Assert.Contains("a.cs(2,1): error Error01: Throwing a diagnostic for #pragma disable", output, StringComparison.Ordinal);

            // TEST: Verify that custom error diagnostic Error01 can be suppressed via /nowarn:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/nowarn:Error01" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /nowarn: overrides /warnaserror+.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror+", "/nowarn:Error01" }, expectedErrorCount: 1);
            Assert.Contains("error CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /nowarn: overrides /warnaserror.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/nowarn:Error01", "/warnaserror" }, expectedErrorCount: 1);
            Assert.Contains("error CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /nowarn: overrides /warnaserror+:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/nowarn:Error01", "/warnaserror+:Error01" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /nowarn: overrides /warnaserror:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror:Error01", "/nowarn:Error01" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /nowarn: overrides /warnaserror-.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-", "/nowarn:Error01" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /nowarn: overrides /warnaserror-.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/nowarn:Error01", "/warnaserror-" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /nowarn: overrides /warnaserror-.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-:Error01", "/nowarn:Error01" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that /nowarn: overrides /warnaserror-.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/nowarn:Error01", "/warnaserror-:Error01" }, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);

            // TEST: Verify that nothing bad happens when using /warnaserror[+/-] when custom error diagnostic Error01 is present.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror" }, expectedErrorCount: 1);
            Assert.Contains("error CS8032", output, StringComparison.Ordinal);

            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror+" }, expectedErrorCount: 1);
            Assert.Contains("error CS8032", output, StringComparison.Ordinal);

            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-" }, expectedErrorCount: 1, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(2,1): error Error01: Throwing a diagnostic for #pragma disable", output, StringComparison.Ordinal);

            // TEST: Verify that nothing bad happens if someone passes custom error diagnostic Error01 to /warnaserror[+/-]:.
            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror:Error01" }, expectedErrorCount: 1, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(2,1): error Error01: Throwing a diagnostic for #pragma disable", output, StringComparison.Ordinal);

            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror+:Error01" }, expectedErrorCount: 1, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(2,1): error Error01: Throwing a diagnostic for #pragma disable", output, StringComparison.Ordinal);

            output = VerifyOutput(dir, file, additionalFlags: new[] { "/warnaserror-:Error01" }, expectedErrorCount: 1, expectedWarningCount: 1);
            Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            Assert.Contains("a.cs(2,1): error Error01: Throwing a diagnostic for #pragma disable", output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(file.Path);
        }

        [Fact]
        [WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")]
        public void ConsistentErrorMessageWhenProvidingNoKeyFile()
        {
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/keyfile:", "/target:library", "/nologo", "/preferreduilang:en", "a.cs" });
            int exitCode = csc.Run(outWriter);

            Assert.Equal(1, exitCode);
            Assert.Equal("error CS2005: Missing file specification for 'keyfile' option", outWriter.ToString().Trim());
        }

        [Fact]
        [WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")]
        public void ConsistentErrorMessageWhenProvidingEmptyKeyFile()
        {
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/keyfile:\"\"", "/target:library", "/nologo", "/preferreduilang:en", "a.cs" });
            int exitCode = csc.Run(outWriter);

            Assert.Equal(1, exitCode);
            Assert.Equal("error CS2005: Missing file specification for 'keyfile' option", outWriter.ToString().Trim());
        }

        [Fact]
        [WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")]
        public void ConsistentErrorMessageWhenProvidingNoKeyFile_PublicSign()
        {
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/keyfile:", "/publicsign", "/target:library", "/nologo", "/preferreduilang:en", "a.cs" });
            int exitCode = csc.Run(outWriter);

            Assert.Equal(1, exitCode);
            Assert.Equal("error CS2005: Missing file specification for 'keyfile' option", outWriter.ToString().Trim());
        }

        [Fact]
        [WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")]
        public void ConsistentErrorMessageWhenProvidingEmptyKeyFile_PublicSign()
        {
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/keyfile:\"\"", "/publicsign", "/target:library", "/nologo", "/preferreduilang:en", "a.cs" });
            int exitCode = csc.Run(outWriter);

            Assert.Equal(1, exitCode);
            Assert.Equal("error CS2005: Missing file specification for 'keyfile' option", outWriter.ToString().Trim());
        }

        [WorkItem(981677, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/981677")]
        [Fact]
        public void NoWarnAndWarnAsError_CompilerErrorDiagnostic()
        {
            string source = @"using System;
class C
{
    static void Main()
    {
        int i = new Exception();
    }
}";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile("a.cs");
            file.WriteAllText(source);

            var output = VerifyOutput(dir, file, includeCurrentAssemblyAsAnalyzerReference: false, expectedErrorCount: 1);
            Assert.Contains("a.cs(6,17): error CS0029: Cannot implicitly convert type 'System.Exception' to 'int'", output, StringComparison.Ordinal);

            // TEST: Verify that compiler error CS0029 can't be suppressed via /warn:0.
            output = VerifyOutput(dir, file, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/warn:0" }, expectedErrorCount: 1);
            Assert.Contains("a.cs(6,17): error CS0029: Cannot implicitly convert type 'System.Exception' to 'int'", output, StringComparison.Ordinal);

            // TEST: Verify that compiler error CS0029 can't be suppressed via /nowarn:.
            output = VerifyOutput(dir, file, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/nowarn:29" }, expectedErrorCount: 1);
            Assert.Contains("a.cs(6,17): error CS0029: Cannot implicitly convert type 'System.Exception' to 'int'", output, StringComparison.Ordinal);

            output = VerifyOutput(dir, file, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/nowarn:CS0029" }, expectedErrorCount: 1);
            Assert.Contains("a.cs(6,17): error CS0029: Cannot implicitly convert type 'System.Exception' to 'int'", output, StringComparison.Ordinal);

            // TEST: Verify that nothing bad happens when using /warnaserror[+/-] when compiler error CS0029 is present.
            output = VerifyOutput(dir, file, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/warnaserror" }, expectedErrorCount: 1);
            Assert.Contains("a.cs(6,17): error CS0029: Cannot implicitly convert type 'System.Exception' to 'int'", output, StringComparison.Ordinal);

            output = VerifyOutput(dir, file, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/warnaserror+" }, expectedErrorCount: 1);
            Assert.Contains("a.cs(6,17): error CS0029: Cannot implicitly convert type 'System.Exception' to 'int'", output, StringComparison.Ordinal);

            output = VerifyOutput(dir, file, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/warnaserror-" }, expectedErrorCount: 1);
            Assert.Contains("a.cs(6,17): error CS0029: Cannot implicitly convert type 'System.Exception' to 'int'", output, StringComparison.Ordinal);

            // TEST: Verify that nothing bad happens if someone passes compiler error CS0029 to /warnaserror[+/-]:.
            output = VerifyOutput(dir, file, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/warnaserror:0029" }, expectedErrorCount: 1);
            Assert.Contains("a.cs(6,17): error CS0029: Cannot implicitly convert type 'System.Exception' to 'int'", output, StringComparison.Ordinal);

            output = VerifyOutput(dir, file, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/warnaserror+:CS0029" }, expectedErrorCount: 1);
            Assert.Contains("a.cs(6,17): error CS0029: Cannot implicitly convert type 'System.Exception' to 'int'", output, StringComparison.Ordinal);

            output = VerifyOutput(dir, file, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/warnaserror-:29" }, expectedErrorCount: 1);
            Assert.Contains("a.cs(6,17): error CS0029: Cannot implicitly convert type 'System.Exception' to 'int'", output, StringComparison.Ordinal);

            output = VerifyOutput(dir, file, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/warnaserror-:CS0029" }, expectedErrorCount: 1);
            Assert.Contains("a.cs(6,17): error CS0029: Cannot implicitly convert type 'System.Exception' to 'int'", output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(file.Path);
        }

        [WorkItem(1021115, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1021115")]
        [Fact]
        public void WarnAsError_LastOneWins1()
        {
            var arguments = DefaultParse(new[] { "/warnaserror-:3001", "/warnaserror" }, null);
            var options = arguments.CompilationOptions;

            var comp = CreateCompilation(@"[assembly: System.CLSCompliant(true)]
public class C
{
    public void M(ushort i)
    {
    }
    public static void Main(string[] args) {}
}", options: options);

            comp.VerifyDiagnostics(
                // (4,26): warning CS3001: Argument type 'ushort' is not CLS-compliant
                //     public void M(ushort i)
                Diagnostic(ErrorCode.WRN_CLS_BadArgType, "i")
                    .WithArguments("ushort")
                    .WithLocation(4, 26)
                    .WithWarningAsError(true));
        }

        [WorkItem(1021115, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1021115")]
        [Fact]
        public void WarnAsError_LastOneWins2()
        {
            var arguments = DefaultParse(new[] { "/warnaserror", "/warnaserror-:3001" }, null);
            var options = arguments.CompilationOptions;

            var comp = CreateCompilation(@"[assembly: System.CLSCompliant(true)]
public class C
{
    public void M(ushort i)
    {
    }
    public static void Main(string[] args) {}
}", options: options);

            comp.VerifyDiagnostics(
                // (4,26): warning CS3001: Argument type 'ushort' is not CLS-compliant
                //     public void M(ushort i)
                Diagnostic(ErrorCode.WRN_CLS_BadArgType, "i")
                    .WithArguments("ushort")
                    .WithLocation(4, 26)
                    .WithWarningAsError(false));
        }

        [WorkItem(1091972, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1091972")]
        [WorkItem(444, "CodePlex")]
        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void Bug1091972()
        {
            var dir = Temp.CreateDirectory();

            var src = dir.CreateFile("a.cs");
            src.WriteAllText(
@"
/// <summary>ABC...XYZ</summary>
class C {
    static void Main()
    {
        var textStreamReader = new System.IO.StreamReader(typeof(C).Assembly.GetManifestResourceStream(""doc.xml""));
        System.Console.WriteLine(textStreamReader.ReadToEnd());
    }
} ");

            var output = ProcessUtilities.RunAndGetOutput(s_CSharpCompilerExecutable, String.Format("/nologo /doc:doc.xml /out:out.exe /resource:doc.xml \"{0}\"", src.ToString()), startFolder: dir.ToString());
            Assert.Equal("", output.Trim());

            Assert.True(File.Exists(Path.Combine(dir.ToString(), "doc.xml")));

            var expected =
@"<?xml version=""1.0""?>
<doc>
    <assembly>
        <name>out</name>
    </assembly>
    <members>
        <member name=""T:C"">
            <summary>ABC...XYZ</summary>
        </member>
    </members>
</doc>".Trim();

            using (var reader = new StreamReader(Path.Combine(dir.ToString(), "doc.xml")))
            {
                var content = reader.ReadToEnd();
                Assert.Equal(expected, content.Trim());
            }

            output = ProcessUtilities.RunAndGetOutput(Path.Combine(dir.ToString(), "out.exe"), startFolder: dir.ToString());
            Assert.Equal(expected, output.Trim());

            CleanupAllGeneratedFiles(src.Path);
        }

        [ConditionalFact(typeof(WindowsOnly))]
        public void CommandLineMisc()
        {
            CSharpCommandLineArguments args = null;
            string baseDirectory = @"c:\test";
            Func<string, CSharpCommandLineArguments> parse = (x) => FullParse(x, baseDirectory);

            args = parse(@"/out:""a.exe""");
            Assert.Equal(@"a.exe", args.OutputFileName);

            args = parse(@"/pdb:""a.pdb""");
            Assert.Equal(Path.Combine(baseDirectory, @"a.pdb"), args.PdbPath);

            // The \ here causes " to be treated as a quote, not as an escaping construct
            args = parse(@"a\""b c""\d.cs");
            Assert.Equal(
                new[] { @"c:\test\a""b", @"c:\test\c\d.cs" },
                args.SourceFiles.Select(x => x.Path));

            args = parse(@"a\\""b c""\d.cs");
            Assert.Equal(
                new[] { @"c:\test\a\b c\d.cs" },
                args.SourceFiles.Select(x => x.Path));

            args = parse(@"/nostdlib /r:""a.dll"",""b.dll"" c.cs");
            Assert.Equal(
                new[] { @"a.dll", @"b.dll" },
                args.MetadataReferences.Select(x => x.Reference));

            args = parse(@"/nostdlib /r:""a-s.dll"",""b-s.dll"" c.cs");
            Assert.Equal(
                new[] { @"a-s.dll", @"b-s.dll" },
                args.MetadataReferences.Select(x => x.Reference));

            args = parse(@"/nostdlib /r:""a,;s.dll"",""b,;s.dll"" c.cs");
            Assert.Equal(
                new[] { @"a,;s.dll", @"b,;s.dll" },
                args.MetadataReferences.Select(x => x.Reference));
        }

        [Fact]
        public void CommandLine_ScriptRunner1()
        {
            var args = ScriptParse(new[] { "--", "script.csx", "b", "c" }, baseDirectory: WorkingDirectory);
            AssertEx.Equal(new[] { Path.Combine(WorkingDirectory, "script.csx") }, args.SourceFiles.Select(f => f.Path));
            AssertEx.Equal(new[] { "b", "c" }, args.ScriptArguments);

            args = ScriptParse(new[] { "--", "@script.csx", "b", "c" }, baseDirectory: WorkingDirectory);
            AssertEx.Equal(new[] { Path.Combine(WorkingDirectory, "@script.csx") }, args.SourceFiles.Select(f => f.Path));
            AssertEx.Equal(new[] { "b", "c" }, args.ScriptArguments);

            args = ScriptParse(new[] { "--", "-script.csx", "b", "c" }, baseDirectory: WorkingDirectory);
            AssertEx.Equal(new[] { Path.Combine(WorkingDirectory, "-script.csx") }, args.SourceFiles.Select(f => f.Path));
            AssertEx.Equal(new[] { "b", "c" }, args.ScriptArguments);

            args = ScriptParse(new[] { "script.csx", "--", "b", "c" }, baseDirectory: WorkingDirectory);
            AssertEx.Equal(new[] { Path.Combine(WorkingDirectory, "script.csx") }, args.SourceFiles.Select(f => f.Path));
            AssertEx.Equal(new[] { "--", "b", "c" }, args.ScriptArguments);

            args = ScriptParse(new[] { "script.csx", "a", "b", "c" }, baseDirectory: WorkingDirectory);
            AssertEx.Equal(new[] { Path.Combine(WorkingDirectory, "script.csx") }, args.SourceFiles.Select(f => f.Path));
            AssertEx.Equal(new[] { "a", "b", "c" }, args.ScriptArguments);

            args = ScriptParse(new[] { "script.csx", "a", "--", "b", "c" }, baseDirectory: WorkingDirectory);
            AssertEx.Equal(new[] { Path.Combine(WorkingDirectory, "script.csx") }, args.SourceFiles.Select(f => f.Path));
            AssertEx.Equal(new[] { "a", "--", "b", "c" }, args.ScriptArguments);

            args = ScriptParse(new[] { "-i", "script.csx", "a", "b", "c" }, baseDirectory: WorkingDirectory);
            Assert.True(args.InteractiveMode);
            AssertEx.Equal(new[] { Path.Combine(WorkingDirectory, "script.csx") }, args.SourceFiles.Select(f => f.Path));
            AssertEx.Equal(new[] { "a", "b", "c" }, args.ScriptArguments);

            args = ScriptParse(new[] { "-i", "--", "script.csx", "a", "b", "c" }, baseDirectory: WorkingDirectory);
            Assert.True(args.InteractiveMode);
            AssertEx.Equal(new[] { Path.Combine(WorkingDirectory, "script.csx") }, args.SourceFiles.Select(f => f.Path));
            AssertEx.Equal(new[] { "a", "b", "c" }, args.ScriptArguments);

            args = ScriptParse(new[] { "-i", "--", "--", "--" }, baseDirectory: WorkingDirectory);
            Assert.True(args.InteractiveMode);
            AssertEx.Equal(new[] { Path.Combine(WorkingDirectory, "--") }, args.SourceFiles.Select(f => f.Path));
            Assert.True(args.SourceFiles[0].IsScript);
            AssertEx.Equal(new[] { "--" }, args.ScriptArguments);

            // TODO: fails on Linux (https://github.com/dotnet/roslyn/issues/5904)
            // Result: C:\/script.csx
            //args = ScriptParse(new[] { "-i", "script.csx", "--", "--" }, baseDirectory: @"C:\");
            //Assert.True(args.InteractiveMode);
            //AssertEx.Equal(new[] { @"C:\script.csx" }, args.SourceFiles.Select(f => f.Path));
            //Assert.True(args.SourceFiles[0].IsScript);
            //AssertEx.Equal(new[] { "--" }, args.ScriptArguments);
        }

        [WorkItem(127403, "https://devdiv.visualstudio.com:443/defaultcollection/DevDiv/_workitems/edit/127403")]
        [Fact]
        public void ParseSeparatedPaths_QuotedComma()
        {
            var paths = CSharpCommandLineParser.ParseSeparatedPaths(@"""a, b""");
            Assert.Equal(
                new[] { @"a, b" },
                paths);
        }

        [Fact]
        [CompilerTrait(CompilerFeature.Determinism)]
        public void PathMapParser()
        {
            var s = PathUtilities.DirectorySeparatorStr;

            var parsedArgs = DefaultParse(new[] { "/pathmap:", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ImmutableArray.Create<KeyValuePair<string, string>>(), parsedArgs.PathMap);

            parsedArgs = DefaultParse(new[] { "/pathmap:K1=V1", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(KeyValuePairUtil.Create("K1" + s, "V1" + s), parsedArgs.PathMap[0]);

            parsedArgs = DefaultParse(new[] { $"/pathmap:abc{s}=/", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(KeyValuePairUtil.Create("abc" + s, "/"), parsedArgs.PathMap[0]);

            parsedArgs = DefaultParse(new[] { "/pathmap:K1=V1,K2=V2", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(KeyValuePairUtil.Create("K1" + s, "V1" + s), parsedArgs.PathMap[0]);
            Assert.Equal(KeyValuePairUtil.Create("K2" + s, "V2" + s), parsedArgs.PathMap[1]);

            parsedArgs = DefaultParse(new[] { "/pathmap:,", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(ImmutableArray.Create<KeyValuePair<string, string>>(), parsedArgs.PathMap);

            parsedArgs = DefaultParse(new[] { "/pathmap:,,", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Count());
            Assert.Equal((int)ErrorCode.ERR_InvalidPathMap, parsedArgs.Errors[0].Code);

            parsedArgs = DefaultParse(new[] { "/pathmap:,,,", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Count());
            Assert.Equal((int)ErrorCode.ERR_InvalidPathMap, parsedArgs.Errors[0].Code);

            parsedArgs = DefaultParse(new[] { "/pathmap:k=,=v", "a.cs" }, WorkingDirectory);
            Assert.Equal(2, parsedArgs.Errors.Count());
            Assert.Equal((int)ErrorCode.ERR_InvalidPathMap, parsedArgs.Errors[0].Code);
            Assert.Equal((int)ErrorCode.ERR_InvalidPathMap, parsedArgs.Errors[1].Code);

            parsedArgs = DefaultParse(new[] { "/pathmap:k=v=bad", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Count());
            Assert.Equal((int)ErrorCode.ERR_InvalidPathMap, parsedArgs.Errors[0].Code);

            parsedArgs = DefaultParse(new[] { "/pathmap:k=", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Count());
            Assert.Equal((int)ErrorCode.ERR_InvalidPathMap, parsedArgs.Errors[0].Code);

            parsedArgs = DefaultParse(new[] { "/pathmap:=v", "a.cs" }, WorkingDirectory);
            Assert.Equal(1, parsedArgs.Errors.Count());
            Assert.Equal((int)ErrorCode.ERR_InvalidPathMap, parsedArgs.Errors[0].Code);

            parsedArgs = DefaultParse(new[] { "/pathmap:\"supporting spaces=is hard\"", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(KeyValuePairUtil.Create("supporting spaces" + s, "is hard" + s), parsedArgs.PathMap[0]);

            parsedArgs = DefaultParse(new[] { "/pathmap:\"K 1=V 1\",\"K 2=V 2\"", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(KeyValuePairUtil.Create("K 1" + s, "V 1" + s), parsedArgs.PathMap[0]);
            Assert.Equal(KeyValuePairUtil.Create("K 2" + s, "V 2" + s), parsedArgs.PathMap[1]);

            parsedArgs = DefaultParse(new[] { "/pathmap:\"K 1\"=\"V 1\",\"K 2\"=\"V 2\"", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(KeyValuePairUtil.Create("K 1" + s, "V 1" + s), parsedArgs.PathMap[0]);
            Assert.Equal(KeyValuePairUtil.Create("K 2" + s, "V 2" + s), parsedArgs.PathMap[1]);

            parsedArgs = DefaultParse(new[] { "/pathmap:\"a ==,,b\"=\"1,,== 2\",\"x ==,,y\"=\"3 4\",", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(KeyValuePairUtil.Create("a =,b" + s, "1,= 2" + s), parsedArgs.PathMap[0]);
            Assert.Equal(KeyValuePairUtil.Create("x =,y" + s, "3 4" + s), parsedArgs.PathMap[1]);

            parsedArgs = DefaultParse(new[] { @"/pathmap:C:\temp\=/_1/,C:\temp\a\=/_2/,C:\temp\a\b\=/_3/", "a.cs", @"a\b.cs", @"a\b\c.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(KeyValuePairUtil.Create(@"C:\temp\a\b\", "/_3/"), parsedArgs.PathMap[0]);
            Assert.Equal(KeyValuePairUtil.Create(@"C:\temp\a\", "/_2/"), parsedArgs.PathMap[1]);
            Assert.Equal(KeyValuePairUtil.Create(@"C:\temp\", "/_1/"), parsedArgs.PathMap[2]);
        }

        [Theory]
        [InlineData("", new string[0])]
        [InlineData(",", new[] { "", "" })]
        [InlineData(",,", new[] { "," })]
        [InlineData(",,,", new[] { ",", "" })]
        [InlineData(",,,,", new[] { ",," })]
        [InlineData("a,", new[] { "a", "" })]
        [InlineData("a,b", new[] { "a", "b" })]
        [InlineData(",,a,,,,,b,,", new[] { ",a,,", "b," })]
        public void SplitWithDoubledSeparatorEscaping(string str, string[] expected)
        {
            AssertEx.Equal(expected, CommandLineParser.SplitWithDoubledSeparatorEscaping(str, ','));
        }

        [ConditionalFact(typeof(WindowsOnly), Reason = "https://github.com/dotnet/roslyn/issues/30289")]
        [CompilerTrait(CompilerFeature.Determinism)]
        public void PathMapPdbParser()
        {
            var dir = Path.Combine(WorkingDirectory, "a");
            var parsedArgs = DefaultParse(new[] { $@"/pathmap:{dir}=b:\", "a.cs", @"/pdb:a\data.pdb", "/debug:full" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(Path.Combine(dir, @"data.pdb"), parsedArgs.PdbPath);

            // This value is calculate during Emit phases and should be null even in the face of a pathmap targeting it.
            Assert.Null(parsedArgs.EmitOptions.PdbFilePath);
        }

        [ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)]
        [CompilerTrait(CompilerFeature.Determinism)]
        public void PathMapPdbEmit()
        {
            void AssertPdbEmit(TempDirectory dir, string pdbPath, string pePdbPath, params string[] extraArgs)
            {
                var source = @"class Program { static void Main() { } }";
                var src = dir.CreateFile("a.cs").WriteAllText(source);
                var defaultArgs = new[] { "/nologo", "a.cs", "/out:a.exe", "/debug:full", $"/pdb:{pdbPath}" };
                var isDeterministic = extraArgs.Contains("/deterministic");
                var args = defaultArgs.Concat(extraArgs).ToArray();
                var outWriter = new StringWriter(CultureInfo.InvariantCulture);

                var csc = CreateCSharpCompiler(null, dir.Path, args);
                int exitCode = csc.Run(outWriter);
                Assert.Equal(0, exitCode);

                var exePath = Path.Combine(dir.Path, "a.exe");
                Assert.True(File.Exists(exePath));
                Assert.True(File.Exists(pdbPath));
                using (var peStream = File.OpenRead(exePath))
                {
                    PdbValidation.ValidateDebugDirectory(peStream, null, pePdbPath, hashAlgorithm: default, hasEmbeddedPdb: false, isDeterministic);
                }
            }

            // Case with no mappings
            using (var dir = new DisposableDirectory(Temp))
            {
                var pdbPath = Path.Combine(dir.Path, "a.pdb");
                AssertPdbEmit(dir, pdbPath, pdbPath);
            }

            // Simple mapping
            using (var dir = new DisposableDirectory(Temp))
            {
                var pdbPath = Path.Combine(dir.Path, "a.pdb");
                AssertPdbEmit(dir, pdbPath, @"q:\a.pdb", $@"/pathmap:{dir.Path}=q:\");
            }

            // Simple mapping deterministic
            using (var dir = new DisposableDirectory(Temp))
            {
                var pdbPath = Path.Combine(dir.Path, "a.pdb");
                AssertPdbEmit(dir, pdbPath, @"q:\a.pdb", $@"/pathmap:{dir.Path}=q:\", "/deterministic");
            }

            // Partial mapping
            using (var dir = new DisposableDirectory(Temp))
            {
                dir.CreateDirectory("pdb");
                var pdbPath = Path.Combine(dir.Path, @"pdb\a.pdb");
                AssertPdbEmit(dir, pdbPath, @"q:\pdb\a.pdb", $@"/pathmap:{dir.Path}=q:\");
            }

            // Legacy feature flag
            using (var dir = new DisposableDirectory(Temp))
            {
                var pdbPath = Path.Combine(dir.Path, "a.pdb");
                AssertPdbEmit(dir, pdbPath, @"a.pdb", $@"/features:pdb-path-determinism");
            }

            // Unix path map
            using (var dir = new DisposableDirectory(Temp))
            {
                var pdbPath = Path.Combine(dir.Path, "a.pdb");
                AssertPdbEmit(dir, pdbPath, @"/a.pdb", $@"/pathmap:{dir.Path}=/");
            }

            // Multi-specified path map with mixed slashes
            using (var dir = new DisposableDirectory(Temp))
            {
                var pdbPath = Path.Combine(dir.Path, "a.pdb");
                AssertPdbEmit(dir, pdbPath, "/goo/a.pdb", $"/pathmap:{dir.Path}=/goo,{dir.Path}{PathUtilities.DirectorySeparatorChar}=/bar");
            }
        }

        [CompilerTrait(CompilerFeature.Determinism)]
        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void DeterministicPdbsRegardlessOfBitness()
        {
            var dir = Temp.CreateDirectory();
            var dir32 = dir.CreateDirectory("32");
            var dir64 = dir.CreateDirectory("64");

            var programExe32 = dir32.CreateFile("Program.exe");
            var programPdb32 = dir32.CreateFile("Program.pdb");
            var programExe64 = dir64.CreateFile("Program.exe");
            var programPdb64 = dir64.CreateFile("Program.pdb");

            var sourceFile = dir.CreateFile("Source.cs").WriteAllText(@"
using System;
using System.Linq;
using System.Collections.Generic;

namespace N
{
    using I4 = System.Int32;

    class Program
    {
        public static IEnumerable<int> F()
        {
            I4 x = 1;
            yield return 1;
            yield return x;
        }

        public static void Main(string[] args)
        {
            dynamic x = 1;
            const int a = 1;
            F().ToArray();
            Console.WriteLine(x + a);
        }
    }
}");
            var csc32src = $@"
using System;
using System.Reflection;

class Runner
{{
    static int Main(string[] args)
    {{
        var assembly = Assembly.LoadFrom(@""{s_CSharpCompilerExecutable}"");
        var program = assembly.GetType(""Microsoft.CodeAnalysis.CSharp.CommandLine.Program"");
        var main = program.GetMethod(""Main"");
        return (int)main.Invoke(null, new object[] {{ args }});
    }}
}}
";
            var csc32 = CreateCompilationWithMscorlib46(csc32src, options: TestOptions.ReleaseExe.WithPlatform(Platform.X86), assemblyName: "csc32");
            var csc32exe = dir.CreateFile("csc32.exe").WriteAllBytes(csc32.EmitToArray());

            dir.CopyFile(Path.ChangeExtension(s_CSharpCompilerExecutable, ".exe.config"), "csc32.exe.config");
            dir.CopyFile(Path.Combine(Path.GetDirectoryName(s_CSharpCompilerExecutable), "csc.rsp"));

            var output = ProcessUtilities.RunAndGetOutput(csc32exe.Path, $@"/nologo /debug:full /deterministic /out:Program.exe /pathmap:""{dir32.Path}""=X:\ ""{sourceFile.Path}""", expectedRetCode: 0, startFolder: dir32.Path);
            Assert.Equal("", output);

            output = ProcessUtilities.RunAndGetOutput(s_CSharpCompilerExecutable, $@"/nologo /debug:full /deterministic /out:Program.exe /pathmap:""{dir64.Path}""=X:\ ""{sourceFile.Path}""", expectedRetCode: 0, startFolder: dir64.Path);
            Assert.Equal("", output);

            AssertEx.Equal(programExe32.ReadAllBytes(), programExe64.ReadAllBytes());
            AssertEx.Equal(programPdb32.ReadAllBytes(), programPdb64.ReadAllBytes());
        }

        [WorkItem(7588, "https://github.com/dotnet/roslyn/issues/7588")]
        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void Version()
        {
            var folderName = Temp.CreateDirectory().ToString();
            var argss = new[]
            {
                "/version",
                "a.cs /version /preferreduilang:en",
                "/version /nologo",
                "/version /help",
            };

            foreach (var args in argss)
            {
                var output = ProcessUtilities.RunAndGetOutput(s_CSharpCompilerExecutable, args, startFolder: folderName);
                Assert.Equal(s_compilerVersion, output.Trim());
            }
        }

        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void RefOut()
        {
            var dir = Temp.CreateDirectory();
            var refDir = dir.CreateDirectory("ref");

            var src = dir.CreateFile("a.cs");
            src.WriteAllText(@"
public class C
{
    /// <summary>Main method</summary>
    public static void Main()
    {
        System.Console.Write(""Hello"");
    }
    /// <summary>Private method</summary>
    private static void PrivateMethod()
    {
        System.Console.Write(""Private"");
    }
}");

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path,
                new[] { "/nologo", "/out:a.exe", "/refout:ref/a.dll", "/doc:doc.xml", "/deterministic", "/langversion:7", "a.cs" });

            int exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);

            var exe = Path.Combine(dir.Path, "a.exe");
            Assert.True(File.Exists(exe));

            MetadataReaderUtils.VerifyPEMetadata(exe,
                new[] { "TypeDefinition:<Module>", "TypeDefinition:C" },
                new[] { "MethodDefinition:Void C.Main()", "MethodDefinition:Void C.PrivateMethod()", "MethodDefinition:Void C..ctor()" },
                new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute" }
                );

            var doc = Path.Combine(dir.Path, "doc.xml");
            Assert.True(File.Exists(doc));

            var content = File.ReadAllText(doc);
            var expectedDoc =
@"<?xml version=""1.0""?>
<doc>
    <assembly>
        <name>a</name>
    </assembly>
    <members>
        <member name=""M:C.Main"">
            <summary>Main method</summary>
        </member>
        <member name=""M:C.PrivateMethod"">
            <summary>Private method</summary>
        </member>
    </members>
</doc>";
            Assert.Equal(expectedDoc, content.Trim());

            var output = ProcessUtilities.RunAndGetOutput(exe, startFolder: dir.Path);
            Assert.Equal("Hello", output.Trim());

            var refDll = Path.Combine(refDir.Path, "a.dll");
            Assert.True(File.Exists(refDll));

            // The types and members that are included needs further refinement.
            // See issue https://github.com/dotnet/roslyn/issues/17612
            MetadataReaderUtils.VerifyPEMetadata(refDll,
                new[] { "TypeDefinition:<Module>", "TypeDefinition:C" },
                new[] { "MethodDefinition:Void C.Main()", "MethodDefinition:Void C..ctor()" },
                new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "ReferenceAssemblyAttribute" }
                );

            // Clean up temp files
            CleanupAllGeneratedFiles(dir.Path);
            CleanupAllGeneratedFiles(refDir.Path);
        }

        [Fact]
        public void RefOutWithError()
        {
            var dir = Temp.CreateDirectory();
            dir.CreateDirectory("ref");

            var src = dir.CreateFile("a.cs");
            src.WriteAllText(@"class C { public static void Main() { error(); } }");

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path,
                new[] { "/nologo", "/out:a.dll", "/refout:ref/a.dll", "/deterministic", "/preferreduilang:en", "a.cs" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal(1, exitCode);

            var dll = Path.Combine(dir.Path, "a.dll");
            Assert.False(File.Exists(dll));

            var refDll = Path.Combine(dir.Path, Path.Combine("ref", "a.dll"));
            Assert.False(File.Exists(refDll));

            Assert.Equal("a.cs(1,39): error CS0103: The name 'error' does not exist in the current context", outWriter.ToString().Trim());

            // Clean up temp files
            CleanupAllGeneratedFiles(dir.Path);
        }

        [Fact]
        public void RefOnly()
        {
            var dir = Temp.CreateDirectory();

            var src = dir.CreateFile("a.cs");
            src.WriteAllText(@"
using System;
class C
{
    /// <summary>Main method</summary>
    public static void Main()
    {
        error(); // semantic error in method body
    }
    private event Action E1
    {
        add { }
        remove { }
    }
    private event Action E2;

    /// <summary>Private Class Field</summary>
    private int field;

    /// <summary>Private Struct</summary>
    private struct S
    {
        /// <summary>Private Struct Field</summary>
        private int field;
    }
}");

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(null, dir.Path,
                new[] { "/nologo", "/out:a.dll", "/refonly", "/debug", "/deterministic", "/langversion:7", "/doc:doc.xml", "a.cs" });
            int exitCode = csc.Run(outWriter);
            Assert.Equal("", outWriter.ToString());
            Assert.Equal(0, exitCode);

            var refDll = Path.Combine(dir.Path, "a.dll");
            Assert.True(File.Exists(refDll));

            // The types and members that are included needs further refinement.
            // See issue https://github.com/dotnet/roslyn/issues/17612
            MetadataReaderUtils.VerifyPEMetadata(refDll,
                new[] { "TypeDefinition:<Module>", "TypeDefinition:C", "TypeDefinition:S" },
                new[] { "MethodDefinition:Void C.Main()", "MethodDefinition:Void C..ctor()" },
                new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "ReferenceAssemblyAttribute" }
                );

            var pdb = Path.Combine(dir.Path, "a.pdb");
            Assert.False(File.Exists(pdb));

            var doc = Path.Combine(dir.Path, "doc.xml");
            Assert.True(File.Exists(doc));

            var content = File.ReadAllText(doc);
            var expectedDoc =
@"<?xml version=""1.0""?>
<doc>
    <assembly>
        <name>a</name>
    </assembly>
    <members>
        <member name=""M:C.Main"">
            <summary>Main method</summary>
        </member>
        <member name=""F:C.field"">
            <summary>Private Class Field</summary>
        </member>
        <member name=""T:C.S"">
            <summary>Private Struct</summary>
        </member>
        <member name=""F:C.S.field"">
            <summary>Private Struct Field</summary>
        </member>
    </members>
</doc>";
            Assert.Equal(expectedDoc, content.Trim());


            // Clean up temp files
            CleanupAllGeneratedFiles(dir.Path);
        }

        [Fact]
        public void CompilingCodeWithInvalidPreProcessorSymbolsShouldProvideDiagnostics()
        {
            var parsedArgs = DefaultParse(new[] { "/define:1", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // warning CS2029: Invalid name for a preprocessing symbol; '1' is not a valid identifier
                Diagnostic(ErrorCode.WRN_DefineIdentifierRequired).WithArguments("1").WithLocation(1, 1));
        }

        [Fact]
        public void WhitespaceInDefine()
        {
            var parsedArgs = DefaultParse(new[] { "/define:\" a\"", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal("a", parsedArgs.ParseOptions.PreprocessorSymbols.Single());
        }

        [Fact]
        public void WhitespaceInDefine_OnlySpaces()
        {
            var parsedArgs = DefaultParse(new[] { "/define:\"   \"", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                Diagnostic(ErrorCode.WRN_DefineIdentifierRequired).WithArguments("   ").WithLocation(1, 1)
                );
            Assert.True(parsedArgs.ParseOptions.PreprocessorSymbols.IsEmpty);
        }

        [Fact]
        public void CompilingCodeWithInvalidLanguageVersionShouldProvideDiagnostics()
        {
            var parsedArgs = DefaultParse(new[] { "/langversion:1000", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // error CS1617: Invalid option '1000' for /langversion. Use '/langversion:?' to list supported values.
                Diagnostic(ErrorCode.ERR_BadCompatMode).WithArguments("1000").WithLocation(1, 1));
        }

        [Fact, WorkItem(16913, "https://github.com/dotnet/roslyn/issues/16913")]
        public void CompilingCodeWithMultipleInvalidPreProcessorSymbolsShouldErrorOut()
        {
            var parsedArgs = DefaultParse(new[] { "/define:valid1,2invalid,valid3", "/define:4,5,valid6", "a.cs" }, WorkingDirectory);
            parsedArgs.Errors.Verify(
                // warning CS2029: Invalid value for '/define'; '2invalid' is not a valid identifier
                Diagnostic(ErrorCode.WRN_DefineIdentifierRequired).WithArguments("2invalid"),
                // warning CS2029: Invalid value for '/define'; '4' is not a valid identifier
                Diagnostic(ErrorCode.WRN_DefineIdentifierRequired).WithArguments("4"),
                // warning CS2029: Invalid value for '/define'; '5' is not a valid identifier
                Diagnostic(ErrorCode.WRN_DefineIdentifierRequired).WithArguments("5"));
        }

        [WorkItem(406649, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=406649")]
        [ConditionalFact(typeof(WindowsDesktopOnly), typeof(IsEnglishLocal), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void MissingCompilerAssembly()
        {
            var dir = Temp.CreateDirectory();
            var cscPath = dir.CopyFile(s_CSharpCompilerExecutable).Path;
            dir.CopyFile(typeof(Compilation).Assembly.Location);

            // Missing Microsoft.CodeAnalysis.CSharp.dll.
            var result = ProcessUtilities.Run(cscPath, arguments: "/nologo /t:library unknown.cs", workingDirectory: dir.Path);
            Assert.Equal(1, result.ExitCode);
            Assert.Equal(
                $"Could not load file or assembly '{typeof(CSharpCompilation).Assembly.FullName}' or one of its dependencies. The system cannot find the file specified.",
                result.Output.Trim());

            // Missing System.Collections.Immutable.dll.
            dir.CopyFile(typeof(CSharpCompilation).Assembly.Location);
            result = ProcessUtilities.Run(cscPath, arguments: "/nologo /t:library unknown.cs", workingDirectory: dir.Path);
            Assert.Equal(1, result.ExitCode);
            Assert.Equal(
                $"Could not load file or assembly '{typeof(ImmutableArray).Assembly.FullName}' or one of its dependencies. The system cannot find the file specified.",
                result.Output.Trim());
        }

#if NET472
        [ConditionalFact(typeof(WindowsDesktopOnly), typeof(IsEnglishLocal), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void LoadinganalyzerNetStandard13()
        {
            var analyzerFileName = "AnalyzerNS13.dll";
            var srcFileName = "src.cs";

            var analyzerDir = Temp.CreateDirectory();

            var analyzerFile = analyzerDir.CreateFile(analyzerFileName).WriteAllBytes(CreateCSharpAnalyzerNetStandard13(Path.GetFileNameWithoutExtension(analyzerFileName)));
            var srcFile = analyzerDir.CreateFile(srcFileName).WriteAllText("public class C { }");

            var result = ProcessUtilities.Run(s_CSharpCompilerExecutable, arguments: $"/nologo /t:library /analyzer:{analyzerFileName} {srcFileName}", workingDirectory: analyzerDir.Path);
            var outputWithoutPaths = Regex.Replace(result.Output, " in .*", "");
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
                $@"warning AD0001: Analyzer 'TestAnalyzer' threw an exception of type 'System.NotImplementedException' with message '28'.
System.NotImplementedException: 28
   at TestAnalyzer.get_SupportedDiagnostics()
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerManager.AnalyzerExecutionContext.<>c__DisplayClass20_0.<ComputeDiagnosticDescriptors>b__0(Object _)
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info)
-----", outputWithoutPaths);

            Assert.Equal(0, result.ExitCode);
        }
#endif

        private static ImmutableArray<byte> CreateCSharpAnalyzerNetStandard13(string analyzerAssemblyName)
        {
            var minSystemCollectionsImmutableSource = @"
[assembly: System.Reflection.AssemblyVersion(""1.2.3.0"")]

namespace System.Collections.Immutable
{
    public struct ImmutableArray<T>
    {
    }
}
";

            var minCodeAnalysisSource = @"
using System;

[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")]

namespace Microsoft.CodeAnalysis.Diagnostics
{
    [AttributeUsage(AttributeTargets.Class)]
    public sealed class DiagnosticAnalyzerAttribute : Attribute
    {
        public DiagnosticAnalyzerAttribute(string firstLanguage, params string[] additionalLanguages) {}
    }

    public abstract class DiagnosticAnalyzer
    {
        public abstract System.Collections.Immutable.ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }
        public abstract void Initialize(AnalysisContext context);
    }

    public abstract class AnalysisContext
    {
    }
}

namespace Microsoft.CodeAnalysis
{
    public sealed class DiagnosticDescriptor
    {
    }
}
";
            var minSystemCollectionsImmutableImage = CSharpCompilation.Create(
                "System.Collections.Immutable",
                new[] { SyntaxFactory.ParseSyntaxTree(minSystemCollectionsImmutableSource) },
                new MetadataReference[] { NetStandard13.SystemRuntime },
                new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, cryptoPublicKey: TestResources.TestKeys.PublicKey_b03f5f7f11d50a3a)).EmitToArray();

            var minSystemCollectionsImmutableRef = MetadataReference.CreateFromImage(minSystemCollectionsImmutableImage);

            var minCodeAnalysisImage = CSharpCompilation.Create(
                "Microsoft.CodeAnalysis",
                new[] { SyntaxFactory.ParseSyntaxTree(minCodeAnalysisSource) },
                new MetadataReference[]
                {
                    NetStandard13.SystemRuntime,
                    minSystemCollectionsImmutableRef
                },
                new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, cryptoPublicKey: TestResources.TestKeys.PublicKey_31bf3856ad364e35)).EmitToArray();

            var minCodeAnalysisRef = MetadataReference.CreateFromImage(minCodeAnalysisImage);

            var analyzerSource = @"
using System;
using System.Collections.ObjectModel;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using System.Net.Security;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Security.AccessControl;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.Win32.SafeHandles;

[DiagnosticAnalyzer(""C#"")]
public class TestAnalyzer : DiagnosticAnalyzer
{
    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => throw new NotImplementedException(new[]                                 
    {                                                                                                                                   
        typeof(Win32Exception),           // Microsoft.Win32.Primitives
        typeof(AppContext),               // System.AppContext
        typeof(Console),                  // System.Console
        typeof(ValueTuple),               // System.ValueTuple
        typeof(FileVersionInfo),          // System.Diagnostics.FileVersionInfo
        typeof(Process),                  // System.Diagnostics.Process
        typeof(ChineseLunisolarCalendar), // System.Globalization.Calendars
        typeof(ZipArchive),               // System.IO.Compression
        typeof(ZipFile),                  // System.IO.Compression.ZipFile
        typeof(FileOptions),              // System.IO.FileSystem
        typeof(FileAttributes),           // System.IO.FileSystem.Primitives
        typeof(HttpClient),               // System.Net.Http
        typeof(AuthenticatedStream),      // System.Net.Security
        typeof(IOControlCode),            // System.Net.Sockets
        typeof(RuntimeInformation),       // System.Runtime.InteropServices.RuntimeInformation
        typeof(SerializationException),   // System.Runtime.Serialization.Primitives
        typeof(GenericIdentity),          // System.Security.Claims
        typeof(Aes),                      // System.Security.Cryptography.Algorithms
        typeof(CspParameters),            // System.Security.Cryptography.Csp
        typeof(AsnEncodedData),           // System.Security.Cryptography.Encoding
        typeof(AsymmetricAlgorithm),      // System.Security.Cryptography.Primitives
        typeof(SafeX509ChainHandle),      // System.Security.Cryptography.X509Certificates
        typeof(IXmlLineInfo),             // System.Xml.ReaderWriter
        typeof(XmlNode),                  // System.Xml.XmlDocument
        typeof(XPathDocument),            // System.Xml.XPath
        typeof(XDocumentExtensions),      // System.Xml.XPath.XDocument
        typeof(CodePagesEncodingProvider),// System.Text.Encoding.CodePages
        typeof(ValueTask<>),              // System.Threading.Tasks.Extensions

        // csc doesn't ship with facades for the following assemblies. 
        // Analyzers can't use them unless they carry the facade with them.

        // typeof(SafePipeHandle),           // System.IO.Pipes
        // typeof(StackFrame),               // System.Diagnostics.StackTrace
        // typeof(BindingFlags),             // System.Reflection.TypeExtensions
        // typeof(AccessControlActions),     // System.Security.AccessControl
        // typeof(SafeAccessTokenHandle),    // System.Security.Principal.Windows
        // typeof(Thread),                   // System.Threading.Thread
    }.Length.ToString());

    public override void Initialize(AnalysisContext context)
    {
    }
}";

            var references =
                new MetadataReference[]
                {
                    minCodeAnalysisRef,
                    minSystemCollectionsImmutableRef
                };
            references = references.Concat(NetStandard13.All).ToArray();

            var analyzerImage = CSharpCompilation.Create(
                analyzerAssemblyName,
                new SyntaxTree[] { SyntaxFactory.ParseSyntaxTree(analyzerSource) },
                references: references,
                new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)).EmitToArray();

            return analyzerImage;
        }

        [WorkItem(406649, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=484417")]
        [ConditionalFact(typeof(WindowsDesktopOnly), typeof(IsEnglishLocal), Reason = "https://github.com/dotnet/roslyn/issues/30321")]
        public void MicrosoftDiaSymReaderNativeAltLoadPath()
        {
            var dir = Temp.CreateDirectory();
            var cscDir = Path.GetDirectoryName(s_CSharpCompilerExecutable);

            // copy csc and dependencies except for DSRN:
            foreach (var filePath in Directory.EnumerateFiles(cscDir))
            {
                var fileName = Path.GetFileName(filePath);

                if (fileName.StartsWith("csc") ||
                    fileName.StartsWith("System.") ||
                    fileName.StartsWith("Microsoft.") && !fileName.StartsWith("Microsoft.DiaSymReader.Native"))
                {
                    dir.CopyFile(filePath);
                }
            }

            dir.CreateFile("Source.cs").WriteAllText("class C { void F() { } }");

            var cscCopy = Path.Combine(dir.Path, "csc.exe");

            var arguments = "/nologo /t:library /debug:full Source.cs";

            // env variable not set (deterministic) -- DSRN is required:
            var result = ProcessUtilities.Run(
                cscCopy,
                arguments + " /deterministic",
                workingDirectory: dir.Path,
                additionalEnvironmentVars: new[] { KeyValuePairUtil.Create("MICROSOFT_DIASYMREADER_NATIVE_ALT_LOAD_PATH", "") });

            AssertEx.AssertEqualToleratingWhitespaceDifferences(
                "error CS0041: Unexpected error writing debug information -- 'Unable to load DLL 'Microsoft.DiaSymReader.Native.amd64.dll': " +
                "The specified module could not be found. (Exception from HRESULT: 0x8007007E)'", result.Output.Trim());

            // env variable not set (non-deterministic) -- globally registered SymReader is picked up:
            result = ProcessUtilities.Run(cscCopy, arguments, workingDirectory: dir.Path);
            AssertEx.AssertEqualToleratingWhitespaceDifferences("", result.Output.Trim());

            // env variable set:
            result = ProcessUtilities.Run(
                cscCopy,
                arguments + " /deterministic",
                workingDirectory: dir.Path,
                additionalEnvironmentVars: new[] { KeyValuePairUtil.Create("MICROSOFT_DIASYMREADER_NATIVE_ALT_LOAD_PATH", cscDir) });

            Assert.Equal("", result.Output.Trim());
        }

        [ConditionalFact(typeof(WindowsOnly))]
        [WorkItem(21935, "https://github.com/dotnet/roslyn/issues/21935")]
        public void PdbPathNotEmittedWithoutPdb()
        {
            var dir = Temp.CreateDirectory();

            var source = @"class Program { static void Main() { } }";
            var src = dir.CreateFile("a.cs").WriteAllText(source);
            var args = new[] { "/nologo", "a.cs", "/out:a.exe", "/debug-" };
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);

            var csc = CreateCSharpCompiler(null, dir.Path, args);
            int exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);

            var exePath = Path.Combine(dir.Path, "a.exe");
            Assert.True(File.Exists(exePath));
            using (var peStream = File.OpenRead(exePath))
            using (var peReader = new PEReader(peStream))
            {
                var debugDirectory = peReader.PEHeaders.PEHeader.DebugTableDirectory;
                Assert.Equal(0, debugDirectory.Size);
                Assert.Equal(0, debugDirectory.RelativeVirtualAddress);
            }
        }

        [Fact]
        public void StrongNameProviderWithCustomTempPath()
        {
            var tempDir = Temp.CreateDirectory();
            var workingDir = Temp.CreateDirectory();
            workingDir.CreateFile("a.cs");

            var buildPaths = new BuildPaths(clientDir: "", workingDir: workingDir.Path, sdkDir: null, tempDir: tempDir.Path);
            var csc = new MockCSharpCompiler(null, buildPaths, args: new[] { "/features:UseLegacyStrongNameProvider", "/nostdlib", "a.cs" });
            var comp = csc.CreateCompilation(new StringWriter(), new TouchedFileLogger(), errorLogger: null);
            Assert.True(!comp.SignUsingBuilder);
        }

        public class QuotedArgumentTests : CommandLineTestBase
        {
            private static readonly string s_rootPath = ExecutionConditionUtil.IsWindows
                ? @"c:\"
                : "/";

            private void VerifyQuotedValid<T>(string name, string value, T expected, Func<CSharpCommandLineArguments, T> getValue)
            {
                var args = DefaultParse(new[] { $"/{name}:{value}", "a.cs" }, s_rootPath);
                Assert.Equal(0, args.Errors.Length);
                Assert.Equal(expected, getValue(args));

                args = DefaultParse(new[] { $@"/{name}:""{value}""", "a.cs" }, s_rootPath);
                Assert.Equal(0, args.Errors.Length);
                Assert.Equal(expected, getValue(args));
            }

            private void VerifyQuotedInvalid<T>(string name, string value, T expected, Func<CSharpCommandLineArguments, T> getValue)
            {
                var args = DefaultParse(new[] { $"/{name}:{value}", "a.cs" }, s_rootPath);
                Assert.Equal(0, args.Errors.Length);
                Assert.Equal(expected, getValue(args));

                args = DefaultParse(new[] { $@"/{name}:""{value}""", "a.cs" }, s_rootPath);
                Assert.True(args.Errors.Length > 0);
            }

            [WorkItem(12427, "https://github.com/dotnet/roslyn/issues/12427")]
            [Fact]
            public void DebugFlag()
            {
                var platformPdbKind = PathUtilities.IsUnixLikePlatform ? DebugInformationFormat.PortablePdb : DebugInformationFormat.Pdb;

                var list = new List<Tuple<string, DebugInformationFormat>>()
                {
                    Tuple.Create("portable", DebugInformationFormat.PortablePdb),
                    Tuple.Create("full", platformPdbKind),
                    Tuple.Create("pdbonly", platformPdbKind),
                    Tuple.Create("embedded", DebugInformationFormat.Embedded)
                };

                foreach (var tuple in list)
                {
                    VerifyQuotedValid("debug", tuple.Item1, tuple.Item2, x => x.EmitOptions.DebugInformationFormat);
                }
            }

            [WorkItem(12427, "https://github.com/dotnet/roslyn/issues/12427")]
            [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30328")]
            public void CodePage()
            {
                VerifyQuotedValid("codepage", "1252", 1252, x => x.Encoding.CodePage);
            }

            [WorkItem(12427, "https://github.com/dotnet/roslyn/issues/12427")]
            [Fact]
            public void Target()
            {
                var list = new List<Tuple<string, OutputKind>>()
                {
                    Tuple.Create("exe", OutputKind.ConsoleApplication),
                    Tuple.Create("winexe", OutputKind.WindowsApplication),
                    Tuple.Create("library", OutputKind.DynamicallyLinkedLibrary),
                    Tuple.Create("module", OutputKind.NetModule),
                    Tuple.Create("appcontainerexe", OutputKind.WindowsRuntimeApplication),
                    Tuple.Create("winmdobj", OutputKind.WindowsRuntimeMetadata)
                };

                foreach (var tuple in list)
                {
                    VerifyQuotedInvalid("target", tuple.Item1, tuple.Item2, x => x.CompilationOptions.OutputKind);
                }
            }

            [WorkItem(12427, "https://github.com/dotnet/roslyn/issues/12427")]
            [Fact]
            public void PlatformFlag()
            {
                var list = new List<Tuple<string, Platform>>()
                {
                    Tuple.Create("x86", Platform.X86),
                    Tuple.Create("x64", Platform.X64),
                    Tuple.Create("itanium", Platform.Itanium),
                    Tuple.Create("anycpu", Platform.AnyCpu),
                    Tuple.Create("anycpu32bitpreferred",Platform.AnyCpu32BitPreferred),
                    Tuple.Create("arm", Platform.Arm)
                };

                foreach (var tuple in list)
                {
                    VerifyQuotedValid("platform", tuple.Item1, tuple.Item2, x => x.CompilationOptions.Platform);
                }
            }

            [WorkItem(12427, "https://github.com/dotnet/roslyn/issues/12427")]
            [Fact]
            public void WarnFlag()
            {
                VerifyQuotedValid("warn", "1", 1, x => x.CompilationOptions.WarningLevel);
            }

            [WorkItem(12427, "https://github.com/dotnet/roslyn/issues/12427")]
            [Fact]
            public void LangVersionFlag()
            {
                VerifyQuotedValid("langversion", "2", LanguageVersion.CSharp2, x => x.ParseOptions.LanguageVersion);
            }
        }

        [Fact]
        [WorkItem(23525, "https://github.com/dotnet/roslyn/issues/23525")]
        public void InvalidPathCharacterInPathMap()
        {
            string filePath = Temp.CreateFile().WriteAllText("").Path;
            var compiler = CreateCSharpCompiler(null, WorkingDirectory, new[]
            {
                filePath,
                "/debug:embedded",
                "/pathmap:test\\=\"",
                "/target:library",
                "/preferreduilang:en"
            });

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = compiler.Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Contains("error CS8101: The pathmap option was incorrectly formatted.", outWriter.ToString(), StringComparison.Ordinal);
        }

        [WorkItem(23525, "https://github.com/dotnet/roslyn/issues/23525")]
        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30289")]
        public void InvalidPathCharacterInPdbPath()
        {
            string filePath = Temp.CreateFile().WriteAllText("").Path;
            var compiler = CreateCSharpCompiler(null, WorkingDirectory, new[]
            {
                filePath,
                "/debug:embedded",
                "/pdb:test\\?.pdb",
                "/target:library",
                "/preferreduilang:en"
            });

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = compiler.Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.Contains("error CS2021: File name 'test\\?.pdb' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long", outWriter.ToString(), StringComparison.Ordinal);
        }

        [WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
        [ConditionalFact(typeof(IsEnglishLocal))]
        public void TestSuppression_CompilerParserWarningAsError()
        {
            string source = @"
class C
{
    long M(int i)
    {
        // warning CS0078 : The 'l' suffix is easily confused with the digit '1' -- use 'L' for clarity
        return 0l;
    }
}
";
            var srcDirectory = Temp.CreateDirectory();
            var srcFile = srcDirectory.CreateFile("a.cs");
            srcFile.WriteAllText(source);

            // Verify that parser warning CS0078 is reported.
            var output = VerifyOutput(srcDirectory, srcFile, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
            Assert.Contains("warning CS0078", output, StringComparison.Ordinal);

            // Verify that parser warning CS0078 is reported as error for /warnaserror.
            output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1,
                additionalFlags: new[] { "/warnAsError" }, includeCurrentAssemblyAsAnalyzerReference: false);
            Assert.Contains("error CS0078", output, StringComparison.Ordinal);

            // Verify that parser warning CS0078 is suppressed with diagnostic suppressor even with /warnaserror
            // and info diagnostic is logged with programmatic suppression information.
            var suppressor = new DiagnosticSuppressorForId("CS0078");
            output = VerifyOutput(srcDirectory, srcFile, expectedInfoCount: 1, expectedWarningCount: 0, expectedErrorCount: 0,
                additionalFlags: new[] { "/warnAsError" },
                includeCurrentAssemblyAsAnalyzerReference: false,
                errorlog: true,
                analyzers: suppressor);
            Assert.DoesNotContain($"error CS0078", output, StringComparison.Ordinal);
            Assert.DoesNotContain($"warning CS0078", output, StringComparison.Ordinal);

            // Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppression ID '{2}' and justification '{3}'
            var suppressionMessage = string.Format(CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
                suppressor.SuppressionDescriptor.SuppressedDiagnosticId,
                new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.WRN_LowercaseEllSuffix, "l"), Location.None).GetMessage(CultureInfo.InvariantCulture),
                suppressor.SuppressionDescriptor.Id,
                suppressor.SuppressionDescriptor.Justification);
            Assert.Contains("info SP0001", output, StringComparison.Ordinal);
            Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(srcFile.Path);
        }

        [WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
        [ConditionalTheory(typeof(IsEnglishLocal)), CombinatorialData]
        public void TestSuppression_CompilerSyntaxWarning(bool skipAnalyzers)
        {
            // warning CS1522: Empty switch block
            // NOTE: Empty switch block warning is reported by the C# language parser
            string source = @"
class C
{
    void M(int i)
    {
        switch (i)
        {
        }
    }
}";
            var srcDirectory = Temp.CreateDirectory();
            var srcFile = srcDirectory.CreateFile("a.cs");
            srcFile.WriteAllText(source);

            // Verify that compiler warning CS1522 is reported.
            var output = VerifyOutput(srcDirectory, srcFile, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false, skipAnalyzers: skipAnalyzers);
            Assert.Contains("warning CS1522", output, StringComparison.Ordinal);

            // Verify that compiler warning CS1522 is suppressed with diagnostic suppressor
            // and info diagnostic is logged with programmatic suppression information.
            var suppressor = new DiagnosticSuppressorForId("CS1522");

            // Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppression ID '{2}' and justification '{3}'
            var suppressionMessage = string.Format(CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
                suppressor.SuppressionDescriptor.SuppressedDiagnosticId,
                new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.WRN_EmptySwitch), Location.None).GetMessage(CultureInfo.InvariantCulture),
                suppressor.SuppressionDescriptor.Id,
                suppressor.SuppressionDescriptor.Justification);

            output = VerifyOutput(srcDirectory, srcFile, expectedInfoCount: 1, expectedWarningCount: 0, includeCurrentAssemblyAsAnalyzerReference: false,
                analyzers: suppressor, errorlog: true, skipAnalyzers: skipAnalyzers);
            Assert.DoesNotContain($"warning CS1522", output, StringComparison.Ordinal);
            Assert.Contains($"info SP0001", output, StringComparison.Ordinal);
            Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);

            // Verify that compiler warning CS1522 is reported as error for /warnaserror.
            output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1,
                additionalFlags: new[] { "/warnAsError" }, includeCurrentAssemblyAsAnalyzerReference: false, skipAnalyzers: skipAnalyzers);
            Assert.Contains("error CS1522", output, StringComparison.Ordinal);

            // Verify that compiler warning CS1522 is suppressed with diagnostic suppressor even with /warnaserror
            // and info diagnostic is logged with programmatic suppression information.
            output = VerifyOutput(srcDirectory, srcFile, expectedInfoCount: 1, expectedWarningCount: 0, expectedErrorCount: 0,
                additionalFlags: new[] { "/warnAsError" },
                includeCurrentAssemblyAsAnalyzerReference: false,
                errorlog: true,
                skipAnalyzers: skipAnalyzers,
                analyzers: suppressor);
            Assert.DoesNotContain($"error CS1522", output, StringComparison.Ordinal);
            Assert.DoesNotContain($"warning CS1522", output, StringComparison.Ordinal);
            Assert.Contains("info SP0001", output, StringComparison.Ordinal);
            Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(srcFile.Path);
        }

        [WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
        [ConditionalTheory(typeof(IsEnglishLocal)), CombinatorialData]
        public void TestSuppression_CompilerSemanticWarning(bool skipAnalyzers)
        {
            string source = @"
class C
{
    // warning CS0169: The field 'C.f' is never used
    private readonly int f;
}";
            var srcDirectory = Temp.CreateDirectory();
            var srcFile = srcDirectory.CreateFile("a.cs");
            srcFile.WriteAllText(source);

            // Verify that compiler warning CS0169 is reported.
            var output = VerifyOutput(srcDirectory, srcFile, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false, skipAnalyzers: skipAnalyzers);
            Assert.Contains("warning CS0169", output, StringComparison.Ordinal);

            // Verify that compiler warning CS0169 is suppressed with diagnostic suppressor
            // and info diagnostic is logged with programmatic suppression information.
            var suppressor = new DiagnosticSuppressorForId("CS0169");

            // Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppression ID '{2}' and justification '{3}'
            var suppressionMessage = string.Format(CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
                suppressor.SuppressionDescriptor.SuppressedDiagnosticId,
                new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.WRN_UnreferencedField, "C.f"), Location.None).GetMessage(CultureInfo.InvariantCulture),
                suppressor.SuppressionDescriptor.Id,
                suppressor.SuppressionDescriptor.Justification);

            output = VerifyOutput(srcDirectory, srcFile, expectedInfoCount: 1, expectedWarningCount: 0, includeCurrentAssemblyAsAnalyzerReference: false,
                analyzers: suppressor, errorlog: true, skipAnalyzers: skipAnalyzers);
            Assert.DoesNotContain($"warning CS0169", output, StringComparison.Ordinal);
            Assert.Contains("info SP0001", output, StringComparison.Ordinal);
            Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);

            // Verify that compiler warning CS0169 is reported as error for /warnaserror.
            output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1,
                additionalFlags: new[] { "/warnAsError" }, includeCurrentAssemblyAsAnalyzerReference: false, skipAnalyzers: skipAnalyzers);
            Assert.Contains("error CS0169", output, StringComparison.Ordinal);

            // Verify that compiler warning CS0169 is suppressed with diagnostic suppressor even with /warnaserror
            // and info diagnostic is logged with programmatic suppression information.
            output = VerifyOutput(srcDirectory, srcFile, expectedInfoCount: 1, expectedWarningCount: 0, expectedErrorCount: 0,
                additionalFlags: new[] { "/warnAsError" },
                includeCurrentAssemblyAsAnalyzerReference: false,
                errorlog: true,
                skipAnalyzers: skipAnalyzers,
                analyzers: suppressor);
            Assert.DoesNotContain($"error CS0169", output, StringComparison.Ordinal);
            Assert.DoesNotContain($"warning CS0169", output, StringComparison.Ordinal);
            Assert.Contains("info SP0001", output, StringComparison.Ordinal);
            Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(srcFile.Path);
        }

        [WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
        [Fact]
        public void TestNoSuppression_CompilerSyntaxError()
        {
            // error CS1001: Identifier expected
            string source = @"
class { }";
            var srcDirectory = Temp.CreateDirectory();
            var srcFile = srcDirectory.CreateFile("a.cs");
            srcFile.WriteAllText(source);

            // Verify that compiler syntax error CS1001 is reported.
            var output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
            Assert.Contains("error CS1001", output, StringComparison.Ordinal);

            // Verify that compiler syntax error CS1001 cannot be suppressed with diagnostic suppressor.
            output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false,
                analyzers: new DiagnosticSuppressorForId("CS1001"));
            Assert.Contains("error CS1001", output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(srcFile.Path);
        }

        [WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
        [Fact]
        public void TestNoSuppression_CompilerSemanticError()
        {
            // error CS0246: The type or namespace name 'UndefinedType' could not be found (are you missing a using directive or an assembly reference?)
            string source = @"
class C
{
    void M(UndefinedType x) { }
}";
            var srcDirectory = Temp.CreateDirectory();
            var srcFile = srcDirectory.CreateFile("a.cs");
            srcFile.WriteAllText(source);

            // Verify that compiler error CS0246 is reported.
            var output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
            Assert.Contains("error CS0246", output, StringComparison.Ordinal);

            // Verify that compiler error CS0246 cannot be suppressed with diagnostic suppressor.
            output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false,
                analyzers: new DiagnosticSuppressorForId("CS0246"));
            Assert.Contains("error CS0246", output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(srcFile.Path);
        }

        [WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
        [ConditionalFact(typeof(IsEnglishLocal))]
        public void TestSuppression_AnalyzerWarning()
        {
            string source = @"
class C { }";
            var srcDirectory = Temp.CreateDirectory();
            var srcFile = srcDirectory.CreateFile("a.cs");
            srcFile.WriteAllText(source);

            // Verify that analyzer warning is reported.
            var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: true);
            var output = VerifyOutput(srcDirectory, srcFile, expectedWarningCount: 1,
                includeCurrentAssemblyAsAnalyzerReference: false,
                analyzers: analyzer);
            Assert.Contains($"warning {analyzer.Descriptor.Id}", output, StringComparison.Ordinal);

            // Verify that analyzer warning is suppressed with diagnostic suppressor
            // and info diagnostic is logged with programmatic suppression information.
            var suppressor = new DiagnosticSuppressorForId(analyzer.Descriptor.Id);

            // Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppression ID '{2}' and justification '{3}'
            var suppressionMessage = string.Format(CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
                suppressor.SuppressionDescriptor.SuppressedDiagnosticId,
                analyzer.Descriptor.MessageFormat,
                suppressor.SuppressionDescriptor.Id,
                suppressor.SuppressionDescriptor.Justification);

            var analyzerAndSuppressor = new DiagnosticAnalyzer[] { analyzer, suppressor };
            output = VerifyOutput(srcDirectory, srcFile, expectedInfoCount: 1, expectedWarningCount: 0,
                includeCurrentAssemblyAsAnalyzerReference: false,
                errorlog: true,
                analyzers: analyzerAndSuppressor);
            Assert.DoesNotContain($"warning {analyzer.Descriptor.Id}", output, StringComparison.Ordinal);
            Assert.Contains("info SP0001", output, StringComparison.Ordinal);
            Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);

            // Verify that analyzer warning is reported as error for /warnaserror.
            output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1,
                additionalFlags: new[] { "/warnAsError" },
                includeCurrentAssemblyAsAnalyzerReference: false,
                analyzers: analyzer);
            Assert.Contains($"error {analyzer.Descriptor.Id}", output, StringComparison.Ordinal);

            // Verify that analyzer warning is suppressed with diagnostic suppressor even with /warnaserror
            // and info diagnostic is logged with programmatic suppression information.
            output = VerifyOutput(srcDirectory, srcFile, expectedInfoCount: 1, expectedWarningCount: 0,
                additionalFlags: new[] { "/warnAsError" },
                includeCurrentAssemblyAsAnalyzerReference: false,
                errorlog: true,
                analyzers: analyzerAndSuppressor);
            Assert.DoesNotContain($"warning {analyzer.Descriptor.Id}", output, StringComparison.Ordinal);
            Assert.Contains("info SP0001", output, StringComparison.Ordinal);
            Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);

            // Verify that "NotConfigurable" analyzer warning cannot be suppressed with diagnostic suppressor.
            analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: false);
            suppressor = new DiagnosticSuppressorForId(analyzer.Descriptor.Id);
            analyzerAndSuppressor = new DiagnosticAnalyzer[] { analyzer, suppressor };
            output = VerifyOutput(srcDirectory, srcFile, expectedWarningCount: 1,
                includeCurrentAssemblyAsAnalyzerReference: false,
                analyzers: analyzerAndSuppressor);
            Assert.Contains($"warning {analyzer.Descriptor.Id}", output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(srcFile.Path);
        }

        [WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
        [Fact]
        public void TestNoSuppression_AnalyzerError()
        {
            string source = @"
class C { }";
            var srcDirectory = Temp.CreateDirectory();
            var srcFile = srcDirectory.CreateFile("a.cs");
            srcFile.WriteAllText(source);

            // Verify that analyzer error is reported.
            var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Error, configurable: true);
            var output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1,
                includeCurrentAssemblyAsAnalyzerReference: false,
                analyzers: analyzer);
            Assert.Contains($"error {analyzer.Descriptor.Id}", output, StringComparison.Ordinal);

            // Verify that analyzer error cannot be suppressed with diagnostic suppressor.
            var suppressor = new DiagnosticSuppressorForId(analyzer.Descriptor.Id);
            var analyzerAndSuppressor = new DiagnosticAnalyzer[] { analyzer, suppressor };
            output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1,
                includeCurrentAssemblyAsAnalyzerReference: false,
                analyzers: analyzerAndSuppressor);
            Assert.Contains($"error {analyzer.Descriptor.Id}", output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(srcFile.Path);
        }

        [WorkItem(38674, "https://github.com/dotnet/roslyn/issues/38674")]
        [InlineData(DiagnosticSeverity.Warning, false)]
        [InlineData(DiagnosticSeverity.Info, true)]
        [InlineData(DiagnosticSeverity.Info, false)]
        [InlineData(DiagnosticSeverity.Hidden, false)]
        [Theory]
        public void TestCategoryBasedBulkAnalyzerDiagnosticConfiguration(DiagnosticSeverity defaultSeverity, bool errorlog)
        {
            var analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, defaultSeverity);

            var diagnosticId = analyzer.Descriptor.Id;
            var category = analyzer.Descriptor.Category;

            // Verify category based configuration without any diagnostic ID configuration is respected.
            var analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.category-{category}.severity = error";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Error);

            // Verify category based configuration does not get applied for suppressed diagnostic.
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Suppress, noWarn: true);

            // Verify category based configuration does not get applied for diagnostic configured in ruleset.
            var rulesetText = $@"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.CodeAnalysis"" RuleNamespace=""Microsoft.CodeAnalysis"">
    <Rule Id=""{diagnosticId}"" Action=""Warning"" />
  </Rules>
</RuleSet>";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Warn, rulesetText: rulesetText);

            // Verify category based configuration before diagnostic ID configuration is not respected.
            analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.category-{category}.severity = error
dotnet_diagnostic.{diagnosticId}.severity = warning";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Warn);

            // Verify category based configuration after diagnostic ID configuration is not respected.
            analyzerConfigText = $@"
[*.cs]
dotnet_diagnostic.{diagnosticId}.severity = warning
dotnet_analyzer_diagnostic.category-{category}.severity = error";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Warn);

            // Verify global config based configuration before diagnostic ID configuration is not respected.
            analyzerConfigText = $@"
is_global = true
dotnet_analyzer_diagnostic.category-{category}.severity = error
dotnet_diagnostic.{diagnosticId}.severity = warning";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Warn);

            // Verify global config based configuration after diagnostic ID configuration is not respected.
            analyzerConfigText = $@"
is_global = true
dotnet_diagnostic.{diagnosticId}.severity = warning
dotnet_analyzer_diagnostic.category-{category}.severity = error";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Warn);

            // Verify category based configuration to warning + /warnaserror reports errors.
            analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.category-{category}.severity = warning";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, warnAsError: true, expectedDiagnosticSeverity: ReportDiagnostic.Error);

            // Verify disabled by default analyzer is not enabled by category based configuration.
            analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: false, defaultSeverity);
            analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.category-{category}.severity = error";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Suppress);

            // Verify disabled by default analyzer is not enabled by category based configuration in global config
            analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: false, defaultSeverity);
            analyzerConfigText = $@"
is_global=true
dotnet_analyzer_diagnostic.category-{category}.severity = error";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Suppress);

            if (defaultSeverity == DiagnosticSeverity.Hidden ||
                defaultSeverity == DiagnosticSeverity.Info && !errorlog)
            {
                // Verify analyzer with Hidden severity OR Info severity + no /errorlog is not executed.
                analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, defaultSeverity, throwOnAllNamedTypes: true);
                TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText: string.Empty, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Suppress);

                // Verify that bulk configuration 'none' entry does not enable this analyzer.
                analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.category-{category}.severity = none";
                TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Suppress);

                // Verify that bulk configuration 'none' entry does not enable this analyzer via global config
                analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.category-{category}.severity = none";
                TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Suppress);
            }
        }

        [WorkItem(38674, "https://github.com/dotnet/roslyn/issues/38674")]
        [InlineData(DiagnosticSeverity.Warning, false)]
        [InlineData(DiagnosticSeverity.Info, true)]
        [InlineData(DiagnosticSeverity.Info, false)]
        [InlineData(DiagnosticSeverity.Hidden, false)]
        [Theory]
        public void TestBulkAnalyzerDiagnosticConfiguration(DiagnosticSeverity defaultSeverity, bool errorlog)
        {
            var analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, defaultSeverity);

            var diagnosticId = analyzer.Descriptor.Id;

            // Verify bulk configuration without any diagnostic ID configuration is respected.
            var analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.severity = error";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Error);

            // Verify bulk configuration does not get applied for suppressed diagnostic.
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Suppress, noWarn: true);

            // Verify bulk configuration does not get applied for diagnostic configured in ruleset.
            var rulesetText = $@"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.CodeAnalysis"" RuleNamespace=""Microsoft.CodeAnalysis"">
    <Rule Id=""{diagnosticId}"" Action=""Warning"" />
  </Rules>
</RuleSet>";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Warn, rulesetText: rulesetText);

            // Verify bulk configuration before diagnostic ID configuration is not respected.
            analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.severity = error
dotnet_diagnostic.{diagnosticId}.severity = warning";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Warn);

            // Verify bulk configuration after diagnostic ID configuration is not respected.
            analyzerConfigText = $@"
[*.cs]
dotnet_diagnostic.{diagnosticId}.severity = warning
dotnet_analyzer_diagnostic.severity = error";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Warn);

            // Verify bulk configuration to warning + /warnaserror reports errors.
            analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.severity = warning";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, warnAsError: true, expectedDiagnosticSeverity: ReportDiagnostic.Error);

            // Verify disabled by default analyzer is not enabled by bulk configuration.
            analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: false, defaultSeverity);
            analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.severity = error";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Suppress);

            if (defaultSeverity == DiagnosticSeverity.Hidden ||
                defaultSeverity == DiagnosticSeverity.Info && !errorlog)
            {
                // Verify analyzer with Hidden severity OR Info severity + no /errorlog is not executed.
                analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, defaultSeverity, throwOnAllNamedTypes: true);
                TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText: string.Empty, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Suppress);

                // Verify that bulk configuration 'none' entry does not enable this analyzer.
                analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.severity = none";
                TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Suppress);
            }
        }

        [WorkItem(38674, "https://github.com/dotnet/roslyn/issues/38674")]
        [InlineData(DiagnosticSeverity.Warning, false)]
        [InlineData(DiagnosticSeverity.Info, true)]
        [InlineData(DiagnosticSeverity.Info, false)]
        [InlineData(DiagnosticSeverity.Hidden, false)]
        [Theory]
        public void TestMixedCategoryBasedAndBulkAnalyzerDiagnosticConfiguration(DiagnosticSeverity defaultSeverity, bool errorlog)
        {
            var analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, defaultSeverity);

            var diagnosticId = analyzer.Descriptor.Id;
            var category = analyzer.Descriptor.Category;

            // Verify category based configuration before bulk analyzer diagnostic configuration is respected.
            var analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.category-{category}.severity = error
dotnet_analyzer_diagnostic.severity = warning";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Error);

            // Verify category based configuration after bulk analyzer diagnostic configuration is respected.
            analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.severity = warning
dotnet_analyzer_diagnostic.category-{category}.severity = error";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Error);

            // Verify neither category based nor bulk diagnostic configuration is respected when specific diagnostic ID is configured in analyzer config.
            analyzerConfigText = $@"
[*.cs]
dotnet_diagnostic.{diagnosticId}.severity = warning
dotnet_analyzer_diagnostic.category-{category}.severity = none
dotnet_analyzer_diagnostic.severity = suggestion";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Warn);

            // Verify neither category based nor bulk diagnostic configuration is respected when specific diagnostic ID is configured in ruleset.
            analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.category-{category}.severity = none
dotnet_analyzer_diagnostic.severity = suggestion";
            var rulesetText = $@"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.CodeAnalysis"" RuleNamespace=""Microsoft.CodeAnalysis"">
    <Rule Id=""{diagnosticId}"" Action=""Warning"" />
  </Rules>
</RuleSet>";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity: ReportDiagnostic.Warn, rulesetText);
        }

        private void TestBulkAnalyzerConfigurationCore(
            NamedTypeAnalyzerWithConfigurableEnabledByDefault analyzer,
            string analyzerConfigText,
            bool errorlog,
            ReportDiagnostic expectedDiagnosticSeverity,
            string rulesetText = null,
            bool noWarn = false,
            bool warnAsError = false)
        {
            var diagnosticId = analyzer.Descriptor.Id;
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText(@"class C { }");
            var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText(analyzerConfigText);

            var arguments = new[] {
                "/nologo",
                "/t:library",
                "/preferreduilang:en",
                "/analyzerconfig:" + analyzerConfig.Path,
                src.Path };
            if (noWarn)
            {
                arguments = arguments.Append($"/nowarn:{diagnosticId}");
            }

            if (warnAsError)
            {
                arguments = arguments.Append($"/warnaserror");
            }

            if (errorlog)
            {
                arguments = arguments.Append($"/errorlog:errorlog");
            }

            if (rulesetText != null)
            {
                var rulesetFile = CreateRuleSetFile(rulesetText);
                arguments = arguments.Append($"/ruleset:{rulesetFile.Path}");
            }

            var cmd = CreateCSharpCompiler(null, dir.Path, arguments,
                analyzers: ImmutableArray.Create<DiagnosticAnalyzer>(analyzer));

            Assert.Equal(analyzerConfig.Path, Assert.Single(cmd.Arguments.AnalyzerConfigPaths));

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);

            var expectedErrorCode = expectedDiagnosticSeverity == ReportDiagnostic.Error ? 1 : 0;
            Assert.Equal(expectedErrorCode, exitCode);

            var prefix = expectedDiagnosticSeverity switch
            {
                ReportDiagnostic.Error => "error",
                ReportDiagnostic.Warn => "warning",
                ReportDiagnostic.Info => errorlog ? "info" : null,
                ReportDiagnostic.Hidden => null,
                ReportDiagnostic.Suppress => null,
                _ => throw ExceptionUtilities.UnexpectedValue(expectedDiagnosticSeverity)
            };

            if (prefix == null)
            {
                Assert.DoesNotContain(diagnosticId, outWriter.ToString());
            }
            else
            {
                Assert.Contains($"{prefix} {diagnosticId}: {analyzer.Descriptor.MessageFormat}", outWriter.ToString());
            }
        }

        [Theory]
        [InlineData(true)]
        [InlineData(false)]
        [WorkItem(37779, "https://github.com/dotnet/roslyn/issues/37779")]
        public void CompilerWarnAsErrorDoesNotEmit(bool warnAsError)
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
    int _f;     // CS0169: unused field
}");

            var docName = "temp.xml";
            var pdbName = "temp.pdb";
            var additionalArgs = new[] { $"/doc:{docName}", $"/pdb:{pdbName}", "/debug" };
            if (warnAsError)
            {
                additionalArgs = additionalArgs.Append("/warnaserror").AsArray();
            }

            var expectedErrorCount = warnAsError ? 1 : 0;
            var expectedWarningCount = !warnAsError ? 1 : 0;
            var output = VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false,
                                      additionalArgs,
                                      expectedErrorCount: expectedErrorCount,
                                      expectedWarningCount: expectedWarningCount);

            var expectedOutput = warnAsError ? "error CS0169" : "warning CS0169";
            Assert.Contains(expectedOutput, output);

            string binaryPath = Path.Combine(dir.Path, "temp.dll");
            Assert.True(File.Exists(binaryPath) == !warnAsError);

            string pdbPath = Path.Combine(dir.Path, pdbName);
            Assert.True(File.Exists(pdbPath) == !warnAsError);

            string xmlDocFilePath = Path.Combine(dir.Path, docName);
            Assert.True(File.Exists(xmlDocFilePath) == !warnAsError);
        }

        [Theory]
        [InlineData(true)]
        [InlineData(false)]
        [WorkItem(37779, "https://github.com/dotnet/roslyn/issues/37779")]
        public void AnalyzerConfigSeverityEscalationToErrorDoesNotEmit(bool analyzerConfigSetToError)
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
    int _f;     // CS0169: unused field
}");

            var docName = "temp.xml";
            var pdbName = "temp.pdb";
            var additionalArgs = new[] { $"/doc:{docName}", $"/pdb:{pdbName}", "/debug" };

            if (analyzerConfigSetToError)
            {
                var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText(@"
[*.cs]
dotnet_diagnostic.cs0169.severity = error");

                additionalArgs = additionalArgs.Append("/analyzerconfig:" + analyzerConfig.Path).ToArray();
            }

            var expectedErrorCount = analyzerConfigSetToError ? 1 : 0;
            var expectedWarningCount = !analyzerConfigSetToError ? 1 : 0;
            var output = VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false,
                                      additionalArgs,
                                      expectedErrorCount: expectedErrorCount,
                                      expectedWarningCount: expectedWarningCount);

            var expectedOutput = analyzerConfigSetToError ? "error CS0169" : "warning CS0169";
            Assert.Contains(expectedOutput, output);

            string binaryPath = Path.Combine(dir.Path, "temp.dll");
            Assert.True(File.Exists(binaryPath) == !analyzerConfigSetToError);

            string pdbPath = Path.Combine(dir.Path, pdbName);
            Assert.True(File.Exists(pdbPath) == !analyzerConfigSetToError);

            string xmlDocFilePath = Path.Combine(dir.Path, docName);
            Assert.True(File.Exists(xmlDocFilePath) == !analyzerConfigSetToError);
        }

        [Theory]
        [InlineData(true)]
        [InlineData(false)]
        [WorkItem(37779, "https://github.com/dotnet/roslyn/issues/37779")]
        public void RulesetSeverityEscalationToErrorDoesNotEmit(bool rulesetSetToError)
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
    int _f;     // CS0169: unused field
}");

            var docName = "temp.xml";
            var pdbName = "temp.pdb";
            var additionalArgs = new[] { $"/doc:{docName}", $"/pdb:{pdbName}", "/debug" };

            if (rulesetSetToError)
            {
                string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.CodeAnalysis"" RuleNamespace=""Microsoft.CodeAnalysis"">
    <Rule Id=""CS0169"" Action=""Error"" />
  </Rules>
</RuleSet>
";
                var rulesetFile = CreateRuleSetFile(source);
                additionalArgs = additionalArgs.Append("/ruleset:" + rulesetFile.Path).ToArray();
            }

            var expectedErrorCount = rulesetSetToError ? 1 : 0;
            var expectedWarningCount = !rulesetSetToError ? 1 : 0;
            var output = VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false,
                                      additionalArgs,
                                      expectedErrorCount: expectedErrorCount,
                                      expectedWarningCount: expectedWarningCount);

            var expectedOutput = rulesetSetToError ? "error CS0169" : "warning CS0169";
            Assert.Contains(expectedOutput, output);

            string binaryPath = Path.Combine(dir.Path, "temp.dll");
            Assert.True(File.Exists(binaryPath) == !rulesetSetToError);

            string pdbPath = Path.Combine(dir.Path, pdbName);
            Assert.True(File.Exists(pdbPath) == !rulesetSetToError);

            string xmlDocFilePath = Path.Combine(dir.Path, docName);
            Assert.True(File.Exists(xmlDocFilePath) == !rulesetSetToError);
        }

        [Theory]
        [InlineData(true)]
        [InlineData(false)]
        [WorkItem(37779, "https://github.com/dotnet/roslyn/issues/37779")]
        public void AnalyzerWarnAsErrorDoesNotEmit(bool warnAsError)
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText("class C { }");

            var additionalArgs = warnAsError ? new[] { "/warnaserror" } : null;
            var expectedErrorCount = warnAsError ? 1 : 0;
            var expectedWarningCount = !warnAsError ? 1 : 0;
            var output = VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false,
                                      additionalArgs,
                                      expectedErrorCount: expectedErrorCount,
                                      expectedWarningCount: expectedWarningCount,
                                      analyzers: new[] { new WarningDiagnosticAnalyzer() });

            var expectedDiagnosticSeverity = warnAsError ? "error" : "warning";
            Assert.Contains($"{expectedDiagnosticSeverity} {WarningDiagnosticAnalyzer.Warning01.Id}", output);

            string binaryPath = Path.Combine(dir.Path, "temp.dll");
            Assert.True(File.Exists(binaryPath) == !warnAsError);
        }

        // Currently, configuring no location diagnostics through editorconfig is not supported.
        [Theory(Skip = "https://github.com/dotnet/roslyn/issues/38042")]
        [CombinatorialData]
        public void AnalyzerConfigRespectedForNoLocationDiagnostic(ReportDiagnostic reportDiagnostic, bool isEnabledByDefault, bool noWarn, bool errorlog)
        {
            var analyzer = new AnalyzerWithNoLocationDiagnostics(isEnabledByDefault);
            TestAnalyzerConfigRespectedCore(analyzer, analyzer.Descriptor, reportDiagnostic, noWarn, errorlog);
        }

        [WorkItem(37876, "https://github.com/dotnet/roslyn/issues/37876")]
        [Theory]
        [CombinatorialData]
        public void AnalyzerConfigRespectedForDisabledByDefaultDiagnostic(ReportDiagnostic analyzerConfigSeverity, bool isEnabledByDefault, bool noWarn, bool errorlog)
        {
            var analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault, defaultSeverity: DiagnosticSeverity.Warning);
            TestAnalyzerConfigRespectedCore(analyzer, analyzer.Descriptor, analyzerConfigSeverity, noWarn, errorlog);
        }

        private void TestAnalyzerConfigRespectedCore(DiagnosticAnalyzer analyzer, DiagnosticDescriptor descriptor, ReportDiagnostic analyzerConfigSeverity, bool noWarn, bool errorlog)
        {
            if (analyzerConfigSeverity == ReportDiagnostic.Default)
            {
                // "dotnet_diagnostic.ID.severity = default" is not supported.
                return;
            }

            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText(@"class C { }");
            var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText($@"
[*.cs]
dotnet_diagnostic.{descriptor.Id}.severity = {analyzerConfigSeverity.ToAnalyzerConfigString()}");

            var arguments = new[] {
                "/nologo",
                "/t:library",
                "/preferreduilang:en",
                "/analyzerconfig:" + analyzerConfig.Path,
                src.Path };
            if (noWarn)
            {
                arguments = arguments.Append($"/nowarn:{descriptor.Id}");
            }

            if (errorlog)
            {
                arguments = arguments.Append($"/errorlog:errorlog");
            }

            var cmd = CreateCSharpCompiler(null, dir.Path, arguments,
                analyzers: ImmutableArray.Create<DiagnosticAnalyzer>(analyzer));

            Assert.Equal(analyzerConfig.Path, Assert.Single(cmd.Arguments.AnalyzerConfigPaths));

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);

            var expectedErrorCode = !noWarn && analyzerConfigSeverity == ReportDiagnostic.Error ? 1 : 0;
            Assert.Equal(expectedErrorCode, exitCode);

            // NOTE: Info diagnostics are only logged on command line when /errorlog is specified. See https://github.com/dotnet/roslyn/issues/42166 for details.
            if (!noWarn &&
                (analyzerConfigSeverity == ReportDiagnostic.Error ||
                analyzerConfigSeverity == ReportDiagnostic.Warn ||
                (analyzerConfigSeverity == ReportDiagnostic.Info && errorlog)))
            {
                var prefix = analyzerConfigSeverity == ReportDiagnostic.Error ? "error" : analyzerConfigSeverity == ReportDiagnostic.Warn ? "warning" : "info";
                Assert.Contains($"{prefix} {descriptor.Id}: {descriptor.MessageFormat}", outWriter.ToString());
            }
            else
            {
                Assert.DoesNotContain(descriptor.Id.ToString(), outWriter.ToString());
            }
        }

        [Fact]
        [WorkItem(3705, "https://github.com/dotnet/roslyn/issues/3705")]
        public void IsUserConfiguredGeneratedCodeInAnalyzerConfig()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
    void M(C? c)
    {
        _ = c.ToString();   // warning CS8602: Dereference of a possibly null reference.
    }
}");
            var output = VerifyOutput(dir, src, additionalFlags: new[] { "/nullable" }, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
            // warning CS8602: Dereference of a possibly null reference.
            Assert.Contains("warning CS8602", output, StringComparison.Ordinal);

            // generated_code = true
            var analyzerConfigFile = dir.CreateFile(".editorconfig");
            var analyzerConfig = analyzerConfigFile.WriteAllText(@"
[*.cs]
generated_code = true");
            output = VerifyOutput(dir, src, additionalFlags: new[] { "/nullable", "/analyzerconfig:" + analyzerConfig.Path }, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
            Assert.DoesNotContain("warning CS8602", output, StringComparison.Ordinal);
            // warning CS8669: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.
            Assert.Contains("warning CS8669", output, StringComparison.Ordinal);

            // generated_code = false
            analyzerConfig = analyzerConfigFile.WriteAllText(@"
[*.cs]
generated_code = false");
            output = VerifyOutput(dir, src, additionalFlags: new[] { "/nullable", "/analyzerconfig:" + analyzerConfig.Path }, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
            // warning CS8602: Dereference of a possibly null reference.
            Assert.Contains("warning CS8602", output, StringComparison.Ordinal);

            // generated_code = auto
            analyzerConfig = analyzerConfigFile.WriteAllText(@"
[*.cs]
generated_code = auto");
            output = VerifyOutput(dir, src, additionalFlags: new[] { "/nullable", "/analyzerconfig:" + analyzerConfig.Path }, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
            // warning CS8602: Dereference of a possibly null reference.
            Assert.Contains("warning CS8602", output, StringComparison.Ordinal);
        }

        [WorkItem(42166, "https://github.com/dotnet/roslyn/issues/42166")]
        [CombinatorialData, Theory]
        public void TestAnalyzerFilteringBasedOnSeverity(DiagnosticSeverity defaultSeverity, bool errorlog)
        {
            // This test verifies that analyzer execution is skipped at build time for the following:
            //   1. Analyzer reporting Hidden diagnostics
            //   2. Analyzer reporting Info diagnostics, when /errorlog is not specified
            var analyzerShouldBeSkipped = defaultSeverity == DiagnosticSeverity.Hidden ||
                defaultSeverity == DiagnosticSeverity.Info && !errorlog;

            // We use an analyzer that throws an exception on every analyzer callback.
            // So an AD0001 analyzer exception diagnostic is reported if analyzer executed, otherwise not.
            var analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, defaultSeverity, throwOnAllNamedTypes: true);

            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText(@"class C { }");
            var args = new[] { "/nologo", "/t:library", "/preferreduilang:en", src.Path };
            if (errorlog)
                args = args.Append("/errorlog:errorlog");

            var cmd = CreateCSharpCompiler(null, dir.Path, args, analyzers: ImmutableArray.Create<DiagnosticAnalyzer>(analyzer));
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
            var output = outWriter.ToString();
            if (analyzerShouldBeSkipped)
            {
                Assert.Empty(output);
            }
            else
            {
                Assert.Contains("warning AD0001: Analyzer 'Microsoft.CodeAnalysis.CommonDiagnosticAnalyzers+NamedTypeAnalyzerWithConfigurableEnabledByDefault' threw an exception of type 'System.NotImplementedException'",
                    output, StringComparison.Ordinal);
            }
        }

        [WorkItem(47017, "https://github.com/dotnet/roslyn/issues/47017")]
        [CombinatorialData, Theory]
        public void TestWarnAsErrorMinusDoesNotEnableDisabledByDefaultAnalyzers(DiagnosticSeverity defaultSeverity, bool isEnabledByDefault)
        {
            // This test verifies that '/warnaserror-:DiagnosticId' does not affect if analyzers are executed or skipped..
            // Setup the analyzer to always throw an exception on analyzer callbacks for cases where we expect analyzer execution to be skipped:
            //   1. Disabled by default analyzer, i.e. 'isEnabledByDefault == false'.
            //   2. Default severity Hidden/Info: We only execute analyzers reporting Warning/Error severity diagnostics on command line builds.
            var analyzerShouldBeSkipped = !isEnabledByDefault ||
                defaultSeverity == DiagnosticSeverity.Hidden ||
                defaultSeverity == DiagnosticSeverity.Info;
            var analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault, defaultSeverity, throwOnAllNamedTypes: analyzerShouldBeSkipped);
            var diagnosticId = analyzer.Descriptor.Id;

            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText(@"class C { }");

            // Verify '/warnaserror-:DiagnosticId' behavior.
            var args = new[] { "/warnaserror+", $"/warnaserror-:{diagnosticId}", "/nologo", "/t:library", "/preferreduilang:en", src.Path };

            var cmd = CreateCSharpCompiler(null, dir.Path, args, analyzers: ImmutableArray.Create<DiagnosticAnalyzer>(analyzer));
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            var expectedExitCode = !analyzerShouldBeSkipped && defaultSeverity == DiagnosticSeverity.Error ? 1 : 0;
            Assert.Equal(expectedExitCode, exitCode);

            var output = outWriter.ToString();
            if (analyzerShouldBeSkipped)
            {
                Assert.Empty(output);
            }
            else
            {
                var prefix = defaultSeverity == DiagnosticSeverity.Warning ? "warning" : "error";
                Assert.Contains($"{prefix} {diagnosticId}: {analyzer.Descriptor.MessageFormat}", output);
            }
        }

        [WorkItem(49446, "https://github.com/dotnet/roslyn/issues/49446")]
        [Theory]
        // Verify '/warnaserror-:ID' prevents escalation to 'Error' when config file bumps severity to 'Warning'
        [InlineData(false, DiagnosticSeverity.Info, DiagnosticSeverity.Warning, DiagnosticSeverity.Error)]
        [InlineData(true, DiagnosticSeverity.Info, DiagnosticSeverity.Warning, DiagnosticSeverity.Warning)]
        // Verify '/warnaserror-:ID' prevents escalation to 'Error' when default severity is 'Warning' and no config file setting is specified.
        [InlineData(false, DiagnosticSeverity.Warning, null, DiagnosticSeverity.Error)]
        [InlineData(true, DiagnosticSeverity.Warning, null, DiagnosticSeverity.Warning)]
        // Verify '/warnaserror-:ID' prevents escalation to 'Error' when default severity is 'Warning' and config file bumps severity to 'Error'
        [InlineData(false, DiagnosticSeverity.Warning, DiagnosticSeverity.Error, DiagnosticSeverity.Error)]
        [InlineData(true, DiagnosticSeverity.Warning, DiagnosticSeverity.Error, DiagnosticSeverity.Warning)]
        // Verify '/warnaserror-:ID' has no effect when default severity is 'Info' and config file bumps severity to 'Error'
        [InlineData(false, DiagnosticSeverity.Info, DiagnosticSeverity.Error, DiagnosticSeverity.Error)]
        [InlineData(true, DiagnosticSeverity.Info, DiagnosticSeverity.Error, DiagnosticSeverity.Error)]
        public void TestWarnAsErrorMinusDoesNotNullifyEditorConfig(
            bool warnAsErrorMinus,
            DiagnosticSeverity defaultSeverity,
            DiagnosticSeverity? severityInConfigFile,
            DiagnosticSeverity expectedEffectiveSeverity)
        {
            var analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, defaultSeverity, throwOnAllNamedTypes: false);
            var diagnosticId = analyzer.Descriptor.Id;

            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText(@"class C { }");
            var additionalFlags = new[] { "/warnaserror+" };

            if (severityInConfigFile.HasValue)
            {
                var severityString = DiagnosticDescriptor.MapSeverityToReport(severityInConfigFile.Value).ToAnalyzerConfigString();
                var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText($@"
[*.cs]
dotnet_diagnostic.{diagnosticId}.severity = {severityString}");

                additionalFlags = additionalFlags.Append($"/analyzerconfig:{analyzerConfig.Path}").ToArray();
            }

            if (warnAsErrorMinus)
            {
                additionalFlags = additionalFlags.Append($"/warnaserror-:{diagnosticId}").ToArray();
            }

            int expectedWarningCount = 0, expectedErrorCount = 0;
            switch (expectedEffectiveSeverity)
            {
                case DiagnosticSeverity.Warning:
                    expectedWarningCount = 1;
                    break;

                case DiagnosticSeverity.Error:
                    expectedErrorCount = 1;
                    break;

                default:
                    throw ExceptionUtilities.UnexpectedValue(expectedEffectiveSeverity);
            }

            VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false,
                expectedWarningCount: expectedWarningCount,
                expectedErrorCount: expectedErrorCount,
                additionalFlags: additionalFlags,
                analyzers: new[] { analyzer });
        }

        [Fact]
        public void SourceGenerators_EmbeddedSources()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");

            var generatedSource = "public class D { }";
            var generator = new SingleFileTestGenerator(generatedSource, "generatedSource.cs");

            VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/debug:embedded", "/out:embed.exe" }, generators: new[] { generator }, analyzers: null);

            var generatorPrefix = GeneratorDriver.GetFilePathPrefixForGenerator(generator);
            ValidateEmbeddedSources_Portable(new Dictionary<string, string> { { Path.Combine(dir.Path, generatorPrefix, $"generatedSource.cs"), generatedSource } }, dir, true);

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);
        }

        [Theory, CombinatorialData]
        [WorkItem(40926, "https://github.com/dotnet/roslyn/issues/40926")]
        public void TestSourceGeneratorsWithAnalyzers(bool includeCurrentAssemblyAsAnalyzerReference, bool skipAnalyzers)
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");

            var generatedSource = "public class D { }";
            var generator = new SingleFileTestGenerator(generatedSource, "generatedSource.cs");

            // 'skipAnalyzers' should have no impact on source generator execution, but should prevent analyzer execution.
            var skipAnalyzersFlag = "/skipAnalyzers" + (skipAnalyzers ? "+" : "-");

            // Verify analyzers were executed only if both the following conditions were satisfied:
            //  1. Current assembly was added as an analyzer reference, i.e. "includeCurrentAssemblyAsAnalyzerReference = true" and
            //  2. We did not explicitly request skipping analyzers, i.e. "skipAnalyzers = false".
            var expectedAnalyzerExecution = includeCurrentAssemblyAsAnalyzerReference && !skipAnalyzers;

            // 'WarningDiagnosticAnalyzer' generates a warning for each named type.
            // We expect two warnings for this test: type "C" defined in source and the source generator defined type.
            // Additionally, we also have an analyzer that generates "warning CS8032: An instance of analyzer cannot be created"
            // CS8032 is generated with includeCurrentAssemblyAsAnalyzerReference even when we are skipping analyzers as we will instantiate all analyzers, just not execute them.
            var expectedWarningCount = expectedAnalyzerExecution ? 3 : (includeCurrentAssemblyAsAnalyzerReference ? 1 : 0);

            var output = VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference,
                expectedWarningCount: expectedWarningCount,
                additionalFlags: new[] { "/debug:embedded", "/out:embed.exe", skipAnalyzersFlag },
                generators: new[] { generator });

            // Verify source generator was executed, regardless of the value of 'skipAnalyzers'.
            var generatorPrefix = GeneratorDriver.GetFilePathPrefixForGenerator(generator);
            ValidateEmbeddedSources_Portable(new Dictionary<string, string> { { Path.Combine(dir.Path, generatorPrefix, "generatedSource.cs"), generatedSource } }, dir, true);

            if (expectedAnalyzerExecution)
            {
                Assert.Contains("warning Warning01", output, StringComparison.Ordinal);
                Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            }
            else if (includeCurrentAssemblyAsAnalyzerReference)
            {
                Assert.Contains("warning CS8032", output, StringComparison.Ordinal);
            }
            else
            {
                Assert.Empty(output);
            }

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
        }

        [Theory]
        [InlineData("partial class D {}", "file1.cs", "partial class E {}", "file2.cs")] // different files, different names
        [InlineData("partial class D {}", "file1.cs", "partial class E {}", "file1.cs")] // different files, same names
        [InlineData("partial class D {}", "file1.cs", "partial class D {}", "file2.cs")] // same files, different names
        [InlineData("partial class D {}", "file1.cs", "partial class D {}", "file1.cs")] // same files, same names
        [InlineData("partial class D {}", "file1.cs", "", "file2.cs")] // empty second file
        public void SourceGenerators_EmbeddedSources_MultipleFiles(string source1, string source1Name, string source2, string source2Name)
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            var generator = new SingleFileTestGenerator(source1, source1Name);
            var generator2 = new SingleFileTestGenerator2(source2, source2Name);

            VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/debug:embedded", "/out:embed.exe" }, generators: new[] { generator, generator2 }, analyzers: null);

            var generator1Prefix = GeneratorDriver.GetFilePathPrefixForGenerator(generator);
            var generator2Prefix = GeneratorDriver.GetFilePathPrefixForGenerator(generator2);

            ValidateEmbeddedSources_Portable(new Dictionary<string, string>
            {
                { Path.Combine(dir.Path, generator1Prefix, source1Name), source1},
                { Path.Combine(dir.Path, generator2Prefix, source2Name), source2},
            }, dir, true);

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);
        }

        [Fact]
        public void SourceGenerators_WriteGeneratedSources()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            var generatedDir = dir.CreateDirectory("generated");

            var generatedSource = "public class D { }";
            var generator = new SingleFileTestGenerator(generatedSource, "generatedSource.cs");

            VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/generatedfilesout:" + generatedDir.Path, "/langversion:preview", "/out:embed.exe" }, generators: new[] { generator }, analyzers: null);

            var generatorPrefix = GeneratorDriver.GetFilePathPrefixForGenerator(generator);
            ValidateWrittenSources(new() { { Path.Combine(generatedDir.Path, generatorPrefix), new() { { "generatedSource.cs", generatedSource } } } });

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);
        }

        [Fact]
        public void SourceGenerators_OverwriteGeneratedSources()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            var generatedDir = dir.CreateDirectory("generated");

            var generatedSource1 = "class D { } class E { }";
            var generator1 = new SingleFileTestGenerator(generatedSource1, "generatedSource.cs");

            VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/generatedfilesout:" + generatedDir.Path, "/langversion:preview", "/out:embed.exe" }, generators: new[] { generator1 }, analyzers: null);

            var generatorPrefix = GeneratorDriver.GetFilePathPrefixForGenerator(generator1);
            ValidateWrittenSources(new() { { Path.Combine(generatedDir.Path, generatorPrefix), new() { { "generatedSource.cs", generatedSource1 } } } });

            var generatedSource2 = "public class D { }";
            var generator2 = new SingleFileTestGenerator(generatedSource2, "generatedSource.cs");

            VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/generatedfilesout:" + generatedDir.Path, "/langversion:preview", "/out:embed.exe" }, generators: new[] { generator2 }, analyzers: null);

            ValidateWrittenSources(new() { { Path.Combine(generatedDir.Path, generatorPrefix), new() { { "generatedSource.cs", generatedSource2 } } } });

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);
        }

        [Theory]
        [InlineData("partial class D {}", "file1.cs", "partial class E {}", "file2.cs")] // different files, different names
        [InlineData("partial class D {}", "file1.cs", "partial class E {}", "file1.cs")] // different files, same names
        [InlineData("partial class D {}", "file1.cs", "partial class D {}", "file2.cs")] // same files, different names
        [InlineData("partial class D {}", "file1.cs", "partial class D {}", "file1.cs")] // same files, same names
        [InlineData("partial class D {}", "file1.cs", "", "file2.cs")] // empty second file
        public void SourceGenerators_WriteGeneratedSources_MultipleFiles(string source1, string source1Name, string source2, string source2Name)
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            var generatedDir = dir.CreateDirectory("generated");

            var generator = new SingleFileTestGenerator(source1, source1Name);
            var generator2 = new SingleFileTestGenerator2(source2, source2Name);

            VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/generatedfilesout:" + generatedDir.Path, "/langversion:preview", "/out:embed.exe" }, generators: new[] { generator, generator2 }, analyzers: null);

            var generator1Prefix = GeneratorDriver.GetFilePathPrefixForGenerator(generator);
            var generator2Prefix = GeneratorDriver.GetFilePathPrefixForGenerator(generator2);

            ValidateWrittenSources(new()
            {
                { Path.Combine(generatedDir.Path, generator1Prefix), new() { { source1Name, source1 } } },
                { Path.Combine(generatedDir.Path, generator2Prefix), new() { { source2Name, source2 } } }
            });

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);
        }

        [ConditionalFact(typeof(DesktopClrOnly))]  //CoreCLR doesn't support SxS loading
        [WorkItem(47990, "https://github.com/dotnet/roslyn/issues/47990")]
        public void SourceGenerators_SxS_AssemblyLoading()
        {
            // compile the generators
            var dir = Temp.CreateDirectory();
            var snk = Temp.CreateFile("TestKeyPair_", ".snk", dir.Path).WriteAllBytes(TestResources.General.snKey);
            var src = dir.CreateFile("generator.cs");
            var virtualSnProvider = new DesktopStrongNameProvider(ImmutableArray.Create(dir.Path));

            string createGenerator(string version)
            {
                var generatorSource = $@"
using Microsoft.CodeAnalysis;
[assembly:System.Reflection.AssemblyVersion(""{version}"")]

[Generator]
public class TestGenerator : ISourceGenerator
{{
            public void Execute(GeneratorExecutionContext context) {{ context.AddSource(""generatedSource.cs"", ""//from version {version}""); }}
            public void Initialize(GeneratorInitializationContext context) {{ }}
 }}";

                var path = Path.Combine(dir.Path, Guid.NewGuid().ToString() + ".dll");

                var comp = CreateEmptyCompilation(source: generatorSource,
                                             references: TargetFrameworkUtil.NetStandard20References.Add(MetadataReference.CreateFromAssemblyInternal(typeof(ISourceGenerator).Assembly)),
                                             options: TestOptions.DebugDll.WithCryptoKeyFile(Path.GetFileName(snk.Path)).WithStrongNameProvider(virtualSnProvider),
                                             assemblyName: "generator");
                comp.VerifyDiagnostics();
                comp.Emit(path);
                return path;
            }

            var gen1 = createGenerator("1.0.0.0");
            var gen2 = createGenerator("2.0.0.0");

            var generatedDir = dir.CreateDirectory("generated");
            VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/generatedfilesout:" + generatedDir.Path, "/analyzer:" + gen1, "/analyzer:" + gen2 }.ToArray());

            // This is wrong! Both generators are writing the same file out, over the top of each other
            // See https://github.com/dotnet/roslyn/issues/47990
            ValidateWrittenSources(new()
            {
                //  { Path.Combine(generatedDir.Path,  "generator", "TestGenerator"), new() { { "generatedSource.cs", "//from version 1.0.0.0" } } },
                { Path.Combine(generatedDir.Path, "generator", "TestGenerator"), new() { { "generatedSource.cs", "//from version 2.0.0.0" } } }
            });
        }

        [Fact]
        public void SourceGenerators_DoNotWriteGeneratedSources_When_No_Directory_Supplied()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            var generatedDir = dir.CreateDirectory("generated");

            var generatedSource = "public class D { }";
            var generator = new SingleFileTestGenerator(generatedSource, "generatedSource.cs");

            VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/langversion:preview", "/out:embed.exe" }, generators: new[] { generator }, analyzers: null);
            ValidateWrittenSources(new() { { generatedDir.Path, new() } });

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);
        }

        [Fact]
        public void SourceGenerators_Error_When_GeneratedDir_NotExist()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");

            var generatedDirPath = Path.Combine(dir.Path, "noexist");
            var generatedSource = "public class D { }";
            var generator = new SingleFileTestGenerator(generatedSource, "generatedSource.cs");

            var output = VerifyOutput(dir, src, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/generatedfilesout:" + generatedDirPath, "/langversion:preview", "/out:embed.exe" }, generators: new[] { generator }, analyzers: null);
            Assert.Contains("CS0016:", output);

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);
        }

        [Fact]
        public void SourceGenerators_GeneratedDir_Has_Spaces()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            var generatedDir = dir.CreateDirectory("generated files");

            var generatedSource = "public class D { }";
            var generator = new SingleFileTestGenerator(generatedSource, "generatedSource.cs");

            VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/generatedfilesout:" + generatedDir.Path, "/langversion:preview", "/out:embed.exe" }, generators: new[] { generator }, analyzers: null);

            var generatorPrefix = GeneratorDriver.GetFilePathPrefixForGenerator(generator);
            ValidateWrittenSources(new() { { Path.Combine(generatedDir.Path, generatorPrefix), new() { { "generatedSource.cs", generatedSource } } } });

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);
        }

        [Fact]
        public void ParseGeneratedFilesOut()
        {
            string root = PathUtilities.IsUnixLikePlatform ? "/" : "c:\\";
            string baseDirectory = Path.Combine(root, "abc", "def");

            var parsedArgs = DefaultParse(new[] { @"/generatedfilesout:", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for '/generatedfilesout:' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "/generatedfilesout:"));
            Assert.Null(parsedArgs.GeneratedFilesOutputDirectory);

            parsedArgs = DefaultParse(new[] { @"/generatedfilesout:""""", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify(
                // error CS2006: Command-line syntax error: Missing '<text>' for '/generatedfilesout:' option
                Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "/generatedfilesout:\"\""));
            Assert.Null(parsedArgs.GeneratedFilesOutputDirectory);

            parsedArgs = DefaultParse(new[] { @"/generatedfilesout:outdir", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(Path.Combine(baseDirectory, "outdir"), parsedArgs.GeneratedFilesOutputDirectory);

            parsedArgs = DefaultParse(new[] { @"/generatedfilesout:""outdir""", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(Path.Combine(baseDirectory, "outdir"), parsedArgs.GeneratedFilesOutputDirectory);

            parsedArgs = DefaultParse(new[] { @"/generatedfilesout:out dir", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(Path.Combine(baseDirectory, "out dir"), parsedArgs.GeneratedFilesOutputDirectory);

            parsedArgs = DefaultParse(new[] { @"/generatedfilesout:""out dir""", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(Path.Combine(baseDirectory, "out dir"), parsedArgs.GeneratedFilesOutputDirectory);

            var absPath = Path.Combine(root, "outdir");
            parsedArgs = DefaultParse(new[] { $@"/generatedfilesout:{absPath}", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(absPath, parsedArgs.GeneratedFilesOutputDirectory);

            parsedArgs = DefaultParse(new[] { $@"/generatedfilesout:""{absPath}""", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(absPath, parsedArgs.GeneratedFilesOutputDirectory);

            absPath = Path.Combine(root, "generated files");
            parsedArgs = DefaultParse(new[] { $@"/generatedfilesout:{absPath}", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(absPath, parsedArgs.GeneratedFilesOutputDirectory);

            parsedArgs = DefaultParse(new[] { $@"/generatedfilesout:""{absPath}""", "a.cs" }, baseDirectory);
            parsedArgs.Errors.Verify();
            Assert.Equal(absPath, parsedArgs.GeneratedFilesOutputDirectory);
        }

        [Fact]
        public void SourceGenerators_Error_When_NoDirectoryArgumentGiven()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            var output = VerifyOutput(dir, src, expectedErrorCount: 2, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/generatedfilesout:", "/langversion:preview", "/out:embed.exe" });
            Assert.Contains("error CS2006: Command-line syntax error: Missing '<text>' for '/generatedfilesout:' option", output);

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);
        }

        [Fact]
        public void SourceGenerators_ReportedWrittenFiles_To_TouchedFilesLogger()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            var generatedDir = dir.CreateDirectory("generated");

            var generatedSource = "public class D { }";
            var generator = new SingleFileTestGenerator(generatedSource, "generatedSource.cs");

            VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/generatedfilesout:" + generatedDir.Path, $"/touchedfiles:{dir.Path}/touched", "/langversion:preview", "/out:embed.exe" }, generators: new[] { generator }, analyzers: null);

            var touchedFiles = Directory.GetFiles(dir.Path, "touched*");
            Assert.Equal(2, touchedFiles.Length);

            string[] writtenText = File.ReadAllLines(Path.Combine(dir.Path, "touched.write"));
            Assert.Equal(2, writtenText.Length);
            Assert.EndsWith("EMBED.EXE", writtenText[0], StringComparison.OrdinalIgnoreCase);
            Assert.EndsWith("GENERATEDSOURCE.CS", writtenText[1], StringComparison.OrdinalIgnoreCase);

            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);
        }

        [Fact]
        [WorkItem(44087, "https://github.com/dotnet/roslyn/issues/44087")]
        public void SourceGeneratorsAndAnalyzerConfig()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText(@"
[*.cs]
key = value");

            var generator = new SingleFileTestGenerator("public class D {}", "generated.cs");

            VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/analyzerconfig:" + analyzerConfig.Path }, generators: new[] { generator }, analyzers: null);
        }

        [Fact]
        public void SourceGeneratorsCanReadAnalyzerConfig()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            var analyzerConfig1 = dir.CreateFile(".globaleditorconfig").WriteAllText(@"
is_global = true
key1 = value1

[*.cs]
key2 = value2

[*.vb]
key3 = value3");

            var analyzerConfig2 = dir.CreateFile(".editorconfig").WriteAllText(@"
[*.cs]
key4 = value4

[*.vb]
key5 = value5");

            var subDir = dir.CreateDirectory("subDir");
            var analyzerConfig3 = subDir.CreateFile(".editorconfig").WriteAllText(@"
[*.cs]
key6 = value6

[*.vb]
key7 = value7");

            var generator = new CallbackGenerator((ic) => { }, (gc) =>
            {
                // can get the global options
                var globalOptions = gc.AnalyzerConfigOptions.GlobalOptions;
                Assert.True(globalOptions.TryGetValue("key1", out var keyValue));
                Assert.Equal("value1", keyValue);
                Assert.False(globalOptions.TryGetValue("key2", out _));
                Assert.False(globalOptions.TryGetValue("key3", out _));
                Assert.False(globalOptions.TryGetValue("key4", out _));
                Assert.False(globalOptions.TryGetValue("key5", out _));
                Assert.False(globalOptions.TryGetValue("key6", out _));
                Assert.False(globalOptions.TryGetValue("key7", out _));

                // can get the options for class C
                var classOptions = gc.AnalyzerConfigOptions.GetOptions(gc.Compilation.SyntaxTrees.First());
                Assert.True(classOptions.TryGetValue("key1", out keyValue));
                Assert.Equal("value1", keyValue);
                Assert.False(classOptions.TryGetValue("key2", out _));
                Assert.False(classOptions.TryGetValue("key3", out _));
                Assert.True(classOptions.TryGetValue("key4", out keyValue));
                Assert.Equal("value4", keyValue);
                Assert.False(classOptions.TryGetValue("key5", out _));
                Assert.False(classOptions.TryGetValue("key6", out _));
                Assert.False(classOptions.TryGetValue("key7", out _));
            });

            var args = new[] {
                "/analyzerconfig:" + analyzerConfig1.Path,
                "/analyzerconfig:" + analyzerConfig2.Path,
                "/analyzerconfig:" + analyzerConfig3.Path,
                "/t:library",
                src.Path
            };

            var cmd = CreateCSharpCompiler(null, dir.Path, args, generators: ImmutableArray.Create<ISourceGenerator>(generator));
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);

            // test for both the original tree and the generated one
            var provider = cmd.AnalyzerOptions.AnalyzerConfigOptionsProvider;

            // get the global options
            var globalOptions = provider.GlobalOptions;
            Assert.True(globalOptions.TryGetValue("key1", out var keyValue));
            Assert.Equal("value1", keyValue);
            Assert.False(globalOptions.TryGetValue("key2", out _));
            Assert.False(globalOptions.TryGetValue("key3", out _));
            Assert.False(globalOptions.TryGetValue("key4", out _));
            Assert.False(globalOptions.TryGetValue("key5", out _));
            Assert.False(globalOptions.TryGetValue("key6", out _));
            Assert.False(globalOptions.TryGetValue("key7", out _));

            // get the options for class C
            var classOptions = provider.GetOptions(cmd.Compilation.SyntaxTrees.First());
            Assert.True(classOptions.TryGetValue("key1", out keyValue));
            Assert.Equal("value1", keyValue);
            Assert.False(classOptions.TryGetValue("key2", out _));
            Assert.False(classOptions.TryGetValue("key3", out _));
            Assert.True(classOptions.TryGetValue("key4", out keyValue));
            Assert.Equal("value4", keyValue);
            Assert.False(classOptions.TryGetValue("key5", out _));
            Assert.False(classOptions.TryGetValue("key6", out _));
            Assert.False(classOptions.TryGetValue("key7", out _));

            // get the options for generated class D
            var generatedOptions = provider.GetOptions(cmd.Compilation.SyntaxTrees.Last());
            Assert.True(generatedOptions.TryGetValue("key1", out keyValue));
            Assert.Equal("value1", keyValue);
            Assert.False(generatedOptions.TryGetValue("key2", out _));
            Assert.False(generatedOptions.TryGetValue("key3", out _));
            Assert.True(classOptions.TryGetValue("key4", out keyValue));
            Assert.Equal("value4", keyValue);
            Assert.False(generatedOptions.TryGetValue("key5", out _));
            Assert.False(generatedOptions.TryGetValue("key6", out _));
            Assert.False(generatedOptions.TryGetValue("key7", out _));
        }

        [Theory]
        [CombinatorialData]
        public void SourceGeneratorsRunRegardlessOfLanguageVersion(LanguageVersion version)
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"class C {}");
            var generator = new CallbackGenerator(i => { }, e => throw null);

            var output = VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/langversion:" + version.ToDisplayString() }, generators: new[] { generator }, expectedWarningCount: 1, expectedErrorCount: 1, expectedExitCode: 0);
            Assert.Contains("CS8785: Generator 'CallbackGenerator' failed to generate source.", output);
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp)]
        private sealed class FieldAnalyzer : DiagnosticAnalyzer
        {
            private static readonly DiagnosticDescriptor _rule = new DiagnosticDescriptor("Id", "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(_rule);

            public override void Initialize(AnalysisContext context)
            {
                context.RegisterSyntaxNodeAction(AnalyzeFieldDeclaration, SyntaxKind.FieldDeclaration);
            }

            private static void AnalyzeFieldDeclaration(SyntaxNodeAnalysisContext context)
            {
            }
        }

        [Fact]
        [WorkItem(44000, "https://github.com/dotnet/roslyn/issues/44000")]
        public void TupleField_ForceComplete()
        {
            var source =
@"namespace System
{
    public struct ValueTuple<T1>
    {
        public T1 Item1;
        public ValueTuple(T1 item1)
        {
            Item1 = item1;
        }
    }
}";
            var srcFile = Temp.CreateFile().WriteAllText(source);
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var csc = CreateCSharpCompiler(
                null,
                WorkingDirectory,
                new[] { "/nologo", "/t:library", srcFile.Path },
               analyzers: ImmutableArray.Create<DiagnosticAnalyzer>(new FieldAnalyzer())); // at least one analyzer required
            var exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            var output = outWriter.ToString();
            Assert.Empty(output);
            CleanupAllGeneratedFiles(srcFile.Path);
        }

        [Fact]
        public void GlobalAnalyzerConfigsAllowedInSameDir()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText(@"
class C
{
    int _f;
}");
            var configText = @"
is_global = true
";

            var analyzerConfig1 = dir.CreateFile("analyzerconfig1").WriteAllText(configText);
            var analyzerConfig2 = dir.CreateFile("analyzerconfig2").WriteAllText(configText);

            var cmd = CreateCSharpCompiler(null, dir.Path, new[] {
                "/nologo",
                "/t:library",
                "/preferreduilang:en",
                "/analyzerconfig:" + analyzerConfig1.Path,
                "/analyzerconfig:" + analyzerConfig2.Path,
                src.Path
            });

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
        }

        [Fact]
        public void GlobalAnalyzerConfigMultipleSetKeys()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            var analyzerConfigFile = dir.CreateFile(".globalconfig");
            var analyzerConfig = analyzerConfigFile.WriteAllText(@"
is_global = true
global_level = 100
option1 = abc");

            var analyzerConfigFile2 = dir.CreateFile(".globalconfig2");
            var analyzerConfig2 = analyzerConfigFile2.WriteAllText(@"
is_global = true
global_level = 100
option1 = def");

            var output = VerifyOutput(dir, src, additionalFlags: new[] { "/analyzerconfig:" + analyzerConfig.Path + "," + analyzerConfig2.Path }, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);

            // warning MultipleGlobalAnalyzerKeys: Multiple global analyzer config files set the same key 'option1' in section 'Global Section'. It has been unset. Key was set by the following files: ...
            Assert.Contains("MultipleGlobalAnalyzerKeys:", output, StringComparison.Ordinal);
            Assert.Contains("'option1'", output, StringComparison.Ordinal);
            Assert.Contains("'Global Section'", output, StringComparison.Ordinal);


            analyzerConfig = analyzerConfigFile.WriteAllText(@"
is_global = true
global_level = 100
[/file.cs]
option1 = abc");

            analyzerConfig2 = analyzerConfigFile2.WriteAllText(@"
is_global = true
global_level = 100
[/file.cs]
option1 = def");

            output = VerifyOutput(dir, src, additionalFlags: new[] { "/analyzerconfig:" + analyzerConfig.Path + "," + analyzerConfig2.Path }, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);

            // warning MultipleGlobalAnalyzerKeys: Multiple global analyzer config files set the same key 'option1' in section 'file.cs'. It has been unset. Key was set by the following files: ...
            Assert.Contains("MultipleGlobalAnalyzerKeys:", output, StringComparison.Ordinal);
            Assert.Contains("'option1'", output, StringComparison.Ordinal);
            Assert.Contains("'/file.cs'", output, StringComparison.Ordinal);
        }

        [Fact]
        public void GlobalAnalyzerConfigWithOptions()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText(@"
class C
{
}");
            var additionalFile = dir.CreateFile("file.txt");
            var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText(@"
[*.cs]
key1 = value1

[*.txt]
key2 = value2");

            var globalConfig = dir.CreateFile(".globalconfig").WriteAllText(@"
is_global = true
key3 = value3");

            var cmd = CreateCSharpCompiler(null, dir.Path, new[] {
                "/nologo",
                "/t:library",
                "/analyzerconfig:" + analyzerConfig.Path,
                "/analyzerconfig:" + globalConfig.Path,
                "/analyzer:" + Assembly.GetExecutingAssembly().Location,
                "/nowarn:8032,Warning01",
                "/additionalfile:" + additionalFile.Path,
                src.Path });

            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal("", outWriter.ToString());
            Assert.Equal(0, exitCode);

            var comp = cmd.Compilation;
            var tree = comp.SyntaxTrees.Single();

            var provider = cmd.AnalyzerOptions.AnalyzerConfigOptionsProvider;
            var options = provider.GetOptions(tree);
            Assert.NotNull(options);
            Assert.True(options.TryGetValue("key1", out string val));
            Assert.Equal("value1", val);
            Assert.False(options.TryGetValue("key2", out _));
            Assert.True(options.TryGetValue("key3", out val));
            Assert.Equal("value3", val);

            options = provider.GetOptions(cmd.AnalyzerOptions.AdditionalFiles.Single());
            Assert.NotNull(options);
            Assert.False(options.TryGetValue("key1", out _));
            Assert.True(options.TryGetValue("key2", out val));
            Assert.Equal("value2", val);
            Assert.True(options.TryGetValue("key3", out val));
            Assert.Equal("value3", val);

            options = provider.GlobalOptions;
            Assert.NotNull(options);
            Assert.False(options.TryGetValue("key1", out _));
            Assert.False(options.TryGetValue("key2", out _));
            Assert.True(options.TryGetValue("key3", out val));
            Assert.Equal("value3", val);
        }

        [Fact]
        [WorkItem(44087, "https://github.com/dotnet/roslyn/issues/44804")]
        public void GlobalAnalyzerConfigDiagnosticOptionsCanBeOverridenByCommandLine()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
    void M()
    {
label1:;
    }
}");
            var globalConfig = dir.CreateFile(".globalconfig").WriteAllText(@"
is_global = true
dotnet_diagnostic.CS0164.severity = error;
");

            var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText(@"
[*.cs]
dotnet_diagnostic.CS0164.severity = warning;
");
            var none = Array.Empty<TempFile>();
            var globalOnly = new[] { globalConfig };
            var globalAndSpecific = new[] { globalConfig, analyzerConfig };

            // by default a warning, which can be suppressed via cmdline
            verify(configs: none, expectedWarnings: 1);
            verify(configs: none, noWarn: "CS0164", expectedWarnings: 0);

            // the global analyzer config ups the warning to an error, but the cmdline setting overrides it
            verify(configs: globalOnly, expectedErrors: 1);
            verify(configs: globalOnly, noWarn: "CS0164", expectedWarnings: 0);
            verify(configs: globalOnly, noWarn: "164", expectedWarnings: 0); // cmdline can be shortened, but still works

            // the editor config downgrades the error back to warning, but the cmdline setting overrides it
            verify(configs: globalAndSpecific, expectedWarnings: 1);
            verify(configs: globalAndSpecific, noWarn: "CS0164", expectedWarnings: 0);


            void verify(TempFile[] configs, int expectedWarnings = 0, int expectedErrors = 0, string noWarn = "0")
                => VerifyOutput(dir, src,
                                expectedErrorCount: expectedErrors,
                                expectedWarningCount: expectedWarnings,
                                includeCurrentAssemblyAsAnalyzerReference: false,
                                analyzers: null,
                                additionalFlags: configs.SelectAsArray(c => "/analyzerconfig:" + c.Path)
                                                         .Add("/noWarn:" + noWarn).ToArray());
        }

        [Fact]
        [WorkItem(44087, "https://github.com/dotnet/roslyn/issues/44804")]
        public void GlobalAnalyzerConfigSpecificDiagnosticOptionsOverrideGeneralCommandLineOptions()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
    void M()
    {
label1:;
    }
}");
            var globalConfig = dir.CreateFile(".globalconfig").WriteAllText($@"
is_global = true
dotnet_diagnostic.CS0164.severity = none;
");

            VerifyOutput(dir, src, additionalFlags: new[] { "/warnaserror+", "/analyzerconfig:" + globalConfig.Path }, includeCurrentAssemblyAsAnalyzerReference: false);
        }

        [Theory, CombinatorialData]
        [WorkItem(43051, "https://github.com/dotnet/roslyn/issues/43051")]
        public void WarnAsErrorIsRespectedForForWarningsConfiguredInRulesetOrGlobalConfig(bool useGlobalConfig)
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
    void M()
    {
label1:;
    }
}");
            var additionalFlags = new[] { "/warnaserror+" };
            if (useGlobalConfig)
            {
                var globalConfig = dir.CreateFile(".globalconfig").WriteAllText($@"
is_global = true
dotnet_diagnostic.CS0164.severity = warning;
");
                additionalFlags = additionalFlags.Append("/analyzerconfig:" + globalConfig.Path).ToArray();
            }
            else
            {
                string ruleSetSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""15.0"">
  <Rules AnalyzerId=""Compiler"" RuleNamespace=""Compiler"">
    <Rule Id=""CS0164"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
                _ = dir.CreateFile("Rules.ruleset").WriteAllText(ruleSetSource);
                additionalFlags = additionalFlags.Append("/ruleset:Rules.ruleset").ToArray();
            }

            VerifyOutput(dir, src, additionalFlags: additionalFlags, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
        }

        [Fact]
        [WorkItem(44087, "https://github.com/dotnet/roslyn/issues/44804")]
        public void GlobalAnalyzerConfigSectionsDoNotOverrideCommandLine()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
    void M()
    {
label1:;
    }
}");
            var globalConfig = dir.CreateFile(".globalconfig").WriteAllText($@"
is_global = true

[{PathUtilities.NormalizeWithForwardSlash(src.Path)}]
dotnet_diagnostic.CS0164.severity = error;
");

            VerifyOutput(dir, src, additionalFlags: new[] { "/nowarn:0164", "/analyzerconfig:" + globalConfig.Path }, expectedErrorCount: 0, includeCurrentAssemblyAsAnalyzerReference: false);
        }

        [Fact]
        [WorkItem(44087, "https://github.com/dotnet/roslyn/issues/44804")]
        public void GlobalAnalyzerConfigCanSetDiagnosticWithNoLocation()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText(@"
class C
{
}");
            var globalConfig = dir.CreateFile(".globalconfig").WriteAllText(@"
is_global = true
dotnet_diagnostic.Warning01.severity = error;
");

            VerifyOutput(dir, src, additionalFlags: new[] { "/analyzerconfig:" + globalConfig.Path }, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false, analyzers: new WarningDiagnosticAnalyzer());

            VerifyOutput(dir, src, additionalFlags: new[] { "/nowarn:Warning01", "/analyzerconfig:" + globalConfig.Path }, includeCurrentAssemblyAsAnalyzerReference: false, analyzers: new WarningDiagnosticAnalyzer());
        }

        [Theory, CombinatorialData]
        public void TestAdditionalFileAnalyzer(bool registerFromInitialize)
        {
            var srcDirectory = Temp.CreateDirectory();

            var source = "class C { }";
            var srcFile = srcDirectory.CreateFile("a.cs");
            srcFile.WriteAllText(source);

            var additionalText = "Additional Text";
            var additionalFile = srcDirectory.CreateFile("b.txt");
            additionalFile.WriteAllText(additionalText);

            var diagnosticSpan = new TextSpan(2, 2);
            var analyzer = new AdditionalFileAnalyzer(registerFromInitialize, diagnosticSpan);

            var output = VerifyOutput(srcDirectory, srcFile, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false,
                additionalFlags: new[] { "/additionalfile:" + additionalFile.Path },
                analyzers: analyzer);
            Assert.Contains("b.txt(1,3): warning ID0001", output, StringComparison.Ordinal);

            CleanupAllGeneratedFiles(srcDirectory.Path);
        }

        [Theory]
        // "/warnaserror" tests
        [InlineData(/*analyzerConfigSeverity*/"warning", "/warnaserror", /*expectError*/true, /*expectWarning*/false)]
        [InlineData(/*analyzerConfigSeverity*/"error", "/warnaserror", /*expectError*/true, /*expectWarning*/false)]
        [InlineData(/*analyzerConfigSeverity*/null, "/warnaserror", /*expectError*/true, /*expectWarning*/false)]
        // "/warnaserror:CS0169" tests
        [InlineData(/*analyzerConfigSeverity*/"warning", "/warnaserror:CS0169", /*expectError*/true, /*expectWarning*/false)]
        [InlineData(/*analyzerConfigSeverity*/"error", "/warnaserror:CS0169", /*expectError*/true, /*expectWarning*/false)]
        [InlineData(/*analyzerConfigSeverity*/null, "/warnaserror:CS0169", /*expectError*/true, /*expectWarning*/false)]
        // "/nowarn" tests
        [InlineData(/*analyzerConfigSeverity*/"warning", "/nowarn:CS0169", /*expectError*/false, /*expectWarning*/false)]
        [InlineData(/*analyzerConfigSeverity*/"error", "/nowarn:CS0169", /*expectError*/false, /*expectWarning*/false)]
        [InlineData(/*analyzerConfigSeverity*/null, "/nowarn:CS0169", /*expectError*/false, /*expectWarning*/false)]
        // Neither "/nowarn" nor "/warnaserror" tests
        [InlineData(/*analyzerConfigSeverity*/"warning", /*additionalArg*/null, /*expectError*/false, /*expectWarning*/true)]
        [InlineData(/*analyzerConfigSeverity*/"error", /*additionalArg*/null, /*expectError*/true, /*expectWarning*/false)]
        [InlineData(/*analyzerConfigSeverity*/null, /*additionalArg*/null, /*expectError*/false, /*expectWarning*/true)]
        [WorkItem(43051, "https://github.com/dotnet/roslyn/issues/43051")]
        public void TestCompilationOptionsOverrideAnalyzerConfig_CompilerWarning(string analyzerConfigSeverity, string additionalArg, bool expectError, bool expectWarning)
        {
            var src = @"
class C
{
    int _f;     // CS0169: unused field
}";
            TestCompilationOptionsOverrideAnalyzerConfigCore(src, diagnosticId: "CS0169", analyzerConfigSeverity, additionalArg, expectError, expectWarning);
        }

        [Theory]
        // "/warnaserror" tests
        [InlineData(/*analyzerConfigSeverity*/"warning", "/warnaserror", /*expectError*/true, /*expectWarning*/false)]
        [InlineData(/*analyzerConfigSeverity*/"error", "/warnaserror", /*expectError*/true, /*expectWarning*/false)]
        [InlineData(/*analyzerConfigSeverity*/null, "/warnaserror", /*expectError*/true, /*expectWarning*/false)]
        // "/warnaserror:DiagnosticId" tests
        [InlineData(/*analyzerConfigSeverity*/"warning", "/warnaserror:" + CompilationAnalyzerWithSeverity.DiagnosticId, /*expectError*/true, /*expectWarning*/false)]
        [InlineData(/*analyzerConfigSeverity*/"error", "/warnaserror:" + CompilationAnalyzerWithSeverity.DiagnosticId, /*expectError*/true, /*expectWarning*/false)]
        [InlineData(/*analyzerConfigSeverity*/null, "/warnaserror:" + CompilationAnalyzerWithSeverity.DiagnosticId, /*expectError*/true, /*expectWarning*/false)]
        // "/nowarn" tests
        [InlineData(/*analyzerConfigSeverity*/"warning", "/nowarn:" + CompilationAnalyzerWithSeverity.DiagnosticId, /*expectError*/false, /*expectWarning*/false)]
        [InlineData(/*analyzerConfigSeverity*/"error", "/nowarn:" + CompilationAnalyzerWithSeverity.DiagnosticId, /*expectError*/false, /*expectWarning*/false)]
        [InlineData(/*analyzerConfigSeverity*/null, "/nowarn:" + CompilationAnalyzerWithSeverity.DiagnosticId, /*expectError*/false, /*expectWarning*/false)]
        // Neither "/nowarn" nor "/warnaserror" tests
        [InlineData(/*analyzerConfigSeverity*/"warning", /*additionalArg*/null, /*expectError*/false, /*expectWarning*/true)]
        [InlineData(/*analyzerConfigSeverity*/"error", /*additionalArg*/null, /*expectError*/true, /*expectWarning*/false)]
        [InlineData(/*analyzerConfigSeverity*/null, /*additionalArg*/null, /*expectError*/false, /*expectWarning*/true)]
        [WorkItem(43051, "https://github.com/dotnet/roslyn/issues/43051")]
        public void TestCompilationOptionsOverrideAnalyzerConfig_AnalyzerWarning(string analyzerConfigSeverity, string additionalArg, bool expectError, bool expectWarning)
        {
            var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: true);
            var src = @"class C { }";
            TestCompilationOptionsOverrideAnalyzerConfigCore(src, CompilationAnalyzerWithSeverity.DiagnosticId, analyzerConfigSeverity, additionalArg, expectError, expectWarning, analyzer);
        }

        private void TestCompilationOptionsOverrideAnalyzerConfigCore(
            string source,
            string diagnosticId,
            string analyzerConfigSeverity,
            string additionalArg,
            bool expectError,
            bool expectWarning,
            params DiagnosticAnalyzer[] analyzers)
        {
            Assert.True(!expectError || !expectWarning);

            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(source);

            var additionalArgs = Array.Empty<string>();
            if (analyzerConfigSeverity != null)
            {
                var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText($@"
[*.cs]
dotnet_diagnostic.{diagnosticId}.severity = {analyzerConfigSeverity}");

                additionalArgs = additionalArgs.Append("/analyzerconfig:" + analyzerConfig.Path).ToArray();
            }

            if (!string.IsNullOrEmpty(additionalArg))
            {
                additionalArgs = additionalArgs.Append(additionalArg);
            }

            var output = VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false,
                                        additionalArgs,
                                        expectedErrorCount: expectError ? 1 : 0,
                                        expectedWarningCount: expectWarning ? 1 : 0,
                                        analyzers: analyzers);
            if (expectError)
            {
                Assert.Contains($"error {diagnosticId}", output);
            }
            else if (expectWarning)
            {
                Assert.Contains($"warning {diagnosticId}", output);
            }
            else
            {
                Assert.DoesNotContain(diagnosticId, output);
            }
        }

        [ConditionalFact(typeof(CoreClrOnly), Reason = "Can't load a coreclr targeting generator on net framework / mono")]
        public void TestGeneratorsCantTargetNetFramework()
        {
            var directory = Temp.CreateDirectory();
            var src = directory.CreateFile("test.cs").WriteAllText(@"
class C
{
}");

            // core
            var coreGenerator = emitGenerator(".NETCoreApp,Version=v5.0");
            VerifyOutput(directory, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/analyzer:" + coreGenerator });

            // netstandard
            var nsGenerator = emitGenerator(".NETStandard,Version=v2.0");
            VerifyOutput(directory, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/analyzer:" + nsGenerator });

            // no target
            var ntGenerator = emitGenerator(targetFramework: null);
            VerifyOutput(directory, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/analyzer:" + ntGenerator });

            // framework
            var frameworkGenerator = emitGenerator(".NETFramework,Version=v4.7.2");
            var output = VerifyOutput(directory, src, expectedWarningCount: 2, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/analyzer:" + frameworkGenerator });
            Assert.Contains("CS8850", output); // ref's net fx
            Assert.Contains("CS8033", output); // no analyzers in assembly

            // framework, suppressed
            output = VerifyOutput(directory, src, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/nowarn:CS8850", "/analyzer:" + frameworkGenerator });
            Assert.Contains("CS8033", output);
            VerifyOutput(directory, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/nowarn:CS8850,CS8033", "/analyzer:" + frameworkGenerator });

            string emitGenerator(string targetFramework)
            {
                string targetFrameworkAttributeText = targetFramework is object
                                                        ? $"[assembly: System.Runtime.Versioning.TargetFramework(\"{targetFramework}\")]"
                                                        : string.Empty;

                string generatorSource = $@"
using Microsoft.CodeAnalysis;

{targetFrameworkAttributeText}

[Generator]
public class Generator : ISourceGenerator
{{
            public void Execute(GeneratorExecutionContext context) {{ }}
            public void Initialize(GeneratorInitializationContext context) {{ }}
 }}";

                var directory = Temp.CreateDirectory();

                var generatorPath = Path.Combine(directory.Path, "generator.dll");

                var compilation = CSharpCompilation.Create($"generator",
                                                           new[] { CSharpSyntaxTree.ParseText(generatorSource) },
                                                           TargetFrameworkUtil.GetReferences(TargetFramework.Standard, new[] { MetadataReference.CreateFromAssemblyInternal(typeof(ISourceGenerator).Assembly) }),
                                                           new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

                compilation.VerifyDiagnostics();
                var result = compilation.Emit(generatorPath);
                Assert.True(result.Success);

                return generatorPath;
            }
        }

        [Theory]
        [InlineData("a.txt", "b.txt", 2)]
        [InlineData("a.txt", "a.txt", 1)]
        [InlineData("abc/a.txt", "def/a.txt", 2)]
        [InlineData("abc/a.txt", "abc/a.txt", 1)]
        [InlineData("abc/a.txt", "abc/../a.txt", 2)]
        [InlineData("abc/a.txt", "abc/./a.txt", 1)]
        [InlineData("abc/a.txt", "abc/../abc/a.txt", 1)]
        [InlineData("abc/a.txt", "abc/.././abc/a.txt", 1)]
        [InlineData("abc/a.txt", "./abc/a.txt", 1)]
        [InlineData("abc/a.txt", "../abc/../abc/a.txt", 2)]
        [InlineData("abc/a.txt", "./abc/../abc/a.txt", 1)]
        [InlineData("../abc/a.txt", "../abc/../abc/a.txt", 1)]
        [InlineData("../abc/a.txt", "../abc/a.txt", 1)]
        [InlineData("./abc/a.txt", "abc/a.txt", 1)]
        public void TestDuplicateAdditionalFiles(string additionalFilePath1, string additionalFilePath2, int expectedCount)
        {
            var srcDirectory = Temp.CreateDirectory();
            var srcFile = srcDirectory.CreateFile("a.cs").WriteAllText("class C { }");

            // make sure any parent or sub dirs exist too
            Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(srcDirectory.Path, additionalFilePath1)));
            Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(srcDirectory.Path, additionalFilePath2)));

            var additionalFile1 = srcDirectory.CreateFile(additionalFilePath1);
            var additionalFile2 = expectedCount == 2 ? srcDirectory.CreateFile(additionalFilePath2) : null;

            string path1 = additionalFile1.Path;
            string path2 = additionalFile2?.Path ?? Path.Combine(srcDirectory.Path, additionalFilePath2);

            int count = 0;
            var generator = new PipelineCallbackGenerator(ctx =>
            {
                ctx.RegisterSourceOutput(ctx.AdditionalTextsProvider, (spc, t) =>
                {
                    count++;
                });
            });

            var output = VerifyOutput(srcDirectory, srcFile, includeCurrentAssemblyAsAnalyzerReference: false,
                additionalFlags: new[] { "/additionalfile:" + path1, "/additionalfile:" + path2 },
                generators: new[] { generator.AsSourceGenerator() });

            Assert.Equal(expectedCount, count);

            CleanupAllGeneratedFiles(srcDirectory.Path);
        }

        [ConditionalTheory(typeof(WindowsOnly))]
        [InlineData("abc/a.txt", "abc\\a.txt", 1)]
        [InlineData("abc\\a.txt", "abc\\a.txt", 1)]
        [InlineData("abc/a.txt", "abc\\..\\a.txt", 2)]
        [InlineData("abc/a.txt", "abc\\..\\abc\\a.txt", 1)]
        [InlineData("abc/a.txt", "../abc\\../abc\\a.txt", 2)]
        [InlineData("abc/a.txt", "./abc\\../abc\\a.txt", 1)]
        [InlineData("../abc/a.txt", "../abc\\../abc\\a.txt", 1)]
        [InlineData("a.txt", "A.txt", 1)]
        [InlineData("abc/a.txt", "ABC\\a.txt", 1)]
        [InlineData("abc/a.txt", "ABC\\A.txt", 1)]
        public void TestDuplicateAdditionalFiles_Windows(string additionalFilePath1, string additionalFilePath2, int expectedCount) => TestDuplicateAdditionalFiles(additionalFilePath1, additionalFilePath2, expectedCount);

        [ConditionalTheory(typeof(LinuxOnly))]
        [InlineData("a.txt", "A.txt", 2)]
        [InlineData("abc/a.txt", "abc/A.txt", 2)]
        [InlineData("abc/a.txt", "ABC/a.txt", 2)]
        [InlineData("abc/a.txt", "./../abc/A.txt", 2)]
        [InlineData("abc/a.txt", "./../ABC/a.txt", 2)]
        public void TestDuplicateAdditionalFiles_Linux(string additionalFilePath1, string additionalFilePath2, int expectedCount) => TestDuplicateAdditionalFiles(additionalFilePath1, additionalFilePath2, expectedCount);

    }

    [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
    internal abstract class CompilationStartedAnalyzer : DiagnosticAnalyzer
    {
        public abstract override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }
        public abstract void CreateAnalyzerWithinCompilation(CompilationStartAnalysisContext context);

        public override void Initialize(AnalysisContext context)
        {
            context.RegisterCompilationStartAction(CreateAnalyzerWithinCompilation);
        }
    }

    [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
    internal class HiddenDiagnosticAnalyzer : CompilationStartedAnalyzer
    {
        internal static readonly DiagnosticDescriptor Hidden01 = new DiagnosticDescriptor("Hidden01", "", "Throwing a diagnostic for #region", "", DiagnosticSeverity.Hidden, isEnabledByDefault: true);
        internal static readonly DiagnosticDescriptor Hidden02 = new DiagnosticDescriptor("Hidden02", "", "Throwing a diagnostic for something else", "", DiagnosticSeverity.Hidden, isEnabledByDefault: true);

        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
        {
            get
            {
                return ImmutableArray.Create(Hidden01, Hidden02);
            }
        }

        private void AnalyzeNode(SyntaxNodeAnalysisContext context)
        {
            context.ReportDiagnostic(Diagnostic.Create(Hidden01, context.Node.GetLocation()));
        }

        public override void CreateAnalyzerWithinCompilation(CompilationStartAnalysisContext context)
        {
            context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.RegionDirectiveTrivia);
        }
    }

    [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
    internal class InfoDiagnosticAnalyzer : CompilationStartedAnalyzer
    {
        internal static readonly DiagnosticDescriptor Info01 = new DiagnosticDescriptor("Info01", "", "Throwing a diagnostic for #pragma restore", "", DiagnosticSeverity.Info, isEnabledByDefault: true);

        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
        {
            get
            {
                return ImmutableArray.Create(Info01);
            }
        }

        private void AnalyzeNode(SyntaxNodeAnalysisContext context)
        {
            if ((context.Node as PragmaWarningDirectiveTriviaSyntax).DisableOrRestoreKeyword.IsKind(SyntaxKind.RestoreKeyword))
            {
                context.ReportDiagnostic(Diagnostic.Create(Info01, context.Node.GetLocation()));
            }
        }

        public override void CreateAnalyzerWithinCompilation(CompilationStartAnalysisContext context)
        {
            context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.PragmaWarningDirectiveTrivia);
        }
    }

    [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
    internal class WarningDiagnosticAnalyzer : CompilationStartedAnalyzer
    {
        internal static readonly DiagnosticDescriptor Warning01 = new DiagnosticDescriptor("Warning01", "", "Throwing a diagnostic for types declared", "", DiagnosticSeverity.Warning, isEnabledByDefault: true);

        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
        {
            get
            {
                return ImmutableArray.Create(Warning01);
            }
        }

        public override void CreateAnalyzerWithinCompilation(CompilationStartAnalysisContext context)
        {
            context.RegisterSymbolAction(
                (symbolContext) =>
                {
                    symbolContext.ReportDiagnostic(Diagnostic.Create(Warning01, symbolContext.Symbol.Locations.First()));
                },
                SymbolKind.NamedType);
        }
    }

    [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
    internal class ErrorDiagnosticAnalyzer : CompilationStartedAnalyzer
    {
        internal static readonly DiagnosticDescriptor Error01 = new DiagnosticDescriptor("Error01", "", "Throwing a diagnostic for #pragma disable", "", DiagnosticSeverity.Error, isEnabledByDefault: true);
        internal static readonly DiagnosticDescriptor Error02 = new DiagnosticDescriptor("Error02", "", "Throwing a diagnostic for something else", "", DiagnosticSeverity.Error, isEnabledByDefault: true);

        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
        {
            get
            {
                return ImmutableArray.Create(Error01, Error02);
            }
        }

        public override void CreateAnalyzerWithinCompilation(CompilationStartAnalysisContext context)
        {
            context.RegisterSyntaxNodeAction(
                (nodeContext) =>
                {
                    if ((nodeContext.Node as PragmaWarningDirectiveTriviaSyntax).DisableOrRestoreKeyword.IsKind(SyntaxKind.DisableKeyword))
                    {
                        nodeContext.ReportDiagnostic(Diagnostic.Create(Error01, nodeContext.Node.GetLocation()));
                    }
                },
                SyntaxKind.PragmaWarningDirectiveTrivia
                );
        }
    }
}
