Skip to content

Commit 3d08a2b

Browse files
authored
cleanup: Add analyzer for public API and revert an unintentional breaking change from 2.3 (#264)
1 parent 38a5c76 commit 3d08a2b

14 files changed

+722
-27
lines changed

CommandLineUtils.sln

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 16
3+
# Visual Studio Version 16
44
VisualStudioVersion = 16.0.0.0
55
MinimumVisualStudioVersion = 16.0.0.0
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{95D4B35E-0A21-4D64-8BAF-27DD6C019FC5}"
@@ -13,7 +13,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "McMaster.Extensions.Command
1313
EndProject
1414
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "files", "files", "{509D6286-77FA-49C1-9EC6-7461DBE79536}"
1515
ProjectSection(SolutionItems) = preProject
16-
.appveyor.yml = .appveyor.yml
1716
.editorconfig = .editorconfig
1817
.gitattributes = .gitattributes
1918
.gitignore = .gitignore
@@ -26,14 +25,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "files", "files", "{509D6286
2625
NOTICE.txt = NOTICE.txt
2726
NuGet.config = NuGet.config
2827
README.md = README.md
29-
releasenotes.props = releasenotes.props
3028
version.props = version.props
31-
.travis.yml = .travis.yml
3229
EndProjectSection
3330
EndProject
34-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "McMaster.Extensions.Hosting.CommandLine", "src\Hosting.CommandLine\McMaster.Extensions.Hosting.CommandLine.csproj", "{407245F7-3F2C-4634-8578-7EFCA9BD26BD}"
31+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "McMaster.Extensions.Hosting.CommandLine", "src\Hosting.CommandLine\McMaster.Extensions.Hosting.CommandLine.csproj", "{407245F7-3F2C-4634-8578-7EFCA9BD26BD}"
3532
EndProject
36-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hosting.CommandLine.Tests", "test\Hosting.CommandLine.Tests\McMaster.Extensions.Hosting.CommandLine.Tests.csproj", "{04A5D2B8-18E4-4C75-AEF9-79D171FAC210}"
33+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "McMaster.Extensions.Hosting.CommandLine.Tests", "test\Hosting.CommandLine.Tests\McMaster.Extensions.Hosting.CommandLine.Tests.csproj", "{04A5D2B8-18E4-4C75-AEF9-79D171FAC210}"
3734
EndProject
3835
Global
3936
GlobalSection(SolutionConfigurationPlatforms) = preSolution

src/CommandLineUtils/CommandLineApplication.Execute.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public static int Execute<TApp>(CommandLineContext context)
3131
where TApp : class
3232
=> ExecuteAsync<TApp>(context).GetAwaiter().GetResult();
3333

34+
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
3435
/// <summary>
3536
/// Creates an instance of <typeparamref name="TApp"/>, matching <see cref="CommandLineContext.Arguments"/>
3637
/// to all attributes on the type, and then invoking a method named "OnExecute" or "OnExecuteAsync" if it exists.
@@ -43,6 +44,7 @@ public static int Execute<TApp>(CommandLineContext context)
4344
/// <exception cref="InvalidOperationException">Thrown when attributes are incorrectly configured.</exception>
4445
/// <returns>The process exit code</returns>
4546
public static async Task<int> ExecuteAsync<TApp>(CommandLineContext context, CancellationToken cancellationToken = default)
47+
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
4648
where TApp : class
4749
{
4850
if (context == null)
@@ -134,6 +136,7 @@ public static Task<int> ExecuteAsync<TApp>(params string[] args)
134136
where TApp : class
135137
=> ExecuteAsync<TApp>(PhysicalConsole.Singleton, args);
136138

139+
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
137140
/// <summary>
138141
/// Creates an instance of <typeparamref name="TApp"/>, matching <paramref name="args"/>
139142
/// to all attributes on the type, and then invoking a method named "OnExecute" or "OnExecuteAsync" if it exists.
@@ -146,6 +149,7 @@ public static Task<int> ExecuteAsync<TApp>(params string[] args)
146149
/// <exception cref="InvalidOperationException">Thrown when attributes are incorrectly configured.</exception>
147150
/// <returns>The process exit code</returns>
148151
public static Task<int> ExecuteAsync<TApp>(string[] args, CancellationToken cancellationToken = default)
152+
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
149153
where TApp : class
150154
{
151155
args ??= Util.EmptyArray<string>();

src/CommandLineUtils/CommandLineApplication.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,13 @@ static CommandLineApplication()
5757
private readonly ConventionContext _conventionContext;
5858
private readonly List<IConvention> _conventions = new List<IConvention>();
5959

60+
#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads.
6061
/// <summary>
6162
/// Initializes a new instance of <see cref="CommandLineApplication"/>.
6263
/// </summary>
6364
/// <param name="throwOnUnexpectedArg">Initial value for <see cref="ThrowOnUnexpectedArgument"/>.</param>
6465
public CommandLineApplication(bool throwOnUnexpectedArg = true)
66+
#pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads.
6567
: this(null, DefaultHelpTextGenerator.Singleton, new DefaultCommandLineContext(), throwOnUnexpectedArg)
6668
{
6769
}
@@ -470,6 +472,7 @@ private void AssertCommandNameIsUnique(string? name, CommandLineApplication? com
470472
}
471473
}
472474

475+
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
473476
/// <summary>
474477
/// Adds a subcommand.
475478
/// </summary>
@@ -478,6 +481,7 @@ private void AssertCommandNameIsUnique(string? name, CommandLineApplication? com
478481
/// <param name="throwOnUnexpectedArg"></param>
479482
/// <returns></returns>
480483
public CommandLineApplication Command(string name, Action<CommandLineApplication> configuration,
484+
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
481485
bool throwOnUnexpectedArg = true)
482486
{
483487
var command = new CommandLineApplication(this, name, throwOnUnexpectedArg);
@@ -489,6 +493,7 @@ public CommandLineApplication Command(string name, Action<CommandLineApplication
489493
return command;
490494
}
491495

496+
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
492497
/// <summary>
493498
/// Adds a subcommand with model of type <typeparamref name="TModel" />.
494499
/// </summary>
@@ -498,6 +503,7 @@ public CommandLineApplication Command(string name, Action<CommandLineApplication
498503
/// <typeparam name="TModel">The model type of the subcommand.</typeparam>
499504
/// <returns></returns>
500505
public CommandLineApplication<TModel> Command<TModel>(string name, Action<CommandLineApplication<TModel>> configuration,
506+
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
501507
bool throwOnUnexpectedArg = true)
502508
where TModel : class
503509
{
@@ -592,6 +598,7 @@ public CommandOption<T> Option<T>(string template, string description, CommandOp
592598
return option;
593599
}
594600

601+
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
595602
/// <summary>
596603
/// Adds a command line argument
597604
/// </summary>
@@ -600,8 +607,10 @@ public CommandOption<T> Option<T>(string template, string description, CommandOp
600607
/// <param name="multipleValues"></param>
601608
/// <returns></returns>
602609
public CommandArgument Argument(string name, string description, bool multipleValues = false)
610+
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
603611
=> Argument(name, description, _ => { }, multipleValues);
604612

613+
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
605614
/// <summary>
606615
/// Adds a command line argument.
607616
/// </summary>
@@ -611,6 +620,7 @@ public CommandArgument Argument(string name, string description, bool multipleVa
611620
/// <param name="multipleValues"></param>
612621
/// <returns></returns>
613622
public CommandArgument Argument(string name, string description, Action<CommandArgument> configuration, bool multipleValues = false)
623+
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
614624
{
615625
var argument = new CommandArgument
616626
{
@@ -623,6 +633,7 @@ public CommandArgument Argument(string name, string description, Action<CommandA
623633
return argument;
624634
}
625635

636+
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
626637
/// <summary>
627638
/// Adds a command line argument with values that should be parsable into <typeparamref name="T" />.
628639
/// </summary>
@@ -633,6 +644,7 @@ public CommandArgument Argument(string name, string description, Action<CommandA
633644
/// <typeparam name="T">The type of the values on the option</typeparam>
634645
/// <returns></returns>
635646
public CommandArgument<T> Argument<T>(string name, string description, Action<CommandArgument> configuration, bool multipleValues = false)
647+
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
636648
{
637649
var parser = ValueParsers.GetParser<T>();
638650

@@ -801,6 +813,7 @@ public int Execute(params string[] args)
801813
return ExecuteAsync(args).GetAwaiter().GetResult();
802814
}
803815

816+
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
804817
/// <summary>
805818
/// Parses an array of strings using <see cref="Parse(string[])"/>.
806819
/// <para>
@@ -820,6 +833,7 @@ public int Execute(params string[] args)
820833
/// <param name="cancellationToken"></param>
821834
/// <returns>The return code from <see cref="Invoke"/>.</returns>
822835
public async Task<int> ExecuteAsync(string[] args, CancellationToken cancellationToken = default)
836+
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
823837
{
824838
var parseResult = Parse(args);
825839
var command = parseResult.SelectedCommand;
@@ -900,6 +914,7 @@ public CommandOption HelpOption(string template, bool inherited)
900914
return OptionHelp;
901915
}
902916

917+
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
903918
/// <summary>
904919
/// Helper method that adds a version option from known versions strings.
905920
/// </summary>
@@ -908,6 +923,7 @@ public CommandOption HelpOption(string template, bool inherited)
908923
/// <param name="longFormVersion"></param>
909924
/// <returns></returns>
910925
public CommandOption VersionOption(string template,
926+
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
911927
string? shortFormVersion,
912928
string? longFormVersion = null)
913929
{
@@ -921,6 +937,7 @@ public CommandOption VersionOption(string template,
921937
}
922938
}
923939

940+
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
924941
/// <summary>
925942
/// Helper method that adds a version option.
926943
/// </summary>
@@ -929,6 +946,7 @@ public CommandOption VersionOption(string template,
929946
/// <param name="longFormVersionGetter"></param>
930947
/// <returns></returns>
931948
public CommandOption VersionOption(string template,
949+
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
932950
Func<string?>? shortFormVersionGetter,
933951
Func<string?>? longFormVersionGetter = null)
934952
{
@@ -995,7 +1013,9 @@ public void ShowHelp(bool usePager)
9951013
[Obsolete("This method has been marked as obsolete and will be removed in a future version. " +
9961014
"The recommended replacement is ShowHelp()")]
9971015
[EditorBrowsable(EditorBrowsableState.Never)]
1016+
#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads.
9981017
public void ShowHelp(string? commandName = null)
1018+
#pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads.
9991019
{
10001020
if (commandName == null)
10011021
{

src/CommandLineUtils/CommandLineApplication{T}.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ public class CommandLineApplication<TModel> : CommandLineApplication, IModelAcce
1919
private Func<TModel> _modelFactory = DefaultModelFactory;
2020

2121
#nullable disable
22+
#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads.
2223
/// <summary>
2324
/// Initializes a new instance of <see cref="CommandLineApplication"/>.
2425
/// </summary>
2526
/// <param name="throwOnUnexpectedArg">Initial value for <see cref="CommandLineApplication.ThrowOnUnexpectedArgument"/>.</param>
2627
public CommandLineApplication(bool throwOnUnexpectedArg = true)
28+
#pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads.
2729
: base(throwOnUnexpectedArg)
2830
{
2931
Initialize();

src/CommandLineUtils/HelpText/HangingIndentWriter.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ public class HangingIndentWriter
1818
/// </summary>
1919
public const int DefaultConsoleWidth = 80;
2020

21-
private bool _indentFirstLine;
22-
private int _indentSize;
23-
private int _maxLineLength;
24-
private string _paddedLine;
21+
private readonly bool _indentFirstLine;
22+
private readonly int _indentSize;
23+
private readonly int _maxLineLength;
24+
private readonly string _paddedLine;
2525

2626
/// <summary>
2727
/// A description formatter for dynamically wrapping the description to print in a CLI usage.

src/CommandLineUtils/IO/PhysicalConsole.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ public class PhysicalConsole : IConsole
1717
/// </summary>
1818
public static IConsole Singleton { get; } = new PhysicalConsole();
1919

20-
private PhysicalConsole()
21-
{
22-
}
20+
// TODO: in 3.0 make this type truly a singleton by adding a private ctor
21+
// this is techinally a breaking change, so wait till 3.0
22+
// private PhysicalConsole()
23+
// {
24+
// }
2325

2426
/// <summary>
2527
/// <see cref="Console.CancelKeyPress"/>.

src/CommandLineUtils/Internal/ResponseFileParser.cs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,13 @@ internal class ResponseFileParser
1313
{
1414
public static IList<string> Parse(string filePath, ResponseFileHandling handling)
1515
{
16-
switch (handling)
16+
return handling switch
1717
{
18-
case ResponseFileHandling.Disabled:
19-
return new[] { filePath };
20-
case ResponseFileHandling.ParseArgsAsSpaceSeparated:
21-
return ParseAsSpaceSeparated(filePath);
22-
case ResponseFileHandling.ParseArgsAsLineSeparated:
23-
return ParseAsLineSeparated(filePath);
24-
default:
25-
throw new ArgumentOutOfRangeException(nameof(handling));
26-
}
18+
ResponseFileHandling.Disabled => new[] { filePath },
19+
ResponseFileHandling.ParseArgsAsSpaceSeparated => ParseAsSpaceSeparated(filePath),
20+
ResponseFileHandling.ParseArgsAsLineSeparated => ParseAsLineSeparated(filePath),
21+
_ => throw new ArgumentOutOfRangeException(nameof(handling)),
22+
};
2723
}
2824

2925
private static IList<string> ParseAsLineSeparated(string filePath)

src/CommandLineUtils/McMaster.Extensions.CommandLineUtils.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
<PropertyGroup>
66
<TargetFrameworks>netstandard2.0;netstandard1.6;net45</TargetFrameworks>
7+
<!--
8+
The ns1.6 API is a subset of the API available in ns2.0 and net45. The public API tool doesn't handle multiple TFMs.
9+
https://github.com/dotnet/roslyn-analyzers/issues/2621
10+
-->
11+
<DisablePublicApiAnalyzer Condition="'$(TargetFramework)' == 'netstandard1.6'">true</DisablePublicApiAnalyzer>
712
<GenerateDocumentationFile>true</GenerateDocumentationFile>
813
<IsPackable>true</IsPackable>
914
<Description>Command-line parsing API.</Description>

0 commit comments

Comments
 (0)