Skip to content

Commit

Permalink
Big start on the epilogue source generator
Browse files Browse the repository at this point in the history
  • Loading branch information
ThadHouse committed Aug 5, 2024
1 parent e5623ab commit 5be304e
Show file tree
Hide file tree
Showing 15 changed files with 1,205 additions and 6 deletions.
65 changes: 65 additions & 0 deletions codehelp/CodeHelpers.Test/LogGenerator/EpilogueLogGeneratorTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
namespace CodeHelpers.Test.LogGenerator;

using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Text;
using Stereologue;
using WPILib.CodeHelpers.LogGenerator.SourceGenerator;

public class EpilogueGeneratorTest
{
[Theory]
[InlineData("bool", "LogBoolean")]
[InlineData("int", "LogInteger")]
[InlineData("long", "LogInteger")]
public async Task TestPrimitives(string type, string output)
{
string testString = @"
using Epilogue;
[Logged]
public partial class MyNewClass
{
[Logged]
public REPLACEME Variable() { return default; }
}
";

string expected = @"partial class MyNewClass
: global::Stereologue.ILogged
{
public void UpdateStereologue(string path, global::Stereologue.Stereologuer logger)
{
logger.REPLACEME($""{path}/Variable"", global::Stereologue.LogType.File | global::Stereologue.LogType.Nt, Variable(), global::Stereologue.LogLevel.Default);
}
}
";
testString = testString.Replace("REPLACEME", type);

// Due to StringBuilder in the source generator,
// We must normalize the output line endings
expected = expected.NormalizeLineEndings();
expected = expected.Replace("REPLACEME", output);

await new CSharpSourceGeneratorTest<LogGeneratorSharp, DefaultVerifier>()
{
TestState = {
AdditionalReferences = {
typeof(LogAttribute).Assembly
},
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
Sources = {
testString,
},
AnalyzerConfigFiles = {
("/.editorconfig", SourceText.From(TestHelpers.EditorConfig, Encoding.UTF8))
},
GeneratedSources = {
($"WPILib.CodeHelpers{Path.DirectorySeparatorChar}WPILib.CodeHelpers.EpilogueGenerator.SourceGenerator.EpilogueGeneratorSharp{Path.DirectorySeparatorChar}MyNewClass.g.cs", SourceText.From(expected, Encoding.UTF8))
},
},
}.RunAsync();
}
}
43 changes: 43 additions & 0 deletions codehelp/CodeHelpers/EpilogueGenerator/CustomLoggerType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;

namespace WPILib.CodeHelpers.EpilogueGenerator;

public record CustomLoggerType(TypeDeclarationModel TypeDeclarations, EquatableArray<TypeDeclarationModel> SupportedTypes);

internal static class CustomLoggerTypeExtensions
{
public static CustomLoggerType GetCustomLoggerType(this ImmutableArray<AttributeData> attributes, INamedTypeSymbol symbol, CancellationToken token)
{
token.ThrowIfCancellationRequested();

var loggableTypes = ImmutableArray.CreateBuilder<TypeDeclarationModel>(1);

foreach (var attribute in attributes)
{
token.ThrowIfCancellationRequested();
foreach (var named in attribute.NamedArguments)
{
token.ThrowIfCancellationRequested();
if (named.Key == "Types")
{
if (!named.Value.IsNull && named.Value.Kind is TypedConstantKind.Array)
{
foreach (var value in named.Value.Values)
{
token.ThrowIfCancellationRequested();
if (!value.IsNull && value.Kind is TypedConstantKind.Type && value.Value is INamedTypeSymbol typeFor)
{
loggableTypes.Add(typeFor.GetTypeDeclarationModel());
}
}
}
}
}
}

token.ThrowIfCancellationRequested();

return new(symbol.GetTypeDeclarationModel(), loggableTypes.ToImmutable());
}
}
15 changes: 15 additions & 0 deletions codehelp/CodeHelpers/EpilogueGenerator/FailureMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace WPILib.CodeHelpers.EpilogueGenerator;

public enum FailureMode
{
None,
AttributeUnknownMemberType,
ProtobufArray,
UnknownTypeNonArray,
UnknownTypeArray,
MethodReturnsVoid,
MethodHasParameters,
UnknownTypeToLog,
NullableStructArray,
MissingGenerateLog,
}
229 changes: 229 additions & 0 deletions codehelp/CodeHelpers/EpilogueGenerator/LogAttributeInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
using System.Collections.Immutable;
using System.Text;
using Epilogue;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.PooledObjects;

namespace WPILib.CodeHelpers.EpilogueGenerator;

