Skip to content

Commit f51ddd9

Browse files
authored
Handle conflicting type names in SourceGenerator (Azure#49347)
* Add ability to deal with name conflicts in models * emit context type into builder spec and update test assertions * add more coverage and analyzers * move dupe calculator from emitter to parser * add aliasing for collection builders * handle dupes across libraries * update mrwContext docs
1 parent 7625550 commit f51ddd9

25 files changed

+1358
-479
lines changed

sdk/core/System.ClientModel/gen/AnalyzerReleases.Unshipped.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
Rule ID | Category | Severity | Notes
66
--------|----------|----------|-------
7-
SCM0001 | ContextGenerator | Error | DiagnosticDescriptors
8-
SCM0002 | ContextGenerator | Error | DiagnosticDescriptors
9-
SCM0003 | ContextGenerator | Warning | DiagnosticDescriptors
7+
SCM0001 | ModelReaderWriterContextGenerator | Error | DiagnosticDescriptors, [Documentation](https://aka.ms/system-clientmodel/diagnostics#scm0001)
8+
SCM0002 | ModelReaderWriterContextGenerator | Error | DiagnosticDescriptors, [Documentation](https://aka.ms/system-clientmodel/diagnostics#scm0002)
9+
SCM0003 | ModelReaderWriterContextGenerator | Warning | DiagnosticDescriptors, [Documentation](https://aka.ms/system-clientmodel/diagnostics#scm0003)
10+
SCM0004 | ModelReaderWriterContextGenerator | Warning | DiagnosticDescriptors, [Documentation](https://aka.ms/system-clientmodel/diagnostics#scm0004)
11+
SCM0005 | ModelReaderWriterContextGenerator | Warning | DiagnosticDescriptors, [Documentation](https://aka.ms/system-clientmodel/diagnostics#scm0005)

sdk/core/System.ClientModel/gen/Model/ModelReaderWriterContextGenerationSpec.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
using System.Collections.Generic;
5+
46
namespace System.ClientModel.SourceGeneration;
57

68
internal sealed record ModelReaderWriterContextGenerationSpec
@@ -9,4 +11,17 @@ internal sealed record ModelReaderWriterContextGenerationSpec
911
required public string Modifier { get; init; }
1012
required public ImmutableEquatableArray<TypeBuilderSpec> TypeBuilders { get; init; }
1113
required public ImmutableEquatableArray<TypeRef> ReferencedContexts { get; init; }
14+
15+
public IEnumerable<TypeRef> GetAllTypeRefs()
16+
{
17+
foreach (var typeRef in ReferencedContexts)
18+
{
19+
yield return typeRef;
20+
}
21+
22+
foreach (var typeBuilder in TypeBuilders)
23+
{
24+
yield return typeBuilder.Type;
25+
}
26+
}
1227
}

sdk/core/System.ClientModel/gen/Model/TypeBuilderSpec.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ internal sealed record TypeBuilderSpec
99
required public string Modifier { get; init; }
1010
required public TypeBuilderKind Kind { get; init; }
1111
required public TypeRef? PersistableModelProxy { get; init; }
12+
required public TypeRef ContextType { get; init; }
1213
}

sdk/core/System.ClientModel/gen/Model/TypeRef.cs

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,82 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
using System.Collections.Generic;
45
using Microsoft.CodeAnalysis;
56

67
namespace System.ClientModel.SourceGeneration;
78

89
internal sealed class TypeRef : IEquatable<TypeRef>
910
{
10-
public TypeRef(string name, string nameSpace, string assembly, TypeRef? itemType = default, int arrayRank = 0)
11+
public TypeRef(string name, string nameSpace, string assembly, TypeRef? itemType = default, int arrayRank = 0, string? alias = null)
1112
{
1213
Name = name;
1314
Namespace = nameSpace;
1415
ItemType = itemType;
1516
Assembly = assembly;
1617
ArrayRank = arrayRank;
18+
Alias = alias;
1719
}
1820

1921
public string Name { get; }
2022
public string Namespace { get; }
2123
public TypeRef? ItemType { get; }
2224
public string Assembly { get; }
2325
public int ArrayRank { get; }
26+
public string? Alias { get; }
2427

25-
internal static TypeRef FromINamedTypeSymbol(ITypeSymbol symbol, TypeSymbolKindCache symbolToKindCache)
28+
private string? _typeCaseName;
29+
public string TypeCaseName => _typeCaseName ??= Name.ToIdentifier(false);
30+
31+
private string? _camelCaseName;
32+
public string CamelCaseName => _camelCaseName ??= TypeCaseName.ToCamelCase();
33+
34+
private string? _typeCaseAlias;
35+
public string? TypeCaseAlias => _typeCaseAlias ??= Alias?.ToIdentifier(false);
36+
37+
private string? _camelCaseAlias;
38+
public string? CamelCaseAlias => _camelCaseAlias ??= TypeCaseAlias?.ToCamelCase();
39+
40+
internal static TypeRef FromTypeSymbol(ITypeSymbol symbol, TypeSymbolKindCache symbolToKindCache, Dictionary<ITypeSymbol, string> dupes)
2641
{
42+
dupes.TryGetValue(symbol, out string alias);
2743
if (symbol is INamedTypeSymbol namedTypeSymbol)
2844
{
45+
var name = symbol.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat);
2946
var itemSymbol = namedTypeSymbol.GetItemSymbol(symbolToKindCache);
47+
TypeRef? itemTypeRef = null;
48+
if (itemSymbol is not null)
49+
{
50+
itemTypeRef = FromTypeSymbol(itemSymbol, symbolToKindCache, dupes);
51+
if (itemTypeRef.Alias is not null)
52+
{
53+
alias = name.Replace(itemSymbol.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat), itemTypeRef.Alias);
54+
}
55+
}
3056

3157
return new TypeRef(
3258
symbol.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat),
3359
symbol.ContainingNamespace.ToDisplayString(),
3460
symbol.ContainingAssembly.ToDisplayString(),
35-
itemSymbol is null ? null : FromINamedTypeSymbol(itemSymbol, symbolToKindCache));
61+
itemSymbol is null ? null : FromTypeSymbol(itemSymbol, symbolToKindCache, dupes),
62+
alias: alias);
3663
}
3764
else if (symbol is IArrayTypeSymbol arrayTypeSymbol)
3865
{
39-
var elementType = FromINamedTypeSymbol(arrayTypeSymbol.ElementType, symbolToKindCache);
66+
var name = arrayTypeSymbol.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat).RemoveAsterisks();
67+
var elementType = FromTypeSymbol(arrayTypeSymbol.ElementType, symbolToKindCache, dupes);
68+
if (elementType.Alias is not null)
69+
{
70+
alias = name.Replace(arrayTypeSymbol.ElementType.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat), elementType.Alias);
71+
}
72+
4073
return new TypeRef(
4174
arrayTypeSymbol.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat).RemoveAsterisks(),
4275
elementType.Namespace,
4376
elementType.Assembly,
4477
elementType,
45-
arrayTypeSymbol.Rank);
78+
arrayTypeSymbol.Rank,
79+
alias: alias);
4680
}
4781
else
4882
{

sdk/core/System.ClientModel/gen/ModelReaderWriterContextGenerator.DiagnosticDescriptors.cs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ internal static class DiagnosticDescriptors
1313
id: "SCM0001",
1414
title: "Multiple contexts are not supported",
1515
messageFormat: "Multiple contexts are not supported",
16-
category: "ContextGenerator",
16+
category: "ModelReaderWriterContextGenerator",
1717
helpLinkUri: "https://aka.ms/system-clientmodel/diagnostics#scm0001",
1818
defaultSeverity: DiagnosticSeverity.Error,
1919
isEnabledByDefault: true);
@@ -22,7 +22,7 @@ internal static class DiagnosticDescriptors
2222
id: "SCM0002",
2323
title: "Requires partial modifier",
2424
messageFormat: "Classes that inherit from ModelReaderWriterContext must have the partial modifier",
25-
category: "ContextGenerator",
25+
category: "ModelReaderWriterContextGenerator",
2626
helpLinkUri: "https://aka.ms/system-clientmodel/diagnostics#scm0002",
2727
defaultSeverity: DiagnosticSeverity.Error,
2828
isEnabledByDefault: true);
@@ -31,9 +31,27 @@ internal static class DiagnosticDescriptors
3131
id: "SCM0003",
3232
title: "Only applicable to subclasses of ModelReaderWriterContext",
3333
messageFormat: "ModelReaderWriterBuildableAttribute can only be applied to classes that inherit from ModelReaderWriterContext",
34-
category: "ContextGenerator",
34+
category: "ModelReaderWriterContextGenerator",
3535
helpLinkUri: "https://aka.ms/system-clientmodel/diagnostics#scm0003",
3636
defaultSeverity: DiagnosticSeverity.Warning,
3737
isEnabledByDefault: true);
38+
39+
public static readonly DiagnosticDescriptor AbstractTypeWithoutProxy = new(
40+
id: "SCM0004",
41+
title: "Abstract type without a proxy",
42+
messageFormat: "Abstract type '{0}' must have a PersistableModelProxy defined",
43+
category: "ModelReaderWriterContextGenerator",
44+
helpLinkUri: "https://aka.ms/system-clientmodel/diagnostics#scm0004",
45+
defaultSeverity: DiagnosticSeverity.Warning,
46+
isEnabledByDefault: true);
47+
48+
public static readonly DiagnosticDescriptor TypeMustHaveParameterlessConstructor = new(
49+
id: "SCM0005",
50+
title: "Type must have a parameterless constructor",
51+
messageFormat: "Type '{0}' must have a parameterless constructor",
52+
category: "ModelReaderWriterContextGenerator",
53+
helpLinkUri: "https://aka.ms/system-clientmodel/diagnostics#scm0005",
54+
defaultSeverity: DiagnosticSeverity.Warning,
55+
isEnabledByDefault: true);
3856
}
3957
}

0 commit comments

Comments
 (0)