Skip to content

Commit 7f3d55d

Browse files
authored
Add support for warn obsolete builders (Azure#49607)
* Add support for warn obsolete builders * pr fb * try removing stj * add back stj * Add support for case sensitive names * pr fb
1 parent da401f8 commit 7f3d55d

File tree

8 files changed

+570
-138
lines changed

8 files changed

+570
-138
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
namespace System.ClientModel.SourceGeneration
5+
{
6+
internal enum ObsoleteLevel
7+
{
8+
None,
9+
Warning,
10+
Error,
11+
}
12+
}

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,22 @@ namespace System.ClientModel.SourceGeneration;
55

66
internal sealed class TypeRef : IEquatable<TypeRef>
77
{
8-
public TypeRef(string name, string nameSpace, string assembly, string fullyQualifiedName, TypeRef? itemType = default, int arrayRank = 0)
8+
public TypeRef(
9+
string name,
10+
string nameSpace,
11+
string assembly,
12+
string fullyQualifiedName,
13+
TypeRef? itemType = default,
14+
int arrayRank = 0,
15+
ObsoleteLevel obsoleteLevel = ObsoleteLevel.None)
916
{
1017
Name = name;
1118
Namespace = nameSpace;
1219
ItemType = itemType;
1320
Assembly = assembly;
1421
ArrayRank = arrayRank;
1522
FullyQualifiedName = fullyQualifiedName;
23+
ObsoleteLevel = obsoleteLevel;
1624
}
1725

1826
public string Name { get; }
@@ -21,6 +29,7 @@ public TypeRef(string name, string nameSpace, string assembly, string fullyQuali
2129
public string Assembly { get; }
2230
public int ArrayRank { get; }
2331
public string FullyQualifiedName { get; }
32+
public ObsoleteLevel ObsoleteLevel { get; init; }
2433

2534
private string? _typeCaseName;
2635
public string TypeCaseName => _typeCaseName ??= Name.ToIdentifier(false);

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

Lines changed: 232 additions & 131 deletions
Large diffs are not rendered by default.

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,13 @@ private void ReportDiagnosticAndEmitSource(
166166
return null;
167167

168168
var type = data.SymbolToTypeRefCache.Get(typeSymbol, data.SymbolToKindCache);
169+
if (type.ObsoleteLevel == ObsoleteLevel.Error)
170+
{
171+
// if its marked as error obsolete we can't create a builder for it
172+
// you cannot suppress the reference to an obsolete type marked as error
173+
return null;
174+
}
175+
169176
var itemType = type.GetInnerItemType();
170177

171178
if (!HasAccessibleParameterlessConstructor(typeSymbol, data.SymbolToKindCache) && itemType.IsSameAssembly(contextType))

sdk/core/System.ClientModel/gen/StringBuilderExtensions.cs

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

4+
using System.Collections.Generic;
45
using System.Runtime.CompilerServices;
56
using System.Text;
67

@@ -52,7 +53,8 @@ public static void AppendVariableList(this StringBuilder builder, int rank, stri
5253
{
5354
for (int i = 0; i < rank; i++)
5455
{
55-
builder.Append("List<");
56+
builder.AppendType(typeof(List<>));
57+
builder.Append("<");
5658
}
5759
builder.Append(elementName);
5860
builder.Append('>', rank);

sdk/core/System.ClientModel/gen/TypeSymbolTypeRefCache.cs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ private TypeRef FromTypeSymbol(ITypeSymbol symbol, TypeSymbolKindCache symbolToK
2626
if (symbol is INamedTypeSymbol namedTypeSymbol)
2727
{
2828
var itemSymbol = namedTypeSymbol.GetItemSymbol(symbolToKindCache);
29+
var itemType = itemSymbol is null ? null : Get(itemSymbol, symbolToKindCache);
2930

3031
return new TypeRef(
3132
symbol.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat),
3233
symbol.ContainingNamespace.ToDisplayString(),
3334
symbol.ContainingAssembly.ToDisplayString(),
3435
symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
35-
itemSymbol is null ? null : Get(itemSymbol, symbolToKindCache));
36+
itemType,
37+
obsoleteLevel: itemType is not null ? itemType.ObsoleteLevel : GetObsoleteLevel(symbol));
3638
}
3739
else if (symbol is IArrayTypeSymbol arrayTypeSymbol)
3840
{
@@ -44,12 +46,35 @@ private TypeRef FromTypeSymbol(ITypeSymbol symbol, TypeSymbolKindCache symbolToK
4446
elementType.Assembly,
4547
arrayTypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
4648
elementType,
47-
arrayTypeSymbol.Rank);
49+
arrayTypeSymbol.Rank,
50+
obsoleteLevel: elementType.ObsoleteLevel);
4851
}
4952
else
5053
{
5154
throw new NotSupportedException($"Unexpected type {symbol.GetType()}");
5255
}
5356
}
57+
58+
public static ObsoleteLevel GetObsoleteLevel(ITypeSymbol typeSymbol)
59+
{
60+
foreach (var attribute in typeSymbol.GetAttributes())
61+
{
62+
if (attribute.AttributeClass?.ToDisplayString() == "System.ObsoleteAttribute")
63+
{
64+
if (attribute.ConstructorArguments.Length == 2 &&
65+
attribute.ConstructorArguments[1].Kind == TypedConstantKind.Primitive &&
66+
attribute.ConstructorArguments[1].Value is bool isError)
67+
{
68+
return isError ? ObsoleteLevel.Error : ObsoleteLevel.Warning;
69+
}
70+
else
71+
{
72+
return ObsoleteLevel.Warning;
73+
}
74+
}
75+
}
76+
77+
return ObsoleteLevel.None;
78+
}
5479
}
5580
}

sdk/core/System.ClientModel/tests/gen.unit/CompilationHelper.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ public static Compilation CreateCompilation(
4141
string assemblyName = "TestAssembly",
4242
bool includeSTJ = true,
4343
CSharpParseOptions? parseOptions = null,
44-
string contextName = "LocalContext")
44+
string contextName = "LocalContext",
45+
HashSet<string>? additionalSuppress = null)
4546
{
4647
List<MetadataReference> references =
4748
[
@@ -90,16 +91,22 @@ public static Compilation CreateCompilation(
9091
diag.ToString().EndsWith($"error CS0117: '{contextName}' does not contain a definition for 'Default'", StringComparison.Ordinal))
9192
continue;
9293

94+
if (additionalSuppress is not null && additionalSuppress.Contains(diag.Id))
95+
continue;
96+
9397
Assert.Fail($"Compilation Error: {diag}");
9498
}
9599

96100
return compilation;
97101
}
98102

