File: CommandLineTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\CommandLine\Microsoft.CodeAnalysis.CSharp.CommandLine.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.CommandLine.UnitTests)
// 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.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 TestResources.Analyzers;
using Xunit;
using Basic.Reference.Assemblies;
using static Microsoft.CodeAnalysis.CommonDiagnosticAnalyzers;
using static Roslyn.Test.Utilities.SharedResourceHelpers;
using System.Diagnostics;
 
namespace Microsoft.CodeAnalysis.CSharp.CommandLine.UnitTests
{
    public class CommandLineTests : CommandLineTestBase
    {
#if NET
        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.IsMonoDesktop ? "mono" : string.Empty;
#endif
        private static readonly string s_CSharpScriptExecutable;
 
        private static readonly string s_compilerVersion = CommonCompiler.GetProductVersion(typeof(CommandLineTests));
 
        static CommandLineTests()
        {
#if NET
            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);
        }
 
        [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/72657")]
        public void AnalyzerConfig_DoubleSlash(bool doubleSlashAnalyzerConfig, bool doubleSlashSource)
        {
            var dir = Temp.CreateDirectory();
            var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: true);
            var src = dir.CreateFile("Class1.cs").WriteAllText("""
                public class C
                {
                    public void M() { }
                }
                """);
 
            // The analyzer should produce a warning.
            var output = VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, analyzers: [analyzer], expectedWarningCount: 1);
            AssertEx.Equal("Class1.cs(1,1): warning ID1000:", output.Trim());
 
            // But not when this editorconfig is applied.
            var editorconfig = dir.CreateFile(".editorconfig").WriteAllText("""
                root = true
 
                [*.cs]
                dotnet_analyzer_diagnostic.severity = none
 
                generated_code = true
                """);
            var cmd = CreateCSharpCompiler(
                [
                    "/nologo",
                    "/preferreduilang:en",
                    "/t:library",
                    "/analyzerconfig:" + modifyPath(editorconfig.Path, doubleSlashAnalyzerConfig),
                    modifyPath(src.Path, doubleSlashSource),
                ],
                [analyzer]);
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
            AssertEx.Equal("", outWriter.ToString());
 
