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
move the report diagnostic for namespace conflict into input client t…
…ype deserialization, which might be incorrect. Will change it later
  • Loading branch information
ArcturusZhang committed Feb 27, 2025
commit 87180c888a50788fabb3b61214c8cab74f242416
Original file line number Diff line number Diff line change
@@ -93,6 +93,7 @@ export function createModel(sdkContext: CSharpEmitterContext): CodeModel {
const clientParameters = fromSdkEndpointParameter(endpointParameter);
const clientName = getClientName(client, parentNames);

sdkContext.__typeCache.crossLanguageDefinitionIds.set(client.crossLanguageDefinitionId, client.__raw.type);
return {
Name: clientName,
Namespace: client.namespace,
@@ -112,6 +113,7 @@ export function createModel(sdkContext: CSharpEmitterContext): CodeModel {
Parent: parentNames.length > 0 ? parentNames[parentNames.length - 1] : undefined,
Parameters: clientParameters,
Decorators: client.decorators,
CrossLanguageDefinitionId: client.crossLanguageDefinitionId,
};
}

3 changes: 2 additions & 1 deletion packages/http-client-csharp/emitter/src/lib/lib.ts
Original file line number Diff line number Diff line change
@@ -63,7 +63,8 @@ const diags: { [code: string]: DiagnosticDefinition<DiagnosticMessages> } = {
"client-namespace-conflict": {
severity: "warning",
messages: {
default: paramMessage`namespace ${"clientNamespace"} conflicts with client ${"clientName"}, please use @clientName to specify a different name for the client.`,
// default: paramMessage`namespace ${"clientNamespace"} conflicts with client ${"clientName"}, please use @clientName to specify a different name for the client.`,
default: paramMessage`${"message"}`,
},
},
"unsupported-endpoint-url": {
Original file line number Diff line number Diff line change
@@ -125,7 +125,7 @@ function updateSdkTypeReferences(
if ("crossLanguageDefinitionId" in sdkType) {
sdkContext.__typeCache.crossLanguageDefinitionIds.set(
sdkType.crossLanguageDefinitionId,
sdkType,
sdkType.__raw,
);
}
}
6 changes: 1 addition & 5 deletions packages/http-client-csharp/emitter/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -90,11 +90,7 @@ function processJsonRpc(context: CSharpEmitterContext, message: string) {
if (crossLanguageDefinitionId === undefined) {
return undefined;
}
const target = context.__typeCache.crossLanguageDefinitionIds.get(crossLanguageDefinitionId);
if (target) {
return target.__raw;
}
return undefined;
return context.__typeCache.crossLanguageDefinitionIds.get(crossLanguageDefinitionId);
}
}

3 changes: 2 additions & 1 deletion packages/http-client-csharp/emitter/src/sdk-context.ts
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import { SdkContext, SdkType } from "@azure-tools/typespec-client-generator-core
import { Logger } from "./lib/logger.js";
import { CSharpEmitterOptions } from "./options.js";
import { InputEnumType, InputModelType, InputType } from "./type/input-type.js";
import { Type } from "@typespec/compiler";

/**
* The emitter context for the CSharp emitter.
@@ -16,7 +17,7 @@ export interface CSharpEmitterContext extends SdkContext<CSharpEmitterOptions> {
}

export interface SdkTypeMap {
crossLanguageDefinitionIds: Map<string, SdkType>;
crossLanguageDefinitionIds: Map<string, Type | undefined>;
types: Map<SdkType, InputType>;
models: Map<string, InputModelType>;
enums: Map<string, InputEnumType>;
Original file line number Diff line number Diff line change
@@ -16,4 +16,5 @@ export interface InputClient {
Parent?: string;
Parameters?: InputParameter[];
Decorators?: DecoratorInfo[];
CrossLanguageDefinitionId: string;
}
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
using System.IO;
using System.Text.Json;

namespace Microsoft.TypeSpec.Generator.EmitterRpc
namespace Microsoft.TypeSpec.Generator.Input.EmitterRpc
{
public sealed class Emitter : IDisposable
{
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@

using System;
using System.IO;
using Microsoft.TypeSpec.Generator.Input.EmitterRpc;

namespace Microsoft.TypeSpec.Generator.Input
{
@@ -11,6 +12,7 @@ public class InputLibrary
private const string CodeModelInputFileName = "tspCodeModel.json";

private readonly string _codeModelPath;
private readonly Emitter _emitter;

// for mocking
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
@@ -19,15 +21,16 @@ protected InputLibrary()
{
}

public InputLibrary(string codeModelPath)
public InputLibrary(string codeModelPath, Emitter emitter)
{
_codeModelPath = codeModelPath;
_emitter = emitter;
}

private InputNamespace? _inputNamespace;
public virtual InputNamespace InputNamespace => _inputNamespace ??= Load(_codeModelPath);
public virtual InputNamespace InputNamespace => _inputNamespace ??= Load(_codeModelPath, _emitter);

internal InputNamespace Load(string outputDirectory)
internal InputNamespace Load(string outputDirectory, Emitter emitter)
{
var codeModelFile = Path.Combine(outputDirectory, CodeModelInputFileName);
if (!File.Exists(codeModelFile))
@@ -37,7 +40,7 @@ internal InputNamespace Load(string outputDirectory)

// Read and deserialize tspCodeModel.json
var json = File.ReadAllText(codeModelFile);
return TypeSpecSerialization.Deserialize(json) ?? throw new InvalidOperationException($"Deserializing {codeModelFile} has failed.");
return TypeSpecSerialization.Deserialize(json, emitter) ?? throw new InvalidOperationException($"Deserializing {codeModelFile} has failed.");
}

private bool? _hasMultipartFormDataOperation;
Original file line number Diff line number Diff line change
@@ -11,21 +11,23 @@ public class InputClient
private readonly string? _key;
private IReadOnlyDictionary<string, InputClientExample>? _examples;

public InputClient(string name, string @namespace, string? summary, string? doc, IReadOnlyList<InputOperation> operations, IReadOnlyList<InputParameter> parameters, string? parent)
public InputClient(string name, string @namespace, string crossLanguageDefinitionId, string? summary, string? doc, IReadOnlyList<InputOperation> operations, IReadOnlyList<InputParameter> parameters, string? parent)
{
Name = name;
Namespace = @namespace;
CrossLanguageDefinitionId = crossLanguageDefinitionId;
Summary = summary;
Doc = doc;
Operations = operations;
Parameters = parameters;
Parent = parent;
}

public InputClient() : this(string.Empty, string.Empty, string.Empty, string.Empty, Array.Empty<InputOperation>(), Array.Empty<InputParameter>(), null) { }
public InputClient() : this(string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, Array.Empty<InputOperation>(), Array.Empty<InputParameter>(), null) { }

public string Name { get; internal set; }
public string Namespace { get; internal set; }
public string CrossLanguageDefinitionId { get; internal set; }
public string? Summary { get; internal set; }
public string? Doc { get; internal set; }
public IReadOnlyList<InputOperation> Operations { get; internal set; }
Original file line number Diff line number Diff line change
@@ -5,15 +5,20 @@
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.TypeSpec.Generator.Input.EmitterRpc;

namespace Microsoft.TypeSpec.Generator.Input
{
internal sealed class TypeSpecInputClientConverter : JsonConverter<InputClient>
{
private const string namespaceConflictCode = "client-namespace-conflict";

private readonly Emitter _emitter;
private readonly TypeSpecReferenceHandler _referenceHandler;

public TypeSpecInputClientConverter(TypeSpecReferenceHandler referenceHandler)
public TypeSpecInputClientConverter(TypeSpecReferenceHandler referenceHandler, Emitter emitter)
{
_emitter = emitter;
_referenceHandler = referenceHandler;
}

@@ -23,7 +28,7 @@ public TypeSpecInputClientConverter(TypeSpecReferenceHandler referenceHandler)
public override void Write(Utf8JsonWriter writer, InputClient value, JsonSerializerOptions options)
=> throw new NotSupportedException("Writing not supported");

private static InputClient? CreateInputClient(ref Utf8JsonReader reader, string? id, JsonSerializerOptions options, ReferenceResolver resolver)
private InputClient? CreateInputClient(ref Utf8JsonReader reader, string? id, JsonSerializerOptions options, ReferenceResolver resolver)
{
if (id == null)
{
@@ -42,6 +47,7 @@ public override void Write(Utf8JsonWriter writer, InputClient value, JsonSeriali
IReadOnlyList<InputParameter>? parameters = null;
IReadOnlyList<InputDecoratorInfo>? decorators = null;
string? parent = null;
string? crossLanguageDefinitionId = null;

while (reader.TokenType != JsonTokenType.EndObject)
{
@@ -52,7 +58,8 @@ public override void Write(Utf8JsonWriter writer, InputClient value, JsonSeriali
|| reader.TryReadWithConverter(nameof(InputClient.Operations), options, ref operations)
|| reader.TryReadWithConverter(nameof(InputClient.Parameters), options, ref parameters)
|| reader.TryReadString(nameof(InputClient.Parent), ref parent)
|| reader.TryReadWithConverter(nameof(InputClient.Decorators), options, ref decorators);
|| reader.TryReadWithConverter(nameof(InputClient.Decorators), options, ref decorators)
|| reader.TryReadString("CrossLanguageDefinitionId", ref crossLanguageDefinitionId);

if (!isKnownProperty)
{
@@ -62,16 +69,20 @@ public override void Write(Utf8JsonWriter writer, InputClient value, JsonSeriali

client.Name = name ?? throw new JsonException("InputClient must have name");
client.Namespace = @namespace ?? string.Empty;
client.CrossLanguageDefinitionId = crossLanguageDefinitionId ?? string.Empty;
client.Summary = summary;
client.Doc = doc;
client.Operations = operations ?? Array.Empty<InputOperation>();
client.Parameters = parameters ?? Array.Empty<InputParameter>();
client.Operations = operations ?? [];
client.Parameters = parameters ?? [];
client.Parent = parent;
client.Decorators = decorators ?? [];

if (GetLastSegment(client.Namespace) == client.Name)
var lastSegment = GetLastSegment(client.Namespace);
if (lastSegment == client.Name)
{
// invalid namespace segment found
// report the diagnostic
_emitter.ReportDiagnostic(namespaceConflictCode, $"namespace {client.Namespace} conflicts with client {client.Name}, please use `@clientName` to specify a different name for the client.", crossLanguageDefinitionId);
// check if the list is already there
// get the list out
var invalidNamespaceSegments = (List<string>)resolver.ResolveReference(TypeSpecSerialization.InvalidNamespaceSegmentsKey);
Original file line number Diff line number Diff line change
@@ -4,13 +4,14 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using AutoRest.CSharp.Common.Input;
using Microsoft.TypeSpec.Generator.Input.EmitterRpc;

namespace Microsoft.TypeSpec.Generator.Input
{
public static class TypeSpecSerialization
{
internal const string InvalidNamespaceSegmentsKey = "InvalidNamespaceSegments";
public static InputNamespace? Deserialize(string json)
public static InputNamespace? Deserialize(string json, Emitter emitter)
{
var referenceHandler = new TypeSpecReferenceHandler();
var options = new JsonSerializerOptions
@@ -31,7 +32,7 @@ public static class TypeSpecSerialization
new TypeSpecInputConstantConverter(referenceHandler),
new TypeSpecInputLiteralTypeConverter(referenceHandler),
new TypeSpecInputUnionTypeConverter(referenceHandler),
new TypeSpecInputClientConverter(referenceHandler),
new TypeSpecInputClientConverter(referenceHandler, emitter),
new TypeSpecInputOperationConverter(referenceHandler),
new TypeSpecInputParameterConverter(referenceHandler),
new TypeSpecInputPrimitiveTypeConverter(referenceHandler),
Original file line number Diff line number Diff line change
@@ -6,8 +6,7 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.TypeSpec.Generator.EmitterRpc;
using Microsoft.TypeSpec.Generator.Providers;
using Microsoft.TypeSpec.Generator.Input.EmitterRpc;
using Microsoft.TypeSpec.Generator.SourceInput;

namespace Microsoft.TypeSpec.Generator
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
using System.ComponentModel.Composition;
using Microsoft.CodeAnalysis;
using Microsoft.TypeSpec.Generator.Input;
using Microsoft.TypeSpec.Generator.Input.EmitterRpc;
using Microsoft.TypeSpec.Generator.Primitives;
using Microsoft.TypeSpec.Generator.Providers;
using Microsoft.TypeSpec.Generator.SourceInput;
@@ -44,7 +45,7 @@ internal static CodeModelPlugin Instance
public CodeModelPlugin(GeneratorContext context)
{
Configuration = context.Configuration;
_inputLibrary = new InputLibrary(Configuration.OutputDirectory);
_inputLibrary = new InputLibrary(Configuration.OutputDirectory, Emitter.Instance);
TypeFactory = new TypeFactory();
}

Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
using System.Diagnostics;
using System.Threading.Tasks;
using CommandLine;
using Microsoft.TypeSpec.Generator.EmitterRpc;
using Microsoft.TypeSpec.Generator.Input.EmitterRpc;

namespace Microsoft.TypeSpec.Generator
{
Original file line number Diff line number Diff line change
@@ -256,11 +256,12 @@ public static OperationResponse OperationResponse(IEnumerable<int>? statusCodes
["application/json"]);
}

public static InputClient Client(string name, string clientNamespace = "Sample", string? doc = null, IEnumerable<InputOperation>? operations = null, IEnumerable<InputParameter>? parameters = null, string? parent = null)
public static InputClient Client(string name, string clientNamespace = "Sample", string? doc = null, IEnumerable<InputOperation>? operations = null, IEnumerable<InputParameter>? parameters = null, string? parent = null, string? crossLanguageDefinitionId = null)
{
return new InputClient(
name,
clientNamespace,
crossLanguageDefinitionId ?? $"{clientNamespace}.{name}",
string.Empty,
doc ?? $"{name} description",
operations is null ? [] : [.. operations],
Original file line number Diff line number Diff line change
@@ -4063,7 +4063,8 @@
"Kind": "Client"
}
],
"Decorators": []
"Decorators": [],
"CrossLanguageDefinitionId": "UnbrandedTypeSpec"
}
],
"Auth": {
Original file line number Diff line number Diff line change
@@ -216,7 +216,8 @@
"Kind": "Client"
}
],
"Decorators": []
"Decorators": [],
"CrossLanguageDefinitionId": "Client.Structure.Service"
},
{
"$id": "23",
@@ -268,7 +269,8 @@
"Kind": "Client"
}
],
"Decorators": []
"Decorators": [],
"CrossLanguageDefinitionId": "Client.Structure.Service.Baz"
},
{
"$id": "28",
@@ -348,7 +350,8 @@
"Kind": "Client"
}
],
"Decorators": []
"Decorators": [],
"CrossLanguageDefinitionId": "Client.Structure.Service.Baz.Foo"
},
{
"$id": "35",
@@ -428,7 +431,8 @@
"Kind": "Client"
}
],
"Decorators": []
"Decorators": [],
"CrossLanguageDefinitionId": "Client.Structure.Service.Qux"
},
{
"$id": "42",
@@ -508,7 +512,8 @@
"Kind": "Client"
}
],
"Decorators": []
"Decorators": [],
"CrossLanguageDefinitionId": "Client.Structure.Service.Qux.Bar"
},
{
"$id": "49",
@@ -615,7 +620,8 @@
"Kind": "Client"
}
],
"Decorators": []
"Decorators": [],
"CrossLanguageDefinitionId": "Client.Structure.Service.Foo"
},
{
"$id": "58",
@@ -722,7 +728,8 @@
"Kind": "Client"
}
],
"Decorators": []
"Decorators": [],
"CrossLanguageDefinitionId": "Client.Structure.Service.Bar"
}
]
}