Skip to content

Commit

Permalink
feat: support C# 8 and nullable reference types (#245)
Browse files Browse the repository at this point in the history
  • Loading branch information
natemcmaster authored Jun 19, 2019
1 parent 8320e50 commit cf1947b
Show file tree
Hide file tree
Showing 98 changed files with 665 additions and 538 deletions.
40 changes: 20 additions & 20 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,32 @@ indent_size = 2
indent_size = 4

# Style I care about
csharp_style_expression_bodied_constructors = false : error
csharp_prefer_braces = true : error
dotnet_sort_system_directives_first = true : error
csharp_style_expression_bodied_constructors = false
csharp_prefer_braces = true
dotnet_sort_system_directives_first = true

# Stuff that is usually best
csharp_style_inlined_variable_declaration = true : warning
csharp_style_var_elsewhere = true : warning
csharp_space_after_cast = true : warning
csharp_style_pattern_matching_over_as_with_null_check = true : warning
csharp_style_pattern_matching_over_is_with_cast_check = true : warning
csharp_style_var_for_built_in_types = true : warning
csharp_style_var_when_type_is_apparent = true : warning
csharp_new_line_before_catch = true : warning
csharp_new_line_before_else = true : warning
csharp_new_line_before_finally = true : warning
csharp_indent_case_contents = true : warning
csharp_style_inlined_variable_declaration = true
csharp_style_var_elsewhere = true
csharp_space_after_cast = true
csharp_style_pattern_matching_over_as_with_null_check = true
csharp_style_pattern_matching_over_is_with_cast_check = true
csharp_style_var_for_built_in_types = true
csharp_style_var_when_type_is_apparent = true
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_indent_case_contents = true
csharp_new_line_before_open_brace = all
csharp_indent_switch_labels = true : warning
csharp_indent_switch_labels = true
csharp_indent_labels = one_less_than_current
csharp_prefer_simple_default_expression = true : warning
csharp_prefer_simple_default_expression = true

# Good defaults, but not always
dotnet_style_object_initializer = true : suggestion
csharp_style_expression_bodied_indexers = true : suggestion
csharp_style_expression_bodied_accessors = true : suggestion
csharp_style_throw_expression = true : suggestion
dotnet_style_object_initializer = true
csharp_style_expression_bodied_indexers = true
csharp_style_expression_bodied_accessors = true
csharp_style_throw_expression = true

# Naming styles

Expand Down
6 changes: 3 additions & 3 deletions CommandLineUtils.sln
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2026
MinimumVisualStudioVersion = 15.0.26124.0
# Visual Studio 16
VisualStudioVersion = 16.0.0.0
MinimumVisualStudioVersion = 16.0.0.0
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{95D4B35E-0A21-4D64-8BAF-27DD6C019FC5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "McMaster.Extensions.CommandLineUtils", "src\CommandLineUtils\McMaster.Extensions.CommandLineUtils.csproj", "{CBCFAFF3-A3B1-4C41-B2D1-092BF7307A4E}"
Expand Down
7 changes: 5 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<PackageIconUrl>https://natemcmaster.github.io/CommandLineUtils/logo.png</PackageIconUrl>
<NoPackageAnalysis>true</NoPackageAnalysis>
<LangVersion>7.2</LangVersion>
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
<!-- Some previews of .NET Core 3 don't have the rename yet -->
<NullableContextOptions>enable</NullableContextOptions>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)src\StrongName.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
<AzureKeyVaultUrl>https://nmcmaster.vault.azure.net</AzureKeyVaultUrl>
Expand All @@ -31,7 +34,7 @@
<CodeSignCertName>DigiCertCodeSign</CodeSignCertName>

<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<GenerateFullPaths Condition="'$(VSCODE_CWD)' != ''">true</GenerateFullPaths>
<GenerateFullPaths Condition="'$(TERM_PROGRAM)' == 'vscode'">true</GenerateFullPaths>
<BaseIntermediateOutputPath>$(MSBuildThisFileDirectory).build\obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
<BaseOutputPath>$(MSBuildThisFileDirectory).build\bin\$(MSBuildProjectName)\</BaseOutputPath>
</PropertyGroup>
Expand Down
28 changes: 21 additions & 7 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,19 @@ variables:
jobs:
- job: Windows
pool:
vmImage: vs2017-win2016
vmImage: windows-2019
steps:
- task: DotNetCoreInstaller@0
displayName: Install .NET Core 2.2
- task: UseDotNet@2
displayName: Install .NET Core 3 SDK
inputs:
version: '2.2.102'
version: '3.x'
packageType: sdk
includePreviewVersions: true
- task: UseDotNet@2
displayName: Install .NET Core 2.2 runtime
inputs:
version: '2.2.x'
packageType: runtime
- powershell: ./build.ps1 -ci
displayName: Invoke build.ps1
condition: eq(variables['kv-access-token'], '')
Expand All @@ -47,10 +54,17 @@ jobs:
pool:
vmImage: 'Ubuntu-16.04'
steps:
- task: DotNetCoreInstaller@0
displayName: Install .NET Core 2.2
- task: UseDotNet@2
displayName: Install .NET Core 3 SDK
inputs:
version: '3.x'
packageType: sdk
includePreviewVersions: true
- task: UseDotNet@2
displayName: Install .NET Core 2.2 runtime
inputs:
version: '2.2.102'
version: '2.2.x'
packageType: runtime
- script: ./build.ps1 -ci
displayName: Invoke build.ps1
- task: PublishTestResults@2
Expand Down
2 changes: 1 addition & 1 deletion docs/generate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ try {
exec git worktree add $targetDir gh-pages 2>&1 | out-null
}

