public void MultiplePatchType_ExtensionAndTypeAddedToSource()
        {
            Compilation inputCompilation            = CreateCompilation(@"
namespace TestCode2
{
    public class Dto0 { public double Property { get; set; }  }
    public class Dto1 { public Dto0 Property { get; set; } }

    public class Program
    {
        public void SomeMethod(LaDeak.JsonMergePatch.Abstractions.Patch<Dto0> data)
        {
        }

        public void SomeMethod(LaDeak.JsonMergePatch.Abstractions.Patch<Dto1> data)
        {
        }
    }
}
");
            JsonMergePatchSourceGenerator generator = new JsonMergePatchSourceGenerator();
            GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

            driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);

            Assert.True(diagnostics.IsEmpty);
            Assert.True(outputCompilation.SyntaxTrees.Count() == 4);
            Assert.True(outputCompilation.GetDiagnostics().IsEmpty);

            GeneratorDriverRunResult runResult = driver.GetRunResult();

            Assert.Equal(3, runResult.GeneratedTrees.Length);
            Assert.Empty(runResult.Diagnostics);
        }
Example #2
0
        public void AddJsonMergePatch_AddsExtensionMethod()
        {
            // Create the 'input' compilation that the generator will act on
            Compilation     inputCompilation = CreateCompilation(@"
namespace TestCode1
{
    public class Dto { public int Property { get; set; }  }
    public class Program
    {
        public void SomeMethod(LaDeak.JsonMergePatch.Abstractions.Patch<Dto> data)
        {
        }
    }
}
");
            var             commonGenerator  = new JsonMergePatchSourceGenerator();
            var             sut    = new AspNetJsonMergePatchSourceGenerator();
            GeneratorDriver driver = CSharpGeneratorDriver.Create(commonGenerator, sut);

            driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);

            Assert.True(diagnostics.IsEmpty);

            GeneratorDriverRunResult runResult       = driver.GetRunResult();
            GeneratorRunResult       testedGenerator = runResult.Results[1];

            Assert.Single(testedGenerator.GeneratedSources);
            Assert.Empty(testedGenerator.Diagnostics);
        }
        public void DefaultRecordType_ExtensionAndTypeAddedToSource()
        {
            // Create the 'input' compilation that the generator will act on
            Compilation inputCompilation            = CreateCompilation(@"
namespace TestCode1
{
    public record Dto(int Property0, string Property1);
    public class Program
    {
        public void SomeMethod(LaDeak.JsonMergePatch.Abstractions.Patch<Dto> data)
        {
        }
    }
}
");
            JsonMergePatchSourceGenerator generator = new JsonMergePatchSourceGenerator();
            GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

            driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);

            Assert.True(diagnostics.IsEmpty);
            Assert.True(outputCompilation.SyntaxTrees.Count() == 3);
            Assert.True(outputCompilation.GetDiagnostics().IsEmpty);

            GeneratorDriverRunResult runResult = driver.GetRunResult();

            Assert.Equal(2, runResult.GeneratedTrees.Length);
            Assert.Empty(runResult.Diagnostics);
        }
        readonly bool IGeneratorTestResult.Compare(GeneratorDriverRunResult result)
        {
            if (result is null || result.GeneratedTrees.IsDefaultOrEmpty)
            {
                return(false);
            }

            if (result.GeneratedTrees[0] is not CSharpSyntaxTree tree)
            {
                return(false);
            }

            return(SyntaxTree?.IsEquivalentTo(tree) ?? false);
        }
Example #5
0
        public void Verify_generator_works()
        {
            // Create the 'input' compilation that the generator will act on
            Compilation inputCompilation = CreateCompilation(@"
[assembly: NServiceBusEndpointName(""test"")]
namespace MyCode
{
    public class Program
    {
        public static void Main(string[] args)
        {
        }
    }
}
");

            // directly create an instance of the generator
            // (Note: in the compiler this is loaded from an assembly, and created via reflection at runtime)
            var generator = new CodeGenLibrary.FunctionEndpointTriggerGenerator();

            // Create the driver that will control the generation, passing in our generator
            GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

            // Run the generation pass
            // (Note: the generator driver itself is immutable, and all calls return an updated version of the driver that you should use for subsequent calls)
            driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);

            // We can now assert things about the resulting compilation:
            Debug.Assert(diagnostics.IsEmpty);                        // there were no diagnostics created by the generators
            Debug.Assert(outputCompilation.SyntaxTrees.Count() == 3); // we have 3 syntax trees, the original 'user' provided one, attribute, and a trigger class
            //Debug.Assert(outputCompilation.GetDiagnostics().IsEmpty); // verify the compilation with the added source has no diagnostics

            // Or we can look at the results directly:
            GeneratorDriverRunResult runResult = driver.GetRunResult();

            // The runResult contains the combined results of all generators passed to the driver
            Debug.Assert(runResult.GeneratedTrees.Length == 2);
            Debug.Assert(runResult.Diagnostics.IsEmpty);

            // Or you can access the individual results on a by-generator basis
            GeneratorRunResult generatorResult = runResult.Results[0];

            Debug.Assert(generatorResult.Generator == generator);
            Debug.Assert(generatorResult.Diagnostics.IsEmpty);
            Debug.Assert(generatorResult.GeneratedSources.Length == 2);
            Debug.Assert(generatorResult.Exception is null);
        }
