Skip to content

Commit

Permalink
Merge pull request #457 from SteveDunn/global-default-for-type-crashe…
Browse files Browse the repository at this point in the history
…s-addvalidation-analyzer

Fix issue where underlying type isn't considered from global config attribute
  • Loading branch information
SteveDunn authored Jul 17, 2023
2 parents 09ad5e7 + 5fb3153 commit bd0bb6d
Show file tree
Hide file tree
Showing 16 changed files with 275 additions and 74 deletions.
6 changes: 3 additions & 3 deletions samples/Vogen.Examples/TypicalScenarios/UsingInterfaces.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ public interface IHaveAnId<out T>
}

// defaults to int
[ValueObject]
[ValueObject<int>]
internal readonly partial struct CustomerId : IHaveAnId<int>
{
}

[ValueObject]
[ValueObject<int>]
internal partial struct AccountId : IHaveAnId<int>
{
}
Expand All @@ -42,7 +42,7 @@ internal partial struct AccountId : IHaveAnId<int>
// then be aware that there could be severe overhead of wrapping a reference type
// as a value type. One of the goals of Vogen is to not add too much overhead
// (in terms of memory/speed) over using the primitive type itself.
[ValueObject]
[ValueObject<int>]
internal partial class Id : IHaveAnId<int> { }

internal class DerivedId1 : Id { }
Expand Down
1 change: 0 additions & 1 deletion src/Vogen/Extensions/CompilationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using Microsoft.CodeAnalysis.Diagnostics;
#endif

using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;

namespace Analyzer.Utilities.Extensions
Expand Down
1 change: 0 additions & 1 deletion src/Vogen/Extensions/IEnumerableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;

namespace Analyzer.Utilities.Extensions
Expand Down
1 change: 0 additions & 1 deletion src/Vogen/Extensions/IMethodSymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis;

Expand Down
1 change: 0 additions & 1 deletion src/Vogen/Extensions/INamedTypeSymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis;

Expand Down
1 change: 0 additions & 1 deletion src/Vogen/Extensions/IPropertySymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#pragma warning disable RS1024

using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis;

Expand Down
1 change: 0 additions & 1 deletion src/Vogen/Extensions/ITypeSymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis;

Expand Down
14 changes: 14 additions & 0 deletions src/Vogen/ManageAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@ public static VogenConfigurationBuildResult GetDefaultConfigFromGlobalAttribute(
return VogenConfigurationBuildResult.Null;
}

return GetDefaultConfigFromGlobalAttribute(compilation);
}

/// <summary>
/// Gets global default configuration from any global (assembly) attribute.
/// If none are specified, then the default configuration is used.
/// If some are specified, then they are validated.
/// If anything is invalid, a compilation error is raised.
/// </summary>
/// <param name="compilation"></param>
/// <returns></returns>
public static VogenConfigurationBuildResult GetDefaultConfigFromGlobalAttribute(
Compilation compilation)
{
var assemblyAttributes = compilation.Assembly.GetAttributes();
if (assemblyAttributes.IsDefaultOrEmpty)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Vogen/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"Vogen": {
"commandName": "Project"
},
"Roslyn": {
"Generators": {
"commandName": "DebugRoslynComponent",
"targetProject": "..\\..\\tests\\Testbench\\Testbench.csproj"
}
Expand Down
95 changes: 61 additions & 34 deletions src/Vogen/Rules/AddValidationAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,64 +52,91 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context)
{
var namedTypeSymbol = (INamedTypeSymbol) context.Symbol;

var ulType = TryResolveUnderlyingType(namedTypeSymbol, context);

if (ulType is null) return;

if (AlreadyContainsAValidationMethod(namedTypeSymbol))
{
return;
}

Dictionary<string, string?> properties = new()
{
{ "PrimitiveType", ulType.Name}
};

var diagnostic = Diagnostic.Create(
descriptor: Rule,
location: namedTypeSymbol.Locations[0],
additionalLocations: null,
properties: properties.ToImmutableDictionary(),
namedTypeSymbol.Name);

context.ReportDiagnostic(diagnostic);
}

private static INamedTypeSymbol? TryResolveUnderlyingType(INamedTypeSymbol namedTypeSymbol, SymbolAnalysisContext context)
{
INamedTypeSymbol voSymbolInformation = namedTypeSymbol;

var attrs = VoFilter.TryGetValueObjectAttributes(voSymbolInformation).ToImmutableArray();

if (attrs.Length != 1) return;
if (attrs.Length != 1) return null;

VogenConfigurationBuildResult buildResult = ManageAttributes.TryBuildConfigurationFromAttribute(attrs[0]);

VogenConfiguration? vogenConfig = buildResult.ResultingConfiguration;

if (!vogenConfig.HasValue) return;
if (!vogenConfig.HasValue) return null;

if (buildResult.Diagnostics.Count > 0)
{
return;
return null;
}

string retType = vogenConfig.Value.UnderlyingType!.Name;
INamedTypeSymbol? ulType = vogenConfig.Value.UnderlyingType;

return ulType ??
TryGetFromGlobalAttribute(context.Compilation) ??
context.Compilation.GetSpecialType(SpecialType.System_Int32);
}

var voSymbol = namedTypeSymbol;
private static INamedTypeSymbol? TryGetFromGlobalAttribute(Compilation compilation)
{
var v = ManageAttributes.GetDefaultConfigFromGlobalAttribute(compilation);

return v.ResultingConfiguration?.UnderlyingType;
}

private static bool AlreadyContainsAValidationMethod(INamedTypeSymbol namedTypeSymbol)
{
bool found = false;

foreach (ISymbol? memberDeclarationSyntax in voSymbol.GetMembers("Validate"))
ImmutableArray<ISymbol> members = namedTypeSymbol.GetMembers("Validate");

foreach (ISymbol? eachMemberSyntax in members)
{
if (memberDeclarationSyntax is IMethodSymbol mds)
if (eachMemberSyntax is not IMethodSymbol mds)
{
if (!IsMethodStatic(mds))
{
continue;
}

if (mds.ReturnType is INamedTypeSymbol s && s.FullName() == "Vogen.Validation")
{
found = true;
break;
}
continue;
}
}

if (found)
{
return;
}
if (!IsMethodStatic(mds))
{
continue;
}

Dictionary<string, string?> properties = new()
{
{ "PrimitiveType", retType}
};
if (mds.ReturnType is not INamedTypeSymbol s || s.FullName() != "Vogen.Validation")
{
continue;
}

var diagnostic = Diagnostic.Create(
descriptor: Rule,
location: namedTypeSymbol.Locations[0],
additionalLocations: null,
properties: properties.ToImmutableDictionary(),
namedTypeSymbol.Name);
found = true;
break;
}

context.ReportDiagnostic(diagnostic);
return found;
}

private static bool IsMethodStatic(IMethodSymbol mds) => mds.IsStatic;
Expand Down
3 changes: 1 addition & 2 deletions src/Vogen/VoFilter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
Expand Down
Loading

0 comments on commit bd0bb6d

Please sign in to comment.