Skip to content

Commit 2929788

Browse files
committed
Improved Source Generator
1 parent 0aee315 commit 2929788

File tree

3 files changed

+173
-38
lines changed

3 files changed

+173
-38
lines changed

nuget/Sqlite_net.SourceGenerator/SQLiteFastColumnSetterGenerator.cs

Lines changed: 137 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Microsoft.CodeAnalysis;
1+
using System;
2+
using Microsoft.CodeAnalysis;
23
using Microsoft.CodeAnalysis.CSharp;
34
using Microsoft.CodeAnalysis.CSharp.Syntax;
45
using Microsoft.CodeAnalysis.Text;
@@ -71,6 +72,11 @@ static bool IsCandidateClass(SyntaxNode node)
7172
return true;
7273
}
7374

75+
// I need to analyse the base class in the semantic model
76+
if (HasBaseClass (classDecl)) {
77+
return true;
78+
}
79+
7480
// Check if any property has SQLite Property Attribute
7581
return classDecl.Members
7682
.OfType<PropertyDeclarationSyntax> ()
@@ -81,77 +87,174 @@ static bool IsCandidateClass(SyntaxNode node)
8187
})));
8288
}
8389

84-
static ClassInfo? GetClassInfo(GeneratorSyntaxContext context)
90+
static bool HasBaseClass (ClassDeclarationSyntax classDecl)
8591
{
86-
var classDecl = (ClassDeclarationSyntax)context.Node;
87-
var semanticModel = context.SemanticModel;
92+
var baseList = classDecl.BaseList;
93+
if (baseList == null)
94+
return false;
8895

89-
var classSymbol = semanticModel.GetDeclaredSymbol(classDecl);
96+
return baseList.Types.Count > 0;
97+
}
98+
99+
static ClassInfo? GetClassInfo (INamedTypeSymbol? classSymbol)
100+
{
90101
if (classSymbol is null)
91102
return null;
92-
103+
93104
// Return null if the class is private
94105
if (classSymbol.DeclaredAccessibility == Accessibility.Private)
95106
return null;
96107

97-
if (classSymbol.IsGenericType)
108+
if (classSymbol.IsGenericType)
109+
return null;
110+
111+
var hasSqliteAttributes = HasTableAttribute (classSymbol);
112+
if (!hasSqliteAttributes) {
113+
hasSqliteAttributes = HasSQLiteAttribute (classSymbol);
114+
}
115+
116+
if (!hasSqliteAttributes) {
98117
return null;
99-
100-
var hasTableAttribute = classSymbol.GetAttributes()
101-
.Any(attr => attr.AttributeClass?.ContainingNamespace.Name == "SQLite" && attr.AttributeClass?.Name == "TableAttribute");
118+
}
102119

103-
var properties = new List<PropertyInfo>();
120+
var properties = new List<PropertyInfo> ();
104121

105122
// Iterate through the class hierarchy to get all properties
106123
var currentType = classSymbol;
107-
while (currentType != null)
108-
{
124+
while (currentType != null) {
109125
foreach (var member in currentType.GetMembers ().OfType<IPropertySymbol> ()) {
110126
if (!member.IsReadOnly) {
111-
var hasSqliteAttributes = member.GetAttributes ()
112-
.Any (attr =>
113-
attr.AttributeClass?.Name != null &&
114-
attr.AttributeClass.ContainingNamespace.Name == "SQLite" &&
115-
attr.AttributeClass.Name != "IgnoreAttribute" &&
116-
SQLitePropertyFullAttributes.Contains (attr.AttributeClass?.Name!));
117-
118-
// Include property if class has TableAttribute or property has ColumnAttribute
119-
if (hasTableAttribute || hasSqliteAttributes) {
127+
var ignore = member.GetAttributes ()
128+
.Any (attr => IsIgnoreAttribute (attr.AttributeClass));
129+
130+
// Include property if not ignored
131+
if (!ignore) {
120132
var columnName = GetColumnName (member);
121133
properties.Add (new PropertyInfo (member.Name, member.Type.ToDisplayString (), columnName));
122134
}
123135
}
124136
}
125-
137+
126138
// Move to base type
127139
currentType = currentType.BaseType;
128-
140+
129141
// Stop at System.Object or if we hit a null base type
130142
if (currentType?.SpecialType == SpecialType.System_Object)
131143
break;
132-
133144
}
134145

135146
if (properties.Count == 0)
136147
return null;
137148

138149
// Handle nested classes by building the full containing type path
139-
var containingTypes = new List<string>();
150+
var containingTypes = new List<string> ();
140151
var currentContaining = classSymbol.ContainingType;
141-
while (currentContaining != null)
142-
{
143-
containingTypes.Insert(0, currentContaining.Name);
152+
while (currentContaining != null) {
153+
containingTypes.Insert (0, currentContaining.Name);
144154
currentContaining = currentContaining.ContainingType;
145155
}
146156

147-
var fullClassName = containingTypes.Count > 0
148-
? $"{string.Join(".", containingTypes)}.{classSymbol.Name}"
157+
var fullClassName = containingTypes.Count > 0
158+
? $"{string.Join (".", containingTypes)}.{classSymbol.Name}"
149159
: classSymbol.Name;
150160

151-
return new ClassInfo(
161+
return new ClassInfo (
152162
fullClassName,
153-
classSymbol.ContainingNamespace?.ToDisplayString() ?? string.Empty,
163+
classSymbol.ContainingNamespace?.ToDisplayString () ?? string.Empty,
154164
properties);
165+
}
166+
167+
private static bool HasSQLiteAttribute (INamedTypeSymbol? classSymbol)
168+
{
169+
while (true) {
170+
if (classSymbol == null || classSymbol.SpecialType == SpecialType.System_Object) {
171+
return false;
172+
}
173+
174+
var members = classSymbol.GetMembers();
175+
foreach (var member in members) {
176+
if (member.GetAttributes().Any(attr => IsSQLiteAttribute (attr.AttributeClass)))
177+
{
178+
return true;
179+
}
180+
}
181+
182+
classSymbol = classSymbol.BaseType;
183+
}
184+
}
185+
186+
private static bool HasTableAttribute (INamedTypeSymbol? classSymbol)
187+
{
188+
while (true) {
189+
if (classSymbol == null || classSymbol.SpecialType == SpecialType.System_Object) {
190+
return false;
191+
}
192+
193+
var hasTableAttribute = classSymbol.GetAttributes ()
194+
.Any (attr => IsTableAttribute (attr.AttributeClass));
195+
if (hasTableAttribute) return true;
196+
197+
classSymbol = classSymbol.BaseType;
198+
}
199+
}
200+
201+
static ClassInfo? GetClassInfo(GeneratorSyntaxContext context)
202+
{
203+
var classDecl = (ClassDeclarationSyntax)context.Node;
204+
var semanticModel = context.SemanticModel;
205+
206+
var classSymbol = semanticModel.GetDeclaredSymbol(classDecl);
207+
return GetClassInfo (classSymbol);
208+
}
209+
210+
private static bool IsTableAttribute (INamedTypeSymbol? attributeClass)
211+
{
212+
while (true) {
213+
if (attributeClass == null) {
214+
return false;
215+
}
216+
217+
if (IsSQLiteNamespace (attributeClass) && attributeClass.Name == "TableAttribute") {
218+
return true;
219+
}
220+
221+
attributeClass = attributeClass.BaseType;
222+
}
223+
}
224+
225+
private static bool IsIgnoreAttribute (INamedTypeSymbol? attributeClass)
226+
{
227+
while (true) {
228+
if (attributeClass == null) {
229+
return false;
230+
}
231+
232+
if (IsSQLiteNamespace (attributeClass) && attributeClass.Name == "IgnoreAttribute") {
233+
return true;
234+
}
235+
236+
attributeClass = attributeClass.BaseType;
237+
}
238+
}
239+
240+
private static bool IsSQLiteAttribute (INamedTypeSymbol? attributeClass)
241+
{
242+
while (true) {
243+
if (attributeClass == null) {
244+
return false;
245+
}
246+
247+
if (IsSQLiteNamespace (attributeClass) && SQLitePropertyFullAttributes.Contains (attributeClass.Name)) {
248+
return true;
249+
}
250+
251+
attributeClass = attributeClass.BaseType;
252+
}
253+
}
254+
255+
private static bool IsSQLiteNamespace (INamedTypeSymbol attributeClass)
256+
{
257+
return attributeClass.ContainingNamespace.Name == "SQLite";
155258
}
156259

157260
static string GetColumnName(IPropertySymbol property)
@@ -300,7 +403,7 @@ static void GeneratePropertySetter(StringBuilder sb, PropertyInfo property)
300403
case "decimal":
301404
case "Decimal":
302405
case "System.Decimal":
303-
sb.AppendLine ($" typedObj.{property.PropertyName} = SQLite3.ColumnDouble(stmt, index);");
406+
sb.AppendLine ($" typedObj.{property.PropertyName} = System.Convert.ToDecimal(SQLite3.ColumnDouble(stmt, index));");
304407
break;
305408

306409
case "float":

tests/SQLite.Tests/SQLite.Tests.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
<!-- SourceGenerator Debugging -->
2525
<PropertyGroup>
2626
<!-- Enable viewing source generated files -->
27-
<!--
27+
2828
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
2929
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)Generated</CompilerGeneratedFilesOutputPath>
30-
-->
30+
3131
</PropertyGroup>
3232

3333
<ItemGroup>

tests/SQLite.Tests/SourceGeneratorTest.cs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313

1414
namespace SQLite.Tests
1515
{
16+
[AttributeUsage (AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
17+
class DerivedIgnoreAttribute : IgnoreAttribute
18+
{
19+
}
20+
1621
public class OuterTestSetter
1722
{
1823
[AutoIncrement, PrimaryKey]
@@ -27,6 +32,9 @@ public class OuterTestSetter
2732
[Ignore]
2833
public string Ignore { get; set; }
2934

35+
[DerivedIgnore]
36+
public string DerivedIgnore { get; set; }
37+
3038
[Column("A")]
3139
public string Z { get; set; }
3240
}
@@ -48,8 +56,10 @@ public class StringTest : BaseTest<string>
4856
{
4957
}
5058

51-
#if NET5_0_OR_GREATER
52-
#endif
59+
public class IntTest : BaseTest<int>
60+
{
61+
}
62+
5363
public class BaseTest<T>
5464
{
5565
[PrimaryKey]
@@ -147,6 +157,17 @@ public void SqliteInitializer_StringTestSetter ()
147157
}
148158
}
149159

160+
[Test]
161+
public void SqliteInitializer_IntTestSetter ()
162+
{
163+
if (SQLite.FastColumnSetter.customSetter.TryGetValue ((typeof (IntTest), nameof (IntTest.Id)), out var setter)) {
164+
Assert.IsTrue (true, "Should be registered");
165+
}
166+
else {
167+
Assert.Fail ("Should be registered");
168+
}
169+
}
170+
150171
[Test]
151172
public void SqliteInitializer_InnerTestSetter ()
152173
{
@@ -192,6 +213,17 @@ public void SqliteInitializer_OuterTestSetter_Ignore_NotRegistered ()
192213
}
193214
}
194215

216+
[Test]
217+
public void SqliteInitializer_OuterTestSetter_DeriveIgnore_NotRegistered ()
218+
{
219+
if (!SQLite.FastColumnSetter.customSetter.TryGetValue ((typeof (OuterTestSetter), nameof (OuterTestSetter.DerivedIgnore)), out var setter)) {
220+
Assert.IsTrue (true, "Should not be registered (Ignore)");
221+
}
222+
else {
223+
Assert.Fail ("Should not be registered (Ignore)");
224+
}
225+
}
226+
195227
[Test]
196228
public void SqliteInitializer_OuterTestSetter ()
197229
{

0 commit comments

Comments
 (0)