Skip to content

Commit

Permalink
Enable nullability checks across DllImportGenerator (#213)
Browse files Browse the repository at this point in the history
  • Loading branch information
jkoritzinsky authored Oct 20, 2020
1 parent 76354fe commit 7b2591d
Show file tree
Hide file tree
Showing 14 changed files with 40 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#nullable enable

namespace System.Runtime.InteropServices
{
Expand Down
1 change: 1 addition & 0 deletions DllImportGenerator/Demo/Demo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<TargetFramework>net5.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>preview</LangVersion>
<Nullable>enable</Nullable>

<!-- Indicate to the compiler to output generated files to disk. Source addded
by the DllImportGenerator can be found in the intermediate directory. -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<IsPackable>false</IsPackable>
<LangVersion>Preview</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<LangVersion>Preview</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ public Test()
ReferenceAssemblies = refAssem;
SolutionTransforms.Add((solution, projectId) =>
{
var project = solution.GetProject(projectId);
var compilationOptions = project.CompilationOptions;
var project = solution.GetProject(projectId)!;
var compilationOptions = project.CompilationOptions!;
compilationOptions = compilationOptions.WithSpecificDiagnosticOptions(
compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings));
solution = solution.WithProjectCompilationOptions(projectId, compilationOptions);
Expand Down
36 changes: 18 additions & 18 deletions DllImportGenerator/DllImportGenerator/DllImportGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,17 @@ public void Execute(GeneratorExecutionContext context)
}

// Process the method syntax and get its SymbolInfo.
var methodSymbolInfo = sm.GetDeclaredSymbol(methodSyntax, context.CancellationToken);
var methodSymbolInfo = sm.GetDeclaredSymbol(methodSyntax, context.CancellationToken)!;

// Process the attributes on the method.
DllImportStub.GeneratedDllImportData dllImportData;
AttributeSyntax dllImportAttr = this.ProcessAttributes(methodSymbolInfo, context.CancellationToken, out dllImportData);
Debug.Assert(!(dllImportAttr is null) && !(dllImportData is null));
Debug.Assert((dllImportAttr is not null) && (dllImportData is not null));

// Create the stub.
var dllImportStub = DllImportStub.Create(methodSymbolInfo, dllImportData, context.Compilation, generatorDiagnostics, context.CancellationToken);
var dllImportStub = DllImportStub.Create(methodSymbolInfo, dllImportData!, context.Compilation, generatorDiagnostics, context.CancellationToken);

PrintGeneratedSource(generatedDllImports, methodSyntax, dllImportStub, dllImportAttr);
PrintGeneratedSource(generatedDllImports, methodSyntax, dllImportStub, dllImportAttr!);
}

Debug.WriteLine(generatedDllImports.ToString()); // [TODO] Find some way to emit this for debugging - logs?
Expand Down Expand Up @@ -161,7 +161,7 @@ private AttributeSyntax ProcessAttributes(

// Found the GeneratedDllImport, but it has an error so report the error.
// This is most likely an issue with targeting an incorrect TFM.
if (attrData.AttributeClass.TypeKind == TypeKind.Error)
if (attrData.AttributeClass?.TypeKind is null or TypeKind.Error)
{
// [TODO] Report GeneratedDllImport has an error - corrupt metadata?
throw new InvalidProgramException();
Expand All @@ -170,7 +170,7 @@ private AttributeSyntax ProcessAttributes(
var newAttributeArgs = new List<AttributeArgumentSyntax>();

// Populate the DllImport data from the GeneratedDllImportAttribute attribute.
dllImportData.ModuleName = attrData.ConstructorArguments[0].Value.ToString();
dllImportData.ModuleName = attrData.ConstructorArguments[0].Value!.ToString();

newAttributeArgs.Add(SyntaxFactory.AttributeArgument(SyntaxFactory.LiteralExpression(
SyntaxKind.StringLiteralExpression,
Expand All @@ -179,60 +179,60 @@ private AttributeSyntax ProcessAttributes(
// All other data on attribute is defined as NamedArguments.
foreach (var namedArg in attrData.NamedArguments)
{
ExpressionSyntax expSyntaxMaybe = null;
ExpressionSyntax? expSyntaxMaybe = null;
switch (namedArg.Key)
{
default:
Debug.Fail($"An unknown member was found on {GeneratedDllImport}");
continue;
case nameof(DllImportStub.GeneratedDllImportData.BestFitMapping):
dllImportData.BestFitMapping = (bool)namedArg.Value.Value;
dllImportData.BestFitMapping = (bool)namedArg.Value.Value!;
expSyntaxMaybe = CreateBoolExpressionSyntax(dllImportData.BestFitMapping);
dllImportData.IsUserDefined |= DllImportStub.DllImportMember.BestFitMapping;
break;
case nameof(DllImportStub.GeneratedDllImportData.CallingConvention):
dllImportData.CallingConvention = (CallingConvention)namedArg.Value.Value;
dllImportData.CallingConvention = (CallingConvention)namedArg.Value.Value!;
expSyntaxMaybe = CreateEnumExpressionSyntax(dllImportData.CallingConvention);
dllImportData.IsUserDefined |= DllImportStub.DllImportMember.CallingConvention;
break;
case nameof(DllImportStub.GeneratedDllImportData.CharSet):
dllImportData.CharSet = (CharSet)namedArg.Value.Value;
dllImportData.CharSet = (CharSet)namedArg.Value.Value!;
expSyntaxMaybe = CreateEnumExpressionSyntax(dllImportData.CharSet);
dllImportData.IsUserDefined |= DllImportStub.DllImportMember.CharSet;
break;
case nameof(DllImportStub.GeneratedDllImportData.EntryPoint):
dllImportData.EntryPoint = (string)namedArg.Value.Value;
expSyntaxMaybe = CreateStringExpressionSyntax(dllImportData.EntryPoint);
dllImportData.EntryPoint = (string)namedArg.Value.Value!;
expSyntaxMaybe = CreateStringExpressionSyntax(dllImportData.EntryPoint!);
dllImportData.IsUserDefined |= DllImportStub.DllImportMember.EntryPoint;
break;
case nameof(DllImportStub.GeneratedDllImportData.ExactSpelling):
dllImportData.ExactSpelling = (bool)namedArg.Value.Value;
dllImportData.ExactSpelling = (bool)namedArg.Value.Value!;
expSyntaxMaybe = CreateBoolExpressionSyntax(dllImportData.ExactSpelling);
dllImportData.IsUserDefined |= DllImportStub.DllImportMember.ExactSpelling;
break;
case nameof(DllImportStub.GeneratedDllImportData.PreserveSig):
dllImportData.PreserveSig = (bool)namedArg.Value.Value;
dllImportData.PreserveSig = (bool)namedArg.Value.Value!;
expSyntaxMaybe = CreateBoolExpressionSyntax(dllImportData.PreserveSig);
dllImportData.IsUserDefined |= DllImportStub.DllImportMember.PreserveSig;
break;
case nameof(DllImportStub.GeneratedDllImportData.SetLastError):
dllImportData.SetLastError = (bool)namedArg.Value.Value;
dllImportData.SetLastError = (bool)namedArg.Value.Value!;
expSyntaxMaybe = CreateBoolExpressionSyntax(dllImportData.SetLastError);
dllImportData.IsUserDefined |= DllImportStub.DllImportMember.SetLastError;
break;
case nameof(DllImportStub.GeneratedDllImportData.ThrowOnUnmappableChar):
dllImportData.ThrowOnUnmappableChar = (bool)namedArg.Value.Value;
dllImportData.ThrowOnUnmappableChar = (bool)namedArg.Value.Value!;
expSyntaxMaybe = CreateBoolExpressionSyntax(dllImportData.ThrowOnUnmappableChar);
dllImportData.IsUserDefined |= DllImportStub.DllImportMember.ThrowOnUnmappableChar;
break;
}

Debug.Assert(!(expSyntaxMaybe is null));
Debug.Assert(expSyntaxMaybe is not null);

// Defer the name equals syntax till we know the value means something. If we created
// an expression we know the key value was valid.
NameEqualsSyntax nameSyntax = SyntaxFactory.NameEquals(namedArg.Key);
newAttributeArgs.Add(SyntaxFactory.AttributeArgument(nameSyntax, null, expSyntaxMaybe));
newAttributeArgs.Add(SyntaxFactory.AttributeArgument(nameSyntax, null, expSyntaxMaybe!));
}

// If the EntryPoint property is not set, we will compute and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>Preview</LangVersion>
<Nullable>enable</Nullable>
<RootNamespace>Microsoft.Interop</RootNamespace>

<!-- Uncomment to generate stub code that simply forwards parameters to p/invoke (i.e. no marshalling) -->
Expand Down
13 changes: 9 additions & 4 deletions DllImportGenerator/DllImportGenerator/DllImportStub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@ internal class DllImportStub
private TypePositionInfo returnTypeInfo;
private IEnumerable<TypePositionInfo> paramsTypeInfo;

// We don't need the warnings around not setting the various
// non-nullable fields/properties on this type in the constructor
// since we always use a property initializer.
#pragma warning disable 8618
private DllImportStub()
{
}
#pragma warning restore

public string StubTypeNamespace { get; private set; }
public string? StubTypeNamespace { get; private set; }

public IEnumerable<TypeDeclarationSyntax> StubContainingTypes { get; private set; }

Expand Down Expand Up @@ -75,7 +80,7 @@ public enum DllImportMember
/// </remarks>
public class GeneratedDllImportData
{
public string ModuleName { get; set; }
public string ModuleName { get; set; } = null!;

/// <summary>
/// Value set by the user on the original declaration.
Expand All @@ -88,7 +93,7 @@ public class GeneratedDllImportData
public bool BestFitMapping { get; set; } = true;
public CallingConvention CallingConvention { get; set; } = CallingConvention.Winapi;
public CharSet CharSet { get; set; } = CharSet.Ansi;
public string EntryPoint { get; set; } = null;
public string EntryPoint { get; set; } = null!;
public bool ExactSpelling { get; set; } = false; // VB has different and unusual default behavior here.
public bool PreserveSig { get; set; } = true;
public bool SetLastError { get; set; } = false;
Expand All @@ -106,7 +111,7 @@ public static DllImportStub Create(
token.ThrowIfCancellationRequested();

// Determine the namespace
string stubTypeNamespace = null;
string? stubTypeNamespace = null;
if (!(method.ContainingNamespace is null)
&& !method.ContainingNamespace.IsGlobalNamespace)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;

#nullable enable

namespace Microsoft.Interop
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#nullable enable

using System.Linq;
using Microsoft.CodeAnalysis;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public MarshallingNotSupportedException(TypePositionInfo info, StubCodeContext c
/// <summary>
/// [Optional] Specific reason marshalling of the supplied type isn't supported.
/// </summary>
public string NotSupportedDetails { get; init; }
public string? NotSupportedDetails { get; init; }
}

internal class MarshallingGenerators
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
using System.Diagnostics;
using System.Runtime.InteropServices;

#nullable enable

namespace Microsoft.Interop
{
// The following types are modeled to fit with the current prospective spec
Expand Down
9 changes: 6 additions & 3 deletions DllImportGenerator/DllImportGenerator/TypePositionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,17 @@ internal sealed class TypePositionInfo
public const int UnsetIndex = int.MinValue;
public const int ReturnIndex = UnsetIndex + 1;

// We don't need the warnings around not setting the various
// non-nullable fields/properties on this type in the constructor
// since we always use a property initializer.
#pragma warning disable 8618
private TypePositionInfo()
{
this.ManagedIndex = UnsetIndex;
this.NativeIndex = UnsetIndex;
this.UnmanagedLCIDConversionArgIndex = UnsetIndex;
}
#pragma warning restore

public string InstanceIdentifier { get; private set; }
public ITypeSymbol ManagedType { get; private set; }
Expand All @@ -46,7 +51,7 @@ private TypePositionInfo()
public int NativeIndex { get; set; }
public int UnmanagedLCIDConversionArgIndex { get; private set; }

public MarshallingInfo MarshallingAttributeInfo { get; private set; }
public MarshallingInfo? MarshallingAttributeInfo { get; private set; }

public static TypePositionInfo CreateForParameter(IParameterSymbol paramSymbol, DefaultMarshallingInfo defaultInfo, Compilation compilation, GeneratorDiagnostics diagnostics)
{
Expand Down Expand Up @@ -78,7 +83,6 @@ public static TypePositionInfo CreateForType(ITypeSymbol type, IEnumerable<Attri
return typeInfo;
}

#nullable enable
private static MarshallingInfo? GetMarshallingInfo(ITypeSymbol type, IEnumerable<AttributeData> attributes, DefaultMarshallingInfo defaultInfo, Compilation compilation, GeneratorDiagnostics diagnostics)
{
MarshallingInfo? marshallingInfo = null;
Expand Down Expand Up @@ -276,7 +280,6 @@ static NativeMarshallingAttributeInfo CreateNativeMarshallingInfo(ITypeSymbol ty
return null;
}
}
#nullable restore

private static SyntaxKind RefKindToSyntax(RefKind refKind)
{
Expand Down
2 changes: 0 additions & 2 deletions DllImportGenerator/DllImportGenerator/TypeSymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

#nullable enable

namespace Microsoft.Interop
{
static class TypeSymbolExtensions
Expand Down

0 comments on commit 7b2591d

Please sign in to comment.