Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Warning AD0001 : Analyzer '' threw an exception of type 'System.NullReferenceException' with message 'Object reference not set to an instance of an object.'. #72046

Closed
thomhurst opened this issue Feb 11, 2024 · 3 comments · Fixed by #73912
Labels
Area-Compilers Concept-API This issue involves adding, removing, clarification, or modification of an API. help wanted The issue is "up for grabs" - add a comment if you are interested in working on it
Milestone

Comments

@thomhurst
Copy link

Version Used:
Microsoft.CodeAnalysis.CSharp 4.3.0

This repository will throw the exception when building:
https://github.com/thomhurst/AD0001.Replication

Basically I'm passing in an argument of null to a params object?[] parameter in an attribute.

The source code for the analyzer that is throwing the exception is here:

using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace TUnit.Analyzers;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class DataDrivenTestArgumentsAnalyzer : ConcurrentDiagnosticAnalyzer
{
    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
        ImmutableArray.Create(Rules.InvalidDataAssertion, Rules.NoDataProvidedAssertion);

    public override void InitializeInternal(AnalysisContext context)
    { 
        context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.MethodDeclaration);
    }
    
    private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
    { 
        if (context.Node is not MethodDeclarationSyntax methodDeclarationSyntax)
        {
            return;
        }

        if (context.SemanticModel.GetDeclaredSymbol(methodDeclarationSyntax)
            is not { } methodSymbol)
        {
            return;
        }

        var attributes = methodSymbol.GetAttributes();
        
        foreach (var dataDrivenTestAttribute in attributes.Where(x => x.AttributeClass?.ToDisplayString(DisplayFormats.FullyQualifiedNonGenericWithGlobalPrefix)
                                                == "global::TUnit.Core.DataDrivenTestAttribute"))
        {
            CheckAttributeAgainstMethod(context, methodSymbol, dataDrivenTestAttribute);
        }
    }

    private void CheckAttributeAgainstMethod(SyntaxNodeAnalysisContext context, IMethodSymbol methodSymbol,
        AttributeData dataDrivenTestAttribute)
    {
        if (!dataDrivenTestAttribute.ConstructorArguments.Any())
        {
            context.ReportDiagnostic(
                Diagnostic.Create(Rules.NoDataProvidedAssertion,
                    dataDrivenTestAttribute.ApplicationSyntaxReference?.GetSyntax().GetLocation())
            );
            return;
        }

        var methodParameterTypes = methodSymbol.Parameters.Select(x => x.Type).ToList();
        var attributeTypesPassedIn = dataDrivenTestAttribute.ConstructorArguments.First().Values.Select(x => x.Type).ToList();
        
        if (methodParameterTypes.Count != attributeTypesPassedIn.Count)
        {
            context.ReportDiagnostic(
                Diagnostic.Create(Rules.InvalidDataAssertion,
                    dataDrivenTestAttribute.ApplicationSyntaxReference?.GetSyntax().GetLocation(),
                    string.Join(", ", attributeTypesPassedIn.Select(x => x?.ToDisplayString())),
                    string.Join(", ", methodParameterTypes.Select(x => x?.ToDisplayString())))
            );
            return;
        }
        
        for (var i = 0; i < methodParameterTypes.Count; i++)
        {
            var methodParameterType = methodParameterTypes[i];
            var attributeArgumentType = attributeTypesPassedIn[i];
            
            if (IsEnumAndInteger(methodParameterType, attributeArgumentType))
            {
                continue;
            }
            
            if (!context.Compilation.HasImplicitConversion(attributeArgumentType, methodParameterType))
            {
                context.ReportDiagnostic(
                    Diagnostic.Create(Rules.InvalidDataAssertion,
                        dataDrivenTestAttribute.ApplicationSyntaxReference?.GetSyntax().GetLocation(),
                        attributeArgumentType?.ToDisplayString(),
                        methodParameterType?.ToDisplayString())
                );
            }
        }
    }

    private bool IsEnumAndInteger(ITypeSymbol type1, ITypeSymbol? type2)
    {
        if (type1.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == "int")
        {
            return type2?.TypeKind == TypeKind.Enum;
        }
        
        if (type2?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == "int")
        {
            return type1.TypeKind == TypeKind.Enum;
        }

        return false;
    }
}

This code has NO nullability warnings and I am also not suppressing any nullability warnings. Yet it is apparently throwing a NullReferenceException.

My best guess is that this line is the issue? But I'm really not sure.

dataDrivenTestAttribute.ConstructorArguments.First().Values.Select(x => x.Type).ToList();
@dotnet-issue-labeler dotnet-issue-labeler bot added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels Feb 11, 2024
@CyrusNajmabadi
Copy link
Member

Can you create a unit test to find exactly what the issue is? Thanks!

@thomhurst
Copy link
Author

System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Collections.Immutable.ImmutableArray`1.ThrowNullRefIfNotInitialized()
   at System.Linq.ImmutableArrayExtensions.Select[T,TResult](ImmutableArray`1 immutableArray, Func`2 selector)

@333fred
Copy link
Member

333fred commented Feb 12, 2024

We'd be happy for a doc clarification here, but the behavior of the API itself is expected. I'll leave this open for such a change.

@333fred 333fred added help wanted The issue is "up for grabs" - add a comment if you are interested in working on it Concept-API This issue involves adding, removing, clarification, or modification of an API. and removed untriaged Issues and PRs which have not yet been triaged by a lead labels Feb 12, 2024
@333fred 333fred added this to the Backlog milestone Feb 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Concept-API This issue involves adding, removing, clarification, or modification of an API. help wanted The issue is "up for grabs" - add a comment if you are interested in working on it
Projects
None yet
3 participants