diff --git a/samples/VisualBasic/SourceGenerators/GeneratedDemo/Cars.csv b/samples/VisualBasic/SourceGenerators/GeneratedDemo/Cars.csv
new file mode 100644
index 000000000..26a5d2051
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/GeneratedDemo/Cars.csv
@@ -0,0 +1,4 @@
+Brand, Model, Year, cc, Favorite
+Fiat, Punto, 2008, 12.3, No
+Ford, Wagon, 1956, 20.3, No
+BMW, "335", 2014, 20.3, Yes
\ No newline at end of file
diff --git a/samples/VisualBasic/SourceGenerators/GeneratedDemo/GeneratedDemo.vbproj b/samples/VisualBasic/SourceGenerators/GeneratedDemo/GeneratedDemo.vbproj
new file mode 100644
index 000000000..08120ed6f
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/GeneratedDemo/GeneratedDemo.vbproj
@@ -0,0 +1,23 @@
+
+
+
+ Exe
+ netcoreapp3.1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/VisualBasic/SourceGenerators/GeneratedDemo/MainSettings.xmlsettings b/samples/VisualBasic/SourceGenerators/GeneratedDemo/MainSettings.xmlsettings
new file mode 100644
index 000000000..96b96f5e5
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/GeneratedDemo/MainSettings.xmlsettings
@@ -0,0 +1,6 @@
+
+
+ False
+ 1234
+ Hello World!
+
\ No newline at end of file
diff --git a/samples/VisualBasic/SourceGenerators/GeneratedDemo/People.csv b/samples/VisualBasic/SourceGenerators/GeneratedDemo/People.csv
new file mode 100644
index 000000000..5179c3539
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/GeneratedDemo/People.csv
@@ -0,0 +1,3 @@
+Name, address, 11Age
+"Luca Bol", "23 Bell Street", 90
+"john doe", "32 Carl street", 45
\ No newline at end of file
diff --git a/samples/VisualBasic/SourceGenerators/GeneratedDemo/Program.vb b/samples/VisualBasic/SourceGenerators/GeneratedDemo/Program.vb
new file mode 100644
index 000000000..154f716e0
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/GeneratedDemo/Program.vb
@@ -0,0 +1,39 @@
+Option Explicit On
+Option Strict On
+Option Infer On
+
+Module Program
+
+ Public Sub Main()
+
+ Console.WriteLine("Running HelloWorld:
+")
+ UseHelloWorldGenerator.Run()
+
+ Console.WriteLine("
+
+Running AutoNotify:
+")
+ UseAutoNotifyGenerator.Run()
+
+ Console.WriteLine("
+
+Running XmlSettings:
+")
+ UseXmlSettingsGenerator.Run()
+
+ Console.WriteLine("
+
+Running CsvGenerator:
+")
+ UseCsvGenerator.Run()
+
+ Console.WriteLine("
+
+Running MustacheGenerator:
+")
+ UseMustacheGenerator.Run()
+
+ End Sub
+
+End Module
\ No newline at end of file
diff --git a/samples/VisualBasic/SourceGenerators/GeneratedDemo/UseAutoNotifyGenerator.vb b/samples/VisualBasic/SourceGenerators/GeneratedDemo/UseAutoNotifyGenerator.vb
new file mode 100644
index 000000000..ea1a1a3d8
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/GeneratedDemo/UseAutoNotifyGenerator.vb
@@ -0,0 +1,42 @@
+Option Explicit On
+Option Strict On
+Option Infer On
+
+Imports AutoNotify
+
+' The view model we'd like to augment
+Partial Public Class ExampleViewModel
+
+
+ Private _text As String = "private field text"
+
+
+ Private _amount As Integer = 5
+
+End Class
+
+Public Module UseAutoNotifyGenerator
+
+ Public Sub Run()
+
+ Dim vm As New ExampleViewModel()
+
+ ' we didn't explicitly create the 'Text' property, it was generated for us
+ Dim text = vm.Text
+ Console.WriteLine($"Text = {text}")
+
+ ' Properties can have differnt names generated based on the PropertyName argument of the attribute
+ Dim count = vm.Count
+ Console.WriteLine($"Count = {count}")
+
+ ' the viewmodel will automatically implement INotifyPropertyChanged
+ AddHandler vm.PropertyChanged, Sub(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 attribute
+ ' You'll see the matching generated properties visibile in IntelliSense in realtime
+
+ End Sub
+
+End Module
\ No newline at end of file
diff --git a/samples/VisualBasic/SourceGenerators/GeneratedDemo/UseCsvGenerator.vb b/samples/VisualBasic/SourceGenerators/GeneratedDemo/UseCsvGenerator.vb
new file mode 100644
index 000000000..78c199326
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/GeneratedDemo/UseCsvGenerator.vb
@@ -0,0 +1,18 @@
+Option Explicit On
+Option Strict On
+Option Infer On
+
+Imports CSV
+
+Friend Class UseCsvGenerator
+
+ Public Shared Sub Run()
+
+ Console.WriteLine("## CARS")
+ Cars.All.ToList().ForEach(Sub(c) Console.WriteLine(c.Brand & vbTab & c.Model & vbTab & c.Year & vbTab & c.Cc & vbTab & c.Favorite))
+ Console.WriteLine(vbCr & "## PEOPLE")
+ People.All.ToList().ForEach(Sub(p) Console.WriteLine(p.Name & vbTab & p.Address & vbTab & p._11Age))
+
+ End Sub
+
+End Class
\ No newline at end of file
diff --git a/samples/VisualBasic/SourceGenerators/GeneratedDemo/UseHelloWorldGenerator.vb b/samples/VisualBasic/SourceGenerators/GeneratedDemo/UseHelloWorldGenerator.vb
new file mode 100644
index 000000000..78a173ca7
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/GeneratedDemo/UseHelloWorldGenerator.vb
@@ -0,0 +1,8 @@
+Public Module UseHelloWorldGenerator
+
+ Public Sub Run()
+ ' The static call below is generated at build time, and will list the syntax trees used in the compilation
+ HelloWorldGenerated.HelloWorld.SayHello()
+ End Sub
+
+End Module
diff --git a/samples/VisualBasic/SourceGenerators/GeneratedDemo/UseMustacheGenerator.vb b/samples/VisualBasic/SourceGenerators/GeneratedDemo/UseMustacheGenerator.vb
new file mode 100644
index 000000000..3c9a97c41
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/GeneratedDemo/UseMustacheGenerator.vb
@@ -0,0 +1,94 @@
+Option Explicit On
+Option Strict On
+Option Infer On
+
+Imports GeneratedDemo.UseMustacheGenerator
+
+
+
+
+
+
+
+Friend Class UseMustacheGenerator
+
+ Public Shared Sub Run()
+ Console.WriteLine(Mustache.Constants.Lottery)
+ Console.WriteLine(Mustache.Constants.HR)
+ Console.WriteLine(Mustache.Constants.HTML)
+ Console.WriteLine(Mustache.Constants.Section)
+ Console.WriteLine(Mustache.Constants.NestedSection)
+ End Sub
+
+ ' Mustache templates and hashes from the manual at https://mustache.github.io/mustache.1.html...
+ Public Const t1 As String = "
+Hello {{name}}
+You have just won {{value}} dollars!
+{{#in_ca}}
+Well, {{taxed_value}} dollars, after taxes.
+{{/in_ca}}
+"
+ Public Const h1 As String = "
+{
+ ""name"": ""Chris"",
+ ""value"": 10000,
+ ""taxed_value"": 5000,
+ ""in_ca"": true
+}
+"
+ Public Const t2 As String = "
+* {{name}}
+* {{age}}
+* {{company}}
+* {{{company}}}
+"
+ Public Const h2 As String = "
+{
+ ""name"": ""Chris"",
+ ""company"": ""GitHub""
+}
+"
+ Public Const t3 As String = "
+ Shown
+ {{#person}}
+ Never shown!
+ {{/person}}
+ "
+ Public Const h3 As String = "
+{
+ ""person"": false
+}
+"
+ Public Const t4 As String = "
+{{#repo}}
+ {{name}}
+{{/repo}}
+"
+ Public Const h4 As String = "
+{
+ ""repo"": [
+ { ""name"": ""resque"" },
+ { ""name"": ""hub"" },
+ { ""name"": ""rip"" }
+ ]
+}
+"
+ Public Const t5 As String = "
+{{#repo}}
+ {{name}}
+ {{#nested}}
+ NestedName: {{name}}
+ {{/nested}}
+{{/repo}}
+"
+ Public Const h5 As String = "
+{
+ ""repo"": [
+ { ""name"": ""resque"", ""nested"":[{""name"":""nestedResque""}] },
+ { ""name"": ""hub"" },
+ { ""name"": ""rip"" }
+ ]
+}
+"
+
+End Class
\ No newline at end of file
diff --git a/samples/VisualBasic/SourceGenerators/GeneratedDemo/UseXmlSettingsGenerator.vb b/samples/VisualBasic/SourceGenerators/GeneratedDemo/UseXmlSettingsGenerator.vb
new file mode 100644
index 000000000..0ace59e74
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/GeneratedDemo/UseXmlSettingsGenerator.vb
@@ -0,0 +1,25 @@
+Imports AutoSettings
+
+Public Module UseXmlSettingsGenerator
+
+ Public Sub 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
+ Dim main As XmlSettings.MainSettings = XmlSettings.Main
+ Console.WriteLine($"Reading settings from {main.GetLocation()}")
+
+ ' settings are strongly typed and can be read directly from the static instance
+ Dim firstRun As Boolean = XmlSettings.Main.FirstRun
+ Console.WriteLine($"Setting firstRun = {firstRun}")
+
+ Dim cacheSize As Integer = 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
+
+ End Sub
+
+End Module
\ No newline at end of file
diff --git a/samples/VisualBasic/SourceGenerators/README.md b/samples/VisualBasic/SourceGenerators/README.md
new file mode 100644
index 000000000..63d0029eb
--- /dev/null
+++ b/samples/VisualBasic/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 information 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.9.0 Preview 2.0** 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 will most likely need to close and reopen the solution in Visual Studio for any changes made to the generators to take effect.
diff --git a/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/AutoNotifyGenerator.vb b/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/AutoNotifyGenerator.vb
new file mode 100644
index 000000000..981ae965d
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/AutoNotifyGenerator.vb
@@ -0,0 +1,201 @@
+Option Explicit On
+Option Infer On
+Option Strict On
+
+Imports System.Text
+
+Imports Microsoft.CodeAnalysis
+Imports Microsoft.CodeAnalysis.Text
+Imports Microsoft.CodeAnalysis.VisualBasic
+Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
+
+Namespace SourceGeneratorSamples
+
+
+ Public Class AutoNotifyGenerator
+ Implements ISourceGenerator
+
+ Private Const ATTRIBUTE_TEXT As String = "
+Imports System
+
+Namespace Global.AutoNotify
+
+ Friend NotInheritable Class AutoNotifyAttribute
+ Inherits Attribute
+
+ Public Sub New()
+ End Sub
+
+ Public Property PropertyName As String
+ End Class
+End Namespace
+"
+
+ Public Sub Initialize(context As GeneratorInitializationContext) Implements ISourceGenerator.Initialize
+ ' Register a syntax receiver that will be created for each generation pass
+ context.RegisterForSyntaxNotifications(Function() As ISyntaxReceiver
+ Return New SyntaxReceiver
+ End Function)
+ End Sub
+
+ Public Sub Execute(context As GeneratorExecutionContext) Implements ISourceGenerator.Execute
+
+ ' add the attribute text
+ context.AddSource("AutoNotifyAttribute", SourceText.From(ATTRIBUTE_TEXT, Encoding.UTF8))
+
+ ' retreive the populated receiver
+ Dim tempVar = TypeOf context.SyntaxReceiver Is SyntaxReceiver
+ Dim receiver = TryCast(context.SyntaxReceiver, SyntaxReceiver)
+ If Not tempVar Then
+ Return
+ End If
+
+ ' 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.
+ Dim options1 = context.Compilation.SyntaxTrees.First().Options
+ Dim compilation1 = context.Compilation.AddSyntaxTrees(VisualBasicSyntaxTree.ParseText(SourceText.From(ATTRIBUTE_TEXT, Encoding.UTF8), CType(options1, VisualBasicParseOptions)))
+
+ ' get the newly bound attribute, and INotifyPropertyChanged
+ Dim attributeSymbol = compilation1.GetTypeByMetadataName("AutoNotify.AutoNotifyAttribute")
+ Dim notifySymbol = compilation1.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanged")
+
+ ' loop over the candidate fields, and keep the ones that are actually annotated
+ Dim fieldSymbols As New List(Of IFieldSymbol)
+
+ For Each field In receiver.CandidateFields
+ Dim model = compilation1.GetSemanticModel(field.SyntaxTree)
+ For Each variable In field.Declarators
+ For Each name In variable.Names
+ ' Get the symbol being decleared by the field, and keep it if its annotated
+ Dim fieldSymbol = TryCast(model.GetDeclaredSymbol(name), IFieldSymbol)
+ If fieldSymbol.GetAttributes().Any(Function(ad) ad.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.[Default])) Then
+ fieldSymbols.Add(fieldSymbol)
+ End If
+ Next
+ Next
+ Next
+
+ ' group the fields by class, and generate the source
+ For Each group In fieldSymbols.GroupBy(Function(f) f.ContainingType)
+ Dim classSource = ProcessClass(group.Key, group.ToList(), attributeSymbol, notifySymbol)
+ context.AddSource($"{group.Key.Name}_AutoNotify.vb", SourceText.From(classSource, Encoding.UTF8))
+ Next
+
+ End Sub
+
+ Private Function ProcessClass(classSymbol As INamedTypeSymbol, fields As List(Of IFieldSymbol), attributeSymbol As ISymbol, notifySymbol As ISymbol) As String
+
+ If Not classSymbol.ContainingSymbol.Equals(classSymbol.ContainingNamespace, SymbolEqualityComparer.[Default]) Then
+ Return Nothing 'TODO: issue a diagnostic that it must be top level
+ End If
+
+ Dim namespaceName = classSymbol.ContainingNamespace.ToDisplayString()
+
+ ' begin building the generated source
+ Dim source = New StringBuilder($"Option Explicit On
+Option Strict On
+Option Infer On
+
+Namespace Global.{namespaceName}
+
+ Partial Public Class {classSymbol.Name}
+ Implements {notifySymbol.ToDisplayString()}
+
+")
+
+ ' if the class doesn't implement INotifyPropertyChanged already, add it
+ If Not classSymbol.Interfaces.Contains(CType(notifySymbol, INamedTypeSymbol)) Then
+ source.Append(" Public Event PropertyChanged As System.ComponentModel.PropertyChangedEventHandler Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
+")
+ End If
+
+ ' create properties for each field
+ For Each fieldSymbol In fields
+ ProcessField(source, fieldSymbol, attributeSymbol)
+ Next
+
+ source.Append("
+ End Class
+
+End Namespace")
+
+ Return source.ToString()
+
+ End Function
+
+ Private Sub ProcessField(source As StringBuilder, fieldSymbol As IFieldSymbol, attributeSymbol As ISymbol)
+
+ Dim chooseName As Func(Of String, TypedConstant, String) =
+ Function(fieldName1 As String, overridenNameOpt1 As TypedConstant) As String
+
+ If Not overridenNameOpt1.IsNull Then
+ Return overridenNameOpt1.Value.ToString()
+ End If
+
+ fieldName1 = fieldName1.TrimStart("_"c)
+ If fieldName1.Length = 0 Then
+ Return String.Empty
+ End If
+
+ If fieldName1.Length = 1 Then
+ Return fieldName1.ToUpper()
+ End If
+
+ Return fieldName1.Substring(0, 1).ToUpper() & fieldName1.Substring(1)
+
+ End Function
+
+ ' get the name and type of the field
+ Dim fieldName = fieldSymbol.Name
+ Dim fieldType = fieldSymbol.Type
+
+ ' get the AutoNotify attribute from the field, and any associated data
+ Dim attributeData = fieldSymbol.GetAttributes().[Single](Function(ad) ad.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.[Default]))
+ Dim overridenNameOpt = attributeData.NamedArguments.SingleOrDefault(Function(kvp) kvp.Key = "PropertyName").Value
+
+ Dim propertyName = chooseName(fieldName, overridenNameOpt)
+ If propertyName.Length = 0 OrElse propertyName = fieldName Then
+ 'TODO: issue a diagnostic that we can't process this field
+ Return
+ End If
+
+ source.Append($"
+ Public Property {propertyName} As {fieldType}
+ Get
+ Return Me.{fieldName}
+ End Get
+ Set(value As {fieldType})
+ Me.{fieldName} = value
+ RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(NameOf({propertyName})))
+ End Set
+ End Property
+")
+
+ End Sub
+
+ '''
+ ''' Created on demand before each generation pass
+ '''
+ Class SyntaxReceiver
+ Implements ISyntaxReceiver
+
+ Public ReadOnly Property CandidateFields As List(Of FieldDeclarationSyntax) = New List(Of FieldDeclarationSyntax)
+
+ '''
+ ''' Called for every syntax node in the compilation, we can inspect the nodes and save any information useful for generation
+ '''
+ Public Sub OnVisitSyntaxNode(syntaxNode As SyntaxNode) Implements ISyntaxReceiver.OnVisitSyntaxNode
+ ' any field with at least one attribute is a candidate for property generation
+ If TypeOf syntaxNode Is FieldDeclarationSyntax Then
+ Dim fieldDeclarationSyntax = TryCast(syntaxNode, FieldDeclarationSyntax)
+ If fieldDeclarationSyntax.AttributeLists.Count > 0 Then
+ CandidateFields.Add(fieldDeclarationSyntax)
+ End If
+ End If
+ End Sub
+
+ End Class
+
+ End Class
+
+End Namespace
\ No newline at end of file
diff --git a/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/CsvGenerator.props b/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/CsvGenerator.props
new file mode 100644
index 000000000..0741032bb
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/CsvGenerator.props
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/CsvGenerator.vb b/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/CsvGenerator.vb
new file mode 100644
index 000000000..d474fdca2
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/CsvGenerator.vb
@@ -0,0 +1,208 @@
+Option Explicit On
+Option Infer On
+Option Strict On
+
+Imports System.IO
+Imports System.Text
+
+Imports Microsoft.CodeAnalysis
+Imports Microsoft.CodeAnalysis.Text
+
+Imports NotVisualBasic.FileIO
+
+' CsvTextFileParser from https://github.com/22222/CsvTextFieldParser adding suppression rules for default VS config
+
+Namespace SourceGeneratorSamples
+
+
+ Public Class CsvGenerator
+ Implements ISourceGenerator
+
+ Public Enum CsvLoadType
+ Startup
+ OnDemand
+ End Enum
+
+ Public Sub Initialize(context As GeneratorInitializationContext) Implements Microsoft.CodeAnalysis.ISourceGenerator.Initialize
+
+ End Sub
+
+ Public Sub Execute(context As GeneratorExecutionContext) Implements ISourceGenerator.Execute
+ Dim options As IEnumerable(Of (CsvLoadType, Boolean, AdditionalText)) = GetLoadOptions(context)
+ Dim nameCodeSequence As IEnumerable(Of (Name As String, Code As String)) = SourceFilesFromAdditionalFiles(options)
+ For Each entry In nameCodeSequence
+ context.AddSource($"Csv_{entry.Name}", SourceText.From(entry.Code, Encoding.UTF8))
+ Next
+ End Sub
+
+ ' Guesses type of property for the object from the value of a csv field
+ Public Shared Function GetCsvFieldType(exemplar As String) As String
+ Dim garbageBoolean As Boolean
+ Dim garbageInteger As Integer
+ Dim garbageDouble As Double
+ Select Case True
+ Case Boolean.TryParse(exemplar, garbageBoolean) : Return "Boolean"
+ Case Integer.TryParse(exemplar, garbageInteger) : Return "Integer"
+ Case Double.TryParse(exemplar, garbageDouble) : Return "Double"
+ Case Else : Return "String"
+ End Select
+ End Function
+
+ ' Examines the header row and the first row in the csv file to gather all header types and names
+ ' Also it returns the first row of data, because it must be read to figure out the types,
+ ' As the CsvTextFieldParser cannot 'Peek' ahead of one line. If there is no first line,
+ ' it consider all properties as strings. The generator returns an empty list of properly
+ ' typed objects in such case. If the file is completely empty, an error is generated.
+ Public Shared Function ExtractProperties(parser As CsvTextFieldParser) As (Types As String(), Names As String(), Fields As String())
+
+ Dim headerFields = parser.ReadFields()
+ If headerFields Is Nothing Then
+ Throw New Exception("Empty csv file!")
+ End If
+
+ Dim firstLineFields = parser.ReadFields()
+ If firstLineFields Is Nothing Then
+ Return (Enumerable.Repeat("String", headerFields.Length).ToArray(), headerFields, firstLineFields)
+ Else
+ Return (firstLineFields.[Select](Function(field) GetCsvFieldType(field)).ToArray(), headerFields.[Select](New Func(Of String, String)(AddressOf StringToValidPropertyName)).ToArray(), firstLineFields)
+ End If
+
+ End Function
+
+ ' Adds a class to the `CSV` namespace for each `csv` file passed in. The class has a static property
+ ' named `All` that returns the list of strongly typed objects generated on demand at first access.
+ ' There is the slight chance of a race condition in a multi-thread program, but the result is relatively benign
+ ' , loading the collection multiple times instead of once. Measures could be taken to avoid that.
+ Public Shared Function GenerateClassFile(className As String, csvText As String, loadTime As CsvLoadType, cacheObjects As Boolean) As String
+
+ Dim sb As New StringBuilder
+ Dim parser As New CsvTextFieldParser(New StringReader(csvText))
+
+ ''' Imports
+ sb.Append("Option Explicit On
+Option Strict On
+Option Infer On
+
+Imports System.Collections.Generic
+
+Namespace Global.CSV
+")
+
+ ''' Class Definition
+ sb.Append($"
+ Public Class {className}
+
+")
+
+ If loadTime = CsvLoadType.Startup Then
+ sb.Append($" Shared Sub New()
+ Dim x = All
+ End Sub
+
+")
+ End If
+
+ Dim tupleTemp = ExtractProperties(parser) : Dim types = tupleTemp.Types, names = tupleTemp.Names, fields = tupleTemp.Fields
+ Dim minLen = Math.Min(types.Length, names.Length)
+
+ For i = 0 To minLen - 1
+ sb.AppendLine($" Public Property {StringToValidPropertyName(names(i))} As {types(i)}")
+ Next
+
+ ''' Loading data
+ sb.Append($"
+ Private Shared m_all As IEnumerable(Of {className})
+
+ Public Shared ReadOnly Property All As IEnumerable(Of {className})
+ Get
+")
+
+ If cacheObjects Then
+ sb.Append(" If m_all IsNot Nothing Then
+ Return m_all
+ End If
+")
+ End If
+
+ sb.Append($" Dim l As New List(Of {className})()
+ Dim c As {className}
+")
+
+ Do
+
+ If fields Is Nothing Then
+ Continue Do
+ End If
+ If fields.Length < minLen Then
+ Throw New Exception("Not enough fields in CSV file.")
+ End If
+
+ sb.AppendLine($" c = New {className}()")
+
+ Dim value As String '= ""
+ For i As Integer = 0 To minLen - 1
+ ' Wrap strings in quotes.
+ value = If(GetCsvFieldType(fields(i)) = "String", $"""{fields(i).Trim().Trim(New Char() {""""c})}""", fields(i))
+ sb.AppendLine($" c.{names(i)} = {value}")
+ Next
+
+ sb.AppendLine(" l.Add(c)")
+
+ fields = parser.ReadFields()
+
+ Loop While fields IsNot Nothing
+
+ sb.Append($" m_all = l
+ Return l
+")
+
+ ' Close things (property, class, namespace)
+ sb.Append(" End Get
+ End Property
+
+ End Class
+
+End Namespace")
+
+ Return sb.ToString()
+
+ End Function
+
+ Private Shared Function StringToValidPropertyName(s As String) As String
+ s = s.Trim()
+ s = If(Char.IsLetter(s(0)), Char.ToUpper(s(0)) & s.Substring(1), s)
+ s = If(Char.IsDigit(s.Trim()(0)), "_" & s, s)
+ s = New String(s.[Select](Function(ch) If(Char.IsDigit(ch) OrElse Char.IsLetter(ch), ch, "_"c)).ToArray())
+ Return s
+ End Function
+
+ Private Shared Function SourceFilesFromAdditionalFile(loadType As CsvLoadType, cacheObjects As Boolean, file As AdditionalText) As IEnumerable(Of (Name As String, Code As String))
+ Dim className = Path.GetFileNameWithoutExtension(file.Path)
+ Dim csvText = file.GetText().ToString()
+ Return New(String, String)() {(className, GenerateClassFile(className, csvText, loadType, cacheObjects))}
+ End Function
+
+ Private Shared Function SourceFilesFromAdditionalFiles(pathsData As IEnumerable(Of (LoadType As CsvLoadType, CacheObjects As Boolean, File As AdditionalText))) As IEnumerable(Of (Name As String, Code As String))
+ Return pathsData.SelectMany(Function(d) SourceFilesFromAdditionalFile(d.LoadType, d.CacheObjects, d.File))
+ End Function
+
+ Private Shared Iterator Function GetLoadOptions(context As GeneratorExecutionContext) As IEnumerable(Of (LoadType As CsvLoadType, CacheObjects As Boolean, File As AdditionalText))
+ For Each file In context.AdditionalFiles
+ If Path.GetExtension(file.Path).Equals(".csv", StringComparison.OrdinalIgnoreCase) Then
+ ' are there any options for it?
+ Dim loadTimeString As String = Nothing
+ context.AnalyzerConfigOptions.GetOptions(file).TryGetValue("build_metadata.additionalfiles.CsvLoadType", loadTimeString)
+ Dim loadType As CsvLoadType = Nothing
+ [Enum].TryParse(loadTimeString, ignoreCase:=True, loadType)
+ Dim cacheObjectsString As String = Nothing
+ context.AnalyzerConfigOptions.GetOptions(file).TryGetValue("build_metadata.additionalfiles.CacheObjects", cacheObjectsString)
+ Dim cacheObjects As Boolean = Nothing
+ Boolean.TryParse(cacheObjectsString, cacheObjects)
+ Yield (loadType, cacheObjects, file)
+ End If
+ Next
+ End Function
+
+ End Class
+
+End Namespace
\ No newline at end of file
diff --git a/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/HelloWorldGenerator.vb b/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/HelloWorldGenerator.vb
new file mode 100644
index 000000000..792c3b62a
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/HelloWorldGenerator.vb
@@ -0,0 +1,66 @@
+Option Explicit On
+Option Infer On
+Option Strict On
+
+Imports System.Text
+
+Imports Microsoft.CodeAnalysis
+Imports Microsoft.CodeAnalysis.Text
+
+Namespace SourceGeneratorSamples
+
+
+ Public Class HelloWorldGenerator
+ Implements ISourceGenerator
+
+ Public Sub Initialize(context As GeneratorInitializationContext) Implements ISourceGenerator.Initialize
+ ' No initialization required
+ End Sub
+
+ Public Sub Execute(context As GeneratorExecutionContext) Implements ISourceGenerator.Execute
+
+ ' begin creating the source we'll inject into the users compilation
+
+ Dim sourceBuilder = New StringBuilder("Option Explicit On
+Option Strict On
+Option Infer On
+
+Namespace Global.HelloWorldGenerated
+
+ Public Module HelloWorld
+
+ Public Sub SayHello()
+
+ Console.WriteLine(""Hello from generated code!"")
+ Console.WriteLine(""The following syntax trees existed in the compilation that created this program:"")
+")
+
+ ' for testing... let's include a comment with the current date/time.
+ sourceBuilder.AppendLine($" ' Generated at {DateTime.Now}")
+
+ ' using the context, get a list of syntax trees in the users compilation
+ ' add the filepath of each tree to the class we're building
+
+ For Each tree In context.Compilation.SyntaxTrees
+ sourceBuilder.AppendLine($" Console.WriteLine("" - {tree.FilePath}"")")
+ Next
+
+ ' finish creating the source to inject
+
+ sourceBuilder.Append("
+
+ End Sub
+
+ End Module
+
+End Namespace")
+
+ ' inject the created source into the users compilation
+
+ context.AddSource("HelloWorldGenerated", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8))
+
+ End Sub
+
+ End Class
+
+End Namespace
\ No newline at end of file
diff --git a/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/MustacheGenerator.vb b/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/MustacheGenerator.vb
new file mode 100644
index 000000000..38004dbe7
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/MustacheGenerator.vb
@@ -0,0 +1,146 @@
+Option Explicit On
+Option Infer On
+Option Strict On
+
+Imports System.Collections.Immutable
+Imports System.Text
+
+Imports Microsoft.CodeAnalysis
+Imports Microsoft.CodeAnalysis.Text
+
+Imports Microsoft.CodeAnalysis.VisualBasic
+Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
+
+Namespace SourceGeneratorSamples
+
+
+ Public Class MustacheGenerator
+ Implements ISourceGenerator
+
+ Public Sub Initialize(context As GeneratorInitializationContext) Implements ISourceGenerator.Initialize
+ ' No initialization required
+ End Sub
+
+ Public Sub Execute(context As GeneratorExecutionContext) Implements ISourceGenerator.Execute
+
+ Dim attributeSource = "Option Explicit On
+Option Strict On
+Option Infer On
+
+Namespace Global
+
+
+ Friend NotInheritable Class MustacheAttribute
+ Inherits System.Attribute
+
+ Public ReadOnly Property Name As String
+ Public ReadOnly Property Template As String
+ Public ReadOnly Property Hash As String
+
+ Public Sub New(name As String, template As String, hash As String)
+ Me.Name = name
+ Me.Template = template
+ Me.Hash = hash
+ End Sub
+
+ End Class
+
+End Namespace
+"
+
+ context.AddSource("Mustache_MainAttributes__", SourceText.From(attributeSource, Encoding.UTF8))
+
+ Dim compilation = context.Compilation
+
+ Dim options = GetMustacheOptions(compilation)
+ Dim namesSources = SourceFilesFromMustachePaths(options)
+
+ For Each entry In namesSources
+ context.AddSource($"Mustache{entry.Item1}", SourceText.From(entry.Item2, Encoding.UTF8))
+ Next
+
+ End Sub
+
+ Private Shared Iterator Function GetMustacheOptions(compilation As Compilation) As IEnumerable(Of (String, String, String))
+
+ ' Get all Mustache attributes
+
+ Dim allNodes = compilation.SyntaxTrees.SelectMany(Function(s) s.GetRoot().DescendantNodes())
+
+
+ Dim allAttributes = allNodes.Where(Function(d) d.IsKind(SyntaxKind.Attribute)).OfType(Of AttributeSyntax)()
+ Dim attributes = allAttributes.Where(Function(d) d.Name.ToString() = "Mustache").ToImmutableArray()
+
+ Dim models = compilation.SyntaxTrees.[Select](Function(st) compilation.GetSemanticModel(st))
+ For Each att In attributes
+
+ Dim mustacheName = ""
+ Dim template = ""
+ Dim hash = ""
+ Dim index = 0
+
+ If att.ArgumentList Is Nothing Then
+ Throw New Exception("Can't be null here")
+ End If
+
+ Dim m = compilation.GetSemanticModel(att.SyntaxTree)
+
+ For Each arg In att.ArgumentList.Arguments
+
+ Dim expr As ExpressionSyntax = Nothing
+ If TypeOf arg Is SimpleArgumentSyntax Then
+ expr = TryCast(arg, SimpleArgumentSyntax).Expression
+ End If
+ If expr Is Nothing Then
+ Continue For
+ End If
+
+ Dim t = m.GetTypeInfo(expr)
+ Dim v = m.GetConstantValue(expr)
+ If index = 0 Then
+ mustacheName = v.ToString()
+ ElseIf index = 1 Then
+ template = v.ToString()
+ Else
+ hash = v.ToString()
+ End If
+ index += 1
+
+ Next
+
+ Yield (mustacheName, template, hash)
+
+ Next
+
+ End Function
+
+ Private Shared Function SourceFileFromMustachePath(name As String, template As String, hash As String) As String
+ Dim tree = HandlebarsDotNet.Handlebars.Compile(template)
+ Dim o = Newtonsoft.Json.JsonConvert.DeserializeObject(hash)
+ Dim mustacheText = tree(o)
+ Return GenerateMustacheClass(name, mustacheText)
+ End Function
+
+ Private Shared Iterator Function SourceFilesFromMustachePaths(pathsData As IEnumerable(Of (Name As String, Template As String, Hash As String))) As IEnumerable(Of (Name As String, Code As String))
+ For Each entry In pathsData
+ Yield (entry.Name, SourceFileFromMustachePath(entry.Name, entry.Template, entry.Hash))
+ Next
+ End Function
+
+ Private Shared Function GenerateMustacheClass(className As String, mustacheText As String) As String
+ Return $"
+
+Namespace Global.Mustache
+
+ Partial Public Module Constants
+
+ Public Const {className} As String = ""{mustacheText.Replace("""", """""")}""
+
+ End Module
+
+End Namespace"
+ End Function
+
+ End Class
+
+End Namespace
\ No newline at end of file
diff --git a/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/SettingsXmlGenerator.vb b/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/SettingsXmlGenerator.vb
new file mode 100644
index 000000000..1754315d8
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/SettingsXmlGenerator.vb
@@ -0,0 +1,102 @@
+Option Explicit On
+Option Infer On
+Option Strict On
+
+Imports System.IO
+Imports System.Text
+Imports System.Xml
+
+Imports Microsoft.CodeAnalysis
+Imports Microsoft.CodeAnalysis.Text
+
+Namespace SourceGeneratorSamples
+
+
+ Public Class SettingsXmlGenerator
+ Implements ISourceGenerator
+
+ Public Sub Initialize(context As GeneratorInitializationContext) Implements ISourceGenerator.Initialize
+
+ End Sub
+
+ Public Sub Execute(context As GeneratorExecutionContext) Implements ISourceGenerator.Execute
+ ' Using the context, get any additional files that end in .xmlsettings
+ For Each settingsFile In context.AdditionalFiles.Where(Function(at) at.Path.EndsWith(".xmlsettings"))
+ ProcessSettingsFile(settingsFile, context)
+ Next
+ End Sub
+
+ Private Sub ProcessSettingsFile(xmlFile As AdditionalText, context As GeneratorExecutionContext)
+
+ ' try and load the settings file
+ Dim xmlDoc As New XmlDocument
+ Dim text = xmlFile.GetText(context.CancellationToken).ToString()
+ Try
+ xmlDoc.LoadXml(text)
+ Catch
+ 'TODO: issue a diagnostic that says we couldn't parse it
+ Return
+ End Try
+
+ ' create a class in the XmlSetting class that represnts this entry, and a static field that contains a singleton instance.
+ Dim fileName = Path.GetFileName(xmlFile.Path)
+ Dim name = xmlDoc.DocumentElement.GetAttribute("name")
+
+ Dim sb = New StringBuilder($"Option Explicit On
+Option Strict On
+Option Infer On
+
+Imports System.Xml
+
+Namespace Global.AutoSettings
+
+ Partial Public Class XmlSettings
+
+ Public Shared ReadOnly Property {name} As {name}Settings = New {name}Settings(""{fileName}"")
+
+ Public Class {name}Settings
+
+ Private m_xmlDoc As New XmlDocument()
+
+ Private m_fileName As String
+
+ Friend Sub New(fileName As String)
+ m_fileName = fileName
+ m_xmlDoc.Load(m_fileName)
+ End Sub
+
+ Public Function GetLocation() As String
+ Return m_fileName
+ End Function")
+
+ For i = 0 To xmlDoc.DocumentElement.ChildNodes.Count - 1
+
+ Dim setting = CType(xmlDoc.DocumentElement.ChildNodes(i), XmlElement)
+ Dim settingName = setting.GetAttribute("name")
+ Dim settingType = setting.GetAttribute("type")
+
+ sb.Append($"
+
+ Public ReadOnly Property {settingName} As {settingType}
+ Get
+ Return DirectCast(Convert.ChangeType(DirectCast(m_xmlDoc.DocumentElement.ChildNodes({i}), XmlElement).InnerText, GetType({settingType})), {settingType})
+ End Get
+ End Property")
+
+ Next
+
+ sb.Append("
+
+ End Class
+
+ End Class
+
+End Namespace")
+
+ context.AddSource($"Settings_{name}", SourceText.From(sb.ToString(), Encoding.UTF8))
+
+ End Sub
+
+ End Class
+
+End Namespace
\ No newline at end of file
diff --git a/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/SourceGeneratorSamples.vbproj b/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/SourceGeneratorSamples.vbproj
new file mode 100644
index 000000000..f719a9496
--- /dev/null
+++ b/samples/VisualBasic/SourceGenerators/SourceGeneratorSamples/SourceGeneratorSamples.vbproj
@@ -0,0 +1,31 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(GetTargetPathDependsOn);GetDependencyTargetPaths
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/VisualBasic/SourceGenerators/SourceGenerators.sln b/samples/VisualBasic/SourceGenerators/SourceGenerators.sln
new file mode 100644
index 000000000..6b7947985
--- /dev/null
+++ b/samples/VisualBasic/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("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "GeneratedDemo", "GeneratedDemo\GeneratedDemo.vbproj", "{08612C19-D039-44D1-9030-D192CEAF05BB}"
+EndProject
+Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "SourceGeneratorSamples", "SourceGeneratorSamples\SourceGeneratorSamples.vbproj", "{90BDB1C3-E353-448C-8A29-E5B2EF10670B}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {08612C19-D039-44D1-9030-D192CEAF05BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {08612C19-D039-44D1-9030-D192CEAF05BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {08612C19-D039-44D1-9030-D192CEAF05BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {08612C19-D039-44D1-9030-D192CEAF05BB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {90BDB1C3-E353-448C-8A29-E5B2EF10670B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {90BDB1C3-E353-448C-8A29-E5B2EF10670B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {90BDB1C3-E353-448C-8A29-E5B2EF10670B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {90BDB1C3-E353-448C-8A29-E5B2EF10670B}.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