            static string modifyPath(string path, bool doubleSlash)
            {
                if (!doubleSlash)
                {
                    return path;
                }
 
                // Find the second-to-last slash.
                char[] separators = ['/', '\\'];
                var lastSlashIndex = path.LastIndexOfAny(separators);
                lastSlashIndex = path.LastIndexOfAny(separators, lastSlashIndex - 1);
 
                // Duplicate that slash.
                var lastSlash = path[lastSlashIndex];
                return path[0..lastSlashIndex] + lastSlash + path[lastSlashIndex..];
            }
        }
 
        [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()
        {
#pragma warning disable SYSLIB0037
            // warning SYSLIB0037: 'AssemblyName.ProcessorArchitecture' is obsolete: 'AssemblyName members HashAlgorithm, ProcessorArchitecture, and VersionCompatibility are obsolete and not supported.'
            Assert.Equal(ProcessorArchitecture.MSIL, AssemblyName.GetAssemblyName(s_CSharpCompilerExecutable).ProcessorArchitecture);
#pragma warning restore SYSLIB0037
        }
 
        [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));
 
            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 issue 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("14")]
        [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 _MaxAvailableLangVersion (a relevant test should break when new version is introduced)
            // - email release management to add to the release notes (see csharp-version in release.json in previous example: https://github.com/dotnet/core/pull/9493)
            AssertEx.SetEqual(new[] { "default", "1", "2", "3", "4", "5", "6", "7.0", "7.1", "7.2", "7.3", "8.0", "9.0", "10.0", "11.0", "12.0", "13.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,
                ErrorCode.ERR_FeatureNotAvailableInVersion11,
                ErrorCode.ERR_FeatureNotAvailableInVersion12,
                ErrorCode.ERR_FeatureNotAvailableInVersion13,
            };
 
            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.CSharp11, LanguageVersion.CSharp11),
            InlineData(LanguageVersion.CSharp12, LanguageVersion.CSharp12),
            InlineData(LanguageVersion.CSharp13, LanguageVersion.CSharp13),
            InlineData(LanguageVersion.CSharp13, LanguageVersion.LatestMajor),
            InlineData(LanguageVersion.CSharp13, LanguageVersion.Latest),
            InlineData(LanguageVersion.CSharp13, 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("11", true, LanguageVersion.CSharp11),
            InlineData("11.0", true, LanguageVersion.CSharp11),
            InlineData("12", true, LanguageVersion.CSharp12),
            InlineData("12.0", true, LanguageVersion.CSharp12),
            InlineData("13", true, LanguageVersion.CSharp13),
            InlineData("13.0", true, LanguageVersion.CSharp13),
            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"" : {} }"{ ""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"" : {} }"{ ""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"" : {} }"{ ""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, WorkItem("https://github.com/dotnet/roslyn/issues/71022")]
        public void ParseReferencesAlias()
        {
            assert(@"/r:a=util.dll", @"util.dll", ["a"], false);
            assert(@"/r:""a=util.dll""", @"a=util.dll", [], false);
            assert(@"/r:""c:\users\app=exe\util.dll""", @"c:\users\app=exe\util.dll", [], false);
            assert(@"/r:a=b=util.dll", @"b=util.dll", ["a"], false);
            assert(@"/r:""a=b""=util.dll", @"a=b=util.dll", [], false);
            assert(@"/r:\""a=b\""=util.dll", @"""a=b""=util.dll", [], false);
            assert(@"/r:a""b=util.dll", "ab=util.dll", [], false);
            assert(@"/r:a\""b=util.dll", @"a""b=util.dll", [], false);
            assert(@"/r:""a""=""util.dll""", @"a=util.dll", [], false);
            assert(@"/r:\""a\""=\""util.dll""", @"""a""=""util.dll", [], false);
 
            void assert(string arg, string expectedRef, string[] expectedAliases, bool expectedEmbed)
            {
                var result = parseRef(arg);
                Assert.Equal(expectedRef, result.Reference);
                Assert.Equal(expectedAliases, result.Properties.Aliases);
                Assert.Equal(expectedEmbed, result.Properties.EmbedInteropTypes);
            }
 
            CommandLineReference parseRef(string refText)
            {
                var parsedArgs = DefaultParse([refText, "test.cs"], WorkingDirectory);
                Assert.Equal(2, parsedArgs.MetadataReferences.Length);
                Assert.Empty(parsedArgs.Errors);
                return parsedArgs.MetadataReferences[1];
            }
        }
 
        [Fact]
        public void ParseReferencesAliasErrors()
        {
            parseRef(@"/reference:a\b=util.dll").Verify(
                Diagnostic(ErrorCode.ERR_BadExternIdentifier).WithArguments(@"a\b").WithLocation(1, 1));
 
            parseRef(@"/reference:a$b=util.dll").Verify(
                Diagnostic(ErrorCode.ERR_BadExternIdentifier).WithArguments(@"a$b").WithLocation(1, 1));
 
            parseRef(@"/reference:a=util.dll,util2.dll").Verify(
                Diagnostic(ErrorCode.ERR_OneAliasPerReference).WithLocation(1, 1));
 
            parseRef(@"/reference:a=").Verify(
                Diagnostic(ErrorCode.ERR_AliasMissingFile).WithArguments("a").WithLocation(1, 1));
 
            ImmutableArray<Diagnostic> parseRef(string refText)
            {
                var parsedArgs = DefaultParse([refText, "test.cs"], WorkingDirectory);
                return parsedArgs.Errors;
            }
        }
 
        [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");
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47310")]
        public void DiagnosticFormatting_UrlFormat_ObsoleteAttribute()
        {
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile("a.cs");
            file.WriteAllText("""
                #pragma warning disable CS0436 // System.Obsolete conflict
                #nullable enable
                using System;
 
                var c1 = new C1();
                var c2 = new C2();
                var c3 = new C3();
                var c4 = new C4();
 
                [Obsolete("Do not use C1", UrlFormat = "https://example.org/{0}")]
                public class C1 { }
                [Obsolete("Do not use C2", error: true, UrlFormat = "https://example.org/2/{0}")]
                public class C2 { }
                [Obsolete("Do not use C3", error: true, DiagnosticId = "OBSOLETEC3", UrlFormat = "https://example.org/3/{0}")]
                public class C3 { }
                [Obsolete("Do not use C4", DiagnosticId = "OBSOLETEC4", UrlFormat = "https://example.org/4")]
                public class C4 { }
 
                namespace System
                {
                    public class ObsoleteAttribute : Attribute
                    {
                        public ObsoleteAttribute() { }
                        public ObsoleteAttribute(string? message) { }
                        public ObsoleteAttribute(string? message, bool error) { }
 
                        public string? DiagnosticId { get; set; }
                        public string? UrlFormat { get; set; }
                    }
                }
                """);
 
            var output = VerifyOutput(dir, file,
                includeCurrentAssemblyAsAnalyzerReference: false,
                expectedWarningCount: 2,
                expectedErrorCount: 2,
                additionalFlags: new[] { "/t:exe" });
 
            AssertEx.Equal("""
                a.cs(5,14): warning CS0618: 'C1' is obsolete: 'Do not use C1' (https://example.org/CS0618)
                a.cs(6,14): error CS0619: 'C2' is obsolete: 'Do not use C2' (https://example.org/2/CS0619)
                a.cs(7,14): error OBSOLETEC3: 'C3' is obsolete: 'Do not use C3' (https://example.org/3/OBSOLETEC3)
                a.cs(8,14): warning OBSOLETEC4: 'C4' is obsolete: 'Do not use C4' (https://example.org/4)
                """,
                output.Trim());
 
            CleanupAllGeneratedFiles(file.Path);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47310")]
        public void DiagnosticFormatting_DiagnosticAnalyzer()
        {
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile("a.cs");
            file.WriteAllText("class C { }");
 
            var output = VerifyOutput(dir, file,
                includeCurrentAssemblyAsAnalyzerReference: false,
                expectedWarningCount: 1,
                analyzers: new[] { new WarningWithUrlDiagnosticAnalyzer() });
 
            AssertEx.Equal("""
                a.cs(1,7): warning Warning02: Throwing a diagnostic for types declared (https://example.org/analyzer)
                """,
                output.Trim());
 
            CleanupAllGeneratedFiles(file.Path);
        }
 
        [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(Silverlight.System).Path;
            var net4_0dll = Temp.CreateFile().WriteAllBytes(Net461.Resources.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_OutputFileIsDirectory()
        {
            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);
            var output = outWriter.ToString().Trim();
            Assert.StartsWith($"error CS2012: Cannot open '{subFolder}' for writing -- ", output); // Cannot create a file when that file already exists.
 
            CleanupAllGeneratedFiles(src.Path);
        }
 
        [ConditionalFact(typeof(WindowsOnly))]
        public void UnableWriteOutput_OutputFileLocked()
        {
            var tempFolder = Temp.CreateDirectory();
            var baseDirectory = tempFolder.ToString();
            var filePath = tempFolder.CreateFile("temp").Path;
 
            using var _ = new FileStream(filePath, FileMode.Open, FileAccess.Write, FileShare.None);
            var currentProcess = Process.GetCurrentProcess();
 
            var src = Temp.CreateFile("a.cs");
            src.WriteAllText("public class C{}");
 
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            int exitCode = CreateCSharpCompiler(responseFile: null, baseDirectory, ["/nologo", "/preferreduilang:en", "/t:library", "/out:" + filePath, src.ToString()]).Run(outWriter);
            Assert.Equal(1, exitCode);
            var output = outWriter.ToString().Trim();
 
            var pattern = @"error CS2012: Cannot open '(?<path>.*)' for writing -- (?<message>.*); file may be locked by '(?<app>.*)' \((?<pid>.*)\)";
            var match = Regex.Match(output, pattern);
            Assert.True(match.Success, $"Expected pattern:{Environment.NewLine}{pattern}{Environment.NewLine}Actual:{Environment.NewLine}{output}");
            Assert.Equal(filePath, match.Groups["path"].Value);
            Assert.Contains("testhost", match.Groups["app"].Value);
            Assert.Equal(currentProcess.Id, int.Parse(match.Groups["pid"].Value));
 
            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));
 
            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));
 
            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 nameGuid = Guid.NewGuid().ToString();
            var name = nameGuid + ".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(nameGuid, assemblyName.Name);
            Assert.Equal("0.0.0.0", assemblyName.Version.ToString());
            Assert.Equal(string.Empty, assemblyName.CultureName);
            Assert.Equal(Array.Empty<byte>(), assemblyName.GetPublicKeyToken());
        }
 
        [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(Net461.Resources.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 CreateCompilationWithDisableLengthBasedSwitch()
        {
            string source = """
public class C
{
}
""";
 
            var fileName = "a.cs";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile(fileName);
            file.WriteAllText(source);
 
            var cmd = CreateCSharpCompiler(null, dir.Path, new[] { "a.cs", "/features:disable-length-based-switch" });
            var comp = cmd.CreateCompilation(TextWriter.Null, new TouchedFileLogger(), NullErrorLogger.Instance);
            Assert.True(((CSharpCompilation)comp).FeatureDisableLengthBasedSwitch);
 
            cmd = CreateCSharpCompiler(null, dir.Path, new[] { "a.cs" });
            comp = cmd.CreateCompilation(TextWriter.Null, new TouchedFileLogger(), NullErrorLogger.Instance);
            Assert.False(((CSharpCompilation)comp).FeatureDisableLengthBasedSwitch);
        }
 
        [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 UTF-8, 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", "/features:noRefSafetyRulesAttribute", 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", "/features:noRefSafetyRulesAttribute", 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", "/features:noRefSafetyRulesAttribute", 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/*""
  }
}
"{
  ""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, "<name>"), 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(
                responseFile: null,
                srcDirectory,
                new[] { "/reportanalyzer", "/t:library", srcFile.Path },
                analyzers: [new WarningDiagnosticAnalyzer(), new DiagnosticSuppressorForId("Warning01", "Suppressor01")],
                generators: new[] { new DoNothingGenerator().AsSourceGenerator() });
            var exitCode = csc.Run(outWriter);
            Assert.Equal(0, exitCode);
            var output = outWriter.ToString();
            Assert.Contains(CodeAnalysisResources.AnalyzerExecutionTimeColumnHeader, output, StringComparison.Ordinal);
            Assert.Contains($"{nameof(WarningDiagnosticAnalyzer)} (Warning01)", output, StringComparison.Ordinal);
            Assert.Contains($"{nameof(DiagnosticSuppressorForId)} (Suppressor01)", output, StringComparison.Ordinal);
            Assert.Contains(CodeAnalysisResources.GeneratorNameColumnHeader, output, StringComparison.Ordinal);
            Assert.Contains(typeof(DoNothingGenerator).FullName, 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);
        }
 
        [Fact]
        [WorkItem(40926, "https://github.com/dotnet/roslyn/issues/40926")]
        public void SkipAnalyzersFlagFiltersAnalyzers()
        {
            var srcFile = Temp.CreateFile().WriteAllText(@"class C {}");
            var srcDirectory = Path.GetDirectoryName(srcFile.Path);
 
            var args = new List<string>() { "/reportanalyzer", "/t:library", "/a:" + typeof(DoNothingGenerator).Assembly.Location, srcFile.Path };
            var csc = CreateCSharpCompiler(
                responseFile: null,
                srcDirectory,
                args.ToArray());
 
            csc.ResolveAnalyzersFromArguments(
                skipAnalyzers: false,
                out _,
                out var analyzers,
                out var generators);
            Assert.NotEmpty(analyzers);
            Assert.NotEmpty(generators);
 
            csc.ResolveAnalyzersFromArguments(
                skipAnalyzers: true,
                out _,
                out analyzers,
                out generators);
            Assert.All(analyzers, static x => Assert.IsAssignableFrom<DiagnosticSuppressor>(x));
            Assert.NotEmpty(generators);
 
            CleanupAllGeneratedFiles(srcFile.Path);
        }
 
        [Theory]
        [CombinatorialData]
        [WorkItem(40926, "https://github.com/dotnet/roslyn/issues/40926")]
        public void NoAnalyzersReportSemantics(bool skipAnalyzers)
        {
            var srcFile = Temp.CreateFile().WriteAllText(@"class C {}");
            var srcDirectory = Path.GetDirectoryName(srcFile.Path);
 
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var analyzers = skipAnalyzers
                ? Array.Empty<DiagnosticAnalyzer>()
                : new DiagnosticAnalyzer[] { new HiddenDiagnosticAnalyzer(), new WarningDiagnosticAnalyzer() };
            var csc = CreateCSharpCompiler(
                responseFile: null,
                srcDirectory,
                new[] { "/reportanalyzer", "/t:library", srcFile.Path },
                analyzers: analyzers);
            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: new[] { 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: new[] { 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: new[] { 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: new[] { 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, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute());
            Assert.Equal("v4.0.30319", ModuleMetadata.CreateFromImage(comp.EmitToArray(new EmitOptions(runtimeMetadataVersion: "v4.0.30319"))).Module.MetadataVersion);
 
            comp = CreateEmptyCompilation(string.Empty, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute());
            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++;
                    spc.AddSource("output.cs", "");
                });
            });
 
            // 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++;
                    spc.AddSource("output.cs", "");
                });
            });
 
            // 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++;
                    spc.AddSource("output.cs", "");
                });
            });
 
            // 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++;
                    spc.AddSource("output.cs", "");
                });
            });
 
            var generator2 = new PipelineCallbackGenerator2((ctx) =>
            {
                ctx.RegisterSourceOutput(ctx.ParseOptionsProvider, (spc, po) =>
                {
                    sourceCallbackCount2++;
                    spc.AddSource("output.cs", "");
                });
            });
 
            // 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(Skip = "Additional file comparison is disabled due to https://github.com/dotnet/roslyn/issues/59209")]
        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++;
                    spc.AddSource("output.cs", "");
                });
 
                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++;
                    spc.AddSource("output.cs", "");
                });
            });
 
            // 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);
        }
 
        [Fact]
        public void Compiler_DoesNot_RunHostOutputs()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
            bool hostOutputRan = false;
            bool sourceOutputRan = false;
 
            var generator = new PipelineCallbackGenerator((ctx) =>
            {
#pragma warning disable RSEXPERIMENTAL004 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
                ctx.RegisterHostOutput(ctx.CompilationProvider, (hostCtx, value) =>
                {
                    hostOutputRan = true;
                    hostCtx.AddOutput("output", "value");
                });
#pragma warning restore RSEXPERIMENTAL004 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
 
                ctx.RegisterSourceOutput(ctx.CompilationProvider, (spc, po) =>
                {
                    sourceOutputRan = true;
                    spc.AddSource("output.cs", "//value");
                });
            });
 
            VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/langversion:preview" }, generators: new[] { generator.AsSourceGenerator() }, analyzers: null);
 
            Assert.False(hostOutputRan);
            Assert.True(sourceOutputRan);
 
            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);
        }
 
        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,
                                           DiagnosticAnalyzer[] analyzers = null,
                                           ISourceGenerator[] generators = null,
                                           GeneratorDriverCache driverCache = null)
        {
            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, generators: generators, 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__DisplayClass21_0.<ComputeDiagnosticDescriptors_NoLock>b__0(Object _)
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info, CancellationToken cancellationToken)
-----
Analyzer 'TestAnalyzer' threw an exception of type 'System.NotImplementedException' with message '28'.
System.NotImplementedException: 28
   at TestAnalyzer.get_SupportedDiagnostics()
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.CreateDisablingMessage(DiagnosticAnalyzer analyzer, String analyzerName)
-----
", 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.References.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.References.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.References.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);
        }
 
        [Theory]
        [InlineData(@"/features:InterceptorsNamespaces=NS1.NS2;NS3.NS4")]
        [InlineData(@"/features:""InterceptorsNamespaces=NS1.NS2;NS3.NS4""")]
        public void FeaturesInterceptorsNamespaces_OptionParsing(string features)
        {
            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, "a.cs" });
            var comp = (CSharpCompilation)csc.CreateCompilation(new StringWriter(), new TouchedFileLogger(), errorLogger: null);
            var options = comp.SyntaxTrees[0].Options;
            Assert.Equal(1, options.Features.Count);
            Assert.Equal("NS1.NS2;NS3.NS4", options.Features["InterceptorsNamespaces"]);
 
            var previewNamespaces = ((CSharpParseOptions)options).InterceptorsNamespaces;
            Assert.Equal(2, previewNamespaces.Length);
            Assert.Equal(new[] { "NS1", "NS2" }, previewNamespaces[0]);
            Assert.Equal(new[] { "NS3", "NS4" }, previewNamespaces[1]);
        }
 
        [Fact]
        public void FeaturesInterceptorsNamespaces_Duplicate()
        {
            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:InterceptorsNamespaces=NS1.NS2", @"/features:InterceptorsNamespaces=NS3.NS4", "a.cs" });
            var comp = (CSharpCompilation)csc.CreateCompilation(new StringWriter(), new TouchedFileLogger(), errorLogger: null);
            var options = comp.SyntaxTrees[0].Options;
            Assert.Equal(1, options.Features.Count);
            Assert.Equal("NS3.NS4", options.Features["InterceptorsNamespaces"]);
 
            var previewNamespaces = ((CSharpParseOptions)options).InterceptorsNamespaces;
            Assert.Equal(1, previewNamespaces.Length);
            Assert.Equal(new[] { "NS3", "NS4" }, previewNamespaces[0]);
        }
 
        [Fact]
        public void FeaturesInterceptorsPreviewNamespaces_NotRecognizedInCommandLine()
        {
            // '<InterceptorsPreviewNamespaces>' is recognized in the build task and passed through as a '/features:InterceptorsNamespaces=...' argument.
            // '/features:InterceptorsPreviewNamespaces=...' is included in the Features dictionary but does not enable the interceptors feature.
            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:InterceptorsPreviewNamespaces=NS1.NS2", "a.cs" });
            var comp = (CSharpCompilation)csc.CreateCompilation(new StringWriter(), new TouchedFileLogger(), errorLogger: null);
            var options = comp.SyntaxTrees[0].Options;
 
            Assert.Equal(1, options.Features.Count);
            Assert.Equal("NS1.NS2", options.Features["InterceptorsPreviewNamespaces"]);
 
            Assert.False(options.Features.ContainsKey("InterceptorsNamespaces"));
            Assert.Empty(((CSharpParseOptions)options).InterceptorsNamespaces);
        }
 
        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: new[] { 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), 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: new[] { 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: new[] { 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: new[] { 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: new[] { 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(62540, "https://github.com/dotnet/roslyn/issues/62540")]
        [ConditionalTheory(typeof(IsEnglishLocal)), CombinatorialData]
        public void TestSuppression_CompilerSyntaxParseError_SuppressWarningCaughtDuringParsingStage(bool skipAnalyzers)
        {
            const string SourceCode = @"
                using System;
 
                class X
                {
                    public bool Select<T>(Func<int, T> selector) => true;
                    public static int operator +(Action a, X right) => 0;
                }
 
                public class PrecedenceInversionClass
                {
                    void M1()
                    {
                        var src = new X();
                        var b = false && from x in src select x; // Parsing warning -- CS8848: Operator 'from' cannot be used here due to precedence
                    }
                }
 
                public class {} // Parsing error -- CS1001: Identifier expected";
 
            var sourceDirectory = Temp.CreateDirectory();
            var sourceFile = sourceDirectory.CreateFile("BuggyCode.cs");
            sourceFile.WriteAllText(SourceCode);
 
            // During the parsing stage, both CS8848 (a warning) and CS1001 (an unsuppressible error) will be detected.
            // This test verifies that CS8848 is correctly suppressed, and that CS1001 is correctly reported.
            var precedenceInversionWarningSuppressor = new DiagnosticSuppressorForId("CS8848");
 
            // Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppression ID '{2}' and justification '{3}'
            var suppressionMessage =
                string.Format(
                    CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
                    precedenceInversionWarningSuppressor.SuppressionDescriptor.SuppressedDiagnosticId,
                    new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.WRN_PrecedenceInversion, "from"), Location.None).GetMessage(CultureInfo.InvariantCulture),
                    precedenceInversionWarningSuppressor.SuppressionDescriptor.Id,
                    precedenceInversionWarningSuppressor.SuppressionDescriptor.Justification);
 
            // CS8848 is automatically suppressed if the warning level is <5.
            // Set the warning level to 5 to ensure that it will not get automatically suppressed, and leave it up to the `precedenceInversionWarningSuppressor` to suppress it.
            var output =
                VerifyOutput(
                    sourceDirectory,
                    sourceFile,
                    additionalFlags: new[] { "/warn:5" },
                    expectedErrorCount: 1,
                    expectedInfoCount: 1,
                    expectedWarningCount: 0,
                    includeCurrentAssemblyAsAnalyzerReference: false,
                    skipAnalyzers: skipAnalyzers,
                    analyzers: new[] { precedenceInversionWarningSuppressor },
                    errorlog: true);
 
            Assert.DoesNotContain("warning CS8848", output, StringComparison.Ordinal);
 
            Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);
            Assert.Contains("info SP0001", output, StringComparison.Ordinal);
            Assert.Contains("error CS1001", output, StringComparison.Ordinal);
 
            // During the parsing stage, both CS8848 (a warning) and CS1001 (an unsuppressible error) will be detected.
            // This test verifies that CS8848 is correctly suppressed even when elevated as an error (using `warnaserror`), and that CS1001 is correctly reported.
            output =
                VerifyOutput(
                    sourceDirectory,
                    sourceFile,
                    expectedErrorCount: 1,
                    expectedInfoCount: 1,
                    expectedWarningCount: 0,
                    additionalFlags: new[] { "/warn:5", "/warnaserror" },
                    includeCurrentAssemblyAsAnalyzerReference: false,
                    skipAnalyzers: skipAnalyzers,
                    errorlog: true,
                    analyzers: new[] { precedenceInversionWarningSuppressor });
 
            Assert.DoesNotContain($"error CS8848", output, StringComparison.Ordinal);
            Assert.DoesNotContain($"warning CS8848", output, StringComparison.Ordinal);
 
            Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);
            Assert.Contains("info SP0001", output, StringComparison.Ordinal);
            Assert.Contains("error CS1001", output, StringComparison.Ordinal);
 
            CleanupAllGeneratedFiles(sourceFile.Path);
        }
 
        [WorkItem(62540, "https://github.com/dotnet/roslyn/issues/62540")]
        [ConditionalTheory(typeof(IsEnglishLocal)), CombinatorialData]
        public void TestSuppression_CompilerSyntaxDeclarationError_SuppressWarningTriggeredByGenerator(bool skipAnalyzers)
        {
            const string SourceCode = @"
                partial struct MyPartialStruct
                {
                    public int MyInt;
 
                    public void SetMyInt(int value)
                    {
                        MyInt = value;
                    }
                }
 
                public abstract class MyAbstractClass
                {
                    // error CS0180: Methods cannot be both extern and abstract -- this is a declaration error
                    public extern abstract void MyFaultyMethod()
                    {
                    }
                }";
            var sourceDirectory = Temp.CreateDirectory();
            var sourceFile = sourceDirectory.CreateFile("NotGenerated.cs");
            sourceFile.WriteAllText(SourceCode);
 
            const string GeneratedCode =
                @"// warning CS0282: Partial struct warning
                partial struct MyPartialStruct
                {
                    public bool MyBoolean;
 
                    public void SetMyBoolean(bool value)
                    {
                        MyBoolean = value;
                    }
                }";
            var partialStructGenerator = new SingleFileTestGenerator(GeneratedCode, "Generated.cs");
 
            // The generated code will trigger `CS0282`. This test verifies 3 things:
            // 1. Compiler warning `CS0282` is suppressed with diagnostic suppressor,
            // 2. Info diagnostic for the suppression is logged with programmatic suppression information,
            // 3. Compiler error `CS0180` is reported.
            var partialStructWarningSuppressor = new DiagnosticSuppressorForId("CS0282");
 
            // Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppression ID '{2}' and justification '{3}'
            var suppressionMessage =
                string.Format(
                    CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
                    partialStructWarningSuppressor.SuppressionDescriptor.SuppressedDiagnosticId,
                    new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.WRN_SequentialOnPartialClass, "MyPartialStruct"), Location.None).GetMessage(CultureInfo.InvariantCulture),
                    partialStructWarningSuppressor.SuppressionDescriptor.Id,
                    partialStructWarningSuppressor.SuppressionDescriptor.Justification);
 
            var output =
                VerifyOutput(
                    sourceDirectory,
                    sourceFile,
                    expectedErrorCount: 1,
                    expectedInfoCount: 1,
                    expectedWarningCount: 0,
                    includeCurrentAssemblyAsAnalyzerReference: false,
                    skipAnalyzers: skipAnalyzers,
                    generators: new[] { partialStructGenerator },
                    analyzers: new[] { partialStructWarningSuppressor },
                    errorlog: true);
 
            Assert.DoesNotContain("warning CS0282", output, StringComparison.Ordinal);
 
            Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);
            Assert.Contains("info SP0001", output, StringComparison.Ordinal);
            Assert.Contains("error CS0180", output, StringComparison.Ordinal);
 
            // The generated code will trigger `CS0282`. This test verifies 3 things:
            // 1. Compiler warning `CS0282` is suppressed with diagnostic suppressor even when elevated as an error (using `/warnaserror`),
            // 2. Info diagnostic for the suppression is logged with programmatic suppression information,
            // 3. Compiler error `CS0180` is reported.
            output =
                VerifyOutput(
                    sourceDirectory,
                    sourceFile,
                    expectedErrorCount: 1,
                    expectedInfoCount: 1,
                    expectedWarningCount: 0,
                    additionalFlags: new[] { "/warnaserror" },
                    includeCurrentAssemblyAsAnalyzerReference: false,
                    skipAnalyzers: skipAnalyzers,
                    generators: new[] { partialStructGenerator },
                    errorlog: true,
                    analyzers: new[] { partialStructWarningSuppressor });
 
            Assert.DoesNotContain($"error CS0282", output, StringComparison.Ordinal);
            Assert.DoesNotContain($"warning CS0282", output, StringComparison.Ordinal);
 
            Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);
            Assert.Contains("info SP0001", output, StringComparison.Ordinal);
            Assert.Contains("error CS0180", output, StringComparison.Ordinal);
 
            CleanupAllGeneratedFiles(sourceFile.Path);
        }
 
        [WorkItem(62540, "https://github.com/dotnet/roslyn/issues/62540")]
        [ConditionalTheory(typeof(IsEnglishLocal)), CombinatorialData]
        public void TestSuppression_CompilerSyntaxBindingError_SuppressWarningTriggeredByGenerator(bool skipAnalyzers)
        {
            const string SourceCode = @"
                // warning CS0282: Partial struct warning
                partial struct MyPartialStruct
                {
                    public int MyInt;
 
                    public void SetMyInt(int value)
                    {
                        MyInt = value;
                    }
                }
 
                public class MyClass
                {
                    void MyPrivateMethod()
                    {
                    }
                }
                public class YourClass
                { 
                    void YourPrivateMethod()
                    {
                        // Cannot access private method
                        new MyClass().MyPrivateMethod();
                    }
                }";
 
            var sourceDir = Temp.CreateDirectory();
            var sourceFile = sourceDir.CreateFile("NotGenerated.cs");
            sourceFile.WriteAllText(SourceCode);
 
            const string GeneratedSource =
                @"// warning CS0282: Partial struct warning
                partial struct MyPartialStruct
                {
                    public bool MyBoolean;
 
                    public void SetMyBoolean(bool value)
                    {
                        MyBoolean = value;
                    }
                }";
            var partialStructGenerator = new SingleFileTestGenerator(GeneratedSource, "Generated.cs");
 
            // The generated code will trigger `CS0282`. This test verifies 3 things:
            // 1. Compiler warning `CS0282` is suppressed with diagnostic suppressor,
            // 2. Info diagnostic for the suppression is logged with programmatic suppression information,
            // 3. Compiler error `CS1001` is reported.
            var partialStructWarningSuppressor = new DiagnosticSuppressorForId("CS0282");
 
            // Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppression ID '{2}' and justification '{3}'
            var suppressionMessage =
                string.Format(
                    CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
                    partialStructWarningSuppressor.SuppressionDescriptor.SuppressedDiagnosticId,
                    new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.WRN_SequentialOnPartialClass, "MyPartialStruct"), Location.None).GetMessage(CultureInfo.InvariantCulture),
                    partialStructWarningSuppressor.SuppressionDescriptor.Id,
                    partialStructWarningSuppressor.SuppressionDescriptor.Justification);
 
            var output =
                VerifyOutput(
                    sourceDir,
                    sourceFile,
                    expectedErrorCount: 1,
                    expectedInfoCount: 1,
                    expectedWarningCount: 0,
                    includeCurrentAssemblyAsAnalyzerReference: false,
                    skipAnalyzers: skipAnalyzers,
                    generators: new[] { partialStructGenerator },
                    analyzers: new[] { partialStructWarningSuppressor },
                    errorlog: true);
 
            Assert.DoesNotContain("warning CS0282", output, StringComparison.Ordinal);
 
            Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);
            Assert.Contains("info SP0001", output, StringComparison.Ordinal);
            Assert.Contains("error CS0122", output, StringComparison.Ordinal);
 
            // The generated code will trigger `CS0282`. This test verifies 3 things:
            // 1. Compiler warning `CS0282` is suppressed with diagnostic suppressor even when elevated as an error (using `/warnaserror`),
            // 2. Info diagnostic for the suppression is logged with programmatic suppression information,
            // 3. Compiler error `CS1001` is reported.
            output =
                VerifyOutput(
                    sourceDir,
                    sourceFile,
                    expectedErrorCount: 1,
                    expectedInfoCount: 1,
                    expectedWarningCount: 0,
                    additionalFlags: new[] { "/warnaserror" },
                    includeCurrentAssemblyAsAnalyzerReference: false,
                    skipAnalyzers: skipAnalyzers,
                    errorlog: true,
                    generators: new[] { partialStructGenerator },
                    analyzers: new[] { partialStructWarningSuppressor });
 
            Assert.DoesNotContain($"error CS0282", output, StringComparison.Ordinal);
            Assert.DoesNotContain($"warning CS0282", output, StringComparison.Ordinal);
 
            Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);
            Assert.Contains("info SP0001", output, StringComparison.Ordinal);
            Assert.Contains("error CS0122", output, StringComparison.Ordinal);
 
            CleanupAllGeneratedFiles(sourceFile.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[] { 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[] { 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: new[] { 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: new[] { 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: new[] { 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, false)]
        [InlineData(DiagnosticSeverity.Warning, false, true)]
        [InlineData(DiagnosticSeverity.Info, true, false)]
        [InlineData(DiagnosticSeverity.Info, false, false)]
        [InlineData(DiagnosticSeverity.Info, true, true)]
        [InlineData(DiagnosticSeverity.Info, false, true)]
        [InlineData(DiagnosticSeverity.Hidden, false, false)]
        [InlineData(DiagnosticSeverity.Hidden, false, true)]
        [Theory]
        public void TestBulkAnalyzerDiagnosticConfiguration(DiagnosticSeverity defaultSeverity, bool errorlog, bool customConfigurable)
        {
            var analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, defaultSeverity, customConfigurable, throwOnAllNamedTypes: false);
 
            var diagnosticId = analyzer.Descriptor.Id;
 
            // Verify bulk configuration without any diagnostic ID configuration is respected,
            // unless analyzer reports 'CustomConfigurable' diagnostics, which explicitly disables bulk configuration.
            var defaultReportDiagnostic = DiagnosticDescriptor.MapSeverityToReport(defaultSeverity);
            var expectedDiagnosticSeverity = customConfigurable ? defaultReportDiagnostic : ReportDiagnostic.Error;
            var analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.severity = error";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity);
 
            // 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.
            // If the analyzer reports 'CustomConfigurable' diagnostics, all editorconfig configurations are ignored.
            expectedDiagnosticSeverity = customConfigurable ? defaultReportDiagnostic : ReportDiagnostic.Warn;
            analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.severity = error
dotnet_diagnostic.{diagnosticId}.severity = warning";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity);
 
            // Verify bulk configuration after diagnostic ID configuration is not respected.
            // If the analyzer reports 'CustomConfigurable' diagnostics, all editorconfig configurations are ignored.
            analyzerConfigText = $@"
[*.cs]
dotnet_diagnostic.{diagnosticId}.severity = warning
dotnet_analyzer_diagnostic.severity = error";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity);
 
            // Verify bulk configuration to warning + /warnaserror reports errors.
            // If the analyzer reports 'CustomConfigurable' diagnostics, all editorconfig configurations are ignored.
            expectedDiagnosticSeverity = customConfigurable && defaultReportDiagnostic != ReportDiagnostic.Warn ? defaultReportDiagnostic : ReportDiagnostic.Error;
            analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.severity = warning";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity, warnAsError: true);
 
            // Verify disabled by default analyzer is not enabled by bulk configuration.
            // However, analyzer reporting 'CustomConfigurable' diagnostics is considered to be enabled.
            analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: false, defaultSeverity, customConfigurable, throwOnAllNamedTypes: false);
            expectedDiagnosticSeverity = customConfigurable ? defaultReportDiagnostic : ReportDiagnostic.Suppress;
            analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.severity = error";
            TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity);
 
            if (defaultSeverity == DiagnosticSeverity.Hidden ||
                defaultSeverity == DiagnosticSeverity.Info && !errorlog)
            {
                // Verify analyzer with Hidden severity OR Info severity + no /errorlog is not executed.
                // Unless the analyzer reports 'CustomConfigurable' diagnostics, in which case it is always executed.
                expectedDiagnosticSeverity = customConfigurable ? defaultReportDiagnostic : ReportDiagnostic.Suppress;
                analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, defaultSeverity, customConfigurable, throwOnAllNamedTypes: !customConfigurable);
                TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText: string.Empty, errorlog, expectedDiagnosticSeverity);
 
                // Verify that bulk configuration 'none' entry does not enable this analyzer.
                // However, analyzer reporting 'CustomConfigurable' diagnostics is considered to be enabled.
                expectedDiagnosticSeverity = customConfigurable ? defaultReportDiagnostic : ReportDiagnostic.Suppress;
                analyzerConfigText = $@"
[*.cs]
dotnet_analyzer_diagnostic.severity = none";
                TestBulkAnalyzerConfigurationCore(analyzer, analyzerConfigText, errorlog, expectedDiagnosticSeverity);
            }
        }
 
        [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: new[] { 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, customConfigurable: false);
        }
 
        [WorkItem(37876, "https://github.com/dotnet/roslyn/issues/37876")]
        [Theory]
        [CombinatorialData]
        public void AnalyzerConfigRespectedForDisabledByDefaultDiagnostic(ReportDiagnostic analyzerConfigSeverity, bool isEnabledByDefault, bool noWarn, bool errorlog, bool customConfigurable)
        {
            var analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault, defaultSeverity: DiagnosticSeverity.Warning, customConfigurable, throwOnAllNamedTypes: false);
            TestAnalyzerConfigRespectedCore(analyzer, analyzer.Descriptor, analyzerConfigSeverity, noWarn, errorlog, customConfigurable);
        }
 
        private void TestAnalyzerConfigRespectedCore(DiagnosticAnalyzer analyzer, DiagnosticDescriptor descriptor, ReportDiagnostic analyzerConfigSeverity, bool noWarn, bool errorlog, bool customConfigurable)
        {
            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()}");
 
            // Severity of 'CustomSeverityConfigurable' diagnostics should not be affected by editorconfig entries.
            if (customConfigurable)
                analyzerConfigSeverity = DiagnosticDescriptor.MapSeverityToReport(descriptor.DefaultSeverity);
 
            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: new[] { 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, bool customConfigurable)
        {
            // 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
            // However, an analyzer that reports diagnostics with "CustomSeverityConfigurable" tag should never be skipped for execution.
            var analyzerShouldBeSkipped = (defaultSeverity == DiagnosticSeverity.Hidden ||
                defaultSeverity == DiagnosticSeverity.Info && !errorlog) && !customConfigurable;
 
            // 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, customConfigurable, 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: new[] { 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, bool customConfigurable)
        {
            // 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.
            // However, an analyzer reporting diagnostics with "CustomSeverityConfigurable" tag should never be skipped for execution.
            var analyzerShouldBeSkipped = (!isEnabledByDefault ||
                defaultSeverity is DiagnosticSeverity.Hidden or DiagnosticSeverity.Info) && !customConfigurable;
 
            var analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault, defaultSeverity, customConfigurable, 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: new[] { 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 || customConfigurable && defaultSeverity is DiagnosticSeverity.Hidden or DiagnosticSeverity.Info)
            {
                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, false, DiagnosticSeverity.Info, DiagnosticSeverity.Warning, null, DiagnosticSeverity.Error)]
        [InlineData(true, false, DiagnosticSeverity.Info, DiagnosticSeverity.Warning, null, DiagnosticSeverity.Warning)]
        [InlineData(false, true, DiagnosticSeverity.Info, DiagnosticSeverity.Warning, null, DiagnosticSeverity.Error)]
        [InlineData(true, true, DiagnosticSeverity.Info, DiagnosticSeverity.Warning, null, DiagnosticSeverity.Warning)]
        // Verify '/warnaserror-:ID' prevents escalation to 'Error' when custom configured analyzer bumps severity to 'Warning'
        [InlineData(false, false, DiagnosticSeverity.Info, null, DiagnosticSeverity.Warning, DiagnosticSeverity.Error)]
        [InlineData(true, false, DiagnosticSeverity.Info, null, DiagnosticSeverity.Warning, DiagnosticSeverity.Warning)]
        [InlineData(false, true, DiagnosticSeverity.Info, null, DiagnosticSeverity.Warning, DiagnosticSeverity.Error)]
        [InlineData(true, true, DiagnosticSeverity.Info, null, DiagnosticSeverity.Warning, DiagnosticSeverity.Warning)]
        // Verify '/warnaserror-:ID' prevents escalation to 'Error' when default severity is 'Warning' and no config file or custom configured setting is specified.
        [InlineData(false, false, DiagnosticSeverity.Warning, null, null, DiagnosticSeverity.Error)]
        [InlineData(true, false, DiagnosticSeverity.Warning, null, null, DiagnosticSeverity.Warning)]
        [InlineData(false, true, DiagnosticSeverity.Warning, null, null, DiagnosticSeverity.Error)]
        [InlineData(true, true, DiagnosticSeverity.Warning, null, null, DiagnosticSeverity.Warning)]
        // Verify '/warnaserror-:ID' prevents escalation to 'Error' when default severity is 'Warning' and config file bumps severity to 'Error'
        [InlineData(false, false, DiagnosticSeverity.Warning, DiagnosticSeverity.Error, null, DiagnosticSeverity.Error)]
        [InlineData(true, false, DiagnosticSeverity.Warning, DiagnosticSeverity.Error, null, DiagnosticSeverity.Warning)]
        [InlineData(false, true, DiagnosticSeverity.Warning, DiagnosticSeverity.Error, null, DiagnosticSeverity.Error)]
        [InlineData(true, true, DiagnosticSeverity.Warning, DiagnosticSeverity.Error, null, DiagnosticSeverity.Warning)]
        // Verify '/warnaserror-:ID' has no effect when default severity is 'Info' and config file bumps severity to 'Error'
        [InlineData(false, false, DiagnosticSeverity.Info, DiagnosticSeverity.Error, null, DiagnosticSeverity.Error)]
        [InlineData(true, false, DiagnosticSeverity.Info, DiagnosticSeverity.Error, null, DiagnosticSeverity.Error)]
        [InlineData(false, true, DiagnosticSeverity.Info, DiagnosticSeverity.Error, null, DiagnosticSeverity.Error)]
        [InlineData(true, true, DiagnosticSeverity.Info, DiagnosticSeverity.Error, null, DiagnosticSeverity.Error)]
        // Verify '/warnaserror-:ID' has no effect when default severity is 'Info' or 'Warning' and custom configured severity is 'Error'
        [InlineData(false, false, DiagnosticSeverity.Info, null, DiagnosticSeverity.Error, DiagnosticSeverity.Error)]
        [InlineData(true, false, DiagnosticSeverity.Info, null, DiagnosticSeverity.Error, DiagnosticSeverity.Error)]
        [InlineData(false, true, DiagnosticSeverity.Info, null, DiagnosticSeverity.Error, DiagnosticSeverity.Error)]
        [InlineData(true, true, DiagnosticSeverity.Info, null, DiagnosticSeverity.Error, DiagnosticSeverity.Error)]
        [InlineData(false, false, DiagnosticSeverity.Warning, null, DiagnosticSeverity.Error, DiagnosticSeverity.Error)]
        [InlineData(true, false, DiagnosticSeverity.Warning, null, DiagnosticSeverity.Error, DiagnosticSeverity.Error)]
        [InlineData(false, true, DiagnosticSeverity.Warning, null, DiagnosticSeverity.Error, DiagnosticSeverity.Error)]
        [InlineData(true, true, DiagnosticSeverity.Warning, null, DiagnosticSeverity.Error, DiagnosticSeverity.Error)]
        public void TestWarnAsErrorMinusDoesNotNullifyEditorConfig(
            bool warnAsErrorMinus,
            bool useGlobalConfig,
            DiagnosticSeverity defaultSeverity,
            DiagnosticSeverity? severityInConfigFile,
            DiagnosticSeverity? customConfiguredSeverityByAnalyzer,
            DiagnosticSeverity expectedEffectiveSeverity)
        {
            var customConfigurable = customConfiguredSeverityByAnalyzer.HasValue;
            var reportedSeverity = customConfiguredSeverityByAnalyzer ?? defaultSeverity;
            var analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, defaultSeverity, reportedSeverity, customConfigurable, 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();
 
                TempFile analyzerConfig;
                if (useGlobalConfig)
                {
                    analyzerConfig = dir.CreateFile(".globalconfig").WriteAllText($@"
is_global = true
dotnet_diagnostic.{diagnosticId}.severity = {severityString}");
                }
                else
                {
                    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(dir.Path, 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(dir.Path, 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(dir.Path, generator);
            var generator2Prefix = GeneratorDriver.GetFilePathPrefixForGenerator(dir.Path, 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_ChecksumAlgorithm()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs");
            src.WriteAllText("class C { }");
 
            var genPath1 = Path.Combine(dir.Path, "Microsoft.CodeAnalysis.Test.Utilities", "Roslyn.Test.Utilities.TestGenerators.TestSourceGenerator", "hint1.cs");
            var genPath2 = Path.Combine(dir.Path, "Microsoft.CodeAnalysis.Test.Utilities", "Roslyn.Test.Utilities.TestGenerators.TestSourceGenerator", "hint2.cs");
 
            var generator = new TestSourceGenerator()
            {
                ExecuteImpl = context =>
                {
                    context.AddSource("hint1", "class G1 { void F() {} }");
                    context.AddSource("hint2", SourceText.From("class G2 { void F() {} }", Encoding.UTF8, checksumAlgorithm: SourceHashAlgorithm.Sha1));
                }
            };
 
            VerifyOutput(
                dir,
                src,
                includeCurrentAssemblyAsAnalyzerReference: false,
                additionalFlags: new[] { "/langversion:preview", "/out:checksum.exe", "/pdb:checksum.pdb", "/debug:portable", "/checksumAlgorithm:SHA256" },
                generators: new[] { generator },
                analyzers: null);
 
            using (Stream peStream = File.OpenRead(Path.Combine(dir.Path, "checksum.exe")), pdbStream = File.OpenRead(Path.Combine(dir.Path, "checksum.pdb")))
            {
                // TOOD: /checksumAlgorithm setting should override any checksum algorithms set by the generators:
                PdbValidation.VerifyPdb(peStream, pdbStream, $@"
<symbols>
  <files>
    <file id=""1"" name=""{src.Path}"" language=""C#"" checksumAlgorithm=""SHA256"" checksum=""A0-78-BB-A8-E8-B1-E1-3B-E8-63-80-7D-CE-CC-4B-0D-14-EF-06-D3-9B-14-52-E1-95-C6-64-D1-36-EC-7C-25"" />
    <file id=""2"" name=""{genPath1}"" language=""C#"" checksumAlgorithm=""SHA1"" checksum=""D8-87-89-A3-FE-EA-FD-AB-49-31-5A-25-B0-05-6B-6F-00-00-C2-DD""><![CDATA[class G1 {{ void F() {{}} }}]]></file>
    <file id=""3"" name=""{genPath2}"" language=""C#"" checksumAlgorithm=""SHA1"" checksum=""F1-D0-FD-F0-08-9F-1B-32-9F-EF-41-A1-58-A3-14-FF-E8-06-A8-38""><![CDATA[class G2 {{ void F() {{}} }}]]></file>
  </files>
</symbols>", PdbValidationOptions.ExcludeMethods);
            }
 
            Directory.Delete(dir.Path, true);
        }
 
        [Theory]
        [InlineData("generatedSource.cs", "", "generatedSource.cs")]
        [InlineData("..", "", "...cs")]
        [InlineData(".", "", "..cs")]
        [InlineData("abc/", "abc", ".cs")]
        [InlineData("abc\\", "abc", ".cs")]
        [InlineData("abc/ ", "abc", " .cs")]
        [InlineData("a/b/c", "a/b", "c.cs")]
        [InlineData("a/b\\c", "a/b", "c.cs")]
        [InlineData("a\\b\\c", "a/b", "c.cs")]
        [InlineData(" abc ", "", " abc .cs")]
        [InlineData(" abc/generated.cs", " abc", "generated.cs")]
        [InlineData(" abc\\generated.cs", " abc", "generated.cs")]
        [InlineData(" a/ b/ generated.cs", " a/ b", " generated.cs")]
        [InlineData(" a\\ b\\ generated.cs", " a/ b", " generated.cs")]
        public void SourceGenerators_WriteGeneratedSources(string hintName, string expectedDir, string expectedFileName)
        {
            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, hintName);
 
            VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/generatedfilesout:" + generatedDir.Path, "/langversion:preview", "/out:embed.exe" }, generators: new[] { generator }, analyzers: null);
 
            var generatorPrefix = GeneratorDriver.GetFilePathPrefixForGenerator(generatedDir.Path, generator);
            ValidateWrittenSources(new()
            {
                { Path.Combine(generatedDir.Path, generatorPrefix, expectedDir), new() { { expectedFileName, 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(generatedDir.Path, 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(generatedDir.Path, generator);
            var generator2Prefix = GeneratorDriver.GetFilePathPrefixForGenerator(generatedDir.Path, generator2);
 
            ValidateWrittenSources(new()
            {
                { generator1Prefix, new() { { source1Name, source1 } } },
                { generator2Prefix, new() { { source2Name, source2 } } }
            });
 
            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);
        }
 
        [Theory]
        [InlineData("subdir")]
        [InlineData("a/b/c")]
        [InlineData("a\\b\\c", "a/b/c")]
        [InlineData(" subdir")]
        [InlineData(" a/ b/ c")]
        [InlineData(" a\\ b/ c", " a/ b/ c")]
        [InlineData("abc/")]
        public void SourceGenerators_WriteGeneratedSources_WithDirectories(string subdir, string expectedDir = null)
        {
            expectedDir ??= subdir;
 
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText("""
                class C
                {
                }
                """);
            var generatedDir = dir.CreateDirectory("generated");
 
            var generatedSource = "public class D { }";
            var generatedFileName = "generatedSource.cs";
            var generatedPath = Path.Combine(subdir, generatedFileName);
            var generator = new SingleFileTestGenerator(generatedSource, generatedPath);
 
            VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/generatedfilesout:" + generatedDir.Path, "/langversion:preview", "/out:embed.exe" }, generators: new[] { generator }, analyzers: null);
 
            var generatorPrefix = GeneratorDriver.GetFilePathPrefixForGenerator(generatedDir.Path, generator);
            ValidateWrittenSources(new()
            {
                { Path.Combine(generatedDir.Path, generatorPrefix, expectedDir), new() { { generatedFileName, generatedSource } } }
            });
 
            // 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(generatedDir.Path, 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]
        public void Interceptors_RelativePath_GeneratedFiles_EndToEnd()
        {
            var dir = Temp.CreateDirectory();
 
            var srcDir = dir.CreateDirectory("src");
            var src = srcDir.CreateFile("Program.cs").WriteAllText("""
                class Program
                {
                    static void Main()
                    {
                        M();
                    }
 
                    public static void M() => throw null!;
                }
                """);
 
            // final path will look like:
            // 'TempDir/{assemblyName}/{generatorName}/Generated.cs'
            // Note that generator will have access to the full path of the generated file, before adding it to the compilation
            // additionally we plan to add public API to determine a "correct relative path" to use here without any additional tricks
            var generatedSource = """
                using System.Runtime.CompilerServices;
                using System;
 
                namespace Generated
                {
                    static class Interceptors
                    {
                        [InterceptsLocation("../../src/Program.cs", 5, 9)]
                        public static void M1() => Console.Write(1);
                    }
                }
 
                namespace System.Runtime.CompilerServices
                {
                    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
                    public sealed class InterceptsLocationAttribute : Attribute
                    {
                        public InterceptsLocationAttribute(string filePath, int line, int character)
                        {
                        }
                    }
                }
                """;
            var generator = new SingleFileTestGenerator(generatedSource, "Generated.cs");
 
            // Future note: it would be good to have end-to-end tests using InterceptableLocation APIs.
            // Once support for path-based attributes is fully dropped, consider porting these 'Interceptors_' tests accordingly.
            VerifyOutput(
                dir,
                src,
                includeCurrentAssemblyAsAnalyzerReference: false,
                additionalFlags: ["/langversion:preview",
                    "/out:embed.exe",
                    "/features:InterceptorsNamespaces=Generated"],
                expectedWarningCount: 1,
                generators: [generator],
                analyzers: null);
            ValidateWrittenSources([]);
 
            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);
        }
 
        [Fact]
        public void Interceptors_RelativePath_GeneratedFiles_EndToEnd_OutputDirectoryNested()
        {
            // Demonstrates the difference between defaulting the generated files base path to 'Arguments.OutputDirectory'
            // versus 'Arguments.BaseDirectory' (which occurs implicitly for relative paths in command line arguments)
 
            var dir = Temp.CreateDirectory();
            var srcDir = dir.CreateDirectory("src");
            var src = srcDir.CreateFile("Program.cs").WriteAllText("""
                class Program
                {
                    static void Main()
                    {
                        M();
                    }
 
                    public static void M() => throw null!;
                }
                """);
 
            // final path will look like:
            // 'TempDir/obj/{assemblyName}/{generatorName}/Generated.cs'
            // Note that generator will have access to the full path of the generated file, before adding it to the compilation
            // additionally we plan to add public API to determine a "correct relative path" to use here without any additional tricks
            var generatedSource = """
                using System.Runtime.CompilerServices;
                using System;
 
                namespace Generated
                {
                    static class Interceptors
                    {
                        [InterceptsLocation("../../../src/Program.cs", 5, 9)]
                        public static void M1() => Console.Write(1);
                    }
                }
 
                namespace System.Runtime.CompilerServices
                {
                    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
                    public sealed class InterceptsLocationAttribute : Attribute
                    {
                        public InterceptsLocationAttribute(string filePath, int line, int character)
                        {
                        }
                    }
                }
                """;
            var generator = new SingleFileTestGenerator(generatedSource, "Generated.cs");
            var objDir = dir.CreateDirectory("obj");
 
            VerifyOutput(
                dir,
                src,
                includeCurrentAssemblyAsAnalyzerReference: false,
                additionalFlags: ["/langversion:preview",
                    $"/out:{objDir.Path}/embed.exe",
                    "/features:InterceptorsNamespaces=Generated"],
                expectedWarningCount: 1,
                generators: [generator],
                analyzers: null);
            ValidateWrittenSources([]);
 
            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);
        }
 
        [Theory]
        [InlineData("")]
        [InlineData($"/pathmap:DIRPATH=/_/")]
        [InlineData($"/pathmap:SRCDIRPATH=a/,OBJDIRPATH=b/")]
        public void Interceptors_RelativePath_GeneratedFiles_EndToEnd_GeneratedFilesOutSpecified(string pathMapArgument)
        {
            var dir = Temp.CreateDirectory();
 
            var srcDir = dir.CreateDirectory("src");
            var src = srcDir.CreateFile("Program.cs").WriteAllText("""
                class Program
                {
                    static void Main()
                    {
                        M();
                    }
 
                    public static void M() => throw null!;
                }
                """);
 
            var objDir = dir.CreateDirectory("obj");
 
            // final path will look like:
            // 'TempDir/obj/{assemblyName}/{generatorName}/Generated.cs'
            // Note that generator will have access to the full path of the generated file, before adding it to the compilation
            // additionally we plan to add public API to determine a "correct relative path" to use here without any additional tricks
            var generatedSource = """
                using System.Runtime.CompilerServices;
                using System;
 
                namespace Generated
                {
                    static class Interceptors
                    {
                        [InterceptsLocation("../../../src/Program.cs", 5, 9)]
                        public static void M1() => Console.Write(1);
                    }
                }
 
                namespace System.Runtime.CompilerServices
                {
                    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
                    public sealed class InterceptsLocationAttribute : Attribute
                    {
                        public InterceptsLocationAttribute(string filePath, int line, int character)
                        {
                        }
                    }
                }
                """;
            var generator = new SingleFileTestGenerator(generatedSource, "Generated.cs");
 
            pathMapArgument = pathMapArgument.Replace("DIRPATH", dir.Path).Replace("SRCDIRPATH", srcDir.Path).Replace("OBJDIRPATH", objDir.Path);
            VerifyOutput(
                dir,
                src,
                includeCurrentAssemblyAsAnalyzerReference: false,
                additionalFlags: [
                    "/generatedfilesout:" + objDir.Path,
                    "/langversion:preview",
                    "/out:embed.exe",
                    "/features:InterceptorsNamespaces=Generated",
                    .. string.IsNullOrEmpty(pathMapArgument) ? default(Span<string>) : [pathMapArgument]
                    ],
                expectedWarningCount: 1,
                generators: [generator],
                analyzers: null);
 
            var generatorPrefix = GeneratorDriver.GetFilePathPrefixForGenerator(objDir.Path, generator);
            ValidateWrittenSources(new()
            {
                { generatorPrefix, new() { { "Generated.cs", generatedSource } } },
            });
 
            // 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: new[] { 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 _));
        }
 
        internal class FailsExecuteGenerator : ISourceGenerator
        {
            public void Initialize(GeneratorInitializationContext context) { }
            public void Execute(GeneratorExecutionContext context) => throw new System.Exception("THROW");
        }
 
        [Fact, WorkItem(65313, "https://github.com/dotnet/roslyn/issues/65313")]
        public void FailedGeneratorExecuteWarning_AsError()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText("""
class C
{
}
""");
 
            var cmd = CreateCSharpCompiler(null, dir.Path,
                new[] { "/t:library", "/nologo", "/warnaserror+", src.Path },
                generators: new[] { new FailsExecuteGenerator() });
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.StartsWith($"error CS8785: Generator 'FailsExecuteGenerator' failed to generate source. It will not contribute to the output and compilation errors may occur as a result. Exception was of type 'Exception' with message 'THROW'",
                outWriter.ToString());
        }
 
        [Fact, WorkItem(65313, "https://github.com/dotnet/roslyn/issues/65313")]
        public void FailedGeneratorExecuteWarning_Suppressed()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText("""
class C
{
}
""");
 
            var cmd = CreateCSharpCompiler(null, dir.Path,
                new[] { "/t:library", "/nologo", "/nowarn:CS8785", src.Path },
                generators: new[] { new FailsExecuteGenerator() });
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString());
        }
 
        internal class FailsInitializeGenerator : ISourceGenerator
        {
            public void Initialize(GeneratorInitializationContext context) => throw new System.Exception("THROW");
            public void Execute(GeneratorExecutionContext context) { }
        }
 
        [Fact, WorkItem(65313, "https://github.com/dotnet/roslyn/issues/65313")]
        public void FailedGeneratorInitializeWarning_AsError()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText("""
class C
{
}
""");
 
            var cmd = CreateCSharpCompiler(null, dir.Path,
                new[] { "/t:library", "/nologo", "/warnaserror+", src.Path },
                generators: new[] { new FailsInitializeGenerator() });
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.StartsWith($"error CS8784: Generator 'FailsInitializeGenerator' failed to initialize. It will not contribute to the output and compilation errors may occur as a result. Exception was of type 'Exception' with message 'THROW'",
                outWriter.ToString());
        }
 
        [Fact, WorkItem(65313, "https://github.com/dotnet/roslyn/issues/65313")]
        public void FailedGeneratorInitializeWarning_Suppressed()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText("""
class C
{
}
""");
 
            var cmd = CreateCSharpCompiler(null, dir.Path,
                new[] { "/t:library", "/nologo", "/nowarn:CS8784", src.Path },
                generators: new[] { new FailsInitializeGenerator() });
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString());
        }
 
        [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);
        }
 
        [Fact]
        [WorkItem(59209, "https://github.com/dotnet/roslyn/issues/59209")]
        public void SourceGenerators_Binary_Additional_File()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
 
            var additionalFile = dir.CreateFile("temp.bin").WriteAllBytes(TestResources.NetFX.Minimal.mincorlib);
 
            var generatedSource = "public class D { }";
            var generator = new SingleFileTestGenerator(generatedSource, "generatedSource.cs");
 
            VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/additionalfile:" + additionalFile.Path, "/langversion:preview", "/out:embed.exe" }, generators: new[] { generator }, analyzers: null);
 
            // Clean up temp files
            CleanupAllGeneratedFiles(src.Path);
            Directory.Delete(dir.Path, true);
        }
 
        [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: new[] { 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[] { new WarningDiagnosticAnalyzer() });
 
            VerifyOutput(dir, src, additionalFlags: new[] { "/nowarn:Warning01", "/analyzerconfig:" + globalConfig.Path }, includeCurrentAssemblyAsAnalyzerReference: false, analyzers: new[] { 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: new[] { 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);
 
        [Fact, WorkItem(1434159, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1434159")]
        public void CanSuppressAnalyzerLoadWarning()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText(@"
class C {} 
");
            var notAnalyzer = dir.CreateFile("random.txt");
 
            // not suppresssed
            var output = VerifyOutput(dir, src, additionalFlags: new[] { "/analyzer:" + notAnalyzer.Path }, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
            Assert.Contains("warning CS8034", output, StringComparison.Ordinal);
 
            // supressed
            VerifyOutput(dir, src, additionalFlags: new[] { "/analyzer:" + notAnalyzer.Path, "/nowarn:CS8034" }, expectedWarningCount: 0, includeCurrentAssemblyAsAnalyzerReference: false);
 
            // elevated
            output = VerifyOutput(dir, src, additionalFlags: new[] { "/analyzer:" + notAnalyzer.Path, "/warnAsError:CS8034" }, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
            Assert.Contains("error CS8034", output, StringComparison.Ordinal);
        }
 
        [Fact, WorkItem(1434159, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1434159")]
        public void GlobalAnalyzerConfigCanSuppressAnalyzerLoadWarning()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText(@"
class C {} 
");
            var globalconfig = dir.CreateFile(".globalconfig").WriteAllText(@"
dotnet_diagnostic.CS8034.severity = none
");
            var notAnalyzer = dir.CreateFile("random.txt");
 
            // not suppresssed
            var output = VerifyOutput(dir, src, additionalFlags: new[] { "/analyzer:" + notAnalyzer.Path }, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
            Assert.Contains("warning CS8034", output, StringComparison.Ordinal);
 
            // suppresssed via global analyzer config
            VerifyOutput(dir, src, additionalFlags: new[] { "/analyzer:" + notAnalyzer.Path, "/analyzerConfig:" + globalconfig.Path }, includeCurrentAssemblyAsAnalyzerReference: false);
        }
 
        [Fact]
        public void ExperimentalAttribute_SuppressedWithEditorConfig()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText("""
C.M();
 
namespace System.Diagnostics.CodeAnalysis
{
    [AttributeUsage(AttributeTargets.All, Inherited = false)]
    public sealed class ExperimentalAttribute : Attribute
    {
        public ExperimentalAttribute(string diagnosticId) { }
 
        public string UrlFormat { get; set; }
    }
}
 
[System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
public class C
{
    public static void M() { }
}
""");
 
            var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText("""
[*.cs]
dotnet_diagnostic.DiagID1.severity = none
""");
            // Without editorconfig
            var cmd = CreateCSharpCompiler(null, dir.Path, new[] {
                "/nologo",
                "/t:exe",
                "/preferreduilang:en",
                src.Path });
 
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(1, exitCode);
 
            // With editorconfig
            cmd = CreateCSharpCompiler(null, dir.Path, new[] {
                "/nologo",
                "/t:exe",
                "/preferreduilang:en",
                "/analyzerconfig:" + analyzerConfig.Path,
                src.Path });
 
            Assert.Equal(analyzerConfig.Path, Assert.Single(cmd.Arguments.AnalyzerConfigPaths));
 
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString());
        }
 
        [Fact]
        public void ExperimentalAttribute_SuppressedWithSpecificNoWarn()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText("""
C.M();
 
namespace System.Diagnostics.CodeAnalysis
{
    [AttributeUsage(AttributeTargets.All, Inherited = false)]
    public sealed class ExperimentalAttribute : Attribute
    {
        public ExperimentalAttribute(string diagnosticId) { }
 
        public string UrlFormat { get; set; }
    }
}
 
[System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
public class C
{
    public static void M() { }
}
""");
 
            // Without nowarn
            var cmd = CreateCSharpCompiler(null, dir.Path, new[] {
               "/nologo",
               "/t:exe",
               "/preferreduilang:en",
               src.Path });
 
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(1, exitCode);
 
            // With nowarn
            cmd = CreateCSharpCompiler(null, dir.Path, new[] {
               "/nologo",
               "/t:exe",
               "/preferreduilang:en",
               "/nowarn:DiagID1",
               src.Path });
 
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
        }
 
        [Fact]
        public void ExperimentalAttribute_SuppressedWithGlobalNoWarn()
        {
            var src = """
C.M();
 
namespace System.Diagnostics.CodeAnalysis
{
    [AttributeUsage(AttributeTargets.All, Inherited = false)]
    public sealed class ExperimentalAttribute : Attribute
    {
        public ExperimentalAttribute(string diagnosticId) { }
 
        public string UrlFormat { get; set; }
    }
}
 
[System.Diagnostics.CodeAnalysis.Experimental("DiagID1")]
public class C
{
    public static void M() { }
}
""";
 
            var comp = CreateCompilation(src, options: TestOptions.DebugExe.WithGeneralDiagnosticOption(ReportDiagnostic.Suppress));
            comp.VerifyDiagnostics();
        }
 
        [Fact]
        public void ExperimentalAttributeWithMessage_SuppressedWithEditorConfig()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText("""
C.M();
 
namespace System.Diagnostics.CodeAnalysis
{
    [AttributeUsage(AttributeTargets.All, Inherited = false)]
    public sealed class ExperimentalAttribute : Attribute
    {
        public ExperimentalAttribute(string diagnosticId) { }
 
        public string UrlFormat { get; set; }
        public string Message { get; set; }
    }
}
 
[System.Diagnostics.CodeAnalysis.Experimental("DiagID1", Message = "use CCC")]
public class C
{
    public static void M() { }
}
""");
 
            var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText("""
[*.cs]
dotnet_diagnostic.DiagID1.severity = none
""");
            // Without editorconfig
            var cmd = CreateCSharpCompiler(null, dir.Path, new[] {
                "/nologo",
                "/t:exe",
                "/preferreduilang:en",
                src.Path });
 
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(1, exitCode);
 
            // With editorconfig
            cmd = CreateCSharpCompiler(null, dir.Path, new[] {
                "/nologo",
                "/t:exe",
                "/preferreduilang:en",
                "/analyzerconfig:" + analyzerConfig.Path,
                src.Path });
 
            Assert.Equal(analyzerConfig.Path, Assert.Single(cmd.Arguments.AnalyzerConfigPaths));
 
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.Equal("", outWriter.ToString());
        }
 
        [Fact]
        public void ExperimentalAttributeWithMessage_SuppressedWithSpecificNoWarn()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText("""
C.M();
 
namespace System.Diagnostics.CodeAnalysis
{
    [AttributeUsage(AttributeTargets.All, Inherited = false)]
    public sealed class ExperimentalAttribute : Attribute
    {
        public ExperimentalAttribute(string diagnosticId) { }
 
        public string UrlFormat { get; set; }
        public string Message { get; set; }
    }
}
 
[System.Diagnostics.CodeAnalysis.Experimental("DiagID1", Message = "use CCC")]
public class C
{
    public static void M() { }
}
""");
 
            // Without nowarn
            var cmd = CreateCSharpCompiler(null, dir.Path, new[] {
               "/nologo",
               "/t:exe",
               "/preferreduilang:en",
               src.Path });
 
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(1, exitCode);
 
            // With nowarn
            cmd = CreateCSharpCompiler(null, dir.Path, new[] {
               "/nologo",
               "/t:exe",
               "/preferreduilang:en",
               "/nowarn:DiagID1",
               src.Path });
 
            outWriter = new StringWriter(CultureInfo.InvariantCulture);
            exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
        }
 
        [Fact]
        public void ExperimentalAttributeWithMessage_SuppressedWithGlobalNoWarn()
        {
            var src = """
C.M();
 
namespace System.Diagnostics.CodeAnalysis
{
    [AttributeUsage(AttributeTargets.All, Inherited = false)]
    public sealed class ExperimentalAttribute : Attribute
    {
        public ExperimentalAttribute(string diagnosticId) { }
 
        public string UrlFormat { get; set; }
        public string Message { get; set; }
    }
}
 
[System.Diagnostics.CodeAnalysis.Experimental("DiagID1", Message = "use CCC")]
public class C
{
    public static void M() { }
}
""";
 
            var comp = CreateCompilation(src, options: TestOptions.DebugExe.WithGeneralDiagnosticOption(ReportDiagnostic.Suppress));
            comp.VerifyDiagnostics();
        }
 
        [Fact]
        public void ExperimentalWithWhitespaceDiagnosticID_WarnForInvalidDiagID()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText("""
C.M();
 
[System.Diagnostics.CodeAnalysis.Experimental(" ")]
public class C
{
    public static void M() { }
}
 
namespace System.Diagnostics.CodeAnalysis
{
    public sealed class ExperimentalAttribute : Attribute
    {
        public ExperimentalAttribute(string diagnosticId) { }
    }
}
""");
            var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText("""
[*.cs]
dotnet_diagnostic.CS9211.severity = warning
""");
            Assert.Equal((ErrorCode)9211, ErrorCode.ERR_InvalidExperimentalDiagID);
            var cmd = CreateCSharpCompiler(null, dir.Path, new[] {
                "/nologo",
                "/t:exe",
                "/preferreduilang:en",
                "/analyzerconfig:" + analyzerConfig.Path,
                src.Path });
 
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.StartsWith("test.cs(3,47): error CS9211: The diagnosticId argument to the 'Experimental' attribute must be a valid identifier",
                outWriter.ToString());
        }
 
        [Fact]
        public void ExperimentalWithValidDiagnosticID_WarnForDiagID()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText("""
C.M();
 
[System.Diagnostics.CodeAnalysis.Experimental("DiagID")]
public class C
{
    public static void M() { }
}
 
namespace System.Diagnostics.CodeAnalysis
{
    public sealed class ExperimentalAttribute : Attribute
    {
        public ExperimentalAttribute(string diagnosticId) { }
    }
}
""");
            var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText("""
[*.cs]
dotnet_diagnostic.DiagID.severity = warning
""");
            var cmd = CreateCSharpCompiler(null, dir.Path, new[] {
                "/nologo",
                "/t:exe",
                "/preferreduilang:en",
                "/analyzerconfig:" + analyzerConfig.Path,
                src.Path });
 
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(0, exitCode);
            Assert.StartsWith("test.cs(1,1): warning DiagID: 'C' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.",
                outWriter.ToString());
        }
 
        [Fact]
        public void ExperimentalWithValidDiagnosticID_WarnForExperimental()
        {
            var dir = Temp.CreateDirectory();
            var src = dir.CreateFile("test.cs").WriteAllText("""
C.M();
 
[System.Diagnostics.CodeAnalysis.Experimental("DiagID")]
public class C
{
    public static void M() { }
}
 
namespace System.Diagnostics.CodeAnalysis
{
    public sealed class ExperimentalAttribute : Attribute
    {
        public ExperimentalAttribute(string diagnosticId) { }
    }
}
""");
            var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText("""
[*.cs]
dotnet_diagnostic.CS9204.severity = warning
""");
            Assert.Equal((ErrorCode)9204, ErrorCode.WRN_Experimental);
            var cmd = CreateCSharpCompiler(null, dir.Path, new[] {
                "/nologo",
                "/t:exe",
                "/preferreduilang:en",
                "/analyzerconfig:" + analyzerConfig.Path,
                src.Path });
 
            var outWriter = new StringWriter(CultureInfo.InvariantCulture);
            var exitCode = cmd.Run(outWriter);
            Assert.Equal(1, exitCode);
            Assert.StartsWith("test.cs(1,1): error DiagID: 'C' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.",
                outWriter.ToString());
        }
    }
 
    [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);
        }
    }
 
    internal class WarningWithUrlDiagnosticAnalyzer : CompilationStartedAnalyzer
    {
        internal static readonly DiagnosticDescriptor Warning02 = new DiagnosticDescriptor("Warning02", "", "Throwing a diagnostic for types declared", "", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: "https://example.org/analyzer");
 
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Warning02);
 
        public override void CreateAnalyzerWithinCompilation(CompilationStartAnalysisContext context)
        {
            context.RegisterSymbolAction(
                static (symbolContext) =>
                {
                    symbolContext.ReportDiagnostic(Diagnostic.Create(Warning02, 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
                );
        }
    }
}