Example #6
0
        public static void AssertGeneratedFile(this GeneratorDriverRunResult result, string fileName, Action <SyntaxTree>?treeAssertion = null)
        {
            SyntaxTree?foundTree = null;

            foreach (var tree in result.GeneratedTrees)
            {
                if (tree.FilePath == $"TypedIds\\TypedIds.Generator\\{fileName}")
                {
                    foundTree = tree;
                    break;
                }
            }

            Assert.NotNull(foundTree);

            treeAssertion?.Invoke(foundTree !);
        }
        readonly bool IGeneratorTestResult.Compare(GeneratorDriverRunResult result)
        {
            if (result.GeneratedTrees.Length != Length)
            {
                return(false);
            }

            int length = Length;

            for (int i = 0; i < length; i++)
            {
                if (!result.GeneratedTrees[i].IsEquivalentTo(GeneratedSources[i].SyntaxTree))
                {
                    return(false);
                }
            }

            return(true);
        }
Example #8
0
        internal static async Task <IReadOnlyList <Diagnostic> > RunGenerator(
            string code, bool compile = false, LanguageVersion langVersion = LanguageVersion.Preview, MetadataReference[]?additionalRefs = null, bool allowUnsafe = false, CancellationToken cancellationToken = default)
        {
            var proj = new AdhocWorkspace()
                       .AddSolution(SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create()))
                       .AddProject("RegexGeneratorTest", "RegexGeneratorTest.dll", "C#")
                       .WithMetadataReferences(additionalRefs is not null ? References.Concat(additionalRefs) : References)
                       .WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: allowUnsafe)
                                               .WithNullableContextOptions(NullableContextOptions.Enable))
                       .WithParseOptions(new CSharpParseOptions(langVersion))
                       .AddDocument("RegexGenerator.g.cs", SourceText.From(code, Encoding.UTF8)).Project;

            Assert.True(proj.Solution.Workspace.TryApplyChanges(proj.Solution));

            Compilation?comp = await proj !.GetCompilationAsync(CancellationToken.None).ConfigureAwait(false);

            Debug.Assert(comp is not null);

            var generator = new RegexGenerator();
            CSharpGeneratorDriver    cgd = CSharpGeneratorDriver.Create(new[] { generator.AsSourceGenerator() }, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(langVersion));
            GeneratorDriver          gd  = cgd.RunGenerators(comp !, cancellationToken);
            GeneratorDriverRunResult generatorResults = gd.GetRunResult();

            if (!compile)
            {
                return(generatorResults.Diagnostics);
            }

            comp = comp.AddSyntaxTrees(generatorResults.GeneratedTrees.ToArray());
            EmitResult results = comp.Emit(Stream.Null, cancellationToken: cancellationToken);
            ImmutableArray <Diagnostic> generatorDiagnostics = generatorResults.Diagnostics.RemoveAll(d => d.Severity <= DiagnosticSeverity.Hidden);
            ImmutableArray <Diagnostic> resultsDiagnostics   = results.Diagnostics.RemoveAll(d => d.Severity <= DiagnosticSeverity.Hidden);

            if (!results.Success || resultsDiagnostics.Length != 0 || generatorDiagnostics.Length != 0)
            {
                throw new ArgumentException(
                          string.Join(Environment.NewLine, resultsDiagnostics.Concat(generatorDiagnostics)) + Environment.NewLine +
                          string.Join(Environment.NewLine, generatorResults.GeneratedTrees.Select(t => t.ToString())));
            }

            return(generatorResults.Diagnostics.Concat(results.Diagnostics).Where(d => d.Severity != DiagnosticSeverity.Hidden).ToArray());
        }
        /// <summary>
        /// Runs a Roslyn generator over a set of source files.
        /// </summary>
        public static async Task <(ImmutableArray <Diagnostic>, ImmutableArray <GeneratedSourceResult>)> RunGenerator(
            ISourceGenerator generator,
            IEnumerable <Assembly>?references,
            IEnumerable <string> sources,
            AnalyzerConfigOptionsProvider?optionsProvider = null,
            bool includeBaseReferences          = true,
            CancellationToken cancellationToken = default)
        {
            Project proj = CreateTestProject(references, includeBaseReferences);

            proj = proj.WithDocuments(sources);

            Assert.True(proj.Solution.Workspace.TryApplyChanges(proj.Solution));

            Compilation?comp = await proj !.GetCompilationAsync(CancellationToken.None).ConfigureAwait(false);

            CSharpGeneratorDriver cgd = CSharpGeneratorDriver.Create(new[] { generator }, optionsProvider: optionsProvider);
            GeneratorDriver       gd  = cgd.RunGenerators(comp !, cancellationToken);

            GeneratorDriverRunResult r = gd.GetRunResult();

            return(r.Results[0].Diagnostics, r.Results[0].GeneratedSources);
        }
