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

Refine the client-namespace-conflict diagnostic #6161

Merged
Merged
Changes from 1 commit
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
Prev Previous commit
Next Next commit
refactor where the emitter located and resolve the disposing issue
  • Loading branch information
ArcturusZhang committed Feb 27, 2025
commit 36c0e7e9c6e2636fdcb04358cdfb379528b3a5e9
Original file line number Diff line number Diff line change
@@ -165,7 +165,7 @@ private string BuildNamespaceCore()
// figure out if this namespace has been changed for this client
if (!StringExtensions.IsLastNamespaceSegmentTheSame(ns, _inputClient.Namespace))
{
Emitter.Instance.ReportDiagnostic(namespaceConflictCode, $"namespace {_inputClient.Namespace} conflicts with client {_inputClient.Name}, please use `@clientName` to specify a different name for the client.", _inputClient.CrossLanguageDefinitionId);
ScmCodeModelPlugin.Instance.Emitter.ReportDiagnostic(namespaceConflictCode, $"namespace {_inputClient.Namespace} conflicts with client {_inputClient.Name}, please use `@clientName` to specify a different name for the client.", _inputClient.CrossLanguageDefinitionId);
}
return ns;
}
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ internal static class PluginInitializer
public static void Initialize()
{
PluginHandler pluginHandler = new PluginHandler();
pluginHandler.LoadPlugin(new CommandLineOptions
pluginHandler.LoadPlugin(null!, new CommandLineOptions
{
OutputDirectory = Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location)!.FullName, "Projects", "Model"),
PluginName = "CodeModelPlugin"
Original file line number Diff line number Diff line change
@@ -6,7 +6,6 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.TypeSpec.Generator.EmitterRpc;
using Microsoft.TypeSpec.Generator.SourceInput;

namespace Microsoft.TypeSpec.Generator
@@ -85,7 +84,7 @@ public async Task ExecuteAsync()
continue;
}
var filename = Path.Combine(outputPath, file.Name);
Emitter.Instance.Info($"Writing {Path.GetFullPath(filename)}");
CodeModelPlugin.Instance.Emitter.Info($"Writing {Path.GetFullPath(filename)}");
Directory.CreateDirectory(Path.GetDirectoryName(filename)!);
await File.WriteAllTextAsync(filename, file.Text);
}
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.ComponentModel.Composition;
using Microsoft.CodeAnalysis;
using Microsoft.TypeSpec.Generator.EmitterRpc;
using Microsoft.TypeSpec.Generator.Input;
using Microsoft.TypeSpec.Generator.Primitives;
using Microsoft.TypeSpec.Generator.Providers;
@@ -46,6 +47,7 @@ public CodeModelPlugin(GeneratorContext context)
Configuration = context.Configuration;
_inputLibrary = new InputLibrary(Configuration.OutputDirectory);
TypeFactory = new TypeFactory();
Emitter = null!;
}

// for mocking
@@ -58,6 +60,8 @@ protected CodeModelPlugin()
internal bool IsNewProject { get; set; }
private InputLibrary _inputLibrary;

public virtual Emitter Emitter { get; internal set; }

// Extensibility points to be implemented by a plugin
public virtual TypeFactory TypeFactory { get; }

Original file line number Diff line number Diff line change
@@ -13,18 +13,15 @@ public sealed class Emitter : IDisposable
private const string Trace = "trace";
private const string Diagnostic = "diagnostic";

private static Emitter? _emitter;
private bool _disposed;

private readonly TextWriter _writer;
private readonly StreamWriter _writer;

internal Emitter()
internal Emitter(Stream stream)
{
_writer = Console.Out;
_writer = new StreamWriter(stream) { AutoFlush = true };
}

public static Emitter Instance => _emitter ??= new Emitter();

