Skip to content

Commit 5257719

Browse files
committed
finish split
1 parent 9b4355a commit 5257719

File tree

96 files changed

+542
-307
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+542
-307
lines changed

src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.Diagnostics.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public static readonly DiagnosticDescriptor
5656
AmbiguousProperties = LibraryWarning("DAP046", "Ambiguous properties", "Properties have same name '{0}' after normalization and can be conflated"),
5757
AmbiguousFields = LibraryWarning("DAP047", "Ambiguous fields", "Fields have same name '{0}' after normalization and can be conflated"),
5858
MoveFromDbString = LibraryWarning("DAP048", "Move from DbString to DbValue", "DbString achieves the same as [DbValue] does. Use it instead."),
59-
BoundMemberNotFound = LibraryError("DAP049", "Bound member not found", "The bound member '{0}' was not found"),
59+
UnableToBindQueryColumns = LibraryError("DAP049", "Unable to bind query columns", "Something went terribly wrong"),
6060

6161
// SQL parse specific
6262
GeneralSqlError = SqlWarning("DAP200", "SQL error", "SQL error: {0}"),

src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,7 @@ internal static Location SharedParseArgsAndFlags(in ParseState ctx, IInvocationO
665665
}
666666
}
667667

668-
if (flags.HasFlag(OperationFlags.Query) && (IsEnabled(ctx, op, Types.StrictTypesAttribute, out _)))
668+
if (flags.HasAny(OperationFlags.Query) && (IsEnabled(ctx, op, Types.StrictTypesAttribute, out _)))
669669
{
670670
flags |= OperationFlags.StrictTypes;
671671
}
@@ -762,17 +762,20 @@ enum ParameterMode
762762

763763
if (reportDiagnostic is not null)
764764
{
765-
foreach (var attrib in member.Member.GetAttributes())
765+
if (member.IsMapped)
766766
{
767-
switch (attrib.AttributeClass!.Name)
767+
foreach (var attrib in member.Member.GetAttributes())
768768
{
769-
case Types.RowCountHintAttribute:
770-
if (attrib.ConstructorArguments.Length != 0)
771-
{
772-
reportDiagnostic.Invoke(Diagnostic.Create(Diagnostics.RowCountHintShouldNotSpecifyValue,
773-
attrib.ApplicationSyntaxReference?.GetSyntax().GetLocation() ?? location));
774-
}
775-
break;
769+
switch (attrib.AttributeClass!.Name)
770+
{
771+
case Types.RowCountHintAttribute:
772+
if (attrib.ConstructorArguments.Length != 0)
773+
{
774+
reportDiagnostic.Invoke(Diagnostic.Create(Diagnostics.RowCountHintShouldNotSpecifyValue,
775+
attrib.ApplicationSyntaxReference?.GetSyntax().GetLocation() ?? location));
776+
}
777+
break;
778+
}
776779
}
777780
}
778781
}
@@ -820,7 +823,7 @@ enum ParameterMode
820823
}
821824
break;
822825
case Types.QueryColumnsAttribute:
823-
queryColumns = ParseQueryColumns(attrib);
826+
queryColumns = ParseQueryColumns(attrib, reportDiagnostic, location);
824827
break;
825828
}
826829
}
@@ -851,7 +854,7 @@ enum ParameterMode
851854

852855

853856
return cmdProps.IsDefaultOrEmpty && rowCountHint <= 0 && rowCountHintMember is null && batchSize is null && queryColumns.IsDefault
854-
? null : new(rowCountHint, rowCountHintMember?.Member.Name, batchSize, cmdProps, queryColumns);
857+
? null : new(rowCountHint, rowCountHintMember?.Member?.Name, batchSize, cmdProps, queryColumns);
855858
}
856859

857860
static void ValidateParameters(MemberMap? parameters, OperationFlags flags, Action<Diagnostic> onDiagnostic)