$docfxVersion = '2.40.5'
$docfxVersion = '2.43.1'
$docfxRoot = "$buildRoot/packages/docfx.console/$docfxVersion"
$docfx = "$docfxRoot/tools/docfx.exe"
if (-not (Test-Path $docfx)) {
Expand Down
11 changes: 11 additions & 0 deletions docs/samples/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)..\, Directory.Build.props))\Directory.Build.props" />

<PropertyGroup>
<!-- Set samples to use the latest stable C# until C# 8 is released -->
<LangVersion>7.3</LangVersion>
<Nullable />
<NullableContextOptions />
</PropertyGroup>

</Project>
2 changes: 1 addition & 1 deletion src/CommandLineUtils/Abstractions/IValueParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ public interface IValueParser
/// <param name="culture">The culture that should be used to parse values.</param>
/// <returns>The parsed value object.</returns>
/// <throws name="System.FormatException">When the value cannot be parsed.</throws>
object Parse(string argName, string value, CultureInfo culture);
object? Parse(string? argName, string? value, CultureInfo culture);
}
}
2 changes: 1 addition & 1 deletion src/CommandLineUtils/Abstractions/IValueParser{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ public interface IValueParser<T> : IValueParser
/// <param name="culture">The culture that should be used to parse values.</param>
/// <returns>The parsed value object.</returns>
/// <throws name="System.FormatException">When the value cannot be parsed.</throws>
new T Parse(string argName, string value, CultureInfo culture);
new T Parse(string? argName, string? value, CultureInfo culture);
}
}
24 changes: 24 additions & 0 deletions src/CommandLineUtils/Abstractions/ParseResult.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,37 @@
// Copyright (c) Nate McMaster.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.ComponentModel;

