File: ContractsGenerator.cs

package info (click to toggle)
golang-github-microsoft-dev-tunnels 0.0.25-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,988 kB
  • sloc: cs: 9,969; java: 2,767; javascript: 328; xml: 186; makefile: 5
file content (114 lines) | stat: -rw-r--r-- 4,281 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// <copyright file="ContractsGenerator.cs" company="Microsoft">
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
// </copyright>

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;

namespace Microsoft.DevTunnels.Generator;

[Generator]
public class ContractsGenerator : ISourceGenerator
{
    private const string DiagnosticPrefix = "TUN";
    private const string DiagnosticCategory = "Tunnels";
    private const string ContractsNamespace = "Microsoft.DevTunnels.Contracts";
    internal static readonly string[] ExcludedContractTypes = new[]
    {
        "TunnelContracts",
        "ThisAssembly",
        "Converter",
    };

    public void Initialize(GeneratorInitializationContext context)
    {
#if DEBUG
        // Note source generators re not covered by normal debugging,
        // because the generator runs at build time, not at application run-time.
        // Un-comment the line below to enable debugging at build time.

        ////System.Diagnostics.Debugger.Launch();
#endif
    }

    public void Execute(GeneratorExecutionContext context)
    {
        // Path of the ThisAssembly type's location will be like:
        //   cs/bin/obj/[projectname]/Release/net6.0/[assemblyname].Version.cs
        var thisAssemblyType = context.Compilation.GetSymbolsWithName(
            nameof(ThisAssembly), SymbolFilter.Type).Single();
        var thisAssemblyPath = thisAssemblyType.Locations.Single().GetLineSpan().Path;
        var repoRoot = Path.GetFullPath(Path.Combine(
            thisAssemblyPath, "..", "..", "..", "..", "..", "..", ".."));

        var writers = new List<ContractWriter>();
        foreach (var language in ContractWriter.SupportedLanguages)
        {
            writers.Add(ContractWriter.Create(language, repoRoot, ContractsNamespace));
        }

        var typeNames = context.Compilation.Assembly.TypeNames;
        var types = typeNames
            .SelectMany((t) => context.Compilation.GetSymbolsWithName(t, SymbolFilter.Type))
            .OfType<ITypeSymbol>()
            .ToArray();
        foreach (var type in types)
        {
            if (ExcludedContractTypes.Contains(type!.Name))
            {
                continue;
            }
            else if (type.ContainingType != null)
            {
                // Nested types will be written as part of their containing type.
                continue;
            }
            else if (type.Name.EndsWith("Attribute"))
            {
                // Attributes are excluded from code-generation.
                continue;
            }

            var path = type.Locations.Single().GetLineSpan().Path;

            foreach (var method in type.GetMembers().OfType<IMethodSymbol>()
                .Where((m) => m.MethodKind == MethodKind.Ordinary))
            {
                if (!method.IsStatic &&
                    method.Name != "ToString" &&
                    method.Name != "GetEnumerator")
                {
                    var title = "Tunnel contracts must not have instance methods other than " +
                            "GetEnumerator() or ToString().";
                    var descriptor = new DiagnosticDescriptor(
                        id: DiagnosticPrefix + "1000",
                        title,
                        messageFormat: title + " Generated contract interfaces cannot support " +
                            "instance methods. Consider converting the method to static " +
                            "or other refactoring.",
                        DiagnosticCategory,
                        DiagnosticSeverity.Error,
                        isEnabledByDefault: true);
                    context.ReportDiagnostic(
                        Diagnostic.Create(descriptor, method.Locations.Single()));
                }
            }

            foreach (var writer in writers)
            {
                context.CancellationToken.ThrowIfCancellationRequested();
                writer.WriteContract(type, types);
            }
        }

        foreach (var writer in writers)
        {
            context.CancellationToken.ThrowIfCancellationRequested();
            writer.WriteCompleted();
        }
    }
}