Example #10
0
 public override void Assert(GeneratorDriverRunResult result)
 {
     result.Diagnostics.ShouldBeEmpty();
 }
Example #11
0
 public static GeneratorDriverRunResultAssertions Should(this GeneratorDriverRunResult generatorDriverRunResult)
 => new (generatorDriverRunResult);
Example #12
0
        internal static async Task <Regex> SourceGenRegexAsync(
            string pattern, RegexOptions?options = null, TimeSpan?matchTimeout = null, CancellationToken cancellationToken = default)
        {
            Assert.True(options is not null || matchTimeout is null);
            string attr = $"[RegexGenerator({SymbolDisplay.FormatLiteral(pattern, quote: true)}";

            if (options is not null)
            {
                attr += $", {string.Join(" | ", options.ToString().Split(',').Select(o => $"RegexOptions.{o.Trim()}"))}";
                if (matchTimeout is not null)
                {
                    attr += string.Create(CultureInfo.InvariantCulture, $", {(int)matchTimeout.Value.TotalMilliseconds}");
                }
            }
            attr += ")]";

            // Create the source boilerplate for the pattern
            string code = $@"
                using System.Text.RegularExpressions;
                public partial class C
                {{
                    {attr}
                    public static partial Regex Get();
                }}";


            // Use a cached compilation to save a little time.  Rather than creating an entirely new workspace
            // for each test, just create a single compilation, cache it, and then replace its syntax tree
            // on each test.
            if (s_compilation is not Compilation comp)
            {
                // Create the project containing the source.
                var proj = new AdhocWorkspace()
                           .AddSolution(SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create()))
                           .AddProject("Test", "test.dll", "C#")
                           .WithMetadataReferences(s_refs)
                           .WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
                                                   .WithNullableContextOptions(NullableContextOptions.Enable))
                           .WithParseOptions(new CSharpParseOptions(LanguageVersion.Preview))
                           .AddDocument("RegexGenerator.g.cs", SourceText.From("// Empty", Encoding.UTF8)).Project;
                Assert.True(proj.Solution.Workspace.TryApplyChanges(proj.Solution));

                s_compilation = comp = await proj !.GetCompilationAsync(CancellationToken.None).ConfigureAwait(false);
                Debug.Assert(comp is not null);
            }

            comp = comp.ReplaceSyntaxTree(comp.SyntaxTrees.First(), CSharpSyntaxTree.ParseText(SourceText.From(code, Encoding.UTF8), s_previewParseOptions));

            // Run the generator
            GeneratorDriverRunResult generatorResults = s_generatorDriver.RunGenerators(comp !, cancellationToken).GetRunResult();

            if (generatorResults.Diagnostics.Length != 0)
            {
                throw new ArgumentException(
                          string.Join(Environment.NewLine, generatorResults.Diagnostics) + Environment.NewLine +
                          string.Join(Environment.NewLine, generatorResults.GeneratedTrees.Select(t => NumberLines(t.ToString()))));
            }

            // Compile the assembly to a stream
            var dll = new MemoryStream();

            comp = comp.AddSyntaxTrees(generatorResults.GeneratedTrees.ToArray());
            EmitResult results = comp.Emit(dll, options: s_emitOptions, cancellationToken: cancellationToken);

            if (!results.Success || results.Diagnostics.Length != 0)
            {
                throw new ArgumentException(
                          string.Join(Environment.NewLine, results.Diagnostics.Concat(generatorResults.Diagnostics)) + Environment.NewLine +
                          string.Join(Environment.NewLine, generatorResults.GeneratedTrees.Select(t => NumberLines(t.ToString()))));
            }
            dll.Position = 0;

            // Load the assembly into its own AssemblyLoadContext.
            var      alc = new RegexLoadContext(Environment.CurrentDirectory);
            Assembly a   = alc.LoadFromStream(dll);

            // Instantiate a regex using the newly created static Get method that was source generated.
            Regex r = (Regex)a.GetType("C") !.GetMethod("Get") !.Invoke(null, null) !;

            // Issue an unload on the ALC, so it'll be collected once the Regex instance is collected
            alc.Unload();

            return(r);
        }
