Skip to content

Commit

Permalink
Add IncrementalSourceGenerator (for microsoft.codeanalysis.csharp 4.1.0)
Browse files Browse the repository at this point in the history
  • Loading branch information
hadashiA committed Dec 10, 2023
1 parent 1301b79 commit 30648d5
Show file tree
Hide file tree
Showing 38 changed files with 3,168 additions and 114 deletions.
2 changes: 1 addition & 1 deletion VYaml.SourceGenerator.Roslyn3/CodeWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace VYaml.SourceGenerator;

public class CodeWriter
class CodeWriter
{
readonly struct IndentScope : IDisposable
{
Expand Down
2 changes: 1 addition & 1 deletion VYaml.SourceGenerator.Roslyn3/ReferenceSymbols.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace VYaml.SourceGenerator;

public class ReferenceSymbols
class ReferenceSymbols
{
public static ReferenceSymbols? Create(Compilation compilation)
{
Expand Down
3 changes: 1 addition & 2 deletions VYaml.SourceGenerator.Roslyn3/SyntaxContextReceiver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,5 @@ or RecordDeclarationSyntax
classDeclarations.Add(typeSyntax);
}
}
}
}
} }
}
121 changes: 121 additions & 0 deletions VYaml.SourceGenerator/CodeWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using System;
using System.Text;

namespace VYaml.SourceGenerator;

class CodeWriter
{
readonly struct IndentScope : IDisposable
{
readonly CodeWriter source;

public IndentScope(CodeWriter source, string? startLine = null)
{
this.source = source;
source.AppendLine(startLine);
source.IncreaseIndent();
}

public void Dispose()
{
source.DecreaseIndent();
}
}

readonly struct BlockScope : IDisposable
{
readonly CodeWriter source;

public BlockScope(CodeWriter source, string? startLine = null)
{
this.source = source;
source.AppendLine(startLine);
source.BeginBlock();
}

public void Dispose()
{
source.EndBlock();
}
}

readonly StringBuilder buffer = new();
int indentLevel;

public void Append(string value, bool indent = true)
{
if (indent)
{
buffer.Append($"{new string(' ', indentLevel * 4)} {value}");
}
else
{
buffer.Append(value);
}
}

public void AppendLine(string? value = null, bool indent = true)
{
if (string.IsNullOrEmpty(value))
{
buffer.AppendLine();
}
else if (indent)
{
buffer.AppendLine($"{new string(' ', indentLevel * 4)} {value}");
}
else
{
buffer.AppendLine(value);
}
}

public void AppendByteArrayString(byte[] bytes)
{
buffer.Append("{ ");
var first = true;
foreach (var x in bytes)
{
if (!first)
{
buffer.Append(", ");
}
buffer.Append(x);
first = false;
}
buffer.Append(" }");
}

public override string ToString() => buffer.ToString();

public IDisposable BeginIndentScope(string? startLine = null) => new IndentScope(this, startLine);
public IDisposable BeginBlockScope(string? startLine = null) => new BlockScope(this, startLine);

public void IncreaseIndent()
{
indentLevel++;
}

public void DecreaseIndent()
{
if (indentLevel > 0)
indentLevel--;
}

public void BeginBlock()
{
AppendLine("{");
IncreaseIndent();
}

public void EndBlock()
{
DecreaseIndent();
AppendLine("}");
}

public void Clear()
{
buffer.Clear();
}
}
128 changes: 128 additions & 0 deletions VYaml.SourceGenerator/DiagnosticDescriptors.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
using Microsoft.CodeAnalysis;

namespace VYaml.SourceGenerator;

