Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Samples updates #739

Merged
merged 4 commits into from
Feb 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<UsingToolNetFrameworkReferenceAssemblies>true</UsingToolNetFrameworkReferenceAssemblies>
<UsingToolMicrosoftNetCompilers>true</UsingToolMicrosoftNetCompilers>
<UsingToolSymbolUploader>true</UsingToolSymbolUploader>
<MicrosoftNetCompilersToolsetVersion>3.8.0-4.20464.1</MicrosoftNetCompilersToolsetVersion>
<MicrosoftNetCompilersToolsetVersion>3.9.0-4.final</MicrosoftNetCompilersToolsetVersion>
<!-- Force prior version due to https://github.com/microsoft/vstest/pull/2192 and https://github.com/microsoft/vstest/pull/2067 -->
<MicrosoftNETTestSdkVersion>16.1.1</MicrosoftNETTestSdkVersion>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>preview</LangVersion>
</PropertyGroup>

<ItemGroup>
<AdditionalFiles Include="People.csv" CsvLoadType="Startup" />
<AdditionalFiles Include="Cars.csv" CsvLoadType="OnDemand" CacheObjects="true" />
<AdditionalFiles Include="Geometry.math" />

<AdditionalFiles Include="MainSettings.xmlsettings" CopyToOutputDirectory="PreserveNewest" />
<None Include="MainSettings.xmlsettings" CopyToOutputDirectory="PreserveNewest" /> <!-- TODO: remove this when AdditionalFiles supports CopyToOutputDirectory -->
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ namespace GeneratedDemo
{
class UseMustacheGenerator
{

public static void Run()
{
WriteLine(Mustache.Constants.Lottery);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class AutoNotifyGenerator : ISourceGenerator
namespace AutoNotify
{
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
[System.Diagnostics.Conditional(""AutoNotifyGenerator_DEBUG"")]
sealed class AutoNotifyAttribute : Attribute
{
public AutoNotifyAttribute()
Expand All @@ -27,48 +28,28 @@ public AutoNotifyAttribute()
}
";


public void Initialize(GeneratorInitializationContext context)
{
// Register the attribute source
context.RegisterForPostInitialization((i) => i.AddSource("AutoNotifyAttribute", attributeText));

// Register a syntax receiver that will be created for each generation pass
context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
}

public void Execute(GeneratorExecutionContext context)
{
// add the attribute text
context.AddSource("AutoNotifyAttribute", SourceText.From(attributeText, Encoding.UTF8));

// retreive the populated receiver
if (!(context.SyntaxReceiver is SyntaxReceiver receiver))
// retrieve the populated receiver
if (!(context.SyntaxContextReceiver 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<IFieldSymbol> fieldSymbols = new List<IFieldSymbol>();
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);
}
}
}
// get the added attribute, and INotifyPropertyChanged
INamedTypeSymbol attributeSymbol = context.Compilation.GetTypeByMetadataName("AutoNotify.AutoNotifyAttribute");
INamedTypeSymbol notifySymbol = context.Compilation.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanged");

// group the fields by class, and generate the source
foreach (IGrouping<INamedTypeSymbol, IFieldSymbol> group in fieldSymbols.GroupBy(f => f.ContainingType))
foreach (IGrouping<INamedTypeSymbol, IFieldSymbol> group in receiver.Fields.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));
Expand Down Expand Up @@ -164,20 +145,28 @@ string chooseName(string fieldName, TypedConstant overridenNameOpt)
/// <summary>
/// Created on demand before each generation pass
/// </summary>
class SyntaxReceiver : ISyntaxReceiver
class SyntaxReceiver : ISyntaxContextReceiver
{
public List<FieldDeclarationSyntax> CandidateFields { get; } = new List<FieldDeclarationSyntax>();
public List<IFieldSymbol> Fields { get; } = new List<IFieldSymbol>();

/// <summary>
/// Called for every syntax node in the compilation, we can inspect the nodes and save any information useful for generation
/// </summary>
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
{
// any field with at least one attribute is a candidate for property generation
if (syntaxNode is FieldDeclarationSyntax fieldDeclarationSyntax
if (context.Node is FieldDeclarationSyntax fieldDeclarationSyntax
&& fieldDeclarationSyntax.AttributeLists.Count > 0)
{
CandidateFields.Add(fieldDeclarationSyntax);
foreach (VariableDeclaratorSyntax variable in fieldDeclarationSyntax.Declaration.Variables)
{
// Get the symbol being declared by the field, and keep it if its annotated
IFieldSymbol fieldSymbol = context.SemanticModel.GetDeclaredSymbol(variable) as IFieldSymbol;
if (fieldSymbol.GetAttributes().Any(ad => ad.AttributeClass.ToDisplayString() == "AutoNotify.AutoNotifyAttribute"))
{
Fields.Add(fieldSymbol);
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,6 @@ private static void Funct(Context ctx) {
[Generator]
public class MathsGenerator : ISourceGenerator
{
private bool libraryIsAdded = false;

private const string libraryCode = @"
using System.Linq;
using System;
Expand All @@ -432,24 +430,19 @@ public static double MySum(int start, int end, Func<double, double> f) =>

public void Execute(GeneratorExecutionContext context)
{

foreach (AdditionalText file in context.AdditionalFiles)
{
if (Path.GetExtension(file.Path).Equals(".math", StringComparison.OrdinalIgnoreCase))
{
if(!libraryIsAdded)
{
context.AddSource("___MathLibrary___.cs", SourceText.From(libraryCode, Encoding.UTF8));
libraryIsAdded = true;
}
// Load formulas from .math files
var mathText = file.GetText();
var mathString = "";

if(mathText != null)
{
mathString = mathText.ToString();
} else
}
else
{
throw new Exception($"Cannot load file {file.Path}");
}
Expand All @@ -468,7 +461,10 @@ public void Execute(GeneratorExecutionContext context)
}
}

public void Initialize(GeneratorInitializationContext context) { }
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForPostInitialization((pi) => pi.AddSource("__MathLibrary__.cs", libraryCode));
}
}
}

Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;

#nullable enable

Expand All @@ -15,9 +12,7 @@ namespace Mustache
[Generator]
public class MustacheGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
string attributeSource = @"
private const string attributeSource = @"
[System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple=true)]
internal sealed class MustacheAttribute: System.Attribute
{
Expand All @@ -28,97 +23,60 @@ public MustacheAttribute(string name, string template, string hash)
=> (Name, Template, Hash) = (name, template, hash);
}
";
context.AddSource("Mustache_MainAttributes__", SourceText.From(attributeSource, Encoding.UTF8));

Compilation compilation = context.Compilation;

IEnumerable<(string, string, string)> options = GetMustacheOptions(compilation);
IEnumerable<(string, string)> namesSources = SourceFilesFromMustachePaths(options);

foreach ((string name, string source) in namesSources)
{
context.AddSource($"Mustache{name}", SourceText.From(source, Encoding.UTF8));
}
}

static IEnumerable<(string, string, string)> GetMustacheOptions(Compilation compilation)
public void Execute(GeneratorExecutionContext context)
{
// Get all Mustache attributes
IEnumerable<SyntaxNode>? allNodes = compilation.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes());
IEnumerable<AttributeSyntax> allAttributes = allNodes.Where((d) => d.IsKind(SyntaxKind.Attribute)).OfType<AttributeSyntax>();
ImmutableArray<AttributeSyntax> attributes = allAttributes.Where(d => d.Name.ToString() == "Mustache")
.ToImmutableArray();

IEnumerable<SemanticModel> models = compilation.SyntaxTrees.Select(st => compilation.GetSemanticModel(st));
foreach (AttributeSyntax att in attributes)
SyntaxReceiver rx = (SyntaxReceiver)context.SyntaxContextReceiver!;
foreach ((string name, string template, string hash) in rx.TemplateInfo)
{
string mustacheName = "", template = "", hash = "";
int index = 0;

if (att.ArgumentList is null) throw new Exception("Can't be null here");

SemanticModel m = compilation.GetSemanticModel(att.SyntaxTree);

foreach (AttributeArgumentSyntax arg in att.ArgumentList.Arguments)
{
ExpressionSyntax expr = arg.Expression;

TypeInfo t = m.GetTypeInfo(expr);
Optional<object?> v = m.GetConstantValue(expr);
if (index == 0)
{
mustacheName = v.ToString();
}
else if (index == 1)
{
template = v.ToString();
}
else
{
hash = v.ToString();
}
index += 1;
}
yield return (mustacheName, template, hash);
string source = SourceFileFromMustachePath(name, template, hash);
context.AddSource($"Mustache{name}", source);
}
}

static string SourceFileFromMustachePath(string name, string template, string hash)
{
Func<object, string> tree = HandlebarsDotNet.Handlebars.Compile(template);
object @object = Newtonsoft.Json.JsonConvert.DeserializeObject(hash);
string mustacheText = tree(@object);

return GenerateMustacheClass(name, mustacheText);
}

static IEnumerable<(string, string)> SourceFilesFromMustachePaths(IEnumerable<(string, string, string)> pathsData)
{

foreach ((string name, string template, string hash) in pathsData)
{
yield return (name, SourceFileFromMustachePath(name, template, hash));
}
}

private static string GenerateMustacheClass(string className, string mustacheText)
{
StringBuilder sb = new StringBuilder();
sb.Append($@"
namespace Mustache {{

public static partial class Constants {{

public const string {className} = @""{mustacheText.Replace("\"", "\"\"")}"";
public const string {name} = @""{mustacheText.Replace("\"", "\"\"")}"";
}}
}}
");
return sb.ToString();

}

public void Initialize(GeneratorInitializationContext context)
{
// No initialization required
context.RegisterForPostInitialization((pi) => pi.AddSource("Mustache_MainAttributes__", attributeSource));
context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
}

class SyntaxReceiver : ISyntaxContextReceiver
{
public List<(string name, string template, string hash)> TemplateInfo = new List<(string name, string template, string hash)>();

public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
{
// find all valid mustache attributes
if (context.Node is AttributeSyntax attrib
&& attrib.ArgumentList?.Arguments.Count == 3
&& context.SemanticModel.GetTypeInfo(attrib).Type?.ToDisplayString() == "MustacheAttribute")
{
string name = context.SemanticModel.GetConstantValue(attrib.ArgumentList.Arguments[0].Expression).ToString();
string template = context.SemanticModel.GetConstantValue(attrib.ArgumentList.Arguments[1].Expression).ToString();
string hash = context.SemanticModel.GetConstantValue(attrib.ArgumentList.Arguments[2].Expression).ToString();

TemplateInfo.Add((name, template, hash));
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,6 @@ namespace Analyzer1
[Generator]
public class SettingsXmlGenerator : ISourceGenerator
{
private const string SettingsFileString = @"
namespace XmlSettings
{
public partial class XmlSettings
{

}
}
";
public void Execute(GeneratorExecutionContext context)
{
// Using the context, get any additional files that end in .xmlsettings
Expand Down