Skip to content

Commit 771beb9

Browse files
Copilotagocke
andcommitted
Adapt docs and snippets to match dotnet/samples#7091 GenerateMembers sample
Co-authored-by: agocke <515774+agocke@users.noreply.github.com>
1 parent fc4ba01 commit 771beb9

13 files changed

Lines changed: 314 additions & 224 deletions

File tree

docs/csharp/roslyn-sdk/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ To learn more, see the following resources:
130130

131131
- [Source generators overview](source-generators-overview.md)
132132
- [Tutorial: Create an incremental source generator](tutorials/incremental-source-generator-tutorial.md)
133+
- [Source generator samples](https://github.com/dotnet/samples/tree/main/csharp/roslyn-sdk/SourceGenerators) in the dotnet/samples repository
133134
- [Roslyn incremental generators specification](https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md)
134135

135136
## Next steps

docs/csharp/roslyn-sdk/snippets/incremental-source-generator-tutorial/csharp/MyApp/.gitignore renamed to docs/csharp/roslyn-sdk/snippets/incremental-source-generator-tutorial/csharp/GenerateMembersDemo/.gitignore

File renamed without changes.

docs/csharp/roslyn-sdk/snippets/incremental-source-generator-tutorial/csharp/MyApp/MyApp.csproj renamed to docs/csharp/roslyn-sdk/snippets/incremental-source-generator-tutorial/csharp/GenerateMembersDemo/GenerateMembersDemo.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net9.0</TargetFramework>
6-
<ImplicitUsings>enable</ImplicitUsings>
5+
<TargetFramework>net8.0</TargetFramework>
76
<Nullable>enable</Nullable>
7+
<ImplicitUsings>enable</ImplicitUsings>
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<ProjectReference Include="../MySourceGenerator/MySourceGenerator.csproj"
11+
<ProjectReference Include="..\GenerateMembersGenerator\GenerateMembersGenerator.csproj"
1212
OutputItemType="Analyzer"
1313
ReferenceOutputAssembly="false" />
1414
</ItemGroup>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// <Program>
2+
using GenerateMembersGenerator;
3+
4+
var person = new Person { FirstName = "Alice", LastName = "Smith", Age = 30 };
5+
6+
Console.WriteLine(person.Describe());
7+
Console.WriteLine("Properties:");
8+
foreach (string name in Person.PropertyNames)
9+
{
10+
Console.WriteLine($" {name}");
11+
}
12+
13+
[GenerateMembers]
14+
public partial class Person
15+
{
16+
public string FirstName { get; set; } = string.Empty;
17+
public string LastName { get; set; } = string.Empty;
18+
public int Age { get; set; }
19+
}
20+
// </Program>

docs/csharp/roslyn-sdk/snippets/incremental-source-generator-tutorial/csharp/MySourceGenerator/.gitignore renamed to docs/csharp/roslyn-sdk/snippets/incremental-source-generator-tutorial/csharp/GenerateMembersGenerator/.gitignore

File renamed without changes.

docs/csharp/roslyn-sdk/snippets/incremental-source-generator-tutorial/csharp/MySourceGenerator/MySourceGenerator.csproj renamed to docs/csharp/roslyn-sdk/snippets/incremental-source-generator-tutorial/csharp/GenerateMembersGenerator/GenerateMembersGenerator.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
<PropertyGroup>
44
<TargetFramework>netstandard2.0</TargetFramework>
55
<LangVersion>latest</LangVersion>
6-
<Nullable>enable</Nullable>
76
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
87
</PropertyGroup>
98

109
<ItemGroup>
11-
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" PrivateAssets="all" />
10+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.10.0" PrivateAssets="all" />
11+
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
1212
</ItemGroup>
1313

1414
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
#nullable enable
2+
3+
using System.Collections.Immutable;
4+
using System.Linq;
5+
using System.Text;
6+
using Microsoft.CodeAnalysis;
7+
using Microsoft.CodeAnalysis.CSharp.Syntax;
8+
using Microsoft.CodeAnalysis.Text;
9+
10+
namespace GenerateMembersGenerator;
11+
12+
// <GenerateMembersGenerator>
13+
/// <summary>
14+
/// A source generator that adds a <c>Describe()</c> method and a
15+
/// <c>PropertyNames</c> list to any <see langword="partial"/> class or struct
16+
/// decorated with <c>[GenerateMembers]</c>.
17+
/// </summary>
18+
[Generator]
19+
public class GenerateMembersIncrementalGenerator : IIncrementalGenerator
20+
{
21+
private const string AttributeFullName = "GenerateMembersGenerator.GenerateMembersAttribute";
22+
23+
// <Initialize>
24+
public void Initialize(IncrementalGeneratorInitializationContext context)
25+
{
26+
// 1. Emit the marker attribute so users don't need a separate reference.
27+
context.RegisterPostInitializationOutput(static ctx =>
28+
{
29+
ctx.AddSource("GenerateMembersAttribute.g.cs", SourceText.From(AttributeSource, Encoding.UTF8));
30+
});
31+
32+
// 2. Filter for type declarations annotated with [GenerateMembers].
33+
IncrementalValuesProvider<TypeInfo> typeInfos = context.SyntaxProvider
34+
.ForAttributeWithMetadataName(
35+
AttributeFullName,
36+
predicate: static (node, _) => node is TypeDeclarationSyntax,
37+
transform: static (ctx, _) => GetTypeInfo(ctx))
38+
.Where(static t => t is not null)!;
39+
40+
// 3. Generate source for each qualifying type.
41+
context.RegisterSourceOutput(typeInfos, static (spc, typeInfo) =>
42+
{
43+
string source = GenerateSource(typeInfo);
44+
string hintName = typeInfo.FullyQualifiedName
45+
.Replace("global::", "")
46+
.Replace("::", ".")
47+
.Replace("<", "_")
48+
.Replace(">", "_");
49+
spc.AddSource($"{hintName}.GeneratedMembers.g.cs", SourceText.From(source, Encoding.UTF8));
50+
});
51+
}
52+
// </Initialize>
53+
54+
// <GetTypeInfo>
55+
private static TypeInfo? GetTypeInfo(GeneratorAttributeSyntaxContext context)
56+
{
57+
if (context.TargetSymbol is not INamedTypeSymbol typeSymbol)
58+
return null;
59+
60+
string typeKeyword = context.TargetNode is StructDeclarationSyntax ? "struct" : "class";
61+
62+
ImmutableArray<IPropertySymbol> properties = typeSymbol.GetMembers()
63+
.OfType<IPropertySymbol>()
64+
.Where(p => !p.IsStatic && !p.IsIndexer)
65+
.ToImmutableArray();
66+
67+
return new TypeInfo(
68+
typeSymbol.ContainingNamespace.IsGlobalNamespace
69+
? null
70+
: typeSymbol.ContainingNamespace.ToDisplayString(),
71+
typeSymbol.Name,
72+
typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
73+
typeKeyword,
74+
properties.Select(p => (p.Name, p.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)))
75+
.ToImmutableArray());
76+
}
77+
// </GetTypeInfo>
78+
79+
// <GenerateSource>
80+
private static string GenerateSource(TypeInfo typeInfo)
81+
{
82+
var sb = new StringBuilder();
83+
84+
sb.AppendLine("// <auto-generated />");
85+
86+
if (typeInfo.Namespace is not null)
87+
{
88+
sb.AppendLine($"namespace {typeInfo.Namespace}");
89+
sb.AppendLine("{");
90+
}
91+
92+
sb.AppendLine($" partial {typeInfo.TypeKeyword} {typeInfo.Name}");
93+
sb.AppendLine(" {");
94+
95+
// PropertyNames
96+
sb.AppendLine(" /// <summary>Gets the names of all instance properties.</summary>");
97+
sb.AppendLine(" public static global::System.Collections.Generic.IReadOnlyList<string> PropertyNames { get; } =");
98+
sb.Append(" new string[] { ");
99+
sb.Append(string.Join(", ", typeInfo.Properties.Select(p => $"\"{p.Name}\"")));
100+
sb.AppendLine(" };");
101+
sb.AppendLine();
102+
103+
// Describe method
104+
sb.AppendLine(" /// <summary>Returns a human-readable description of this instance.</summary>");
105+
sb.AppendLine(" public string Describe()");
106+
sb.AppendLine(" {");
107+
sb.AppendLine($" var sb = new global::System.Text.StringBuilder();");
108+
sb.AppendLine($" sb.AppendLine(\"{typeInfo.Name}\");");
109+
foreach (var (name, _) in typeInfo.Properties)
110+
{
111+
sb.AppendLine($" sb.AppendLine($\" {name} = {{{name}}}\");");
112+
}
113+
sb.AppendLine(" return sb.ToString();");
114+
sb.AppendLine(" }");
115+
116+
sb.AppendLine(" }");
117+
118+
if (typeInfo.Namespace is not null)
119+
{
120+
sb.AppendLine("}");
121+
}
122+
123+
return sb.ToString();
124+
}
125+
// </GenerateSource>
126+
127+
// <AttributeSource>
128+
private const string AttributeSource = @"// <auto-generated />
129+
namespace GenerateMembersGenerator
130+
{
131+
/// <summary>
132+
/// Add this attribute to a partial class or struct to automatically generate
133+
/// a <c>Describe()</c> method and a <c>PropertyNames</c> list.
134+
/// </summary>
135+
[global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Struct)]
136+
internal sealed class GenerateMembersAttribute : global::System.Attribute
137+
{
138+
}
139+
}
140+
";
141+
// </AttributeSource>
142+
143+
// <TypeInfoClass>
144+
private sealed class TypeInfo
145+
{
146+
public string? Namespace { get; }
147+
public string Name { get; }
148+
public string FullyQualifiedName { get; }
149+
public string TypeKeyword { get; }
150+
public ImmutableArray<(string Name, string TypeFullName)> Properties { get; }
151+
152+
public TypeInfo(
153+
string? ns,
154+
string name,
155+
string fullyQualifiedName,
156+
string typeKeyword,
157+
ImmutableArray<(string Name, string TypeFullName)> properties)
158+
{
159+
Namespace = ns;
160+
Name = name;
161+
FullyQualifiedName = fullyQualifiedName;
162+
TypeKeyword = typeKeyword;
163+
Properties = properties;
164+
}
165+
}
166+
// </TypeInfoClass>
167+
}
168+
// </GenerateMembersGenerator>

docs/csharp/roslyn-sdk/snippets/incremental-source-generator-tutorial/csharp/MyApp/Greeter.cs

Lines changed: 0 additions & 8 deletions
This file was deleted.

docs/csharp/roslyn-sdk/snippets/incremental-source-generator-tutorial/csharp/MyApp/Program.cs

Lines changed: 0 additions & 5 deletions
This file was deleted.

docs/csharp/roslyn-sdk/snippets/incremental-source-generator-tutorial/csharp/MySourceGenerator/HelloFromGenerator.cs

Lines changed: 0 additions & 138 deletions
This file was deleted.

0 commit comments

Comments
 (0)