Example #13
0
    public void SimpleGeneratorTest()
    {
        // Input for our generator.
        Compilation inputCompilation = CreateCompilation(@"
namespace WalletWasabi.Fluent.ViewModels
{
	public class TestViewModel2
	{
		[AutoNotify(PropertyName = ""TestName"")] private bool _prop1;
		[AutoNotify(SetterModifier = AccessModifier.None)] private bool _prop2 = false;
		[AutoNotify(SetterModifier = AccessModifier.Public)] private bool _prop3;
		[AutoNotify(SetterModifier = AccessModifier.Protected)] private bool _prop4;
		[AutoNotify(SetterModifier = AccessModifier.Private)] private bool _prop5;
		[AutoNotify(SetterModifier = AccessModifier.Internal)] private bool _prop6;
	}
}
");

        AutoNotifyGenerator generator = new();
        GeneratorDriver     driver    = CSharpGeneratorDriver.Create(generator);

        // Run the generation pass
        driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);

        Assert.True(diagnostics.IsEmpty);
        Assert.Equal(4, outputCompilation.SyntaxTrees.Count());

        GeneratorDriverRunResult runResult = driver.GetRunResult();

        Assert.Equal(3, runResult.GeneratedTrees.Length);
        Assert.True(runResult.Diagnostics.IsEmpty);

        GeneratorRunResult generatorResult = runResult.Results[0];

        Assert.True(generatorResult.Exception is null);
        Assert.Equal(generatorResult.Generator, generator);
        Assert.True(generatorResult.Diagnostics.IsEmpty);
        Assert.Equal(3, generatorResult.GeneratedSources.Length);

        string expectedGeneratedSourceCode = @"
// <auto-generated />
#nullable enable
using ReactiveUI;

namespace WalletWasabi.Fluent.ViewModels
{
    public partial class TestViewModel2 : ReactiveUI.ReactiveObject
    {
        public bool TestName
        {
            get => _prop1;
            set => this.RaiseAndSetIfChanged(ref _prop1, value);
        }
        public bool Prop2
        {
            get => _prop2;
        }
        public bool Prop3
        {
            get => _prop3;
            set => this.RaiseAndSetIfChanged(ref _prop3, value);
        }
        public bool Prop4
        {
            get => _prop4;
            protected set => this.RaiseAndSetIfChanged(ref _prop4, value);
        }
        public bool Prop5
        {
            get => _prop5;
            private set => this.RaiseAndSetIfChanged(ref _prop5, value);
        }
        public bool Prop6
        {
            get => _prop6;
            internal set => this.RaiseAndSetIfChanged(ref _prop6, value);
        }
    }
}".Trim();

        Assert.Equal(expectedGeneratedSourceCode, generatorResult.GeneratedSources[2].SourceText.ToString());
    }
        private static void ExecuteGenerator(IEnumerable <AdditionalText> additionalTexts, out Compilation outputCompilation, out GeneratorDriverRunResult runResult)
        {
            Compilation inputCompilation = CreateCompilation();

            GeneratorDriver driver = CSharpGeneratorDriver.Create(new[] { new FriendlyWordsSourceGenerator() }, additionalTexts);

            // (Note: the generator driver itself is immutable, and all calls return an updated version of the driver that you should use for subsequent calls)
            driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out outputCompilation, out ImmutableArray <Diagnostic> _);

            runResult = driver.GetRunResult();
        }
Example #15
0
 public abstract void Assert(GeneratorDriverRunResult result);
