Skip to content

Commit 444c77a

Browse files
authored
Rewrite NpgsqlConnectionStringBuilderSourceGenerator as incremental (#6186)
And bump Microsoft.CodeAnalysis.Analyzers and Microsoft.CodeAnalysis.CSharp
1 parent ee97842 commit 444c77a

2 files changed

Lines changed: 83 additions & 82 deletions

File tree

Directory.Packages.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
<!-- Build -->
1515
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
1616
<PackageVersion Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="3.3.4" />
17-
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
18-
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
17+
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" />
18+
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
1919
<PackageVersion Include="Scriban.Signed" Version="6.2.1" />
2020

2121
<!-- Tests -->

src/Npgsql.SourceGenerators/NpgsqlConnectionStringBuilderSourceGenerator.cs

Lines changed: 81 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
namespace Npgsql.SourceGenerators;
1010

1111
[Generator]
12-
public class NpgsqlConnectionStringBuilderSourceGenerator : ISourceGenerator
12+
public class NpgsqlConnectionStringBuilderSourceGenerator : IIncrementalGenerator
1313
{
1414
static readonly DiagnosticDescriptor InternalError = new DiagnosticDescriptor(
1515
id: "PGXXXX",
@@ -19,106 +19,107 @@ public class NpgsqlConnectionStringBuilderSourceGenerator : ISourceGenerator
1919
DiagnosticSeverity.Error,
2020
isEnabledByDefault: true);
2121

22-
public void Initialize(GeneratorInitializationContext context) {}
23-
24-
public void Execute(GeneratorExecutionContext context)
22+
public void Initialize(IncrementalGeneratorInitializationContext context)
2523
{
26-
if (context.Compilation.Assembly.GetTypeByMetadataName("Npgsql.NpgsqlConnectionStringBuilder") is not { } type)
27-
return;
28-
29-
if (context.Compilation.Assembly.GetTypeByMetadataName("Npgsql.NpgsqlConnectionStringPropertyAttribute") is not
30-
{ } connectionStringPropertyAttribute)
31-
{
32-
context.ReportDiagnostic(Diagnostic.Create(
33-
InternalError,
34-
location: null,
35-
"Could not find Npgsql.NpgsqlConnectionStringPropertyAttribute"));
36-
return;
37-
}
38-
39-
var obsoleteAttribute = context.Compilation.GetTypeByMetadataName("System.ObsoleteAttribute");
40-
var displayNameAttribute = context.Compilation.GetTypeByMetadataName("System.ComponentModel.DisplayNameAttribute");
41-
var defaultValueAttribute = context.Compilation.GetTypeByMetadataName("System.ComponentModel.DefaultValueAttribute");
42-
43-
if (obsoleteAttribute is null || displayNameAttribute is null || defaultValueAttribute is null)
24+
var compilationProvider = context.CompilationProvider;
25+
context.RegisterSourceOutput(compilationProvider, (spc, compilation) =>
4426
{
45-
context.ReportDiagnostic(Diagnostic.Create(
46-
InternalError,
47-
location: null,
48-
"Could not find ObsoleteAttribute, DisplayNameAttribute or DefaultValueAttribute"));
49-
return;
50-
}
51-
52-
var properties = new List<PropertyDetails>();
53-
var propertiesByKeyword = new Dictionary<string, PropertyDetails>();
54-
foreach (var member in type.GetMembers())
55-
{
56-
if (member is not IPropertySymbol property ||
57-
property.GetAttributes().FirstOrDefault(a => connectionStringPropertyAttribute.Equals(a.AttributeClass, SymbolEqualityComparer.Default)) is not { } propertyAttribute ||
58-
property.GetAttributes()
59-
.FirstOrDefault(a => displayNameAttribute.Equals(a.AttributeClass, SymbolEqualityComparer.Default))
60-
?.ConstructorArguments[0].Value is not string displayName)
27+
var type = compilation.Assembly.GetTypeByMetadataName("Npgsql.NpgsqlConnectionStringBuilder");
28+
if (type is null)
29+
return;
30+
31+
var connectionStringPropertyAttribute = compilation.Assembly.GetTypeByMetadataName("Npgsql.NpgsqlConnectionStringPropertyAttribute");
32+
if (connectionStringPropertyAttribute is null)
6133
{
62-
continue;
34+
spc.ReportDiagnostic(Diagnostic.Create(
35+
InternalError,
36+
location: null,
37+
"Could not find Npgsql.NpgsqlConnectionStringPropertyAttribute"));
38+
return;
6339
}
6440

65-
var explicitDefaultValue = property.GetAttributes()
66-
.FirstOrDefault(a => defaultValueAttribute.Equals(a.AttributeClass, SymbolEqualityComparer.Default))
67-
?.ConstructorArguments[0].Value;
68-
69-
if (explicitDefaultValue is string s)
70-
explicitDefaultValue = '"' + s.Replace("\"", "\"\"") + '"';
41+
var obsoleteAttribute = compilation.GetTypeByMetadataName("System.ObsoleteAttribute");
42+
var displayNameAttribute = compilation.GetTypeByMetadataName("System.ComponentModel.DisplayNameAttribute");
43+
var defaultValueAttribute = compilation.GetTypeByMetadataName("System.ComponentModel.DefaultValueAttribute");
7144

72-
if (explicitDefaultValue is not null && property.Type.TypeKind == TypeKind.Enum)
45+
if (obsoleteAttribute is null || displayNameAttribute is null || defaultValueAttribute is null)
7346
{
74-
explicitDefaultValue = $"({property.Type.Name}){explicitDefaultValue}";
75-
// var foo = property.Type.Name;
76-
// explicitDefaultValue += $"/* {foo} */";
47+
spc.ReportDiagnostic(Diagnostic.Create(
48+
InternalError,
49+
location: null,
50+
"Could not find ObsoleteAttribute, DisplayNameAttribute or DefaultValueAttribute"));
51+
return;
7752
}
7853

79-
var propertyDetails = new PropertyDetails
54+
var properties = new List<PropertyDetails>();
55+
var propertiesByKeyword = new Dictionary<string, PropertyDetails>();
56+
foreach (var member in type.GetMembers())
8057
{
81-
Name = property.Name,
82-
CanonicalName = displayName,
83-
TypeName = property.Type.Name,
84-
IsEnum = property.Type.TypeKind == TypeKind.Enum,
85-
IsObsolete = property.GetAttributes().Any(a => obsoleteAttribute.Equals(a.AttributeClass, SymbolEqualityComparer.Default)),
86-
DefaultValue = explicitDefaultValue
87-
};
58+
if (member is not IPropertySymbol property ||
59+
property.GetAttributes().FirstOrDefault(a => connectionStringPropertyAttribute.Equals(a.AttributeClass, SymbolEqualityComparer.Default)) is not { } propertyAttribute ||
60+
property.GetAttributes()
61+
.FirstOrDefault(a => displayNameAttribute.Equals(a.AttributeClass, SymbolEqualityComparer.Default))
62+
?.ConstructorArguments[0].Value is not string displayName)
63+
{
64+
continue;
65+
}
8866

89-
properties.Add(propertyDetails);
67+
var explicitDefaultValue = property.GetAttributes()
68+
.FirstOrDefault(a => defaultValueAttribute.Equals(a.AttributeClass, SymbolEqualityComparer.Default))
69+
?.ConstructorArguments[0].Value;
9070

91-
propertiesByKeyword[displayName.ToUpperInvariant()] = propertyDetails;
92-
if (property.Name != displayName)
93-
{
94-
var propertyName = property.Name.ToUpperInvariant();
95-
if (!propertiesByKeyword.ContainsKey(propertyName))
96-
propertyDetails.Alternatives.Add(propertyName);
97-
}
71+
if (explicitDefaultValue is string s)
72+
explicitDefaultValue = '"' + s.Replace("\"", "\"\"") + '"';
9873

99-
if (propertyAttribute.ConstructorArguments.Length == 1)
100-
{
101-
foreach (var synonymArg in propertyAttribute.ConstructorArguments[0].Values)
74+
if (explicitDefaultValue is not null && property.Type.TypeKind == TypeKind.Enum)
10275
{
103-
if (synonymArg.Value is string synonym)
76+
explicitDefaultValue = $"({property.Type.Name}){explicitDefaultValue}";
77+
}
78+
79+
var propertyDetails = new PropertyDetails
80+
{
81+
Name = property.Name,
82+
CanonicalName = displayName,
83+
TypeName = property.Type.Name,
84+
IsEnum = property.Type.TypeKind == TypeKind.Enum,
85+
IsObsolete = property.GetAttributes().Any(a => obsoleteAttribute.Equals(a.AttributeClass, SymbolEqualityComparer.Default)),
86+
DefaultValue = explicitDefaultValue
87+
};
88+
89+
properties.Add(propertyDetails);
90+
91+
propertiesByKeyword[displayName.ToUpperInvariant()] = propertyDetails;
92+
if (property.Name != displayName)
93+
{
94+
var propertyName = property.Name.ToUpperInvariant();
95+
if (!propertiesByKeyword.ContainsKey(propertyName))
96+
propertyDetails.Alternatives.Add(propertyName);
97+
}
98+
99+
if (propertyAttribute.ConstructorArguments.Length == 1)
100+
{
101+
foreach (var synonymArg in propertyAttribute.ConstructorArguments[0].Values)
104102
{
105-
var synonymName = synonym.ToUpperInvariant();
106-
if (!propertiesByKeyword.ContainsKey(synonymName))
107-
propertyDetails.Alternatives.Add(synonymName);
103+
if (synonymArg.Value is string synonym)
104+
{
105+
var synonymName = synonym.ToUpperInvariant();
106+
if (!propertiesByKeyword.ContainsKey(synonymName))
107+
propertyDetails.Alternatives.Add(synonymName);
108+
}
108109
}
109110
}
110111
}
111-
}
112112

113-
var template = Template.Parse(EmbeddedResource.GetContent("NpgsqlConnectionStringBuilder.snbtxt"), "NpgsqlConnectionStringBuilder.snbtxt");
113+
var template = Template.Parse(EmbeddedResource.GetContent("NpgsqlConnectionStringBuilder.snbtxt"), "NpgsqlConnectionStringBuilder.snbtxt");
114114

115-
var output = template.Render(new
116-
{
117-
Properties = properties,
118-
PropertiesByKeyword = propertiesByKeyword
119-
});
115+
var output = template.Render(new
116+
{
117+
Properties = properties,
118+
PropertiesByKeyword = propertiesByKeyword
119+
});
120120

121-
context.AddSource(type.Name + ".Generated.cs", SourceText.From(output, Encoding.UTF8));
121+
spc.AddSource(type.Name + ".Generated.cs", SourceText.From(output, Encoding.UTF8));
122+
});
122123
}
123124

124125
sealed class PropertyDetails

0 commit comments

Comments
 (0)