Skip to content

Commit

Permalink
feat(csharp): enable nullable reference types (#1246)
Browse files Browse the repository at this point in the history
This allows users to write safter code by being aware of what is nullable
and what is not. This does not affect the ABI of the libraries, making them
compatible with previous assemblies -- since this is merely additional
metadata in the assembly that enables new compiler warnings.
  • Loading branch information
RomainMuller authored Feb 11, 2020
1 parent c324439 commit cbc7258
Show file tree
Hide file tree
Showing 415 changed files with 1,460 additions and 676 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<AssemblyName>Amazon.JSII.Analyzers.UnitTests</AssemblyName>

<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public abstract partial class DiagnosticVerifier
/// <param name="language">The language the source classes are in</param>
/// <param name="analyzer">The analyzer to be run on the sources</param>
/// <returns>An IEnumerable of Diagnostics that surfaced in the source code, sorted by Location</returns>
private static Diagnostic[] GetSortedDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer)
private static Diagnostic[] GetSortedDiagnostics(string[] sources, string language, DiagnosticAnalyzer? analyzer)
{
return GetSortedDiagnosticsFromDocuments(analyzer, GetDocuments(sources, language));
}
Expand All @@ -46,7 +46,7 @@ private static Diagnostic[] GetSortedDiagnostics(string[] sources, string langua
/// <param name="analyzer">The analyzer to run on the documents</param>
/// <param name="documents">The Documents that the analyzer will be run on</param>
/// <returns>An IEnumerable of Diagnostics that surfaced in the source code, sorted by Location</returns>
protected static Diagnostic[] GetSortedDiagnosticsFromDocuments(DiagnosticAnalyzer analyzer, Document[] documents)
protected static Diagnostic[] GetSortedDiagnosticsFromDocuments(DiagnosticAnalyzer? analyzer, Document[] documents)
{
var projects = new HashSet<Project>();
foreach (var document in documents)
Expand Down Expand Up @@ -162,7 +162,7 @@ private static Project CreateProject(string[] sources, string language = Languag
solution = solution.AddDocument(documentId, newFileName, SourceText.From(source));
count++;
}
return solution.GetProject(projectId);
return solution.GetProject(projectId)!;
}
#endregion
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ public abstract partial class DiagnosticVerifier
/// <summary>
/// Get the CSharp analyzer being tested - to be implemented in non-abstract class
/// </summary>
protected virtual DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
protected virtual DiagnosticAnalyzer? GetCSharpDiagnosticAnalyzer()
{
return null;
}

/// <summary>
/// Get the Visual Basic analyzer being tested (C#) - to be implemented in non-abstract class
/// </summary>
protected virtual DiagnosticAnalyzer GetBasicDiagnosticAnalyzer()
protected virtual DiagnosticAnalyzer? GetBasicDiagnosticAnalyzer()
{
return null;
}
Expand Down Expand Up @@ -84,7 +84,7 @@ protected void VerifyBasicDiagnostic(string[] sources, params DiagnosticResult[]
/// <param name="language">The language of the classes represented by the source strings</param>
/// <param name="analyzer">The analyzer to be run on the source code</param>
/// <param name="expected">DiagnosticResults that should appear after the analyzer is run on the sources</param>
private void VerifyDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expected)
private void VerifyDiagnostics(string[] sources, string language, DiagnosticAnalyzer? analyzer, params DiagnosticResult[] expected)
{
var diagnostics = GetSortedDiagnostics(sources, language, analyzer);
VerifyDiagnosticResults(diagnostics, analyzer, expected);
Expand All @@ -100,7 +100,7 @@ private void VerifyDiagnostics(string[] sources, string language, DiagnosticAnal
/// <param name="actualResults">The Diagnostics found by the compiler after running the analyzer on the source code</param>
/// <param name="analyzer">The analyzer that was being run on the sources</param>
/// <param name="expectedResults">Diagnostic Results that should have appeared in the code</param>
private static void VerifyDiagnosticResults(IEnumerable<Diagnostic> actualResults, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expectedResults)
private static void VerifyDiagnosticResults(IEnumerable<Diagnostic> actualResults, DiagnosticAnalyzer? analyzer, params DiagnosticResult[] expectedResults)
{
int expectedCount = expectedResults.Count();
int actualCount = actualResults.Count();
Expand Down Expand Up @@ -176,7 +176,7 @@ private static void VerifyDiagnosticResults(IEnumerable<Diagnostic> actualResult
/// <param name="diagnostic">The diagnostic that was found in the code</param>
/// <param name="actual">The Location of the Diagnostic found in the code</param>
/// <param name="expected">The DiagnosticResultLocation that should have been found</param>
private static void VerifyDiagnosticLocation(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Location actual, DiagnosticResultLocation expected)
private static void VerifyDiagnosticLocation(DiagnosticAnalyzer? analyzer, Diagnostic diagnostic, Location actual, DiagnosticResultLocation expected)
{
var actualSpan = actual.GetLineSpan();

Expand Down Expand Up @@ -217,15 +217,19 @@ private static void VerifyDiagnosticLocation(DiagnosticAnalyzer analyzer, Diagno
/// <param name="analyzer">The analyzer that this verifier tests</param>
/// <param name="diagnostics">The Diagnostics to be formatted</param>
/// <returns>The Diagnostics formatted as a string</returns>
private static string FormatDiagnostics(DiagnosticAnalyzer analyzer, params Diagnostic[] diagnostics)
private static string FormatDiagnostics(DiagnosticAnalyzer? analyzer, params Diagnostic[] diagnostics)
{
var builder = new StringBuilder();
for (int i = 0; i < diagnostics.Length; ++i)
{
builder.AppendLine("// " + diagnostics[i].ToString());

var analyzerType = analyzer.GetType();
var rules = analyzer.SupportedDiagnostics;
var analyzerType = analyzer?.GetType();
if (analyzerType == null)
{
continue;
}
var rules = analyzer!.SupportedDiagnostics;

foreach (var rule in rules)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
/// <returns>true if the TypeInfo is related to a Jsii class, false otherwise</returns>
private static bool IsJsiiClass(TypeInfo typeInfo)
{
if (typeInfo.Type == null)
{
return false;
}
var typeAttributes = typeInfo.Type.GetAttributes().ToArray();
return typeAttributes.Any(a => a.AttributeClass.Name == "JsiiClassAttribute");
}
Expand All @@ -119,6 +123,10 @@ private static bool IsJsiiClass(TypeInfo typeInfo)
/// <returns>true if the TypeInfo is related to a Jsii datatype, false otherwise</returns>
private static bool IsJsiiDatatype(TypeInfo typeInfo)
{
if (typeInfo.Type == null)
{
return false;
}
var typeAttributes = typeInfo.Type.GetAttributes().ToArray();
return typeAttributes.Any(a => a.AttributeClass.Name == "JsiiByValueAttribute");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
<IsPackable>false</IsPackable>
<AssemblyName>Amazon.JSII.JsonModel.UnitTests</AssemblyName>
<RootNamespace>Amazon.JSII.JsonModel.UnitTests</RootNamespace>

<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ public void ShouldThrowOnMissingName()
{
Assert.Throws<ArgumentNullException>(() => new Assembly
(
#pragma warning disable CS8625
name: null,
#pragma warning restore CS8625
description: "",
homepage: "",
repository: new Assembly.AssemblyRepository(type: "", url: ""),
Expand Down Expand Up @@ -140,7 +142,9 @@ public void ShouldThrowOnMissingVersion()
repository: new Assembly.AssemblyRepository(type: "", url: ""),
author: new Person(name: "", roles: new string[] { }),
fingerprint: "",
#pragma warning disable CS8625
version: null,
#pragma warning restore CS8625
license: "",
targets: new AssemblyTargets(new AssemblyTargets.DotNetTarget(
@namespace: "Dot.Net.Namespace",
Expand Down Expand Up @@ -375,7 +379,7 @@ public void ShouldDeserializeAllMembers()
Assert.Empty(actual.Types);
Assert.Empty(actual.Dependencies);
Assert.Empty(actual.Bundled);
Assert.Equal("hello", actual.Docs.Summary);
Assert.Equal("hello", actual.Docs?.Summary);
}

[Fact(DisplayName = Prefix + nameof(ShouldDeserializeAllMembersWithNoTypes))]
Expand Down Expand Up @@ -411,7 +415,7 @@ public void ShouldDeserializeAllMembersWithNoTypes()
Assert.Null(actual.Types);
Assert.Empty(actual.Dependencies);
Assert.Empty(actual.Bundled);
Assert.Equal("hello", actual.Docs.Summary);
Assert.Equal("hello", actual.Docs?.Summary);
}


Expand Down Expand Up @@ -455,19 +459,19 @@ public void ShouldDeserializeAllMembersWithDotNetTarget()
Assert.Equal("jsii/0.9.0", actual.Schema, ignoreLineEndingDifferences: true);
Assert.Equal("myName", actual.Name, ignoreLineEndingDifferences: true);

AssemblyTargets.DotNetTarget dotNetTarget = actual.Targets.DotNet;
AssemblyTargets.DotNetTarget? dotNetTarget = actual.Targets?.DotNet;
Assert.NotNull(dotNetTarget);
Assert.Equal("Dot.Net.Namespace", dotNetTarget.Namespace);
Assert.Equal("Dot.Net.PackageId", dotNetTarget.PackageId);
Assert.True(dotNetTarget.SignAssembly);
Assert.Equal("key.snk", dotNetTarget.AssemblyOriginatorKeyFile);
Assert.Equal("http://www.example.com/icon.png", dotNetTarget.IconUrl);
Assert.Equal("Dot.Net.Namespace", dotNetTarget?.Namespace);
Assert.Equal("Dot.Net.PackageId", dotNetTarget?.PackageId);
Assert.True(dotNetTarget?.SignAssembly);
Assert.Equal("key.snk", dotNetTarget?.AssemblyOriginatorKeyFile);
Assert.Equal("http://www.example.com/icon.png", dotNetTarget?.IconUrl);

Assert.Equal("myVersion", actual.Version, ignoreLineEndingDifferences: true);
Assert.Empty(actual.Types);
Assert.Empty(actual.Dependencies);
Assert.Empty(actual.Bundled);
Assert.Equal("hello", actual.Docs.Summary);
Assert.Equal("hello", actual.Docs?.Summary);
}

[Fact(DisplayName = Prefix + nameof(ShouldThrowOnMissingName))]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Amazon.JSII.JsonModel.Spec;
using Amazon.JSII.JsonModel.Spec;
using Newtonsoft.Json;
using System;
using System.Linq;
Expand Down Expand Up @@ -56,7 +56,9 @@ public void ShouldThrowOnMissingFullyQualifiedName()
{
Assert.Throws<ArgumentNullException>(() => new ClassType
(
#pragma warning disable CS8625
fullyQualifiedName: null,
#pragma warning restore CS8625
assembly: "myModule",
name: "myName",
@namespace: "myNamespace",
Expand All @@ -76,7 +78,9 @@ public void ShouldThrowOnMissingAssembly()
Assert.Throws<ArgumentNullException>(() => new ClassType
(
fullyQualifiedName: "myFqn",
#pragma warning disable CS8625
assembly: null,
#pragma warning restore CS8625
name: "myName",
@namespace: "myNamespace",
isAbstract: true,
Expand All @@ -96,7 +100,9 @@ public void ShouldThrowOnMissingName()
(
fullyQualifiedName: "myFqn",
assembly: "myModule",
#pragma warning disable CS8625
name: null,
#pragma warning restore CS8625
@namespace: "myNamespace",
isAbstract: true,
docs: new Docs(),
Expand Down Expand Up @@ -388,7 +394,7 @@ public void ShouldDeserializeAllMembers()
Assert.Equal("myName", actual.Name, ignoreLineEndingDifferences: true);
Assert.Equal("myNamespace", actual.Namespace, ignoreLineEndingDifferences: true);
Assert.Equal(TypeKind.Class, actual.Kind);
Assert.Equal("hello", actual.Docs.Summary);
Assert.Equal("hello", actual.Docs?.Summary);
}

[Fact(DisplayName = Prefix + nameof(ShouldThrowOnMissingFullyQualifiedName))]
Expand Down Expand Up @@ -623,8 +629,8 @@ public void DeserializesAsyncMethod()

ClassType actual = JsonConvert.DeserializeObject<ClassType>(json);

Assert.True(actual.Methods.Length == 1);
Assert.True(actual.Methods[0]?.IsAsync);
Assert.Equal(1, actual.Methods?.Length);
Assert.True(actual.Methods?[0]?.IsAsync);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Amazon.JSII.JsonModel.Spec;
using Amazon.JSII.JsonModel.Spec;
using Newtonsoft.Json;
using System;
using Xunit;
Expand Down Expand Up @@ -41,7 +41,9 @@ public void ShouldDeserialize(string kind, CollectionKind expected)
[Fact(DisplayName = Prefix + nameof(ShouldThrowOnNull))]
public void ShouldThrowOnNull()
{
#pragma warning disable CS8625
Assert.Throws<ArgumentNullException>(() => JsonConvert.DeserializeObject<CollectionKind>(null));
#pragma warning restore CS8625
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Amazon.JSII.JsonModel.Spec;
using Amazon.JSII.JsonModel.Spec;
using Newtonsoft.Json;
using System;
using Xunit;
Expand Down Expand Up @@ -36,7 +36,9 @@ public void ShouldSerializeAllMembers()
[Fact(DisplayName = Prefix + nameof(ShouldThrowOnMissingElementType))]
public void ShouldThrowOnMissingElementType()
{
#pragma warning disable CS8625
Assert.Throws<ArgumentNullException>(() => new CollectionTypeReference(CollectionKind.Array, null));
#pragma warning restore CS8625
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Amazon.JSII.JsonModel.Spec;
using Amazon.JSII.JsonModel.Spec;
using Newtonsoft.Json;
using System;
using Xunit;
Expand Down Expand Up @@ -30,7 +30,9 @@ public void ShouldSerializeAllMembers()
[Fact(DisplayName = Prefix + nameof(ShouldThrowOnMissingName))]
public void ShouldThrowOnMissingName()
{
#pragma warning disable CS8625
Assert.Throws<ArgumentNullException>(() => new EnumMember(null, new Docs()));
#pragma warning restore CS8625
}

[Fact(DisplayName = Prefix + nameof(ShouldNotSerializeMissingDocs))]
Expand Down Expand Up @@ -61,8 +63,8 @@ public void ShouldDeserializeAllMembers()

EnumMember actual = JsonConvert.DeserializeObject<EnumMember>(json);

Assert.Equal("myName", actual.Name, ignoreLineEndingDifferences: true);
Assert.Equal("hello", actual.Docs.Summary);
Assert.Equal("myName", actual?.Name, ignoreLineEndingDifferences: true);
Assert.Equal("hello", actual?.Docs?.Summary);
}

[Fact(DisplayName = Prefix + nameof(ShouldThrowOnMissingName))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
<PackageId>Amazon.JSII.JsonModel</PackageId>
<Title>.NET JsonModel for JSII</Title>
<PackageIcon>icon.png</PackageIcon>

<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ public class Callback
public Callback
(
string callbackId,
string cookie = null,
InvokeRequest invoke = null,
GetRequest get = null,
SetRequest set = null
string? cookie = null,
InvokeRequest? invoke = null,
GetRequest? get = null,
SetRequest? set = null
)
{
CallbackId = callbackId ?? throw new ArgumentNullException(nameof(callbackId));
Expand All @@ -27,15 +27,15 @@ public Callback
public string CallbackId { get; }

[JsonProperty("cookie", NullValueHandling = NullValueHandling.Ignore)]
public string Cookie { get; }
public string? Cookie { get; }

[JsonProperty("invoke", NullValueHandling = NullValueHandling.Ignore)]
public InvokeRequest Invoke { get; }
public InvokeRequest? Invoke { get; }

[JsonProperty("get", NullValueHandling = NullValueHandling.Ignore)]
public GetRequest Get { get; }
public GetRequest? Get { get; }

[JsonProperty("set", NullValueHandling = NullValueHandling.Ignore)]
public SetRequest Set { get; }
public SetRequest? Set { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ public class Override
{
public Override
(
string method = null,
string property = null,
string cookie = null
string? method = null,
string? property = null,
string? cookie = null
)
{
Method = method;
Expand All @@ -18,12 +18,12 @@ public Override
}

[JsonProperty("method", NullValueHandling = NullValueHandling.Ignore)]
public string Method { get; }
public string? Method { get; }

[JsonProperty("property", NullValueHandling = NullValueHandling.Ignore)]
public string Property { get; }
public string? Property { get; }

[JsonProperty("cookie", NullValueHandling = NullValueHandling.Ignore)]
public string Cookie { get; }
public string? Cookie { get; }
}
}
Loading

0 comments on commit cbc7258

Please sign in to comment.