Skip to content

Commit

Permalink
Enhanced analyzer for derived types
Browse files Browse the repository at this point in the history
  • Loading branch information
Laszlo Deak committed Feb 28, 2023
1 parent 7e2881a commit e105e96
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 13 deletions.
111 changes: 109 additions & 2 deletions ProtobufSourceGenerator.Tests/AnalyzerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,8 @@ public partial class Derived : Base
await test.RunAsync();
}


[Fact]
public async Task ProtoIncludeBaseTypeNonGenerated_IssuesWarning()
public async Task ProtoIncludeBaseTypeNonGenerated_NoWarning()
{
string code = @"namespace Test;
[ProtoBuf.ProtoContract]
Expand All @@ -287,6 +286,114 @@ public partial class Base
}
[ProtoBuf.ProtoContract]
public partial class Derived : Base
{
public int Id { get; init; }
}";
var test = new AnalyzeCS() { TestCode = code };
await test.RunAsync();
}

[Fact]
public async Task NonGeneratingDerived_BaseType_NoWarning()
{
string code = @"namespace Test;
public class Base
{
public int Value { get; set; }
}
public class Derived : Base
{
public int Id { get; init; }
}";
var test = new AnalyzeCS() { TestCode = code };
await test.RunAsync();
}

[Fact]
public async Task ProtoIncludeNotPartial_BaseType_NoWarning()
{
string code = @"namespace Test;
[ProtoBuf.ProtoContract]
[ProtoBuf.ProtoInclude(10, typeof(Derived))]
public class Base
{
public int Value { get; set; }
}
[ProtoBuf.ProtoContract]
public partial class Derived : Base
{
public int Id { get; init; }
}";
var test = new AnalyzeCS() { TestCode = code };
await test.RunAsync();
}

[Fact]
public async Task ProtoInclude_NotMatchingDerivedType_Warning()
{
string code = @"namespace Test;
[ProtoBuf.ProtoContract]
[ProtoBuf.ProtoInclude(10, typeof(SomeOther))]
public class Base
{
public int Value { get; set; }
}
public class SomeOther : Base
{
public int Data { get; set; }
}
[ProtoBuf.ProtoContract]
public partial class Derived : Base
{
public int Id { get; init; }
}";
var test = new AnalyzeCS() { TestCode = code };
DiagnosticResult expected = VerifyCS.Diagnostic("Proto04").WithLocation(13, 22).WithArguments(string.Empty);
test.ExpectedDiagnostics.Add(expected);
await test.RunAsync();
}

[Fact]
public async Task ProtoInclude_NotMatchingDerivedTypeString_Warning()
{
string code = @"namespace Test;
[ProtoBuf.ProtoContract]
[ProtoBuf.ProtoInclude(10, ""Test.SomeOther"")]
public class Base
{
public int Value { get; set; }
}
public class SomeOther : Base
{
public int Data { get; set; }
}
[ProtoBuf.ProtoContract]
public partial class Derived : Base
{
public int Id { get; init; }
}";
var test = new AnalyzeCS() { TestCode = code };
DiagnosticResult expected = VerifyCS.Diagnostic("Proto04").WithLocation(13, 22).WithArguments(string.Empty);
test.ExpectedDiagnostics.Add(expected);
await test.RunAsync();
}

[Fact]
public async Task ProtoInclude_MatchingDerivedTypeString_NoWarning()
{
string code = @"namespace Test;
[ProtoBuf.ProtoContract]
[ProtoBuf.ProtoInclude(10, ""Test.Derived"")]
public class Base
{
public int Value { get; set; }
}
public class SomeOther : Base
{
public int Data { get; set; }
}
[ProtoBuf.ProtoContract]
public partial class Derived : Base
{
public int Id { get; init; }
}";
Expand Down
40 changes: 32 additions & 8 deletions ProtobufSourceGenerator/Analyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,35 @@ public override void Initialize(AnalysisContext context)

private void AnalyzeTypeHierarchy(SymbolAnalysisContext context)
{
if (context.Symbol is not INamedTypeSymbol analyzedTypeSymbol)
if (context.Symbol is not INamedTypeSymbol namedType)
return;

var typeSymbol = analyzedTypeSymbol.BaseType;
ValidateBaseTypes(context, namedType);
ValidateNestedTypes(context, namedType);
}

private void ValidateBaseTypes(SymbolAnalysisContext context, INamedTypeSymbol namedType)
{
if (!IsPartial(namedType) || !HasProtoContractAttribute(namedType))
return;

var typeSymbol = namedType.BaseType;
while (typeSymbol.SpecialType != SpecialType.System_Object
&& typeSymbol.SpecialType != SpecialType.System_Enum
&& typeSymbol.SpecialType != SpecialType.System_ValueType)
{
if (!IsPartial(typeSymbol)
|| !HasProtoContractAttribute(typeSymbol)
|| !HasProtoIncludeAttribute(typeSymbol))
if (!HasProtoContractAttribute(typeSymbol) || !HasProtoIncludeAttribute(typeSymbol, namedType))
{
context.ReportDiagnostic(Diagnostic.Create(Rule04, context.Symbol.Locations.First(), string.Empty));
return;
}
typeSymbol = typeSymbol.BaseType;
}
}

typeSymbol = analyzedTypeSymbol.ContainingType;
private void ValidateNestedTypes(SymbolAnalysisContext context, INamedTypeSymbol namedType)
{
var typeSymbol = namedType.ContainingType;
while (typeSymbol != null)
{
if (!IsPartial(typeSymbol))
Expand Down Expand Up @@ -94,9 +104,23 @@ private static bool HasProtoContractAttribute(INamedTypeSymbol namedType)
return namedType.GetAttributes().Any(x => x.AttributeClass.Name == "ProtoContractAttribute" && x.AttributeClass.ContainingNamespace.Name == "ProtoBuf");
}

private static bool HasProtoIncludeAttribute(INamedTypeSymbol namedType)
private static bool HasProtoIncludeAttribute(INamedTypeSymbol namedType, INamedTypeSymbol matchingType)
{
return namedType.GetAttributes().Any(x => x.AttributeClass.Name == "ProtoIncludeAttribute" && x.AttributeClass.ContainingNamespace.Name == "ProtoBuf");
var protoIncludeAttribute = namedType.GetAttributes().FirstOrDefault(x => x.AttributeClass.Name == "ProtoIncludeAttribute" && x.AttributeClass.ContainingNamespace.Name == "ProtoBuf");
if (protoIncludeAttribute is null)
return false;

var matchingTypeFound = protoIncludeAttribute.ConstructorArguments.Any(x => x.Type.Name == "Type"
&& x.Type.ContainingNamespace.Name == "System"
&& x.Value is INamedTypeSymbol knownType
&& knownType.Equals(matchingType, SymbolEqualityComparer.Default));
if (matchingTypeFound)
return true;

return protoIncludeAttribute.ConstructorArguments.Any(x => x.Type.Name == "String"
&& x.Type.ContainingNamespace.Name == "System"
&& x.Value is string knownType
&& knownType == matchingType.ToString());
}

private bool IsPartial(INamedTypeSymbol namedType)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq;
using Microsoft.CodeAnalysis;

namespace ProtobufSourceGenerator.Incremental;
Expand Down

0 comments on commit e105e96

Please sign in to comment.