private void SendNotification(string method, object content)
{
var paramsContent = JsonSerializer.Serialize(content);
Original file line number Diff line number Diff line change
@@ -2,15 +2,16 @@
// Licensed under the MIT License.

using System.Threading.Tasks;
using Microsoft.TypeSpec.Generator.EmitterRpc;

namespace Microsoft.TypeSpec.Generator
{
internal class GeneratorRunner
{
public async Task RunAsync(CommandLineOptions options)
public async Task RunAsync(Emitter emitter, CommandLineOptions options)
{
PluginHandler pluginHandler = new();
pluginHandler.LoadPlugin(options);
pluginHandler.LoadPlugin(emitter, options);

var csharpGen = new CSharpGen();
await csharpGen.ExecuteAsync();
Original file line number Diff line number Diff line change
@@ -5,23 +5,24 @@
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using Microsoft.TypeSpec.Generator.EmitterRpc;

namespace Microsoft.TypeSpec.Generator
{
internal class PluginHandler
{
public void LoadPlugin(CommandLineOptions options)
public void LoadPlugin(Emitter emitter, CommandLineOptions options)
{
using DirectoryCatalog directoryCatalog = new(AppContext.BaseDirectory);
using CompositionContainer container = new(directoryCatalog);

container.ComposeExportedValue(new GeneratorContext(Configuration.Load(options.OutputDirectory)));
container.ComposeParts(this);

SelectPlugin(options);
SelectPlugin(emitter, options);
}

internal void SelectPlugin(CommandLineOptions options)
internal void SelectPlugin(Emitter emitter, CommandLineOptions options)
{
bool loaded = false;
foreach (var plugin in Plugins!)
@@ -30,6 +31,7 @@ internal void SelectPlugin(CommandLineOptions options)
{
CodeModelPlugin.Instance = plugin.Value;
CodeModelPlugin.Instance.IsNewProject = options.IsNewProject;
CodeModelPlugin.Instance.Emitter = emitter;
loaded = true;
CodeModelPlugin.Instance.Configure();
break;
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ public static async Task<int> Main(string[] args)

private static async Task<int> Run(CommandLineOptions options, GeneratorRunner runner)
{
using var emitter = Emitter.Instance;
using var emitter = new Emitter(Console.OpenStandardOutput());

if (options.ShouldDebug)
{
@@ -41,7 +41,7 @@ private static async Task<int> Run(CommandLineOptions options, GeneratorRunner r

try
{
await runner.RunAsync(options);
await runner.RunAsync(emitter, options);
}
catch (Exception e)
{
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ namespace Microsoft.TypeSpec.Generator.Tests.EmitterRpc
{
public class EmitterTests
{
private StringWriter? _stream;
private MemoryStream? _stream;
private Emitter? _emitter;

public EmitterTests()
@@ -20,9 +20,8 @@ public EmitterTests()
[SetUp]
public void Setup()
{
_stream = new StringWriter();
Console.SetOut(_stream);
_emitter = new Emitter();
_stream = new MemoryStream();
_emitter = new Emitter(_stream);
}

[TearDown]
@@ -37,35 +36,43 @@ public void TestInfo()
{
_emitter?.Info("Test message");

Assert.AreEqual(@"{""method"":""trace"",""params"":{""level"":""info"",""message"":""Test message""}}", _stream?.ToString().TrimEnd());
Assert.AreEqual(@"{""method"":""trace"",""params"":{""level"":""info"",""message"":""Test message""}}", GetResult());
}

[TestCase]
public void TestDebug()
{
_emitter?.Debug("Test message");
Assert.AreEqual(@"{""method"":""trace"",""params"":{""level"":""debug"",""message"":""Test message""}}", _stream?.ToString().TrimEnd());

Assert.AreEqual(@"{""method"":""trace"",""params"":{""level"":""debug"",""message"":""Test message""}}", GetResult());
}

[TestCase]
public void TestVerbose()
{
_emitter?.Verbose("Test message");
Assert.AreEqual(@"{""method"":""trace"",""params"":{""level"":""verbose"",""message"":""Test message""}}", _stream?.ToString().TrimEnd());
Assert.AreEqual(@"{""method"":""trace"",""params"":{""level"":""verbose"",""message"":""Test message""}}", GetResult());
}

[TestCase]
public void TestReportDiagnosticWithTarget()
{
_emitter?.ReportDiagnostic("test-code", "Test message", "Test target");
Assert.AreEqual(@"{""method"":""diagnostic"",""params"":{""code"":""test-code"",""message"":""Test message"",""crossLanguageDefinitionId"":""Test target""}}", _stream?.ToString().TrimEnd());
Assert.AreEqual(@"{""method"":""diagnostic"",""params"":{""code"":""test-code"",""message"":""Test message"",""crossLanguageDefinitionId"":""Test target""}}", GetResult());
}

[TestCase]
public void TestReportDiagnosticWithoutTarget()
{
_emitter?.ReportDiagnostic("test-code", "Test message");
Assert.AreEqual(@"{""method"":""diagnostic"",""params"":{""code"":""test-code"",""message"":""Test message""}}", _stream?.ToString().TrimEnd());
Assert.AreEqual(@"{""method"":""diagnostic"",""params"":{""code"":""test-code"",""message"":""Test message""}}", GetResult());
}

private string GetResult()
{
_stream?.Seek(0, SeekOrigin.Begin);
using var reader = new StreamReader(_stream!);
return reader.ReadToEnd().TrimEnd();
}
}
}
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ public void SelectPluginFindsMatchingPlugin()
};
CommandLineOptions options = new() { PluginName = "MockPlugin" };

Assert.DoesNotThrow(() => pluginHandler.SelectPlugin(options));
Assert.DoesNotThrow(() => pluginHandler.SelectPlugin(null!, options));
mockPlugin.Verify(p => p.Configure(), Times.Once);
}

@@ -41,7 +41,7 @@ public void SelectPluginThrowsWhenNoMatch()
};
CommandLineOptions options = new() { PluginName = "NonExistentPlugin" };

Assert.Throws<System.InvalidOperationException>(() => pluginHandler.SelectPlugin(options));
Assert.Throws<System.InvalidOperationException>(() => pluginHandler.SelectPlugin(null!, options));
mockPlugin.Verify(p => p.Configure(), Times.Never);
}
}
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
using System.IO;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.TypeSpec.Generator.EmitterRpc;
using Microsoft.TypeSpec.Generator.Input;
using Microsoft.TypeSpec.Generator.Primitives;
using Microsoft.TypeSpec.Generator.Providers;
@@ -69,6 +70,8 @@ public static Mock<CodeModelPlugin> LoadMockPlugin(
// initialize the singleton instance of the plugin
var mockPlugin = new Mock<CodeModelPlugin>(new GeneratorContext(Configuration.Load(configFilePath, configuration))) { CallBase = true };

mockPlugin.Setup(p => p.Emitter).Returns(new Emitter(Console.OpenStandardOutput()));

var mockTypeFactory = new Mock<TypeFactory>() { CallBase = true };

if (createCSharpTypeCore != null)
Loading
Oops, something went wrong.