src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.Single.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ static void WriteSingleImplementation(
9292
break;
9393
}
9494
}
95-
sb.AppendReader(resultType, readers, additionalCommandState?.QueryColumns ?? default);
95+
sb.AppendReader(resultType, readers, flags, additionalCommandState?.QueryColumns ?? default);
9696
}
9797
else if (flags.HasAny(OperationFlags.Execute))
9898
{

src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs

Lines changed: 114 additions & 102 deletions
Large diffs are not rendered by default.

src/Dapper.AOT.Analyzers/CodeAnalysis/TypeAccessorInterceptorGenerator.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,14 +215,15 @@ public void WriteFileHeader(Compilation compilation)
215215
bool allowUnsafe = compilation.Options is CSharpCompilationOptions cSharp && cSharp.AllowUnsafe;
216216
if (allowUnsafe)
217217
{
218-
_sb.Append("#nullable enable").NewLine();
218+
_sb.Append("#nullable enable").NewLine()
219+
.Append("#pragma warning disable IDE0078 // unnecessary suppression is necessary").NewLine()
220+
.Append("#pragma warning disable CS9270 // SDK-dependent change to interceptors usage").NewLine();
219221
}
220222
}
221223

222224
public void WriteInterceptorsClass(Action innerWriter)
223225
{
224-
_sb.Append("#nullable enable").NewLine()
225-
.Append("namespace ").Append(FeatureKeys.CodegenNamespace)
226+
_sb.Append("namespace ").Append(FeatureKeys.CodegenNamespace)
226227
.Append(" // interceptors must be in a known namespace").Indent().NewLine()
227228
.Append("file static class DapperTypeAccessorGeneratedInterceptors").Indent().NewLine();
228229
innerWriter();

src/Dapper.AOT.Analyzers/Internal/AdditionalCommandState.cs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public bool Equals(in AdditionalCommandState other)
110110
&& BatchSize == other.BatchSize
111111
&& RowCountHintMemberName == other.RowCountHintMemberName
112112
&& ((CommandProperties.IsDefaultOrEmpty && other.CommandProperties.IsDefaultOrEmpty) || Equals(CommandProperties, other.CommandProperties))
113-
&& QueryColumns.Equals(other.QueryColumns);
113+
&& Equals(QueryColumns, other.QueryColumns);
114114

115115
private static bool Equals(in ImmutableArray<CommandProperty> x, in ImmutableArray<CommandProperty> y)
116116
{
@@ -146,9 +146,34 @@ static int GetHashCode(in ImmutableArray<CommandProperty> x)
146146
return value;
147147
}
148148

149+
internal static bool Equals(in ImmutableArray<string> x, in ImmutableArray<string> y)
150+
{
151+
if (x.IsDefaultOrEmpty)
152+
{
153+
return x.IsDefault ? y.IsDefault : y.IsEmpty;
154+
}
155+
if (y.IsDefaultOrEmpty || x.Length != y.Length)
156+
{
157+
return false;
158+
}
159+
return x.AsSpan().SequenceEqual(y.AsSpan());
160+
}
161+
162+
internal static int GetHashCode(in ImmutableArray<string> x)
163+
{
164+
if (x.IsDefaultOrEmpty) return x.IsDefault ? -1 : 0;
165+
166+
int value = x.Length;
167+
foreach (string xVal in x.AsSpan())
168+
{
169+
value = (value * -42) + xVal.GetHashCode();
170+
}
171+
return value;
172+
}
173+
149174
public override int GetHashCode()
150175
=> (RowCountHint + BatchSize.GetValueOrDefault()
151176
+ (RowCountHintMemberName is null ? 0 : RowCountHintMemberName.GetHashCode()))
152177
^ (CommandProperties.IsDefaultOrEmpty ? 0 : GetHashCode(in CommandProperties))
153-
^ QueryColumns.GetHashCode();
178+
^ GetHashCode(QueryColumns);
154179
}