// Contains all information about a [Logged] attribute
public record LogAttributeInfo(string? Name, LogStrategy LogStrategy, LogImportance LogImportance)
{
// public string GetLogStrageyString(LanguageKind language)
// {
// if (language == LanguageKind.CSharp)
// {
// return AllLevelValues[(int)LogLevel];
// }
// else if (language == LanguageKind.VisualBasic)
// {
// return AllLevelValuesVb[(int)LogLevel];
// }
// return "";
// }

// public string GetLogTypeString(LanguageKind language)
// {
// if (language == LanguageKind.CSharp)
// {
// return AllTypeValues[(int)LogType];
// }
// else if (language == LanguageKind.VisualBasic)
// {
// return AllTypeValuesVb[(int)LogType];
// }
// return "";
// }

// private static ImmutableList<string> GetAllLevelValues()
// {
// LogLevel[] allLogLevels = (LogLevel[])Enum.GetValues(typeof(LogLevel));
// var builder = ImmutableList.CreateBuilder<string>();
// string fullName = typeof(LogLevel).FullName;
// string rootName = $"global::{fullName}.";
// foreach (var i in allLogLevels)
// {
// builder.Add($"{rootName}{i}");
// }
// return builder.ToImmutable();
// }

// private static ImmutableList<string> GetAllTypeValues()
// {
// LogType[] allLogTypes = (LogType[])Enum.GetValues(typeof(LogType));
// var builder = ImmutableList.CreateBuilder<string>();
// LogType baseLog = LogType.None;
// foreach (var i in allLogTypes)
// {
// baseLog |= i;
// }
// int permutations = (int)baseLog;
// permutations += 1;
// string fullName = typeof(LogType).FullName;
// string rootName = $"global::{fullName}.";
// builder.Add($"{rootName}{nameof(LogType.None)}");
// StringBuilder stringBuilder = new();
// for (int i = 1; i < permutations; i++)
// {
// LogType type = (LogType)i;

// if ((type & LogType.File) != 0)
// {
// if (stringBuilder.Length != 0)
// {
// stringBuilder.Append(" | ");
// }
// stringBuilder.Append($"{rootName}{nameof(LogType.File)}");
// }
// if ((type & LogType.Nt) != 0)
// {
// if (stringBuilder.Length != 0)
// {
// stringBuilder.Append(" | ");
// }
// stringBuilder.Append($"{rootName}{nameof(LogType.Nt)}");
// }
// if ((type & LogType.Once) != 0)
// {
// if (stringBuilder.Length != 0)
// {
// stringBuilder.Append(" | ");
// }
// stringBuilder.Append($"{rootName}{nameof(LogType.Once)}");
// }
// builder.Add(stringBuilder.ToString());
// stringBuilder.Clear();
// }

// return builder.ToImmutable();
// }

// private static ImmutableList<string> GetAllLevelValuesVb()
// {
// LogLevel[] allLogLevels = (LogLevel[])Enum.GetValues(typeof(LogLevel));
// var builder = ImmutableList.CreateBuilder<string>();
// string fullName = typeof(LogLevel).FullName;
// string rootName = $"Global.{fullName}.";
// foreach (var i in allLogLevels)
// {
// builder.Add($"{rootName}{i}");
// }
// return builder.ToImmutable();
// }

// private static ImmutableList<string> GetAllTypeValuesVb()
// {
// LogType[] allLogTypes = (LogType[])Enum.GetValues(typeof(LogType));
// var builder = ImmutableList.CreateBuilder<string>();
// LogType baseLog = LogType.None;
// foreach (var i in allLogTypes)
// {
// baseLog |= i;
// }
// int permutations = (int)baseLog;
// permutations += 1;
// string fullName = typeof(LogType).FullName;
// string rootName = $"Global.{fullName}.";
// builder.Add($"{rootName}{nameof(LogType.None)}");
// StringBuilder stringBuilder = new();
// for (int i = 1; i < permutations; i++)
// {
// LogType type = (LogType)i;

// if ((type & LogType.File) != 0)
// {
// if (stringBuilder.Length != 0)
// {
// stringBuilder.Append(" Or ");
// }
// stringBuilder.Append($"{rootName}{nameof(LogType.File)}");
// }
// if ((type & LogType.Nt) != 0)
// {
// if (stringBuilder.Length != 0)
// {
// stringBuilder.Append(" Or ");
// }
// stringBuilder.Append($"{rootName}{nameof(LogType.Nt)}");
// }
// if ((type & LogType.Once) != 0)
// {
// if (stringBuilder.Length != 0)
// {
// stringBuilder.Append(" Or ");
// }
// stringBuilder.Append($"{rootName}{nameof(LogType.Once)}");
// }
// builder.Add(stringBuilder.ToString());
// stringBuilder.Clear();
// }

// return builder.ToImmutable();
// }

// public static ImmutableList<string> AllTypeValues { get; } = GetAllTypeValues();

// public static ImmutableList<string> AllLevelValues { get; } = GetAllLevelValues();

// public static ImmutableList<string> AllTypeValuesVb { get; } = GetAllTypeValuesVb();

// public static ImmutableList<string> AllLevelValuesVb { get; } = GetAllLevelValuesVb();
}

internal static class LogAttributeInfoExtensions
{
public static LogAttributeInfo? ToAttributeInfo(this AttributeData attributeData, INamedTypeSymbol? attributeClass, CancellationToken token, out bool notLogged)
{
if (attributeClass is null)
{
notLogged = false;
return null;
}

if (attributeClass.IsNotLoggedAttributeClass())
{
notLogged = true;
return null;
}
notLogged = false;
if (attributeClass.IsLoggedAttributeClass())
{
token.ThrowIfCancellationRequested();

string? path = null;
LogStrategy logStrategyEnum = LogStrategyExtensions.DefaultLogStrategy;
LogImportance logImportanceEnum = LogImportanceExtensions.DefaultLogImportance;

// Get the log attribute
foreach (var named in attributeData.NamedArguments)
{
if (named.Key == "Name")
{
if (!named.Value.IsNull)
{
path = SymbolDisplay.FormatPrimitive(named.Value.Value!, false, false);
}
token.ThrowIfCancellationRequested();
}
else if (named.Key == "Strategy")
{
// A boxed primitive can be unboxed to an enum with the same underlying type.
logStrategyEnum = (LogStrategy)named.Value.Value!;
token.ThrowIfCancellationRequested();
}
else if (named.Key == "Importance")
{
// A boxed primitive can be unboxed to an enum with the same underlying type.
logImportanceEnum = (LogImportance)named.Value.Value!;
token.ThrowIfCancellationRequested();
}
}

return new LogAttributeInfo(path, logStrategyEnum, logImportanceEnum);
}
return null;
}


}
Loading

0 comments on commit 5be304e

Please sign in to comment.