Skip to content

Commit 8649ee3

Browse files
authored
Fix scenario where source generator isn't detecting parameters passed to WinRT mapped interfaces (#1867)
* Fix scenario where source generator isn't detecting parameters passed to WinRT mapped interfaces * Add another test case
1 parent 58c60a9 commit 8649ee3

File tree

7 files changed

+60
-24
lines changed

7 files changed

+60
-24
lines changed

src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,7 @@ private static (VtableAttribute, EquatableArray<VtableAttribute>) GetVtableAttri
239239
bool isCsWinRTCcwLookupTableGeneratorEnabled)
240240
{
241241
var isManagedOnlyType = GeneratorHelper.IsManagedOnlyType(compilation);
242-
var isWinRTTypeFunc = checkForComponentTypes ?
243-
GeneratorHelper.IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(compilation) :
244-
GeneratorHelper.IsWinRTType;
242+
var isWinRTTypeFunc = GeneratorHelper.IsWinRTType(compilation, checkForComponentTypes);
245243
var vtableAttribute = GetVtableAttributeToAdd(symbol, isManagedOnlyType, isWinRTTypeFunc, typeMapper, compilation, false);
246244
if (vtableAttribute != default)
247245
{
@@ -317,7 +315,7 @@ private static VtableAttribute GetVtableAttributesForTaskAdapters(GeneratorSynta
317315
return GetVtableAttributeToAdd(
318316
constructedAdapterType,
319317
GeneratorHelper.IsManagedOnlyType(context.SemanticModel.Compilation),
320-
!isCsWinRTComponent ? GeneratorHelper.IsWinRTType : GeneratorHelper.IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(context.SemanticModel.Compilation),
318+
GeneratorHelper.IsWinRTType(context.SemanticModel.Compilation, isCsWinRTComponent),
321319
typeMapper,
322320
context.SemanticModel.Compilation,
323321
false);
@@ -1176,20 +1174,21 @@ private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupT
11761174
TypeMapper typeMapper,
11771175
bool isCsWinRTComponent)
11781176
{
1177+
var isWinRTType = GeneratorHelper.IsWinRTType(context.SemanticModel.Compilation, isCsWinRTComponent);
11791178
return GetVtableAttributesToAddOnLookupTable(
11801179
context,
11811180
typeMapper,
11821181
GeneratorHelper.IsManagedOnlyType(context.SemanticModel.Compilation),
1183-
!isCsWinRTComponent ? GeneratorHelper.IsWinRTType : GeneratorHelper.IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(context.SemanticModel.Compilation),
1184-
GeneratorHelper.IsWinRTClass(context.SemanticModel.Compilation));
1182+
isWinRTType,
1183+
GeneratorHelper.IsWinRTClassOrInterface(context.SemanticModel.Compilation, isWinRTType, typeMapper));
11851184
}
11861185

11871186
private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupTable(
11881187
GeneratorSyntaxContext context,
11891188
TypeMapper typeMapper,
11901189
Func<ISymbol, bool> isManagedOnlyType,
11911190
Func<ISymbol, TypeMapper, bool> isWinRTType,
1192-
Func<ISymbol, bool> isWinRTClass)
1191+
Func<ISymbol, bool, bool> isWinRTClassOrInterface)
11931192
{
11941193
HashSet<ITypeSymbol> visitedTypes = new(SymbolEqualityComparer.Default);
11951194
HashSet<VtableAttribute> vtableAttributes = new();
@@ -1203,7 +1202,7 @@ private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupT
12031202
// and end up calling a projection function (i.e. ones generated by XAML compiler)
12041203
// In theory, another library can also be called which can call a projected function
12051204
// but not handling those scenarios for now.
1206-
(isWinRTClass(methodSymbol.ContainingSymbol) ||
1205+
(isWinRTClassOrInterface(methodSymbol.ContainingSymbol, true) ||
12071206
SymbolEqualityComparer.Default.Equals(methodSymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly)))
12081207
{
12091208
// Get the concrete types directly from the argument rather than
@@ -1232,13 +1231,14 @@ private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupT
12321231
{
12331232
var leftSymbol = context.SemanticModel.GetSymbolInfo(assignment.Left).Symbol;
12341233
if (leftSymbol is IPropertySymbol propertySymbol &&
1235-
(isWinRTClass(propertySymbol.ContainingSymbol) ||
1234+
(isWinRTClassOrInterface(propertySymbol.ContainingSymbol, true) ||
12361235
SymbolEqualityComparer.Default.Equals(propertySymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly)))
12371236
{
12381237
AddVtableAttributesForType(context.SemanticModel.GetTypeInfo(assignment.Right), propertySymbol.Type);
12391238
}
12401239
else if (leftSymbol is IFieldSymbol fieldSymbol &&
1241-
(isWinRTClass(fieldSymbol.ContainingSymbol) ||
1240+
// WinRT interfaces don't have fields, so we don't need to check for them.
1241+
(isWinRTClassOrInterface(fieldSymbol.ContainingSymbol, false) ||
12421242
SymbolEqualityComparer.Default.Equals(fieldSymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly)))
12431243
{
12441244
AddVtableAttributesForType(context.SemanticModel.GetTypeInfo(assignment.Right), fieldSymbol.Type);
@@ -1423,7 +1423,7 @@ private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupT
14231423
bool isCsWinRTComponent)
14241424
{
14251425
var isManagedOnlyType = GeneratorHelper.IsManagedOnlyType(context.SemanticModel.Compilation);
1426-
var isWinRTType = !isCsWinRTComponent ? GeneratorHelper.IsWinRTType : GeneratorHelper.IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(context.SemanticModel.Compilation);
1426+
var isWinRTType = GeneratorHelper.IsWinRTType(context.SemanticModel.Compilation, isCsWinRTComponent);
14271427
HashSet<VtableAttribute> vtableAttributes = new();
14281428

14291429
foreach (var attributeData in context.Attributes)

src/Authoring/WinRT.SourceGenerator/Helper.cs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -368,16 +368,22 @@ public static bool AllowUnsafe(Compilation compilation)
368368
return compilation is CSharpCompilation csharpCompilation && csharpCompilation.Options.AllowUnsafe;
369369
}
370370

371-
// Whether the class itself is a WinRT projected class.
372-
// This is similar to whether it is a WinRT type, but custom type mappings
373-
// are excluded given those are C# implemented classes.
374-
public static Func<ISymbol, bool> IsWinRTClass(Compilation compilation)
371+
// Returns whether it is a WinRT class or interface.
372+
// If the bool parameter is true, then custom mapped interfaces are also considered.
373+
// This function is similar to whether it is a WinRT type, but custom type mapped
374+
// classes are excluded given those are C# implemented classes such as string.
375+
public static Func<ISymbol, bool, bool> IsWinRTClassOrInterface(Compilation compilation, Func<ISymbol, TypeMapper, bool> isWinRTType, TypeMapper mapper)
375376
{
376377
var winrtRuntimeTypeAttribute = compilation.GetTypeByMetadataName("WinRT.WindowsRuntimeTypeAttribute");
377-
return IsWinRTClassHelper;
378+
return IsWinRTClassOrInterfaceHelper;
378379

379-
bool IsWinRTClassHelper(ISymbol type)
380+
bool IsWinRTClassOrInterfaceHelper(ISymbol type, bool includeMappedInterfaces)
380381
{
382+
if (type is ITypeSymbol typeSymbol && typeSymbol.TypeKind == TypeKind.Interface)
383+
{
384+
return isWinRTType(type, mapper);
385+
}
386+
381387
return HasAttributeWithType(type, winrtRuntimeTypeAttribute);
382388
}
383389
}
@@ -668,14 +674,14 @@ public static bool IsManagedOnlyType(ISymbol symbol, ITypeSymbol winrtExposedTyp
668674
return false;
669675
}
670676

671-
public static Func<ISymbol, TypeMapper, bool> IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(Compilation compilation)
677+
public static Func<ISymbol, TypeMapper, bool> IsWinRTType(Compilation compilation, bool isComponentProject)
672678
{
673679
var winrtTypeAttribute = compilation.GetTypeByMetadataName("WinRT.WindowsRuntimeTypeAttribute");
674680
return IsWinRTTypeHelper;
675681

676682
bool IsWinRTTypeHelper(ISymbol type, TypeMapper typeMapper)
677683
{
678-
return IsWinRTType(type, winrtTypeAttribute, typeMapper, true, compilation.Assembly);
684+
return IsWinRTType(type, winrtTypeAttribute, typeMapper, isComponentProject, compilation.Assembly);
679685
}
680686
}
681687

src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ public override void Initialize(AnalysisContext context)
146146
{
147147
context.RegisterSyntaxNodeAction(context =>
148148
{
149-
var isWinRTClass = GeneratorHelper.IsWinRTClass(context.SemanticModel.Compilation);
149+
var isWinRTType = GeneratorHelper.IsWinRTType(context.SemanticModel.Compilation, isComponentProject);
150+
var isWinRTClassOrInterface = GeneratorHelper.IsWinRTClassOrInterface(context.SemanticModel.Compilation, isWinRTType, typeMapper);
150151

151152
if (context.Node is InvocationExpressionSyntax invocation)
152153
{
@@ -164,7 +165,7 @@ public override void Initialize(AnalysisContext context)
164165
// and end up calling a projection function (i.e. ones generated by XAML compiler)
165166
// In theory, another library can also be called which can call a projected function
166167
// but not handling those scenarios for now.
167-
else if(isWinRTClass(methodSymbol.ContainingSymbol) ||
168+
else if(isWinRTClassOrInterface(methodSymbol.ContainingSymbol, true) ||
168169
SymbolEqualityComparer.Default.Equals(methodSymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly))
169170
{
170171
// Get the concrete types directly from the argument rather than
@@ -198,7 +199,7 @@ public override void Initialize(AnalysisContext context)
198199
{
199200
var leftSymbol = context.SemanticModel.GetSymbolInfo(assignment.Left).Symbol;
200201
if (leftSymbol is IPropertySymbol propertySymbol &&
201-
(isWinRTClass(propertySymbol.ContainingSymbol) ||
202+
(isWinRTClassOrInterface(propertySymbol.ContainingSymbol, true) ||
202203
SymbolEqualityComparer.Default.Equals(propertySymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly)))
203204
{
204205
var assignmentType = context.SemanticModel.GetTypeInfo(assignment.Right);
@@ -209,7 +210,8 @@ public override void Initialize(AnalysisContext context)
209210
}
210211
}
211212
else if (leftSymbol is IFieldSymbol fieldSymbol &&
212-
(isWinRTClass(fieldSymbol.ContainingSymbol) ||
213+
// WinRT interfaces don't have fields, so we don't need to check for them.
214+
(isWinRTClassOrInterface(fieldSymbol.ContainingSymbol, false) ||
213215
SymbolEqualityComparer.Default.Equals(fieldSymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly)))
214216
{
215217
var assignmentType = context.SemanticModel.GetTypeInfo(assignment.Right);

src/Tests/FunctionalTests/Collections/Program.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Collections.ObjectModel;
34
using System.ComponentModel;
45
using System.Linq;
56
using System.Threading;
@@ -268,6 +269,25 @@
268269
Action<int, int> s = (a, b) => { _ = a + b; };
269270
ActionToFunction(s)(2, 3);
270271

272+
var intToListDict = instance.GetIntToListDictionary();
273+
intToListDict.Add(2, new List<EnumValue> { EnumValue.One, EnumValue.Two });
274+
intToListDict[4] = new Collection<EnumValue> { EnumValue.Two };
275+
if (intToListDict.Count != 3 ||
276+
intToListDict[1].First() != EnumValue.One)
277+
{
278+
return 101;
279+
}
280+
281+
if (intToListDict[2].Count != 2 || intToListDict[2].First() != EnumValue.One)
282+
{
283+
return 101;
284+
}
285+
286+
if (intToListDict[4].Count != 1 || intToListDict[4].First() != EnumValue.Two)
287+
{
288+
return 101;
289+
}
290+
271291
return 100;
272292

273293
static bool SequencesEqual<T>(IEnumerable<T> x, params IEnumerable<T>[] list) => list.All((y) => x.SequenceEqual(y));

src/Tests/TestComponentCSharp/Class.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,12 @@ namespace winrt::TestComponentCSharp::implementation
10971097
{ L"String2", ComposedNonBlittableStruct{ { 2 }, { L"String2" }, { true, false, true, false }, { 2 } } }
10981098
});
10991099
}
1100+
1101+
IMap<int32_t, Windows::Foundation::Collections::IVector<TestComponentCSharp::EnumValue>> Class::GetIntToListDictionary()
1102+
{
1103+
auto vector = winrt::single_threaded_vector(std::vector { TestComponentCSharp::EnumValue::One });
1104+
return single_threaded_map<int32_t, Windows::Foundation::Collections::IVector<TestComponentCSharp::EnumValue>>(std::map<int32_t, Windows::Foundation::Collections::IVector<TestComponentCSharp::EnumValue>>{ {1, vector}});
1105+
}
11001106

11011107
struct ComposedBlittableStructComparer
11021108
{

src/Tests/TestComponentCSharp/Class.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,8 @@ namespace winrt::TestComponentCSharp::implementation
291291
Windows::Foundation::Collections::IMap<hstring, TestComponentCSharp::ComposedBlittableStruct> GetStringToBlittableDictionary();
292292
Windows::Foundation::Collections::IMap<hstring, TestComponentCSharp::ComposedNonBlittableStruct> GetStringToNonBlittableDictionary();
293293
Windows::Foundation::Collections::IMap<TestComponentCSharp::ComposedBlittableStruct, Windows::Foundation::IInspectable> GetBlittableToObjectDictionary();
294-
294+
Windows::Foundation::Collections::IMap<int32_t, Windows::Foundation::Collections::IVector<TestComponentCSharp::EnumValue>> GetIntToListDictionary();
295+
295296
// Test IIDOptimizer -- testing the windows projection covers most code paths, and these two types exercise the rest.
296297
Windows::Foundation::Collections::IVectorView<Microsoft::UI::Xaml::Data::DataErrorsChangedEventArgs> GetEventArgsVector();
297298
Windows::Foundation::Collections::IVectorView<TestComponentCSharp::ProvideUri> GetNonGenericDelegateVector();

src/Tests/TestComponentCSharp/TestComponentCSharp.idl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ namespace TestComponentCSharp
338338
Windows.Foundation.Collections.IMap<String, ComposedBlittableStruct> GetStringToBlittableDictionary();
339339
Windows.Foundation.Collections.IMap<String, ComposedNonBlittableStruct> GetStringToNonBlittableDictionary();
340340
Windows.Foundation.Collections.IMap<ComposedBlittableStruct, Object> GetBlittableToObjectDictionary();
341+
Windows.Foundation.Collections.IMap<Int32, Windows.Foundation.Collections.IVector<EnumValue> > GetIntToListDictionary();
341342

342343
// Test IIDOptimizer
343344
Windows.Foundation.Collections.IVectorView<Microsoft.UI.Xaml.Data.DataErrorsChangedEventArgs> GetEventArgsVector();

0 commit comments

Comments
 (0)