src/Dapper.AOT.Analyzers/Internal/CodeWriter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,15 +328,15 @@ public string ToStringRecycle()
328328
return s;
329329
}
330330

331-
internal CodeWriter AppendReader(ITypeSymbol? resultType, RowReaderState readers, ImmutableArray<string> queryColumns)
331+
internal CodeWriter AppendReader(ITypeSymbol? resultType, RowReaderState readers, OperationFlags flags, ImmutableArray<string> queryColumns)
332332
{
333333
if (IsInbuilt(resultType, out var helper))
334334
{
335335
return Append("global::Dapper.RowFactory.Inbuilt.").Append(helper);
336336
}
337337
else
338338
{
339-
return Append("RowFactory").Append(readers.GetIndex(resultType!, queryColumns)).Append(".Instance");
339+
return Append("RowFactory").Append(readers.GetIndex(resultType!, flags, queryColumns)).Append(".Instance");
340340
}
341341

342342
static bool IsInbuilt(ITypeSymbol? type, out string? helper)

src/Dapper.AOT.Analyzers/Internal/Inspection.cs

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Data;
1111
using System.Data.Common;
1212
using System.Diagnostics;
13+
using System.Diagnostics.CodeAnalysis;
1314
using System.Linq;
1415
using System.Threading;
1516

@@ -147,7 +148,7 @@ public static bool IsEnabled(in ParseState ctx, IOperation op, string attributeN
147148
return false;
148149
}
149150

150-
public static ImmutableArray<string> ParseQueryColumns(AttributeData attrib)
151+
public static ImmutableArray<string> ParseQueryColumns(AttributeData attrib, Action<Diagnostic>? reportDiagnostic, Location? location)
151152
{
152153
ImmutableArray<string> result = default;
153154
if (attrib is not null && attrib.ConstructorArguments.Length == 1
@@ -179,7 +180,14 @@ public static ImmutableArray<string> ParseQueryColumns(AttributeData attrib)
179180
}
180181
if (fail) break;
181182
}
182-
if (!fail) result = ImmutableArray.Create<string>(arr, 0, columnNames.Length);
183+
if (fail)
184+
{
185+
reportDiagnostic?.Invoke(Diagnostic.Create(DapperAnalyzer.Diagnostics.UnableToBindQueryColumns, location));
186+
}
187+
else
188+
{
189+
result = ImmutableArray.Create<string>(arr, 0, columnNames.Length);
190+
}
183191
ArrayPool<string?>.Shared.Return(arr);
184192
}
185193
return result;
@@ -532,10 +540,15 @@ public string DbName
532540
return CodeName;
533541
}
534542
}
535-
public string CodeName => Member.Name;
536-
public ISymbol Member { get; }
537-
public ITypeSymbol CodeType => Member switch
543+
public string CodeName => Member?.Name ?? "";
544+
public ISymbol? Member { get; }
545+
546+
[MemberNotNullWhen(true, nameof(Member), nameof(CodeType))]
547+
public bool IsMapped => Member is not null;
548+
549+
public ITypeSymbol? CodeType => Member switch
538550
{
551+
null => null,
539552
IPropertySymbol prop => prop.Type,
540553
_ => ((IFieldSymbol)Member).Type,
541554
};
@@ -545,7 +558,7 @@ public string DbName
545558

546559
public ElementMemberKind Kind { get; }
547560

548-
public SymbolKind SymbolKind => Member.Kind;
561+
public SymbolKind SymbolKind => Member?.Kind ?? SymbolKind.ErrorType;
549562

550563
public bool IsRowCount => (Kind & ElementMemberKind.RowCount) != 0;
551564
public bool IsRowCountHint => (Kind & ElementMemberKind.RowCountHint) != 0;
@@ -654,20 +667,23 @@ public override bool Equals(object obj) => obj is ElementMember other
654667