namespace McMaster.Extensions.CommandLineUtils.Abstractions
{
/// <summary>
/// The result of parsing command line arguments.
/// </summary>
public class ParseResult
{
/// <summary>
/// Initializes <see cref="ParseResult"/>.
/// </summary>
/// <param name="selectedCommand">The command selected for execution.</param>
public ParseResult(CommandLineApplication selectedCommand)
{
SelectedCommand = selectedCommand ?? throw new ArgumentNullException(nameof(selectedCommand));
}

#pragma warning disable CS8618 // Non-nullable field is uninitialized.
/// <summary>
/// This constructor is obsolete and will be removed in a future version.
/// The recommended replacement is <see cref="ParseResult(CommandLineApplication)" />
/// </summary>
[Obsolete("This constructor is obsolete and will be removed in a future version. The recommended replacement is ctor(CommandLineApplication selectedCommand)")]
[EditorBrowsable(EditorBrowsableState.Never)]
public ParseResult()
{
}
#pragma warning restore CS8618 // Non-nullable field is uninitialized.

/// <summary>
/// The application or subcommand that matches the command line arguments.
/// </summary>
Expand Down
16 changes: 8 additions & 8 deletions src/CommandLineUtils/Abstractions/ValueParserProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public IValueParser GetParser(Type type)
/// </remarks>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public IValueParser<T> GetParser<T>()
public IValueParser<T>? GetParser<T>()
{
var parser = GetParserImpl<T>();
if (parser == null)
Expand All @@ -87,7 +87,7 @@ public IValueParser<T> GetParser<T>()
return new GenericParserAdapter<T>(parser);
}

internal IValueParser GetParserImpl<T>()
internal IValueParser? GetParserImpl<T>()
{
var type = typeof(T);
if (_parsers.TryGetValue(type, out var parser))
Expand All @@ -102,7 +102,7 @@ internal IValueParser GetParserImpl<T>()
return EnumParser.Create(type);
}

if (ReflectionHelper.IsNullableType(typeInfo, out var wrappedType))
if (ReflectionHelper.IsNullableType(typeInfo, out var wrappedType) && wrappedType != null)
{
if (wrappedType.GetTypeInfo().IsEnum)
{
Expand Down Expand Up @@ -187,7 +187,7 @@ private void SafeAdd(IValueParser parser, bool andReplace = false)
throw new ArgumentNullException(nameof(parser));
}

var targetType = parser.TargetType;
Type targetType = parser.TargetType;

if (targetType == null)
{
Expand All @@ -197,7 +197,7 @@ private void SafeAdd(IValueParser parser, bool andReplace = false)
}

// strip nullable wrappers since we have a dedicated nullable value parser
targetType = ReflectionHelper.IsNullableType(targetType.GetTypeInfo(), out var wrappedType)
targetType = ReflectionHelper.IsNullableType(targetType.GetTypeInfo(), out var wrappedType) && wrappedType != null
? wrappedType
: targetType;

Expand All @@ -219,7 +219,7 @@ private void SafeAdd(IValueParser parser, bool andReplace = false)

private sealed class GenericParserAdapter<T> : IValueParser<T>
{
private IValueParser _inner;
private readonly IValueParser _inner;

public GenericParserAdapter(IValueParser inner)
{
Expand All @@ -228,9 +228,9 @@ public GenericParserAdapter(IValueParser inner)

public Type TargetType => _inner.TargetType;

public T Parse(string argName, string value, CultureInfo culture) => (T)_inner.Parse(argName, value, culture);
public T Parse(string? argName, string? value, CultureInfo culture) => (T)_inner.Parse(argName, value, culture)!;

object IValueParser.Parse(string argName, string value, CultureInfo culture) => _inner.Parse(argName, value, culture);
object? IValueParser.Parse(string? argName, string? value, CultureInfo culture) => _inner.Parse(argName, value, culture);
}
}
}
8 changes: 4 additions & 4 deletions src/CommandLineUtils/Attributes/ArgumentAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public ArgumentAttribute(int order)
/// </summary>
/// <param name="order">The order</param>
/// <param name="name">The name</param>
public ArgumentAttribute(int order, string name)
public ArgumentAttribute(int order, string? name)
: this(order, name, null)
{ }

Expand All @@ -37,7 +37,7 @@ public ArgumentAttribute(int order, string name)
/// <param name="order">The order</param>
/// <param name="name">The name</param>
/// <param name="description">The description</param>
public ArgumentAttribute(int order, string name, string description)
public ArgumentAttribute(int order, string? name, string? description)
{
Order = order;
Name = name;
Expand All @@ -52,7 +52,7 @@ public ArgumentAttribute(int order, string name, string description)
/// <summary>
/// The name of the argument. <seealso cref="CommandArgument.Name"/>.
/// </summary>
public string Name { get; set; }
public string? Name { get; set; }

/// <summary>
/// Determines if the argument appears in the generated help-text. <seealso cref="CommandArgument.ShowInHelpText"/>.
Expand All @@ -62,7 +62,7 @@ public ArgumentAttribute(int order, string name, string description)
/// <summary>
/// A description of the argument. <seealso cref="CommandArgument.Description"/>.
/// </summary>
public string Description { get; set; }
public string? Description { get; set; }

internal CommandArgument Configure(PropertyInfo prop)
{
Expand Down
20 changes: 15 additions & 5 deletions src/CommandLineUtils/Attributes/CommandAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,20 @@ public CommandAttribute(params string[] names)
/// The name of the command line application. When this is a subcommand, it is the name of the word used to invoke the subcommand.
/// <seealso cref="CommandLineApplication.Name" />
/// </summary>
public string Name
public string? Name
{
get => _names.Length > 0 ? _names[0] : null;
set => _names = new[] { value };
set
{
if (value != null)
{
_names = new[] { value };
}
else
{
_names = Util.EmptyArray<string>();
}
}
}

/// <summary>
Expand All @@ -58,12 +68,12 @@ public string Name
/// <summary>
/// The full name of the command line application to show in help text. <seealso cref="CommandLineApplication.FullName" />
/// </summary>
public string FullName { get; set; }
public string? FullName { get; set; }

/// <summary>
/// A description of the command. <seealso cref="CommandLineApplication.Description"/>
/// </summary>
public string Description { get; set; }
public string? Description { get; set; }

/// <summary>
/// Determines if this command appears in generated help text. <seealso cref="CommandLineApplication.ShowInHelpText"/>
Expand All @@ -73,7 +83,7 @@ public string Name
/// <summary>
/// Additional text that appears at the bottom of generated help text. <seealso cref="CommandLineApplication.ExtendedHelpText"/>
/// </summary>
public string ExtendedHelpText { get; set; }
public string? ExtendedHelpText { get; set; }

/// <summary>
/// Throw when unexpected arguments are encountered. <seealso cref="CommandLineApplication.ThrowOnUnexpectedArgument"/>
Expand Down
5 changes: 5 additions & 0 deletions src/CommandLineUtils/Attributes/HelpOptionAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public HelpOptionAttribute(string template)
Description = Strings.DefaultHelpOptionDescription;
}

/// <summary>
/// The option template. This is parsed into the short and long name.
/// </summary>
public new string Template { get; set; }

internal CommandOption Configure(CommandLineApplication app)
{
var opt = app.HelpOption(Template);
Expand Down
2 changes: 1 addition & 1 deletion src/CommandLineUtils/Attributes/OptionAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public OptionAttribute(string template, CommandOptionType optionType)
/// <param name="template">The template</param>
/// <param name="description">The option description</param>
/// <param name="optionType">The option type</param>
public OptionAttribute(string template, string description, CommandOptionType optionType)
public OptionAttribute(string? template, string? description, CommandOptionType optionType)
{
Template = template;
Description = description;
Expand Down
Loading

0 comments on commit cf1947b

Please sign in to comment.