diff --git a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs index 2e9e88d8df1ed..d063ba7b9ceb4 100644 --- a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs +++ b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs @@ -97,7 +97,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar bool? delaySignSetting = null; string? keyFileSetting = null; string? keyContainerSetting = null; - List managedResources = new List(); + List managedResources = new List(); List sourceFiles = new List(); List additionalFiles = new List(); var analyzerConfigPaths = ArrayBuilder.GetInstance(); @@ -742,8 +742,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar break; // Dev11 reports unrecognized option } - var embeddedResource = ParseResourceDescription(arg, valueMemory.Value, baseDirectory, diagnostics, embedded: true); - if (embeddedResource != null) + if (TryParseResourceDescription(arg, valueMemory.Value, baseDirectory, diagnostics, isEmbedded: true, out var embeddedResource)) { managedResources.Add(embeddedResource); resourcesOrModulesSpecified = true; @@ -758,8 +757,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar break; // Dev11 reports unrecognized option } - var linkedResource = ParseResourceDescription(arg, valueMemory.Value, baseDirectory, diagnostics, embedded: false); - if (linkedResource != null) + if (TryParseResourceDescription(arg, valueMemory.Value, baseDirectory, diagnostics, isEmbedded: false, out var linkedResource)) { managedResources.Add(linkedResource); resourcesOrModulesSpecified = true; @@ -1580,7 +1578,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar DisplayHelp = displayHelp, DisplayVersion = displayVersion, DisplayLangVersions = displayLangVersions, - ManifestResources = managedResources.AsImmutable(), + ManifestResourceArguments = managedResources.AsImmutable(), CompilationOptions = options, ParseOptions = parseOptions, EmitOptions = emitOptions, @@ -2015,78 +2013,51 @@ private static IEnumerable ParseInstrumentationKinds(string } } - internal static ResourceDescription? ParseResourceDescription( - string arg, - string resourceDescriptor, - string? baseDirectory, - IList diagnostics, - bool embedded) => - ParseResourceDescription(arg, resourceDescriptor.AsMemory(), baseDirectory, diagnostics, embedded); - - internal static ResourceDescription? ParseResourceDescription( - string arg, + internal static bool TryParseResourceDescription( + string argName, ReadOnlyMemory resourceDescriptor, string? baseDirectory, IList diagnostics, - bool embedded) + bool isEmbedded, + out CommandLineResource resource) { - string? filePath; - string? fullPath; - string? fileName; - string? resourceName; - string? accessibility; - - ParseResourceDescription( + if (!TryParseResourceDescription( resourceDescriptor, baseDirectory, - false, - out filePath, - out fullPath, - out fileName, - out resourceName, - out accessibility); - - bool isPublic; - if (accessibility == null) - { - // If no accessibility is given, we default to "public". - // NOTE: Dev10 distinguishes between null and empty/whitespace-only. - isPublic = true; - } - else if (string.Equals(accessibility, "public", StringComparison.OrdinalIgnoreCase)) + skipLeadingSeparators: false, + allowEmptyAccessibility: false, + out var filePath, + out var fullPath, + out var fileName, + out var resourceName, + out var isPublic, + out var rawAccessibility)) { - isPublic = true; - } - else if (string.Equals(accessibility, "private", StringComparison.OrdinalIgnoreCase)) - { - isPublic = false; - } - else - { - AddDiagnostic(diagnostics, ErrorCode.ERR_BadResourceVis, accessibility); - return null; - } + if (isPublic == null) + { + AddDiagnostic(diagnostics, ErrorCode.ERR_BadResourceVis, rawAccessibility ?? ""); + } + else if (RoslynString.IsNullOrWhiteSpace(filePath)) + { + AddDiagnostic(diagnostics, ErrorCode.ERR_NoFileSpec, argName); + } + else + { + Debug.Assert(!PathUtilities.IsValidFilePath(fullPath)); + AddDiagnostic(diagnostics, ErrorCode.FTL_InvalidInputFileName, filePath); + } - if (RoslynString.IsNullOrWhiteSpace(filePath)) - { - AddDiagnostic(diagnostics, ErrorCode.ERR_NoFileSpec, arg); - return null; + resource = default; + return false; } - Debug.Assert(!resourceName.IsEmpty()); // see ParseResourceDescription's check on filePath - if (!PathUtilities.IsValidFilePath(fullPath)) - { - AddDiagnostic(diagnostics, ErrorCode.FTL_InvalidInputFileName, filePath); - return null; - } + resource = new CommandLineResource( + resourceName: resourceName, + fullPath: fullPath, + linkedResourceFileName: isEmbedded ? null : fileName, + isPublic.Value); - Func dataProvider = () => - { - // Use FileShare.ReadWrite because the file could be opened by the current process. - // For example, it is an XML doc file produced by the build. - return new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - }; - return new ResourceDescription(resourceName, fileName, dataProvider, isPublic, embedded, checkArgs: false); + return true; } private static void ParseWarnings(ReadOnlyMemory value, ArrayBuilder ids) diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 8b56952f07c83..e5fb9a3c65b75 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -913,210 +913,196 @@ public void Win32ResQuotes() } [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30289")] - public void ParseResources() + public void TryParseResourceDescription() { var diags = new List(); + CommandLineResource resource; - ResourceDescription desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar", WorkingDirectory, diags, embedded: false); + Assert.True(TryParse(@"\somepath\someFile.goo.bar", out resource)); Assert.Equal(0, diags.Count); - Assert.Equal(@"someFile.goo.bar", desc.FileName); - Assert.Equal("someFile.goo.bar", desc.ResourceName); + Assert.Equal(@"someFile.goo.bar", resource.LinkedResourceFileName); + Assert.Equal("someFile.goo.bar", resource.ResourceName); - desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,someName", WorkingDirectory, diags, embedded: false); + Assert.True(TryParse(@"\somepath\someFile.goo.bar,someName", out resource)); Assert.Equal(0, diags.Count); - Assert.Equal(@"someFile.goo.bar", desc.FileName); - Assert.Equal("someName", desc.ResourceName); + Assert.Equal(@"someFile.goo.bar", resource.LinkedResourceFileName); + Assert.Equal("someName", resource.ResourceName); - desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\s""ome Fil""e.goo.bar,someName", WorkingDirectory, diags, embedded: false); + Assert.True(TryParse(@"\somepath\s""ome Fil""e.goo.bar,someName", out resource)); Assert.Equal(0, diags.Count); - Assert.Equal(@"some File.goo.bar", desc.FileName); - Assert.Equal("someName", desc.ResourceName); + Assert.Equal(@"some File.goo.bar", resource.LinkedResourceFileName); + Assert.Equal("someName", resource.ResourceName); - desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,""some Name"",public", WorkingDirectory, diags, embedded: false); + Assert.True(TryParse(@"\somepath\someFile.goo.bar,""some Name"",public", out resource)); Assert.Equal(0, diags.Count); - Assert.Equal(@"someFile.goo.bar", desc.FileName); - Assert.Equal("some Name", desc.ResourceName); - Assert.True(desc.IsPublic); + Assert.Equal(@"someFile.goo.bar", resource.LinkedResourceFileName); + Assert.Equal("some Name", resource.ResourceName); + Assert.True(resource.IsPublic); // Use file name in place of missing resource name. - desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,,private", WorkingDirectory, diags, embedded: false); + Assert.True(TryParse(@"\somepath\someFile.goo.bar,,private", out resource)); Assert.Equal(0, diags.Count); - Assert.Equal(@"someFile.goo.bar", desc.FileName); - Assert.Equal("someFile.goo.bar", desc.ResourceName); - Assert.False(desc.IsPublic); + Assert.Equal(@"someFile.goo.bar", resource.LinkedResourceFileName); + Assert.Equal("someFile.goo.bar", resource.ResourceName); + Assert.False(resource.IsPublic); // Quoted accessibility is fine. - desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,,""private""", WorkingDirectory, diags, embedded: false); + Assert.True(TryParse(@"\somepath\someFile.goo.bar,,""private""", out resource)); Assert.Equal(0, diags.Count); - Assert.Equal(@"someFile.goo.bar", desc.FileName); - Assert.Equal("someFile.goo.bar", desc.ResourceName); - Assert.False(desc.IsPublic); + Assert.Equal(@"someFile.goo.bar", resource.LinkedResourceFileName); + Assert.Equal("someFile.goo.bar", resource.ResourceName); + Assert.False(resource.IsPublic); // Leading commas are not ignored... - desc = CSharpCommandLineParser.ParseResourceDescription("", @",,\somepath\someFile.goo.bar,,private", WorkingDirectory, diags, embedded: false); + Assert.False(TryParse(@",,\somepath\someFile.goo.bar,,private", out resource)); 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); + Assert.False(TryParse(@", ,\somepath\someFile.goo.bar,,private", out resource)); 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); + Assert.True(TryParse(@"\somepath\someFile.goo.bar,,private", out resource)); diags.Verify(); diags.Clear(); - Assert.Equal("someFile.goo.bar", desc.FileName); - Assert.Equal("someFile.goo.bar", desc.ResourceName); - Assert.False(desc.IsPublic); + Assert.Equal("someFile.goo.bar", resource.LinkedResourceFileName); + Assert.Equal("someFile.goo.bar", resource.ResourceName); + Assert.False(resource.IsPublic); // ...even if there's whitespace between them. - desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,,private, ,", WorkingDirectory, diags, embedded: false); + Assert.True(TryParse(@"\somepath\someFile.goo.bar,,private, ,", out resource)); diags.Verify(); diags.Clear(); - Assert.Equal("someFile.goo.bar", desc.FileName); - Assert.Equal("someFile.goo.bar", desc.ResourceName); - Assert.False(desc.IsPublic); + Assert.Equal("someFile.goo.bar", resource.LinkedResourceFileName); + Assert.Equal("someFile.goo.bar", resource.ResourceName); + Assert.False(resource.IsPublic); - desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,someName,publi", WorkingDirectory, diags, embedded: false); + Assert.False(TryParse(@"\somepath\someFile.goo.bar,someName,publi", out resource)); 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); + Assert.False(TryParse(@"D:rive\relative\path,someName,public", out resource)); 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); + Assert.False(TryParse(@"inva\l*d?path,someName,public", out resource)); 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); + Assert.False(TryParse((string)null, out resource)); diags.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("")); - Assert.Null(desc); diags.Clear(); - desc = CSharpCommandLineParser.ParseResourceDescription("", "", WorkingDirectory, diags, embedded: false); + Assert.False(TryParse("", out resource)); diags.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("")); - Assert.Null(desc); diags.Clear(); - desc = CSharpCommandLineParser.ParseResourceDescription("", " ", WorkingDirectory, diags, embedded: false); + Assert.False(TryParse(" ", out resource)); 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); + Assert.False(TryParse(" , ", out resource)); 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); + Assert.True(TryParse("path, ", out resource)); diags.Verify(); diags.Clear(); - Assert.Equal("path", desc.FileName); - Assert.Equal("path", desc.ResourceName); - Assert.True(desc.IsPublic); + Assert.Equal("path", resource.LinkedResourceFileName); + Assert.Equal("path", resource.ResourceName); + Assert.True(resource.IsPublic); - desc = CSharpCommandLineParser.ParseResourceDescription("", " ,name", WorkingDirectory, diags, embedded: false); + Assert.False(TryParse(" ,name", out resource)); 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); + Assert.False(TryParse(" , , ", out resource)); 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); + Assert.False(TryParse("path, , ", out resource)); 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); + Assert.False(TryParse(" ,name, ", out resource)); 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); + Assert.False(TryParse(" , ,private", out resource)); 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); + Assert.False(TryParse("path,name,", out resource)); 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); + Assert.False(TryParse("path,name,,", out resource)); 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); + Assert.False(TryParse("path,name, ", out resource)); 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); + Assert.True(TryParse("path, ,private", out resource)); diags.Verify(); diags.Clear(); - Assert.Equal("path", desc.FileName); - Assert.Equal("path", desc.ResourceName); - Assert.False(desc.IsPublic); + Assert.Equal("path", resource.LinkedResourceFileName); + Assert.Equal("path", resource.ResourceName); + Assert.False(resource.IsPublic); - desc = CSharpCommandLineParser.ParseResourceDescription("", " ,name,private", WorkingDirectory, diags, embedded: false); + Assert.False(TryParse(" ,name,private", out resource)); 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); + Assert.True(TryParse($"path,{longE},private", out resource)); diags.Verify(); // Now checked during emit. diags.Clear(); - Assert.Equal("path", desc.FileName); - Assert.Equal(longE, desc.ResourceName); - Assert.False(desc.IsPublic); + Assert.Equal("path", resource.LinkedResourceFileName); + Assert.Equal(longE, resource.ResourceName); + Assert.False(resource.IsPublic); var longI = new String('i', 260); - desc = CSharpCommandLineParser.ParseResourceDescription("", String.Format("{0},e,private", longI), WorkingDirectory, diags, embedded: false); + Assert.False(TryParse($"{longI},e,private", out resource)); 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)); + // error CS2021: File name '...' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long + Diagnostic(ErrorCode.FTL_InvalidInputFileName).WithArguments(longI).WithLocation(1, 1)); + + bool TryParse(string value, out CommandLineResource resource) => + CSharpCommandLineParser.TryParseResourceDescription(argName: "", value.AsMemory(), WorkingDirectory, diags, isEmbedded: false, out resource); } [Fact] diff --git a/src/Compilers/Core/Portable/CommandLine/CommandLineArguments.cs b/src/Compilers/Core/Portable/CommandLine/CommandLineArguments.cs index cc79c598cd69f..75ae0055ff582 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommandLineArguments.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommandLineArguments.cs @@ -239,9 +239,9 @@ public abstract class CommandLineArguments public bool NoWin32Manifest { get; internal set; } /// - /// Resources specified as arguments to the compilation. + /// Manifest resource information parsed from /resource arguments. /// - public ImmutableArray ManifestResources { get; internal set; } + public ImmutableArray ManifestResourceArguments { get; internal set; } /// /// Encoding to be used for source files or 'null' for autodetect/default. @@ -308,13 +308,24 @@ public CompilationOptions CompilationOptions /// public CultureInfo? PreferredUILang { get; internal set; } + // Cache the values so that underlying file streams are not created multiple times for the same files. + private readonly Lazy> _lazyManifestResources; + internal StrongNameProvider GetStrongNameProvider(StrongNameFileSystem fileSystem) => new DesktopStrongNameProvider(KeyFileSearchPaths, fileSystem); internal CommandLineArguments() { + _lazyManifestResources = new Lazy>( + () => ManifestResourceArguments.SelectAsArray(static r => r.ToDescription())); } + /// + /// Resources specified as arguments to the compilation. + /// + public ImmutableArray ManifestResources + => _lazyManifestResources.Value; + /// /// Returns a full path of the file that the compiler will generate the assembly to if compilation succeeds. /// diff --git a/src/Compilers/Core/Portable/CommandLine/CommandLineParser.cs b/src/Compilers/Core/Portable/CommandLine/CommandLineParser.cs index f591e49836df7..ac05da9bf7f0d 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommandLineParser.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommandLineParser.cs @@ -822,21 +822,24 @@ static bool isClientArgsOption(string arg, string optionName, out bool hasValue, private static readonly char[] s_resourceSeparators = { ',' }; - internal static void ParseResourceDescription( + internal static bool TryParseResourceDescription( ReadOnlyMemory resourceDescriptor, string? baseDirectory, - bool skipLeadingSeparators, //VB does this - out string? filePath, - out string? fullPath, - out string? fileName, - out string resourceName, - out string? accessibility) + bool skipLeadingSeparators, // VB does this + bool allowEmptyAccessibility, // VB does this + [NotNullWhen(true)] out string? filePath, + [NotNullWhen(true)] out string? fullPath, + [NotNullWhen(true)] out string? fileName, + [NotNullWhen(true)] out string? resourceName, + [NotNullWhen(true)] out bool? isPublic, + out string? rawAccessibility) { filePath = null; fullPath = null; fileName = null; - resourceName = ""; - accessibility = null; + resourceName = null; + isPublic = null; + rawAccessibility = null; // resource descriptor is: "[,[,public|private]]" var parts = ArrayBuilder>.GetInstance(); @@ -867,17 +870,41 @@ internal static void ParseResourceDescription( if (length >= 3) { - accessibility = RemoveQuotesAndSlashes(parts[offset + 2]); + rawAccessibility = RemoveQuotesAndSlashes(parts[offset + 2]); + } + + if (rawAccessibility == null || rawAccessibility == "" && allowEmptyAccessibility) + { + // If no accessibility is given, we default to "public". + // NOTE: Dev10 distinguishes between null and empty. + isPublic = true; + } + else if (string.Equals(rawAccessibility, "public", StringComparison.OrdinalIgnoreCase)) + { + isPublic = true; + } + else if (string.Equals(rawAccessibility, "private", StringComparison.OrdinalIgnoreCase)) + { + isPublic = false; + } + else + { + isPublic = null; } parts.Free(); - if (RoslynString.IsNullOrWhiteSpace(filePath)) + + if (isPublic == null || RoslynString.IsNullOrWhiteSpace(filePath)) { - return; + return false; } fileName = PathUtilities.GetFileName(filePath); fullPath = FileUtilities.ResolveRelativePath(filePath, baseDirectory); + if (!PathUtilities.IsValidFilePath(fullPath)) + { + return false; + } // The default resource name is the file name. // Also use the file name for the name when user specifies string like "filePath,,private" @@ -885,6 +912,8 @@ internal static void ParseResourceDescription( { resourceName = fileName; } + + return true; } /// diff --git a/src/Compilers/Core/Portable/CommandLine/CommandLineResource.cs b/src/Compilers/Core/Portable/CommandLine/CommandLineResource.cs new file mode 100644 index 0000000000000..d3e6900bb3953 --- /dev/null +++ b/src/Compilers/Core/Portable/CommandLine/CommandLineResource.cs @@ -0,0 +1,79 @@ +// 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. + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis; + +/// +/// Describes a manifest resource specification stored in command line arguments. +/// +public readonly struct CommandLineResource +{ + /// + /// Name of the manifest resource as it appears in metadata. + /// + public string ResourceName { get; } + + /// + /// Full path to the resource content file. + /// + public string FullPath { get; } + + /// + /// Accessibility of the resource. + /// + public bool IsPublic { get; } + + /// + /// File name of a linked resource, or null if the resource is embedded. + /// + public string? LinkedResourceFileName { get; } + + internal CommandLineResource(string resourceName, string fullPath, string? linkedResourceFileName, bool isPublic) + { + Debug.Assert(!resourceName.IsEmpty()); + Debug.Assert(PathUtilities.IsAbsolute(fullPath)); + + ResourceName = resourceName; + FullPath = fullPath; + LinkedResourceFileName = linkedResourceFileName; + IsPublic = isPublic; + } + + /// + /// True if the resource is embedded. + /// + public bool IsEmbedded + => LinkedResourceFileName == null; + + /// + /// True if the resource is linked. + /// + [MemberNotNullWhen(true, nameof(LinkedResourceFileName))] + public bool IsLinked + => LinkedResourceFileName != null; + + /// + /// Creates for this resource. + /// + internal ResourceDescription ToDescription() + { + // fail fast if the method is called on default(CommandLineResource) + var fullPath = FullPath ?? throw new NullReferenceException(); + + Func dataProvider = () => + { + // Use FileShare.ReadWrite because the file could be opened by the current process. + // For example, it is an XML doc file produced by the build. + return new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + }; + + return new ResourceDescription(ResourceName, LinkedResourceFileName, dataProvider, IsPublic, isEmbedded: IsEmbedded, checkArgs: false); + } +} diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index d93281dfa3460..96dbeda4ce56e 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -1,3 +1,12 @@ +Microsoft.CodeAnalysis.CommandLineArguments.ManifestResourceArguments.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.CommandLineResource +Microsoft.CodeAnalysis.CommandLineResource.CommandLineResource() -> void +Microsoft.CodeAnalysis.CommandLineResource.FullPath.get -> string! +Microsoft.CodeAnalysis.CommandLineResource.IsEmbedded.get -> bool +Microsoft.CodeAnalysis.CommandLineResource.IsLinked.get -> bool +Microsoft.CodeAnalysis.CommandLineResource.IsPublic.get -> bool +Microsoft.CodeAnalysis.CommandLineResource.LinkedResourceFileName.get -> string? +Microsoft.CodeAnalysis.CommandLineResource.ResourceName.get -> string! Microsoft.CodeAnalysis.Compilation.EmitDifference(Microsoft.CodeAnalysis.Emit.EmitBaseline! baseline, System.Collections.Generic.IEnumerable! edits, System.Func! isAddedSymbol, System.IO.Stream! metadataStream, System.IO.Stream! ilStream, System.IO.Stream! pdbStream, Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.Emit.EmitDifferenceResult! Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions.EmitDifferenceOptions() -> void diff --git a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb index 70dfaf80e5cc6..707f5b985cd23 100644 --- a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb +++ b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb @@ -116,7 +116,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim win32ResourceFile As String = Nothing Dim win32IconFile As String = Nothing Dim noWin32Manifest As Boolean = False - Dim managedResources = New List(Of ResourceDescription)() + Dim managedResources = New List(Of CommandLineResource)() Dim sourceFiles = New List(Of CommandLineSourceFile)() Dim hasSourceFiles = False Dim additionalFiles = New List(Of CommandLineSourceFile)() @@ -704,15 +704,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Continue For Case "res", "resource" - Dim embeddedResource = ParseResourceDescription(name, value, baseDirectory, diagnostics, embedded:=True) - If embeddedResource IsNot Nothing Then + Dim embeddedResource As CommandLineResource + If TryParseResourceDescription(name, value, baseDirectory, diagnostics, isEmbedded:=True, embeddedResource) Then managedResources.Add(embeddedResource) End If Continue For Case "linkres", "linkresource" - Dim linkedResource = ParseResourceDescription(name, value, baseDirectory, diagnostics, embedded:=False) - If linkedResource IsNot Nothing Then + Dim linkedResource As CommandLineResource + If TryParseResourceDescription(name, value, baseDirectory, diagnostics, isEmbedded:=False, linkedResource) Then managedResources.Add(linkedResource) End If Continue For @@ -1509,7 +1509,7 @@ lVbRuntimePlus: .DisplayHelp = displayHelp, .DisplayVersion = displayVersion, .DisplayLangVersions = displayLangVersions, - .ManifestResources = managedResources.AsImmutable(), + .ManifestResourceArguments = managedResources.AsImmutable(), .CompilationOptions = options, .ParseOptions = parseOptions, .EmitOptions = emitOptions, @@ -1525,7 +1525,7 @@ lVbRuntimePlus: .SkipAnalyzers = skipAnalyzers, .EmbeddedFiles = embeddedFiles.AsImmutable(), .GeneratedFilesOutputDirectory = generatedFilesOutputDirectory, - .ReportInternalsVisibleToAttributes = reportIVTs + .ReportInternalsVisibleToAttributes = reportIvts } End Function @@ -1705,10 +1705,18 @@ lVbRuntimePlus: End Function ' See ParseCommandLine in vbc.cpp. - Friend Overloads Shared Function ParseResourceDescription(name As String, resourceDescriptor As String, baseDirectory As String, diagnostics As IList(Of Diagnostic), embedded As Boolean) As ResourceDescription + Friend Overloads Shared Function TryParseResourceDescription( + argName As String, + resourceDescriptor As String, + baseDirectory As String, + diagnostics As IList(Of Diagnostic), + isEmbedded As Boolean, + ByRef resource As CommandLineResource) As Boolean + If String.IsNullOrEmpty(resourceDescriptor) Then - AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":") - Return Nothing + AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, argName, ":") + resource = Nothing + Return False End If ' NOTE: these are actually passed to out parameters of .ParseResourceDescription. @@ -1716,52 +1724,41 @@ lVbRuntimePlus: Dim fullPath As String = Nothing Dim fileName As String = Nothing Dim resourceName As String = Nothing - Dim accessibility As String = Nothing + Dim isPublic As Boolean? = Nothing + Dim rawAccessibility As String = Nothing - ParseResourceDescription( + If Not TryParseResourceDescription( resourceDescriptor.AsMemory(), baseDirectory, - True, + skipLeadingSeparators:=True, + allowEmptyAccessibility:=True, filePath, fullPath, fileName, resourceName, - accessibility) + isPublic, + rawAccessibility) Then - If String.IsNullOrWhiteSpace(filePath) Then - AddInvalidSwitchValueDiagnostic(diagnostics, name, filePath) - Return Nothing - End If + If isPublic Is Nothing Then + AddInvalidSwitchValueDiagnostic(diagnostics, argName, rawAccessibility) + ElseIf RoslynString.IsNullOrWhiteSpace(filePath) Then + AddInvalidSwitchValueDiagnostic(diagnostics, argName, filePath) + Else + Debug.Assert(Not PathUtilities.IsValidFilePath(fullPath)) + AddDiagnostic(diagnostics, ERRID.FTL_InvalidInputFileName, filePath) + End If - If Not PathUtilities.IsValidFilePath(fullPath) Then - AddDiagnostic(diagnostics, ERRID.FTL_InvalidInputFileName, filePath) - Return Nothing + resource = Nothing + Return False End If - Dim isPublic As Boolean - If String.IsNullOrEmpty(accessibility) Then - ' If no accessibility is given, we default to "public". - ' NOTE: Dev10 treats empty the same as null (the difference being that empty indicates a comma after the resource name). - ' NOTE: Dev10 distinguishes between empty and whitespace-only. - isPublic = True - ElseIf String.Equals(accessibility, "public", StringComparison.OrdinalIgnoreCase) Then - isPublic = True - ElseIf String.Equals(accessibility, "private", StringComparison.OrdinalIgnoreCase) Then - isPublic = False - Else - AddInvalidSwitchValueDiagnostic(diagnostics, name, accessibility) - Return Nothing - End If + resource = New CommandLineResource( + resourceName:=resourceName, + fullPath:=fullPath, + linkedResourceFileName:=If(isEmbedded, Nothing, fileName), + isPublic.Value) - Dim dataProvider As Func(Of Stream) = Function() - ' Use FileShare.ReadWrite because the file could be opened by the current process. - ' For example, it Is an XML doc file produced by the build. - Return New FileStream(fullPath, - FileMode.Open, - FileAccess.Read, - FileShare.ReadWrite) - End Function - Return New ResourceDescription(resourceName, fileName, dataProvider, isPublic, embedded, checkArgs:=False) + Return True End Function Private Shared Sub AddInvalidSwitchValueDiagnostic(diagnostics As IList(Of Diagnostic), ByVal name As String, ByVal nullStringText As String) diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index 8841ef4e3fa67..b2cc251b377d1 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -1158,180 +1158,165 @@ End Module").Path End Sub - Public Sub ParseResourceDescription() + Public Sub TryParseResourceDescription() Dim diags = New List(Of Diagnostic)() - Dim desc As ResourceDescription + Dim resource As CommandLineResource - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "\somepath\someFile.goo.bar", _baseDirectory, diags, embedded:=False) + Assert.True(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "\somepath\someFile.goo.bar", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify() diags.Clear() - Assert.Equal("someFile.goo.bar", desc.FileName) - Assert.Equal("someFile.goo.bar", desc.ResourceName) - Assert.True(desc.IsPublic) + Assert.Equal("someFile.goo.bar", resource.LinkedResourceFileName) + Assert.Equal("someFile.goo.bar", resource.ResourceName) + Assert.True(resource.IsPublic) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "\somepath\someFile.goo.bar,someName", _baseDirectory, diags, embedded:=False) + Assert.True(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "\somepath\someFile.goo.bar,someName", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify() diags.Clear() - Assert.Equal("someFile.goo.bar", desc.FileName) - Assert.Equal("someName", desc.ResourceName) - Assert.True(desc.IsPublic) + Assert.Equal("someFile.goo.bar", resource.LinkedResourceFileName) + Assert.Equal("someName", resource.ResourceName) + Assert.True(resource.IsPublic) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "\somepath\someFile.goo.bar,someName,public", _baseDirectory, diags, embedded:=False) + Assert.True(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "\somepath\someFile.goo.bar,someName,public", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify() diags.Clear() - Assert.Equal("someFile.goo.bar", desc.FileName) - Assert.Equal("someName", desc.ResourceName) - Assert.True(desc.IsPublic) + Assert.Equal("someFile.goo.bar", resource.LinkedResourceFileName) + Assert.Equal("someName", resource.ResourceName) + Assert.True(resource.IsPublic) ' use file name in place of missing resource name - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "\somepath\someFile.goo.bar,,private", _baseDirectory, diags, embedded:=False) + Assert.True(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "\somepath\someFile.goo.bar,,private", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify() diags.Clear() - Assert.Equal("someFile.goo.bar", desc.FileName) - Assert.Equal("someFile.goo.bar", desc.ResourceName) - Assert.False(desc.IsPublic) + Assert.Equal("someFile.goo.bar", resource.LinkedResourceFileName) + Assert.Equal("someFile.goo.bar", resource.ResourceName) + Assert.False(resource.IsPublic) ' quoted accessibility is fine - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "\somepath\someFile.goo.bar,,""private""", _baseDirectory, diags, embedded:=False) + Assert.True(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "\somepath\someFile.goo.bar,,""private""", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify() diags.Clear() - Assert.Equal("someFile.goo.bar", desc.FileName) - Assert.Equal("someFile.goo.bar", desc.ResourceName) - Assert.False(desc.IsPublic) + Assert.Equal("someFile.goo.bar", resource.LinkedResourceFileName) + Assert.Equal("someFile.goo.bar", resource.ResourceName) + Assert.False(resource.IsPublic) ' leading commas are ignored... - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", ",,\somepath\someFile.goo.bar,,private", _baseDirectory, diags, embedded:=False) + Assert.True(VisualBasicCommandLineParser.TryParseResourceDescription("resource", ",,\somepath\someFile.goo.bar,,private", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify() diags.Clear() - Assert.Equal("someFile.goo.bar", desc.FileName) - Assert.Equal("someFile.goo.bar", desc.ResourceName) - Assert.False(desc.IsPublic) + Assert.Equal("someFile.goo.bar", resource.LinkedResourceFileName) + Assert.Equal("someFile.goo.bar", resource.ResourceName) + Assert.False(resource.IsPublic) ' ...as long as there's no whitespace between them - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", ", ,\somepath\someFile.goo.bar,,private", _baseDirectory, diags, embedded:=False) + Assert.False(VisualBasicCommandLineParser.TryParseResourceDescription("resource", ", ,\somepath\someFile.goo.bar,,private", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify(Diagnostic(ERRID.ERR_InvalidSwitchValue).WithArguments("resource", " ")) diags.Clear() - Assert.Null(desc) ' trailing commas are ignored... - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "\somepath\someFile.goo.bar,,private", _baseDirectory, diags, embedded:=False) + Assert.True(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "\somepath\someFile.goo.bar,,private", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify() diags.Clear() - Assert.Equal("someFile.goo.bar", desc.FileName) - Assert.Equal("someFile.goo.bar", desc.ResourceName) - Assert.False(desc.IsPublic) + Assert.Equal("someFile.goo.bar", resource.LinkedResourceFileName) + Assert.Equal("someFile.goo.bar", resource.ResourceName) + Assert.False(resource.IsPublic) ' ...even if there's whitespace between them - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "\somepath\someFile.goo.bar,,private, ,", _baseDirectory, diags, embedded:=False) + Assert.True(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "\somepath\someFile.goo.bar,,private, ,", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify() diags.Clear() - Assert.Equal("someFile.goo.bar", desc.FileName) - Assert.Equal("someFile.goo.bar", desc.ResourceName) - Assert.False(desc.IsPublic) + Assert.Equal("someFile.goo.bar", resource.LinkedResourceFileName) + Assert.Equal("someFile.goo.bar", resource.ResourceName) + Assert.False(resource.IsPublic) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "\somepath\someFile.goo.bar,someName,publi", _baseDirectory, diags, embedded:=False) + Assert.False(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "\somepath\someFile.goo.bar,someName,publi", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify(Diagnostic(ERRID.ERR_InvalidSwitchValue).WithArguments("resource", "publi")) diags.Clear() - Assert.Null(desc) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "D:rive\relative\path,someName,public", _baseDirectory, diags, embedded:=False) + Assert.False(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "D:rive\relative\path,someName,public", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify(Diagnostic(ERRID.FTL_InvalidInputFileName).WithArguments("D:rive\relative\path")) diags.Clear() - Assert.Null(desc) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "inva\l*d?path,someName,public", _baseDirectory, diags, embedded:=False) + Assert.False(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "inva\l*d?path,someName,public", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify(Diagnostic(ERRID.FTL_InvalidInputFileName).WithArguments("inva\l*d?path")) diags.Clear() - Assert.Null(desc) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", Nothing, _baseDirectory, diags, embedded:=False) + Assert.False(VisualBasicCommandLineParser.TryParseResourceDescription("resource", Nothing, _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify(Diagnostic(ERRID.ERR_ArgumentRequired).WithArguments("resource", ":")) diags.Clear() - Assert.Null(desc) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "", _baseDirectory, diags, embedded:=False) + Assert.False(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify(Diagnostic(ERRID.ERR_ArgumentRequired).WithArguments("resource", ":")) diags.Clear() - Assert.Null(desc) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", " ", _baseDirectory, diags, embedded:=False) + Assert.False(VisualBasicCommandLineParser.TryParseResourceDescription("resource", " ", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify(Diagnostic(ERRID.ERR_InvalidSwitchValue).WithArguments("resource", " ")) diags.Clear() - Assert.Null(desc) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", " , ", _baseDirectory, diags, embedded:=False) + Assert.False(VisualBasicCommandLineParser.TryParseResourceDescription("resource", " , ", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify(Diagnostic(ERRID.ERR_InvalidSwitchValue).WithArguments("resource", " ")) diags.Clear() - Assert.Null(desc) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "path, ", _baseDirectory, diags, embedded:=False) + Assert.True(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "path, ", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify() diags.Clear() - Assert.Equal("path", desc.FileName) - Assert.Equal("path", desc.ResourceName) - Assert.True(desc.IsPublic) + Assert.Equal("path", resource.LinkedResourceFileName) + Assert.Equal("path", resource.ResourceName) + Assert.True(resource.IsPublic) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", " ,name", _baseDirectory, diags, embedded:=False) + Assert.False(VisualBasicCommandLineParser.TryParseResourceDescription("resource", " ,name", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify(Diagnostic(ERRID.ERR_InvalidSwitchValue).WithArguments("resource", " ")) diags.Clear() - Assert.Null(desc) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", " , , ", _baseDirectory, diags, embedded:=False) + Assert.False(VisualBasicCommandLineParser.TryParseResourceDescription("resource", " , , ", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify(Diagnostic(ERRID.ERR_InvalidSwitchValue).WithArguments("resource", " ")) diags.Clear() - Assert.Null(desc) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "path, , ", _baseDirectory, diags, embedded:=False) + Assert.False(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "path, , ", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify(Diagnostic(ERRID.ERR_InvalidSwitchValue).WithArguments("resource", " ")) diags.Clear() - Assert.Null(desc) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", " ,name, ", _baseDirectory, diags, embedded:=False) + Assert.False(VisualBasicCommandLineParser.TryParseResourceDescription("resource", " ,name, ", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify(Diagnostic(ERRID.ERR_InvalidSwitchValue).WithArguments("resource", " ")) diags.Clear() - Assert.Null(desc) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", " , ,private", _baseDirectory, diags, embedded:=False) + Assert.False(VisualBasicCommandLineParser.TryParseResourceDescription("resource", " , ,private", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify(Diagnostic(ERRID.ERR_InvalidSwitchValue).WithArguments("resource", " ")) diags.Clear() - Assert.Null(desc) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "path,name,", _baseDirectory, diags, embedded:=False) + Assert.True(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "path,name,", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify() diags.Clear() - Assert.Equal("path", desc.FileName) - Assert.Equal("name", desc.ResourceName) - Assert.True(desc.IsPublic) + Assert.Equal("path", resource.LinkedResourceFileName) + Assert.Equal("name", resource.ResourceName) + Assert.True(resource.IsPublic) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "path,name,,", _baseDirectory, diags, embedded:=False) + Assert.True(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "path,name,,", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify() diags.Clear() - Assert.Equal("path", desc.FileName) - Assert.Equal("name", desc.ResourceName) - Assert.True(desc.IsPublic) + Assert.Equal("path", resource.LinkedResourceFileName) + Assert.Equal("name", resource.ResourceName) + Assert.True(resource.IsPublic) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "path,name, ", _baseDirectory, diags, embedded:=False) + Assert.False(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "path,name, ", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify(Diagnostic(ERRID.ERR_InvalidSwitchValue).WithArguments("resource", " ")) diags.Clear() - Assert.Null(desc) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", "path, ,private", _baseDirectory, diags, embedded:=False) + Assert.True(VisualBasicCommandLineParser.TryParseResourceDescription("resource", "path, ,private", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify() diags.Clear() - Assert.Equal("path", desc.FileName) - Assert.Equal("path", desc.ResourceName) - Assert.False(desc.IsPublic) + Assert.Equal("path", resource.LinkedResourceFileName) + Assert.Equal("path", resource.ResourceName) + Assert.False(resource.IsPublic) - desc = VisualBasicCommandLineParser.ParseResourceDescription("resource", " ,name,private", _baseDirectory, diags, embedded:=False) + Assert.False(VisualBasicCommandLineParser.TryParseResourceDescription("resource", " ,name,private", _baseDirectory, diags, isEmbedded:=False, resource)) diags.Verify(Diagnostic(ERRID.ERR_InvalidSwitchValue).WithArguments("resource", " ")) diags.Clear() - Assert.Null(desc) Dim longI = New String("i"c, 260) - desc = VisualBasicCommandLineParser.ParseResourceDescription("", String.Format("{0},e,private", longI), _baseDirectory, diags, embedded:=False) - ' // error BC2032: File name 'iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long - diags.Verify(Diagnostic(ERRID.FTL_InvalidInputFileName).WithArguments("iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii").WithLocation(1, 1)) + Assert.False(VisualBasicCommandLineParser.TryParseResourceDescription("", String.Format("{0},e,private", longI), _baseDirectory, diags, isEmbedded:=False, resource)) + ' // error BC2032: File name '...' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long + diags.Verify(Diagnostic(ERRID.FTL_InvalidInputFileName).WithArguments(longI).WithLocation(1, 1)) End Sub