655668
public Location? GetLocation()
656669
{
657-
// `ISymbol.Locations` gives the best location of member
658-
// (i.e. for property it will be NAME of an element without modifiers \ getters and setters),
659-
// but it also returns implicitly defined members -> so we need to double check if that can be used
660-
if (Member.Locations.Length > 0)
670+
if (Member is not null)
661671
{
662-
var sourceLocation = Member.Locations.FirstOrDefault(loc => loc.IsInSource);
663-
if (sourceLocation is not null) return sourceLocation;
664-
}
672+
// `ISymbol.Locations` gives the best location of member
673+
// (i.e. for property it will be NAME of an element without modifiers \ getters and setters),
674+
// but it also returns implicitly defined members -> so we need to double check if that can be used
675+
if (Member.Locations.Length > 0)
676+
{
677+
var sourceLocation = Member.Locations.FirstOrDefault(loc => loc.IsInSource);
678+
if (sourceLocation is not null) return sourceLocation;
679+
}
665680

666-
foreach (var node in Member.DeclaringSyntaxReferences)
667-
{
668-
if (node.GetSyntax().GetLocation() is { } loc)
681+
foreach (var node in Member.DeclaringSyntaxReferences)
669682
{
670-
return loc;
683+
if (node.GetSyntax().GetLocation() is { } loc)
684+
{
685+
return loc;
686+
}
671687
}
672688
}
673689
return null;
@@ -687,7 +703,7 @@ public override bool Equals(object obj) => obj is ElementMember other
687703
}
688704
}
689705