static class DiagnosticDescriptors
{
const string Category = "VYaml.SourceGenerator";

public static readonly DiagnosticDescriptor UnexpectedErrorDescriptor = new(
id: "VYAML001",
title: "Unexpected error during source code generation",
messageFormat: "Unexpected error occurred during source code code generation: {0}",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor MustBePartial = new(
id: "VYAML002",
title: "VYaml serializable type declaration must be partial",
messageFormat: "The VYaml serializable type declaration '{0}' must be partial",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor NestedNotAllow = new(
id: "VYAML003",
title: "VYaml serializable type must not be nested type",
messageFormat: "The VYaml serializable object '{0}' must be not nested type",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor YamlMemberPropertyMustHaveSetter = new(
id: "VYAML004",
title: "A yaml serializable property with must have setter.",
messageFormat: "The VYaml serializable object '{0}' property '{1}' must have setter.",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor YamlMemberFieldCannotBeReadonly = new(
id: "VYAML005",
title: "A yaml serializable field cannot be readonly",
messageFormat: "The VYaml serializable object '{0}' field '{1}' cannot be readonly.",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor AbstractMustUnion = new(
id: "VYAML006",
title: "abstract/interface type of `[YamlObject]` must annotate with Union",
messageFormat: "abstract/interface type of `[YamlObject]` '{0}' must annotate with Union",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor ConcreteTypeCantBeUnion = new(
id: "VYAML007",
title: "Concrete type can't be union",
messageFormat: "The object that has `[YamlObject]` '{0}' can be Union, only allow abstract or interface",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor UnionTagDuplicate = new(
id: "VYAML008",
title: "Union tag is duplicate",
messageFormat: "The object that has `[YamlObject]` '{0}' union tag value is duplicate",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor UnionMemberTypeNotImplementBaseType = new(
id: "VYAML009",
title: "Union member not implement union interface",
messageFormat: "The object '{0}' union member '{1}' not implment union interface",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor UnionMemberTypeNotDerivedBaseType = new(
id: "VYAML010",
title: "Union member not dervided union base type",
messageFormat: "The object '{0}' union member '{1}' not derived union type",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor UnionMemberNotAllowStruct = new(
id: "VYAML011",
title: "Union member can't be struct",
messageFormat: "The object '{0}' union member '{1}' can't be member, not allows struct",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor UnionMemberMustBeYamlObject = new(
id: "VYAML012",
title: "Union member must be YamlObject",
messageFormat: "The object '{0}' union member '{1}' must be [YamlObject]",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor MultipleConstructorAttribute = new(
id: "VYAML013",
title: "[YamlConstructor] exists in multiple constructors",
messageFormat: "Mupltiple [YamlConstructor] exists in '{0}' but allows only single ctor",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor MultipleConstructorWithoutAttribute = new(
id: "VYAML014",
title: "Require [YamlConstructor] when exists multiple constructors",
messageFormat: "The Yaml object '{0}' must annotate with [YamlConstructor] when exists multiple constructors",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor ConstructorHasNoMatchedParameter = new(
id: "VYAML0015",
title: "VYaml's constructor has no matched parameter",
messageFormat: "The VYaml object '{0}' constructor's parameter '{1}' must match a serialized member name(case-insensitive)",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
}
75 changes: 75 additions & 0 deletions VYaml.SourceGenerator/KeyNameMutator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System;

namespace VYaml.SourceGenerator;

enum NamingConvention
{
LowerCamelCase,
UpperCamelCase,
SnakeCase,
KebabCase,
}

static class KeyNameMutator
{
public static string Mutate(string s, NamingConvention namingConvention)
{
return namingConvention switch
{
NamingConvention.LowerCamelCase => ToLowerCamelCase(s),
NamingConvention.UpperCamelCase => s,
NamingConvention.SnakeCase => ToSnakeCase(s),
NamingConvention.KebabCase => ToSnakeCase(s, '-'),
_ => throw new ArgumentOutOfRangeException(nameof(namingConvention), namingConvention, null)
};
}

public static string ToLowerCamelCase(string s)
{
var span = s.AsSpan();
if (span.Length <= 0 ||
(span.Length <= 1 && char.IsLower(span[0])))
{
return s;
}

Span<char> buf = stackalloc char[span.Length];
span.CopyTo(buf);
buf[0] = char.ToLowerInvariant(span[0]);
return buf.ToString();
}

public static string ToSnakeCase(string s, char separator = '_')
{
var span = s.AsSpan();
if (span.Length <= 0) return s;

Span<char> buf = stackalloc char[span.Length * 2];
var written = 0;
foreach (var ch in span)
{
if (char.IsUpper(ch))
{
if (written == 0 || // first
char.IsUpper(span[written - 1])) // WriteIO => write_io
{
buf[written++] = char.ToLowerInvariant(ch);
}
else
{
buf[written++] = separator;
if (buf.Length <= written)
{
buf = new char[buf.Length * 2];
}
buf[written++] = char.ToLowerInvariant(ch);
}
}
else
{
buf[written++] = ch;
}
}
return buf.Slice(0, written).ToString();
}
}
Loading

0 comments on commit 30648d5

Please sign in to comment.