-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Big start on the epilogue source generator
- Loading branch information
Showing
15 changed files
with
1,205 additions
and
6 deletions.
There are no files selected for viewing
65 changes: 65 additions & 0 deletions
65
codehelp/CodeHelpers.Test/LogGenerator/EpilogueLogGeneratorTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
43
codehelp/CodeHelpers/EpilogueGenerator/CustomLoggerType.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
229
codehelp/CodeHelpers/EpilogueGenerator/LogAttributeInfo.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.