diff --git a/Samples.sln b/Samples.sln
index 9e8c36486..349b0c8cd 100644
--- a/Samples.sln
+++ b/Samples.sln
@@ -111,6 +111,12 @@ Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "VisualBasicToCSharpConverte
EndProject
Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "VisualBasicToCSharpConverter.UnitTests", "samples\VisualBasic\VisualBasicToCSharpConverter\VisualBasicToCSharpConverter.Test\VisualBasicToCSharpConverter.UnitTests.vbproj", "{5B7D7569-B5EE-4C01-9AFA-BC1958588160}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SourceGenerators", "SourceGenerators", "{14D18F51-6B59-49D5-9AB7-08B38417A459}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SourceGeneratorSamples", "samples\CSharp\SourceGenerators\SourceGeneratorSamples\SourceGeneratorSamples.csproj", "{2ADE5CFA-5DF4-44A9-BD67-E884BCFBA045}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GeneratedDemo", "samples\CSharp\SourceGenerators\GeneratedDemo\GeneratedDemo.csproj", "{EC4DB63B-C2B4-4D06-AF98-15253035C6D5}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -269,6 +275,14 @@ Global
{5B7D7569-B5EE-4C01-9AFA-BC1958588160}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5B7D7569-B5EE-4C01-9AFA-BC1958588160}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B7D7569-B5EE-4C01-9AFA-BC1958588160}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2ADE5CFA-5DF4-44A9-BD67-E884BCFBA045}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2ADE5CFA-5DF4-44A9-BD67-E884BCFBA045}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2ADE5CFA-5DF4-44A9-BD67-E884BCFBA045}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2ADE5CFA-5DF4-44A9-BD67-E884BCFBA045}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EC4DB63B-C2B4-4D06-AF98-15253035C6D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EC4DB63B-C2B4-4D06-AF98-15253035C6D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EC4DB63B-C2B4-4D06-AF98-15253035C6D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EC4DB63B-C2B4-4D06-AF98-15253035C6D5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -325,6 +339,9 @@ Global
{8E1C9AEC-6EF1-43A8-A378-52C5C0E40532} = {CDA94F62-E35A-4913-8045-D9D42416513C}
{ECB83742-8023-4609-B139-D7B78DD66ED9} = {8E1C9AEC-6EF1-43A8-A378-52C5C0E40532}
{5B7D7569-B5EE-4C01-9AFA-BC1958588160} = {8E1C9AEC-6EF1-43A8-A378-52C5C0E40532}
+ {14D18F51-6B59-49D5-9AB7-08B38417A459} = {C3FB27E9-C8EE-4F76-B0AA-7CD67A7E652B}
+ {2ADE5CFA-5DF4-44A9-BD67-E884BCFBA045} = {14D18F51-6B59-49D5-9AB7-08B38417A459}
+ {EC4DB63B-C2B4-4D06-AF98-15253035C6D5} = {14D18F51-6B59-49D5-9AB7-08B38417A459}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B849838B-3D7A-4B6B-BE07-285DCB1588F4}
diff --git a/global.json b/global.json
index f5c64ea51..264f27e2e 100644
--- a/global.json
+++ b/global.json
@@ -1,12 +1,12 @@
{
"tools": {
- "dotnet": "3.0.101",
+ "dotnet": "3.1.300-preview-015115",
"vs": {
- "version": "16.3"
+ "version": "16.6"
},
"xcopy-msbuild": "16.3.0-alpha"
},
"msbuild-sdks": {
- "Microsoft.DotNet.Arcade.Sdk": "5.0.0-beta.20117.3"
+ "Microsoft.DotNet.Arcade.Sdk": "5.0.0-beta.20224.11"
}
}
diff --git a/samples/CSharp/SourceGenerators/GeneratedDemo/GeneratedDemo.csproj b/samples/CSharp/SourceGenerators/GeneratedDemo/GeneratedDemo.csproj
new file mode 100644
index 000000000..dce269206
--- /dev/null
+++ b/samples/CSharp/SourceGenerators/GeneratedDemo/GeneratedDemo.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ netcoreapp3.1
+ preview
+
+
+
+
+
+ Always
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/CSharp/SourceGenerators/GeneratedDemo/MainSettings.xmlsettings b/samples/CSharp/SourceGenerators/GeneratedDemo/MainSettings.xmlsettings
new file mode 100644
index 000000000..457d6b508
--- /dev/null
+++ b/samples/CSharp/SourceGenerators/GeneratedDemo/MainSettings.xmlsettings
@@ -0,0 +1,5 @@
+
+
+ false
+ 1234
+
\ No newline at end of file
diff --git a/samples/CSharp/SourceGenerators/GeneratedDemo/Program.cs b/samples/CSharp/SourceGenerators/GeneratedDemo/Program.cs
new file mode 100644
index 000000000..56ee7a801
--- /dev/null
+++ b/samples/CSharp/SourceGenerators/GeneratedDemo/Program.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace GeneratedDemo
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ // Run the various scenarios
+ Console.WriteLine("Running HelloWorld:\n");
+ UseHelloWorldGenerator.Run();
+
+ Console.WriteLine("\n\nRunning AutoNotify:\n");
+ UseAutoNotifyGenerator.Run();
+
+ Console.WriteLine("\n\nRunning XmlSettings:\n");
+ UseXmlSettingsGenerator.Run();
+ }
+ }
+}
diff --git a/samples/CSharp/SourceGenerators/GeneratedDemo/UseAutoNotifyGenerator.cs b/samples/CSharp/SourceGenerators/GeneratedDemo/UseAutoNotifyGenerator.cs
new file mode 100644
index 000000000..77e346c9a
--- /dev/null
+++ b/samples/CSharp/SourceGenerators/GeneratedDemo/UseAutoNotifyGenerator.cs
@@ -0,0 +1,39 @@
+using System;
+using AutoNotify;
+
+namespace GeneratedDemo
+{
+ // The view model we'd like to augment
+ public partial class ExampleViewModel
+ {
+ [AutoNotify]
+ private string _text = "private field text";
+
+ [AutoNotify(PropertyName = "Count")]
+ private int _amount = 5;
+ }
+
+ public static class UseAutoNotifyGenerator
+ {
+ public static void Run()
+ {
+ ExampleViewModel vm = new ExampleViewModel();
+
+ // we didn't explicitly create the 'Text' property, it was generated for us
+ string text = vm.Text;
+ Console.WriteLine($"Text = {text}");
+
+ // Properties can have differnt names generated based on the PropertyName argument of the attribute
+ int count = vm.Count;
+ Console.WriteLine($"Count = {count}");
+
+ // the viewmodel will automatically implement INotifyPropertyChanged
+ vm.PropertyChanged += (o, e) => Console.WriteLine($"Property {e.PropertyName} was changed");
+ vm.Text = "abc";
+ vm.Count = 123;
+
+ // Try adding fields to the ExampleViewModel class above and tagging them with the [AutoNotify] attribute
+ // You'll see the matching generated properties visibile in IntelliSense in realtime
+ }
+ }
+}
diff --git a/samples/CSharp/SourceGenerators/GeneratedDemo/UseHelloWorldGenerator.cs b/samples/CSharp/SourceGenerators/GeneratedDemo/UseHelloWorldGenerator.cs
new file mode 100644
index 000000000..000328660
--- /dev/null
+++ b/samples/CSharp/SourceGenerators/GeneratedDemo/UseHelloWorldGenerator.cs
@@ -0,0 +1,11 @@
+namespace GeneratedDemo
+{
+ public static class UseHelloWorldGenerator
+ {
+ public static void Run()
+ {
+ // The static call below is generated at build time, and will list the syntax trees used in the compilation
+ HelloWorldGenerated.HelloWorld.SayHello();
+ }
+ }
+}
diff --git a/samples/CSharp/SourceGenerators/GeneratedDemo/UseXmlSettingsGenerator.cs b/samples/CSharp/SourceGenerators/GeneratedDemo/UseXmlSettingsGenerator.cs
new file mode 100644
index 000000000..213181e51
--- /dev/null
+++ b/samples/CSharp/SourceGenerators/GeneratedDemo/UseXmlSettingsGenerator.cs
@@ -0,0 +1,27 @@
+using System;
+using AutoSettings;
+
+namespace GeneratedDemo
+{
+ public static class UseXmlSettingsGenerator
+ {
+ public static void Run()
+ {
+ // This XmlSettings generator makes a static property in the XmlSettings class for each .xmlsettings file
+
+ // here we have the 'Main' settings file from MainSettings.xmlsettings
+ // the name is determined by the 'name' attribute of the root settings element
+ XmlSettings.MainSettings main = XmlSettings.Main;
+ Console.WriteLine($"Reading settings from {main.GetLocation()}");
+
+ // settings are strongly typed and can be read directly from the static instance
+ bool firstRun = XmlSettings.Main.FirstRun;
+ Console.WriteLine($"Setting firstRun = {firstRun}");
+
+ int cacheSize = XmlSettings.Main.CacheSize;
+ Console.WriteLine($"Setting cacheSize = {cacheSize}");
+
+ // Try adding some keys to the settings file and see the settings become available to read from
+ }
+ }
+}
diff --git a/samples/CSharp/SourceGenerators/README.md b/samples/CSharp/SourceGenerators/README.md
new file mode 100644
index 000000000..56976224a
--- /dev/null
+++ b/samples/CSharp/SourceGenerators/README.md
@@ -0,0 +1,35 @@
+🚧 Work In Progress
+========
+
+These samples are for an in-progress feature of Roslyn. As such they may change or break as the feature is developed, and no level of support is implied.
+
+For more infomation on the Source Generators feature, see the [design document](https://github.com/dotnet/roslyn/blob/master/docs/features/source-generators.md).
+
+Prerequisites
+-----
+
+These samples require Visual Studio 16.6 or higher.
+
+Building the samples
+-----
+Open `SourceGenerators.sln` in Visual Studio or run `dotnet build` from the `\SourceGenerators` directory.
+
+Running the samples
+-----
+
+The generators must be run as part of another build, as they inject source into the project being built. This repo contains a sample project `GeneratorDemo` that relies of the sample generators to add code to it's compilation.
+
+Run `GeneratedDemo` in Visual studio or run `dotnet run` from the `GeneratorDemo` directory.
+
+Using the samples in your project
+-----
+
+You can add the sample generators to your own project by adding an item group containing an analyzer reference:
+
+```xml
+
+
+
+```
+
+You may need to close and reopen the solution in Visual Studio for the change to take effect.
diff --git a/samples/CSharp/SourceGenerators/SourceGeneratorSamples/AutoNotifyGenerator.cs b/samples/CSharp/SourceGenerators/SourceGeneratorSamples/AutoNotifyGenerator.cs
new file mode 100644
index 000000000..94be4a9c8
--- /dev/null
+++ b/samples/CSharp/SourceGenerators/SourceGeneratorSamples/AutoNotifyGenerator.cs
@@ -0,0 +1,185 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Analyzer1
+{
+ [Generator]
+ public class AutoNotifyGenerator : ISourceGenerator
+ {
+ private const string attributeText = @"
+using System;
+namespace AutoNotify
+{
+ [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
+ sealed class AutoNotifyAttribute : Attribute
+ {
+ public AutoNotifyAttribute()
+ {
+ }
+ public string PropertyName { get; set; }
+ }
+}
+";
+
+ public void Initialize(InitializationContext context)
+ {
+ // Register a syntax receiver that will be created for each generation pass
+ context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
+ }
+
+ public void Execute(SourceGeneratorContext context)
+ {
+ // add the attribute text
+ context.AddSource("AutoNotifyAttribute", SourceText.From(attributeText, Encoding.UTF8));
+
+ // retreive the populated receiver
+ if (!(context.SyntaxReceiver is SyntaxReceiver receiver))
+ return;
+
+ // we're going to create a new compilation that contains the attribute.
+ // TODO: we should allow source generators to provide source during initialize, so that this step isn't required.
+ CSharpParseOptions options = (context.Compilation as CSharpCompilation).SyntaxTrees[0].Options as CSharpParseOptions;
+ Compilation compilation = context.Compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(SourceText.From(attributeText, Encoding.UTF8), options));
+
+ // get the newly bound attribute, and INotifyPropertyChanged
+ INamedTypeSymbol attributeSymbol = compilation.GetTypeByMetadataName("AutoNotify.AutoNotifyAttribute");
+ INamedTypeSymbol notifySymbol = compilation.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanged");
+
+ // loop over the candidate fields, and keep the ones that are actually annotated
+ List fieldSymbols = new List();
+ foreach (FieldDeclarationSyntax field in receiver.CandidateFields)
+ {
+ SemanticModel model = compilation.GetSemanticModel(field.SyntaxTree);
+ foreach (VariableDeclaratorSyntax variable in field.Declaration.Variables)
+ {
+ // Get the symbol being decleared by the field, and keep it if its annotated
+ IFieldSymbol fieldSymbol = model.GetDeclaredSymbol(variable) as IFieldSymbol;
+ if (fieldSymbol.GetAttributes().Any(ad => ad.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default)))
+ {
+ fieldSymbols.Add(fieldSymbol);
+ }
+ }
+ }
+
+ // group the fields by class, and generate the source
+ foreach (IGrouping group in fieldSymbols.GroupBy(f => f.ContainingType))
+ {
+ string classSource = ProcessClass(group.Key, group.ToList(), attributeSymbol, notifySymbol, context);
+ context.AddSource($"{group.Key.Name}_autoNotify.cs", SourceText.From(classSource, Encoding.UTF8));
+ }
+ }
+
+ private string ProcessClass(INamedTypeSymbol classSymbol, List fields, ISymbol attributeSymbol, ISymbol notifySymbol, SourceGeneratorContext context)
+ {
+ if (!classSymbol.ContainingSymbol.Equals(classSymbol.ContainingNamespace, SymbolEqualityComparer.Default))
+ {
+ return null; //TODO: issue a diagnostic that it must be top level
+ }
+
+ string namespaceName = classSymbol.ContainingNamespace.ToDisplayString();
+
+ // begin building the generated source
+ StringBuilder source = new StringBuilder($@"
+namespace {namespaceName}
+{{
+ public partial class {classSymbol.Name} : {notifySymbol.ToDisplayString()}
+ {{
+");
+
+ // if the class doesn't implement INotifyPropertyChanged already, add it
+ if (!classSymbol.Interfaces.Contains(notifySymbol))
+ {
+ source.Append("public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;");
+ }
+
+ // create properties for each field
+ foreach (IFieldSymbol fieldSymbol in fields)
+ {
+ ProcessField(source, fieldSymbol, attributeSymbol);
+ }
+
+ source.Append("} }");
+ return source.ToString();
+ }
+
+ private void ProcessField(StringBuilder source, IFieldSymbol fieldSymbol, ISymbol attributeSymbol)
+ {
+ // get the name and type of the field
+ string fieldName = fieldSymbol.Name;
+ ITypeSymbol fieldType = fieldSymbol.Type;
+
+ // get the AutoNotify attribute from the field, and any associated data
+ AttributeData attributeData = fieldSymbol.GetAttributes().Single(ad => ad.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default));
+ TypedConstant overridenNameOpt = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == "PropertyName").Value;
+
+ string propertyName = chooseName(fieldName, overridenNameOpt);
+ if (propertyName.Length == 0 || propertyName == fieldName)
+ {
+ //TODO: issue a diagnostic that we can't process this field
+ return;
+ }
+
+ source.Append($@"
+public {fieldType} {propertyName}
+{{
+ get
+ {{
+ return this.{fieldName};
+ }}
+
+ set
+ {{
+ this.{fieldName} = value;
+ this.PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(nameof({propertyName})));
+ }}
+}}
+
+");
+
+ string chooseName(string fieldName, TypedConstant overridenNameOpt)
+ {
+ if (!overridenNameOpt.IsNull)
+ {
+ return overridenNameOpt.Value.ToString();
+ }
+
+ fieldName = fieldName.TrimStart('_');
+ if (fieldName.Length == 0)
+ return string.Empty;
+
+ if (fieldName.Length == 1)
+ return fieldName.ToUpper();
+
+ return fieldName.Substring(0, 1).ToUpper() + fieldName.Substring(1);
+ }
+
+ }
+
+ ///
+ /// Created on demand before each generation pass
+ ///
+ class SyntaxReceiver : ISyntaxReceiver
+ {
+ public List CandidateFields { get; } = new List();
+
+ ///
+ /// Called for every syntax node in the compilation, we can inspect the nodes and save any information useful for generation
+ ///
+ public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
+ {
+ // any field with at least one attribute is a candidate for property generation
+ if (syntaxNode is FieldDeclarationSyntax fieldDeclarationSyntax
+ && fieldDeclarationSyntax.AttributeLists.Count > 0)
+ {
+ CandidateFields.Add(fieldDeclarationSyntax);
+ }
+ }
+ }
+ }
+}
diff --git a/samples/CSharp/SourceGenerators/SourceGeneratorSamples/HelloWorldGenerator.cs b/samples/CSharp/SourceGenerators/SourceGeneratorSamples/HelloWorldGenerator.cs
new file mode 100644
index 000000000..2beb0aebe
--- /dev/null
+++ b/samples/CSharp/SourceGenerators/SourceGeneratorSamples/HelloWorldGenerator.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+
+namespace SourceGeneratorSamples
+{
+ [Generator]
+ public class HelloWorldGenerator : ISourceGenerator
+ {
+ public void Execute(SourceGeneratorContext context)
+ {
+ // begin creating the source we'll inject into the users compilation
+ StringBuilder sourceBuilder = new StringBuilder(@"
+using System;
+namespace HelloWorldGenerated
+{
+ public static class HelloWorld
+ {
+ public static void SayHello()
+ {
+ Console.WriteLine(""Hello from generated code!"");
+ Console.WriteLine(""The following syntax trees existed in the compilation that created this program:"");
+");
+
+ // using the context, get a list of syntax trees in the users compilation
+ IEnumerable syntaxTrees = context.Compilation.SyntaxTrees;
+
+ // add the filepath of each tree to the class we're building
+ foreach (SyntaxTree tree in syntaxTrees)
+ {
+ sourceBuilder.AppendLine($@"Console.WriteLine(@"" - {tree.FilePath}"");");
+ }
+
+ // finish creating the source to inject
+ sourceBuilder.Append(@"
+ }
+ }
+}");
+
+ // inject the created source into the users compilation
+ context.AddSource("helloWorldGenerated", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
+ }
+
+ public void Initialize(InitializationContext context)
+ {
+ // No initialization required
+ }
+ }
+}
diff --git a/samples/CSharp/SourceGenerators/SourceGeneratorSamples/SettingsXmlGenerator.cs b/samples/CSharp/SourceGenerators/SourceGeneratorSamples/SettingsXmlGenerator.cs
new file mode 100644
index 000000000..e277d2cf8
--- /dev/null
+++ b/samples/CSharp/SourceGenerators/SourceGeneratorSamples/SettingsXmlGenerator.cs
@@ -0,0 +1,108 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Xml;
+
+namespace Analyzer1
+{
+ [Generator]
+ public class SettingsXmlGenerator : ISourceGenerator
+ {
+ private const string SettingsFileString = @"
+namespace XmlSettings
+{
+ public partial class XmlSettings
+ {
+
+ }
+}
+";
+ public void Execute(SourceGeneratorContext context)
+ {
+ // Using the context, get any additional files that end in .xmlsettings
+ IEnumerable settingsFiles = context.AdditionalFiles.Where(at => at.Path.EndsWith(".xmlsettings"));
+ foreach (AdditionalText settingsFile in settingsFiles)
+ {
+ ProcessSettingsFile(settingsFile, context);
+ }
+ }
+
+ private void ProcessSettingsFile(AdditionalText xmlFile, SourceGeneratorContext context)
+ {
+ // try and load the settings file
+ XmlDocument xmlDoc = new XmlDocument();
+ string text = xmlFile.GetText(context.CancellationToken).ToString();
+ try
+ {
+ xmlDoc.LoadXml(text);
+ }
+ catch
+ {
+ //TODO: issue a diagnostic that says we couldn't parse it
+ return;
+ }
+
+
+ // create a class in the XmlSetting class that represnts this entry, and a static field that contains a singleton instance.
+ string fileName = Path.GetFileName(xmlFile.Path);
+ string name = xmlDoc.DocumentElement.GetAttribute("name");
+
+ StringBuilder sb = new StringBuilder($@"
+namespace AutoSettings
+{{
+ using System;
+ using System.Xml;
+
+ public partial class XmlSettings
+ {{
+
+ public static {name}Settings {name} {{ get; }} = new {name}Settings(""{fileName}"");
+
+ public class {name}Settings
+ {{
+
+ XmlDocument xmlDoc = new XmlDocument();
+
+ private string fileName;
+
+ public string GetLocation() => fileName;
+
+ internal {name}Settings(string fileName)
+ {{
+ this.fileName = fileName;
+ xmlDoc.Load(fileName);
+ }}
+");
+
+ for(int i = 0; i < xmlDoc.DocumentElement.ChildNodes.Count; i++)
+ {
+ XmlElement setting = (XmlElement)xmlDoc.DocumentElement.ChildNodes[i];
+ string settingName = setting.GetAttribute("name");
+ string settingType = setting.GetAttribute("type");
+
+ sb.Append($@"
+
+public {settingType} {settingName}
+{{
+ get
+ {{
+ return ({settingType}) Convert.ChangeType(((XmlElement)xmlDoc.DocumentElement.ChildNodes[{i}]).InnerText, typeof({settingType}));
+ }}
+}}
+");
+ }
+
+ sb.Append("} } }");
+
+ context.AddSource($"Settings_{name}", SourceText.From(sb.ToString(), Encoding.UTF8));
+ }
+
+ public void Initialize(InitializationContext context)
+ {
+ }
+ }
+}
diff --git a/samples/CSharp/SourceGenerators/SourceGeneratorSamples/SourceGeneratorSamples.csproj b/samples/CSharp/SourceGenerators/SourceGeneratorSamples/SourceGeneratorSamples.csproj
new file mode 100644
index 000000000..e66e22427
--- /dev/null
+++ b/samples/CSharp/SourceGenerators/SourceGeneratorSamples/SourceGeneratorSamples.csproj
@@ -0,0 +1,17 @@
+
+
+
+ netstandard2.0
+ 8.0
+
+
+
+ https://dotnet.myget.org/F/roslyn/api/v3/index.json ;$(RestoreAdditionalProjectSources)
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/CSharp/SourceGenerators/SourceGenerators.sln b/samples/CSharp/SourceGenerators/SourceGenerators.sln
new file mode 100644
index 000000000..1e5b737dc
--- /dev/null
+++ b/samples/CSharp/SourceGenerators/SourceGenerators.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30022.13
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SourceGeneratorSamples", "SourceGeneratorSamples\SourceGeneratorSamples.csproj", "{B452269D-856C-4FE6-8900-3D81461AF864}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GeneratedDemo", "GeneratedDemo\GeneratedDemo.csproj", "{DB138C8B-7C34-4AC7-A443-A0B29D1CE8A5}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B452269D-856C-4FE6-8900-3D81461AF864}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B452269D-856C-4FE6-8900-3D81461AF864}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B452269D-856C-4FE6-8900-3D81461AF864}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B452269D-856C-4FE6-8900-3D81461AF864}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DB138C8B-7C34-4AC7-A443-A0B29D1CE8A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DB138C8B-7C34-4AC7-A443-A0B29D1CE8A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DB138C8B-7C34-4AC7-A443-A0B29D1CE8A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DB138C8B-7C34-4AC7-A443-A0B29D1CE8A5}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {623C84B6-B8A4-4F29-8E68-4ED37D4529D5}
+ EndGlobalSection
+EndGlobal