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
add a few test cases for the emitter rpc
  • Loading branch information
ArcturusZhang committed Feb 27, 2025
commit 05c59a7f4f4bf6f42bd55bf4e0f20212ea5a21b4
Original file line number Diff line number Diff line change
@@ -151,10 +151,7 @@ public ClientProvider(InputClient inputClient)
private const string namespaceConflictCode = "client-namespace-conflict";

private string? _namespace;
protected override string BuildNamespace()
{
return _namespace ??= BuildNamespaceCore();
}
protected override string BuildNamespace() => _namespace ??= BuildNamespaceCore();

private string BuildNamespaceCore()
{
@@ -166,39 +163,13 @@ private string BuildNamespaceCore()
var ns = ScmCodeModelPlugin.Instance.TypeFactory.GetCleanNameSpace(_inputClient.Namespace);

// figure out if this namespace has been changed for this client
if (!IsLastSegmentTheSame(ns, _inputClient.Namespace))
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);
}
return ns;
}

internal static bool IsLastSegmentTheSame(string left, string right)
{
// finish this via Span API
var leftSpan = left.AsSpan();
var rightSpan = right.AsSpan();
var count = Math.Min(leftSpan.Length, rightSpan.Length);
for (int i = 1; i <= count; i++)
{
var lc = leftSpan[^i];
var rc = rightSpan[^i];
// check if each char is the same from the right-most side
// if both of them are dot, we finished scanning the last segment - and if we could be here, meaning all of them are the same, return true.
if (lc == '.' && rc == '.')
{
return true;
}
// if these are different - there is one different character, return false.
if (lc != rc)
{
return false;
}
}

return leftSpan.Length == rightSpan.Length;
}

private IReadOnlyList<ParameterProvider> GetSubClientInternalConstructorParameters()
{
var subClientParameters = new List<ParameterProvider>
Original file line number Diff line number Diff line change
@@ -16,11 +16,11 @@ public sealed class Emitter : IDisposable
private static Emitter? _emitter;
private bool _disposed;

private readonly StreamWriter _writer;
private readonly TextWriter _writer;

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

public static Emitter Instance => _emitter ??= new Emitter();
@@ -30,6 +30,7 @@ private void SendNotification(string method, object content)
var paramsContent = JsonSerializer.Serialize(content);
var message = string.Format(BasicNotificationFormat, AsStringLiteral(method), paramsContent);
_writer.WriteLine(message);
_writer.Flush();
}

private static string AsStringLiteral(string input) => $"\"{input}\"";
Original file line number Diff line number Diff line change
@@ -244,5 +244,50 @@ public static string ToApiVersionMemberName(this string version)

return CultureInfo.InvariantCulture.TextInfo.ToTitleCase(sb.ToString());
}

/// <summary>
/// Checks if two namespaces share the same last segment
/// </summary>
/// <param name="left">the first namespace</param>
/// <param name="right">the second namespace</param>
/// <returns></returns>
public static bool IsLastNamespaceSegmentTheSame(string left, string right)
{
// finish this via Span API
var leftSpan = left.AsSpan();
var rightSpan = right.AsSpan();
// swap if left is longer, we ensure left is the shorter one
if (leftSpan.Length > rightSpan.Length)
{
var temp = leftSpan;
leftSpan = rightSpan;
rightSpan = temp;
}
for (int i = 1; i <= leftSpan.Length; i++)
{
var lc = leftSpan[^i];
var rc = rightSpan[^i];
// check if each char is the same from the right-most side
// if both of them are dot, we finished scanning the last segment - and if we could be here, meaning all of them are the same, return true.
if (lc == '.' && rc == '.')
{
return true;
}
// if these are different - there is one different character, return false.
if (lc != rc)
{
return false;
}
}

// we come here because we run out of characters in left - which means left does not have a dot.
// if they have the same length, they are identical, return true
if (leftSpan.Length == rightSpan.Length)
{
return true;
}
// otherwise, right is longer, we check its next character, if it is the dot, return true, otherwise return false.
return rightSpan[^(leftSpan.Length + 1)] == '.';
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.IO;
using Microsoft.TypeSpec.Generator.EmitterRpc;
using NUnit.Framework;

namespace Microsoft.TypeSpec.Generator.Tests.EmitterRpc
{
public class EmitterTests
{
private StringWriter? _stream;
private Emitter? _emitter;

public EmitterTests()
{
}

[SetUp]
public void Setup()
{
_stream = new StringWriter();
Console.SetOut(_stream);
_emitter = new Emitter();
}

[TearDown]
public void TearDown()
{
_stream?.Dispose();
(_emitter as IDisposable)?.Dispose();
}

[TestCase]
public void TestInfo()
{
_emitter?.Info("Test message");

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

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

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

[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());
}

[TestCase]
public void TestReportDiagnosticWithoutTarget()
{
_emitter?.ReportDiagnostic("test-code", "Test message");
Assert.AreEqual(@"{""method"":""diagnostic"",""params"":{""code"":""test-code"",""message"":""Test message""}}", _stream?.ToString().TrimEnd());
}
}
}
Original file line number Diff line number Diff line change
@@ -9,7 +9,6 @@ namespace Microsoft.TypeSpec.Generator.Tests.Utilities
{
public class StringExtensionsTests
{

[TestCase("abstract", true)]
[TestCase("add", true)]
[TestCase("alias", true)]
@@ -143,6 +142,24 @@ public void ValidateGetFormattableStringFormatParts(string format, IReadOnlyList
}
}

[TestCase("Foo", "Foo", ExpectedResult = true)]
[TestCase("Foo", "Bar", ExpectedResult = false)]
[TestCase("Foo", "_Foo", ExpectedResult = false)]
[TestCase("_Foo", "Foo", ExpectedResult = false)]
[TestCase("Foo", "Bar.Foo", ExpectedResult = true)]
[TestCase("Bar.Foo", "Foo", ExpectedResult = true)]
[TestCase("Foo", "Bar._Foo", ExpectedResult = false)]
[TestCase("Bar._Foo", "Foo", ExpectedResult = false)]
[TestCase("Foo", "/Foo", ExpectedResult = false)]
[TestCase("/Foo", "Foo", ExpectedResult = false)]
[TestCase(".Foo", ".Foo", ExpectedResult = true)]
[TestCase("Foo", ".Foo", ExpectedResult = true)]
[TestCase(".Foo", "Foo", ExpectedResult = true)]
public bool ValidateIsLastNamespaceSegmentTheSame(string left, string right)
{
return StringExtensions.IsLastNamespaceSegmentTheSame(left, right);
}

public record Part(string Value, bool IsLiteral, int ArgumentIndex);

public static IEnumerable<TestCaseData> BuildFormattableStringFormatParts
Loading
Oops, something went wrong.