99103
public static GeneratorResult RunSourceGenerator(Compilation compilation, bool disableDiagnosticValidation = false)
100-
=> RunSourceGenerator(compilation, out _, disableDiagnosticValidation);
104+
=> RunSourceGenerator(compilation, out _, out _, disableDiagnosticValidation);
105+
106+
public static GeneratorResult RunSourceGenerator(Compilation compilation, out Compilation newCompilation, bool disableDiagnosticValidation = false, HashSet<string>? additionalSuppress = null)
107+
=> RunSourceGenerator(compilation, out newCompilation, out _, disableDiagnosticValidation, additionalSuppress);
101108

102-
public static GeneratorResult RunSourceGenerator(Compilation compilation, out Compilation newCompilation, bool disableDiagnosticValidation = false)
109+
public static GeneratorResult RunSourceGenerator(Compilation compilation, out Compilation newCompilation, out ImmutableArray<GeneratedSourceResult> generatedSources, bool disableDiagnosticValidation = false, HashSet<string>? additionalSuppress = null)
103110
{
104111
ModelReaderWriterContextGenerationSpec? generatedSpecs = null;
105112
var generator = new ModelReaderWriterContextGenerator
@@ -110,6 +117,8 @@ public static GeneratorResult RunSourceGenerator(Compilation compilation, out Co
110117
CSharpGeneratorDriver driver = CreateJsonSourceGeneratorDriver(compilation, generator);
111118
var newDriver = driver.RunGeneratorsAndUpdateCompilation(compilation, out newCompilation, out ImmutableArray<Diagnostic> diagnostics);
112119
var runResult = newDriver.GetRunResult();
120+
var contextSourceGenerator = runResult.Results.First(runResult => runResult.Generator.GetGeneratorType().Equals(typeof(ModelReaderWriterContextGenerator)));
121+
generatedSources = contextSourceGenerator.GeneratedSources;
113122

114123
var finalDiagnostics = newCompilation.GetDiagnostics().Where(d => !s_noWarn.Contains(d.Descriptor.Id));
115124
foreach (var diagnostic in finalDiagnostics)
@@ -131,6 +140,10 @@ public static GeneratorResult RunSourceGenerator(Compilation compilation, out Co
131140
}
132141
}
133142
}
143+
144+
if (additionalSuppress is not null && additionalSuppress.Contains(diagnostic.Id))
145+
continue;
146+
134147
Assert.Fail($"Compilation Error: {diagnostic}");
135148
}
136149

0 commit comments

Comments
 (0)