Example #16
0
        public void GenerationFormat()
        {
            var         source           = @"using DevExpress.Mvvm.CodeGenerators;

namespace Test {
    [GenerateViewModel(ImplementINotifyPropertyChanging = true, ImplementIDataErrorInfo = true, ImplementISupportServices = true)]
    partial class Example {
        [GenerateProperty]
        [System.ComponentModel.DataAnnotations.Range(0,
                                                     1)]
        int property;
        
        [GenerateCommand]
        public void Method(int arg) { }
    }

    public class Program {
        public static void Main(string[] args) { }
    }
}
";
            Compilation inputCompilation = CSharpCompilation.Create("MyCompilation",
                                                                    new[] { CSharpSyntaxTree.ParseText(source) },
                                                                    new[] {
                MetadataReference.CreateFromFile(typeof(DelegateCommand).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(System.ComponentModel.DataAnnotations.RangeAttribute).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(System.Windows.Input.ICommand).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
            },
                                                                    new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
            ViewModelGenerator generator = new ViewModelGenerator();

            GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

            driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);

            GeneratorDriverRunResult runResult       = driver.GetRunResult();
            GeneratorRunResult       generatorResult = runResult.Results[0];

            var generatedCode = generatorResult.GeneratedSources[1].SourceText.ToString();
            var tabs          = 0;

            foreach (var str in generatedCode.Split(new[] { Environment.NewLine }, StringSplitOptions.None))
            {
                if (string.IsNullOrEmpty(str))
                {
                    continue;
                }

                if (str.Contains("}") && !str.Contains("{"))
                {
                    if (str.EndsWith("}"))
                    {
                        tabs--;
                    }
                    else
                    {
                        Assert.Fail();
                    }
                }

                var expectedLeadingWhitespaceCount = tabs * 4;
                var leadingWhitespaceCount         = str.Length - str.TrimStart().Length;
                Assert.AreEqual(expectedLeadingWhitespaceCount, leadingWhitespaceCount);

                if (str.EndsWith("{"))
                {
                    tabs++;
                }
            }
        }
        public void Test_InterfaceExtensionsGenerator()
        {
            // Create the 'input' compilation that the generator will act on
            Compilation inputCompilation = CreateCompilation(@"
using System;

namespace MyCode {

    [UltimateOrb.CodeAnalysis.SourceGenerators.GeneratExtensions]
    interface IInterfaceA<T> {

        bool Property1 {

            get;
        }

        internal bool Property2 {

            set;
        }

        public bool Property3 {

            get;
            protected set;
        }

        static int Method4() {
            return 123;
        }

        public long Method5() {
            return 123;
        }

        void Method6(ref long arg1, in uint? arg2 , in string? arg3, Guid arg4) {
            return;
        }

        protected void Method7(ref ulong arg1, in uint? arg2, in string? arg3, Guid arg4);

        void Method8(ref nint arg1, in UIntPtr? arg2, in string? arg3, Guid arg4);

    }
}
");

            // directly create an instance of the generator
            // (Note: in the compiler this is loaded from an assembly, and created via reflection at runtime)
            var generator = new InterfaceExtensionsGenerator();

            // Create the driver that will control the generation, passing in our generator
            GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

            // Run the generation pass
            // (Note: the generator driver itself is immutable, and all calls return an updated version of the driver that you should use for subsequent calls)
            driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);

            // We can now assert things about the resulting compilation:
            Debug.Assert(diagnostics.IsEmpty);                        // there were no diagnostics created by the generators
            Debug.Assert(outputCompilation.SyntaxTrees.Count() == 2); // we have two syntax trees, the original 'user' provided one, and the one added by the generator
            System.Collections.Immutable.ImmutableArray <Diagnostic> diagnostics1 = outputCompilation.GetDiagnostics();
            Debug.Assert(diagnostics1.IsEmpty);                       // verify the compilation with the added source has no diagnostics

            // Or we can look at the results directly:
            GeneratorDriverRunResult runResult = driver.GetRunResult();

            // The runResult contains the combined results of all generators passed to the driver
            Debug.Assert(runResult.GeneratedTrees.Length == 1);
            Debug.Assert(runResult.Diagnostics.IsEmpty);

            // Or you can access the individual results on a by-generator basis
            GeneratorRunResult generatorResult = runResult.Results[0];

            Debug.Assert(generatorResult.Generator == generator);
            Debug.Assert(generatorResult.Diagnostics.IsEmpty);
            Debug.Assert(generatorResult.GeneratedSources.Length == 1);
            Debug.Assert(generatorResult.Exception is null);
        }