690-
internal struct ColumnAttributeData
706+
internal readonly struct ColumnAttributeData
691707
{
692708
public UseColumnAttributeState UseColumnAttribute { get; }
693709
public ColumnAttributeState ColumnAttribute { get; }

src/Dapper.AOT.Analyzers/Internal/MemberMap.cs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using Microsoft.CodeAnalysis;
1+
using Dapper.CodeAnalysis;
2+
using Microsoft.CodeAnalysis;
3+
using System;
24
using System.Collections.Immutable;
35
using System.Linq;
46
using static Dapper.Internal.Inspection;
@@ -72,7 +74,7 @@ private MemberMap(bool forParameters, Location? location, ITypeSymbol declaredTy
7274
Constructor = constructor;
7375
break;
7476
}
75-
77+
7678
switch (ChooseFactoryMethod(ElementType, out var factoryMethod))
7779
{
7880
case FactoryMethodResult.SuccessSingleExplicit:
@@ -83,6 +85,42 @@ private MemberMap(bool forParameters, Location? location, ITypeSymbol declaredTy
8385
Members = GetMembers(forParameters, ElementType, Constructor, FactoryMethod);
8486
}
8587

88+
public ImmutableArray<ElementMember> MapQueryColumns(ImmutableArray<string> queryColumns)
89+
{
90+
if (queryColumns.IsDefault) return Members; // not bound
91+
92+
var result = ImmutableArray.CreateBuilder<ElementMember>(queryColumns.Length);
93+
foreach (var seek in queryColumns)
94+
{
95+
ElementMember found = default;
96+
if (!string.IsNullOrWhiteSpace(seek))
97+
{
98+
foreach (var member in Members) // look for direct match
99+
{
100+
if (string.Equals(member.CodeName, seek, StringComparison.InvariantCultureIgnoreCase))
101+
{
102+
found = member;
103+
break;
104+
}
105+
}
106+
if (found.Member is null) // additional underscore-etc deviation
107+
{
108+
var normalizedSeek = StringHashing.Normalize(seek); // note: should already *be* normalized, but: be sure
109+
foreach (var member in Members)
110+
{
111+
if (StringHashing.NormalizedEquals(member.CodeName, normalizedSeek))
112+
{
113+
found = member;
114+
break;
115+
}
116+
}
117+
}
118+
}
119+
result.Add(found);
120+
}
121+
return result.ToImmutable();
122+
}
123+
86124
static bool IsDynamicParameters(ITypeSymbol? type)
87125
{
88126
if (type is null || type.SpecialType != SpecialType.None) return false;

src/Dapper.AOT.Analyzers/Internal/RowReaderState.cs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,41 @@
66

77
namespace Dapper.Internal;
88

9-
internal readonly struct RowReaderState : IEnumerable<(ITypeSymbol Type, ImmutableArray<string> QueryColumns, int Index)>
9+
internal readonly struct RowReaderState : IEnumerable<(ITypeSymbol Type, OperationFlags Flags, ImmutableArray<string> QueryColumns, int Index)>
1010
{
1111
public RowReaderState() { }
12-
private readonly Dictionary<(ITypeSymbol Type, ImmutableArray<string> QueryColumns), int> resultTypes = new ();
12+
private readonly Dictionary<(ITypeSymbol Type, OperationFlags Flags, ImmutableArray<string> QueryColumns), int> resultTypes = new (KeyComparer.Instance);
1313

14-
public int Count() => resultTypes.Count();
14+
public int Count() => resultTypes.Count;
1515

16-
public IEnumerator<(ITypeSymbol Type, ImmutableArray<string> QueryColumns, int Index)> GetEnumerator()
16+
public IEnumerator<(ITypeSymbol Type, OperationFlags Flags, ImmutableArray<string> QueryColumns, int Index)> GetEnumerator()
1717
{
1818
// retain discovery order
19-
return resultTypes.OrderBy(x => x.Value).Select(x => (x.Key.Type, x.Key.QueryColumns, x.Value)).GetEnumerator();
19+
return resultTypes.OrderBy(x => x.Value).Select(x => (x.Key.Type, x.Key.Flags, x.Key.QueryColumns, x.Value)).GetEnumerator();
2020
}
2121

2222
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
2323

24-
public int GetIndex(ITypeSymbol type, ImmutableArray<string> queryColumns)
24+
public int GetIndex(ITypeSymbol type, OperationFlags flags, ImmutableArray<string> queryColumns)
2525
{
26-
if (!resultTypes.TryGetValue((type, queryColumns), out var index))
26+
const OperationFlags SIGNIFICANT_FLAGS = OperationFlags.StrictTypes; // restrict to flags that impact the reader
27+
var key = (type, flags & SIGNIFICANT_FLAGS, queryColumns);
28+
if (!resultTypes.TryGetValue(key, out var index))
2729
{
28-
resultTypes.Add((type, queryColumns), index = resultTypes.Count);
30+
resultTypes.Add(key, index = resultTypes.Count);
2931
}
3032
return index;
3133
}
34+
35+
private sealed class KeyComparer : IEqualityComparer<(ITypeSymbol Type, OperationFlags Flags, ImmutableArray<string> QueryColumns)>
36+
{
37+
private KeyComparer() { }
38+
public static readonly KeyComparer Instance = new();
39+
40+
bool IEqualityComparer<(ITypeSymbol Type, OperationFlags Flags, ImmutableArray<string> QueryColumns)>.Equals((ITypeSymbol Type, OperationFlags Flags, ImmutableArray<string> QueryColumns) x, (ITypeSymbol Type, OperationFlags Flags, ImmutableArray<string> QueryColumns) y)
41+
=> SymbolEqualityComparer.Default.Equals(x.Type, y.Type) && x.Flags == y.Flags && AdditionalCommandState.Equals(x.QueryColumns, y.QueryColumns);
42+
43+
int IEqualityComparer<(ITypeSymbol Type, OperationFlags Flags, ImmutableArray<string> QueryColumns)>.GetHashCode((ITypeSymbol Type, OperationFlags Flags, ImmutableArray<string> QueryColumns) obj)
44+
=> SymbolEqualityComparer.Default.GetHashCode(obj.Type) ^ (int)obj.Flags ^ AdditionalCommandState.GetHashCode(obj.QueryColumns);
45+
}
3246
}

0 commit comments

Comments
 (0)