From d0c169b54bfd6049cf7fe2d781e95a8d4235ab47 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 20 Jan 2026 06:55:40 +0000 Subject: [PATCH 01/22] Initial plan From b0bde0a38821fd9c60ed9bfef3394ffbf295ac89 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 20 Jan 2026 07:05:10 +0000 Subject: [PATCH 02/22] Refactor emitter to export API with diagnostic return values Co-authored-by: ArcturusZhang <10554446+ArcturusZhang@users.noreply.github.com> --- .../http-client-csharp/emitter/src/emitter.ts | 38 ++++++++++--- .../emitter/src/lib/client-model-builder.ts | 7 +-- .../emitter/src/lib/logger.ts | 24 +++++++-- .../emitter/test/Unit/auth.test.ts | 30 +++++------ .../test/Unit/client-converter.test.ts | 8 +-- .../test/Unit/client-initialization.test.ts | 8 +-- .../test/Unit/client-model-builder.test.ts | 16 +++--- .../emitter/test/Unit/constant-type.test.ts | 8 +-- .../emitter/test/Unit/decorator-list.test.ts | 10 ++-- .../emitter/test/Unit/emitter.test.ts | 2 +- .../emitter/test/Unit/encode.test.ts | 12 ++--- .../emitter/test/Unit/input-parameter.test.ts | 54 +++++++++---------- .../emitter/test/Unit/model-type.test.ts | 36 ++++++------- .../test/Unit/operation-converter.test.ts | 14 ++--- .../test/Unit/operation-paging.test.ts | 24 ++++----- .../emitter/test/Unit/property-type.test.ts | 10 ++-- .../emitter/test/Unit/scalar.test.ts | 2 +- .../emitter/test/Unit/string-format.test.ts | 4 +- .../emitter/test/Unit/type-converter.test.ts | 6 +-- .../emitter/test/Unit/usage.test.ts | 30 +++++------ 20 files changed, 192 insertions(+), 151 deletions(-) diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index 8bd34db8889..4ab553d2f69 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -3,6 +3,7 @@ import { createSdkContext, SdkContext } from "@azure-tools/typespec-client-generator-core"; import { + Diagnostic, EmitContext, getDirectoryPath, joinPaths, @@ -48,17 +49,23 @@ function findProjectRoot(path: string): string | undefined { } /** - * The entry point for the emitter. This function is called by the typespec compiler. + * Creates a code model by executing the full emission logic. + * This function can be called by downstream emitters to generate a code model and collect diagnostics. * @param context - The emit context + * @returns A tuple containing void and any diagnostics that were generated during the emission * @beta */ -export async function $onEmit(context: EmitContext) { +export async function createCodeModel( + context: EmitContext, +): Promise<[void, readonly Diagnostic[]]> { const program: Program = context.program; const options = resolveOptions(context); const outputFolder = context.emitterOutputDir; /* set the log level. */ - const logger = new Logger(program, options.logLevel ?? LoggerLevel.INFO); + const logger = new Logger(program, options.logLevel ?? LoggerLevel.INFO, true); + + const diagnostics: Diagnostic[] = []; if (!program.compilerOptions.noEmit && !program.hasError()) { // Write out the dotnet model to the output path @@ -70,12 +77,15 @@ export async function $onEmit(context: EmitContext) { ), logger, ); - program.reportDiagnostics(sdkContext.diagnostics); + diagnostics.push(...sdkContext.diagnostics); - let root = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + diagnostics.push(...modelDiagnostics); if (root) { - root = options["update-code-model"](root, sdkContext); + const updatedRoot = options["update-code-model"](root, sdkContext); + diagnostics.push(...logger.getDiagnostics()); + const generatedFolder = resolvePath(outputFolder, "src", "Generated"); if (!fs.existsSync(generatedFolder)) { @@ -83,9 +93,9 @@ export async function $onEmit(context: EmitContext) { } // emit tspCodeModel.json - await writeCodeModel(sdkContext, root, outputFolder); + await writeCodeModel(sdkContext, updatedRoot, outputFolder); - const namespace = root.name; + const namespace = updatedRoot.name; const configurations: Configuration = createConfiguration(options, namespace, sdkContext); //emit configuration.json @@ -133,6 +143,18 @@ export async function $onEmit(context: EmitContext) { } } } + + return [undefined as void, diagnostics]; +} + +/** + * The entry point for the emitter. This function is called by the typespec compiler. + * @param context - The emit context + * @beta + */ +export async function $onEmit(context: EmitContext) { + const [, diagnostics] = await createCodeModel(context); + context.program.reportDiagnostics(diagnostics); } export function createConfiguration( diff --git a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts index 94f1bfd767d..33c88272158 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts @@ -7,6 +7,7 @@ import { SdkHttpOperation, UsageFlags, } from "@azure-tools/typespec-client-generator-core"; +import { Diagnostic } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { CodeModel } from "../type/code-model.js"; import { InputEnumType, InputLiteralType, InputModelType } from "../type/input-type.js"; @@ -23,10 +24,10 @@ import { /** * Creates the code model from the SDK context. * @param sdkContext - The SDK context - * @returns The code model + * @returns A tuple containing the code model and any diagnostics that were generated * @beta */ -export function createModel(sdkContext: CSharpEmitterContext): CodeModel { +export function createModel(sdkContext: CSharpEmitterContext): [CodeModel, readonly Diagnostic[]] { const sdkPackage = sdkContext.sdkPackage; // TO-DO: Consider exposing the namespace hierarchy in the code model https://github.com/microsoft/typespec/issues/8332 @@ -60,7 +61,7 @@ export function createModel(sdkContext: CSharpEmitterContext): CodeModel { auth: processServiceAuthentication(sdkContext, sdkPackage), }; - return clientModel; + return [clientModel, sdkContext.logger.getDiagnostics()]; } /** diff --git a/packages/http-client-csharp/emitter/src/lib/logger.ts b/packages/http-client-csharp/emitter/src/lib/logger.ts index 8b087a400f3..f99f726c061 100644 --- a/packages/http-client-csharp/emitter/src/lib/logger.ts +++ b/packages/http-client-csharp/emitter/src/lib/logger.ts @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -import { DiagnosticReport, NoTarget, Program, Tracer } from "@typespec/compiler"; +import { Diagnostic, DiagnosticReport, NoTarget, Program, Tracer } from "@typespec/compiler"; import { + createDiagnostic, DiagnosticMessagesMap, getTracer, reportDiagnostic as libReportDiagnostic, @@ -17,11 +18,22 @@ export class Logger { private tracer: Tracer; private level: LoggerLevel; private program: Program; + private collectedDiagnostics: Diagnostic[] | undefined; - public constructor(program: Program, level: LoggerLevel) { + public constructor(program: Program, level: LoggerLevel, collectDiagnostics: boolean = false) { this.tracer = getTracer(program); this.level = level; this.program = program; + this.collectedDiagnostics = collectDiagnostics ? [] : undefined; + } + + /** + * Get collected diagnostics. Only available if the logger was created with collectDiagnostics=true. + * @returns The collected diagnostics. + * @beta + */ + public getDiagnostics(): readonly Diagnostic[] { + return this.collectedDiagnostics ?? []; } trace(level: LoggerLevel, message: string): void { @@ -63,7 +75,13 @@ export class Logger { reportDiagnostic( diag: DiagnosticReport, ): void { - libReportDiagnostic(this.program, diag); + if (this.collectedDiagnostics) { + // In collecting mode, store the diagnostic instead of reporting it + this.collectedDiagnostics.push(createDiagnostic(diag)); + } else { + // In normal mode, report the diagnostic directly + libReportDiagnostic(this.program, diag); + } } warn(message: string): void { diff --git a/packages/http-client-csharp/emitter/test/Unit/auth.test.ts b/packages/http-client-csharp/emitter/test/Unit/auth.test.ts index ee94b078d41..4aff753d192 100644 --- a/packages/http-client-csharp/emitter/test/Unit/auth.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/auth.test.ts @@ -30,7 +30,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const diagnostics = context.program.diagnostics; const noAuthDiagnostics = diagnostics.filter( @@ -72,7 +72,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const diagnostics = context.program.diagnostics; const noAuthDiagnostics = diagnostics.filter( @@ -114,7 +114,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const diagnostics = context.program.diagnostics; const noAuthDiagnostics = diagnostics.filter( @@ -154,7 +154,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const diagnostics = context.program.diagnostics; const noAuthDiagnostic = diagnostics.find( @@ -182,7 +182,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const diagnostics = context.program.diagnostics; const noAuthDiagnostics = diagnostics.filter( @@ -216,7 +216,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const diagnostics = context.program.diagnostics; const noAuthDiagnostics = diagnostics.filter( @@ -249,7 +249,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const diagnostics = context.program.diagnostics; // Should have no auth-related diagnostics @@ -290,7 +290,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -322,7 +322,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -352,7 +352,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -389,7 +389,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 2); @@ -426,7 +426,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -454,7 +454,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -482,7 +482,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // Should have both OAuth2 and API key auth ok(root.auth?.oAuth2); @@ -513,7 +513,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); diff --git a/packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts index ab683388071..3e2546f9626 100644 --- a/packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts @@ -28,7 +28,7 @@ describe("isMultiServiceClient", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok(client, "Client should exist"); @@ -81,7 +81,7 @@ describe("isMultiServiceClient", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.name, "Service.MultiService", "Root namespace should be Service.MultiService"); const client = root.clients[0]; @@ -147,7 +147,7 @@ describe("isMultiServiceClient", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.name, "Service.MultiService", "Root namespace should be Service.MultiService"); const client = root.clients[0]; @@ -219,7 +219,7 @@ describe("isMultiServiceClient", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.name, "Service.MultiService", "Root namespace should be Service.MultiService"); const clients = root.clients; diff --git a/packages/http-client-csharp/emitter/test/Unit/client-initialization.test.ts b/packages/http-client-csharp/emitter/test/Unit/client-initialization.test.ts index 8715259052f..d9b508b9a16 100644 --- a/packages/http-client-csharp/emitter/test/Unit/client-initialization.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/client-initialization.test.ts @@ -34,7 +34,7 @@ describe("ClientInitialization", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok(client, "Client should exist"); @@ -59,7 +59,7 @@ describe("ClientInitialization", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const client = root.clients[0]; // initializedBy field should exist on the client (may be undefined or have a value) @@ -84,7 +84,7 @@ describe("ClientInitialization", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok(client.parameters, "Client should have parameters"); @@ -113,7 +113,7 @@ describe("ClientInitialization", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok("initializedBy" in client, "Parent client should have initializedBy field"); diff --git a/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts b/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts index 611fafc9ec9..dbd26ede69c 100644 --- a/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts @@ -58,7 +58,7 @@ describe("fixNamingConflicts", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // Find the real enum const realEnum = root.enums.find( @@ -142,7 +142,7 @@ describe("fixNamingConflicts", () => { namespace: targetNamespace, } as any); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // Get all ErrorResponse models - fixNamingConflicts should have resolved the conflicts const errorModels = root.models.filter( @@ -194,7 +194,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // The root apiVersions should include the version from the Versions enum // which is defined in the default namespace with version "2023-01-01-preview" @@ -228,7 +228,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // The root apiVersions should include all versions from the TestVersions enum strictEqual(root.apiVersions.length, 3, "Root apiVersions should have 3 versions"); @@ -247,7 +247,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // Single service client should have apiVersions from the @versioned decorator ok(root.apiVersions.length > 0, "Root apiVersions should not be empty for single service"); @@ -299,7 +299,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.apiVersions.length === 0, "Root apiVersions should be empty for multiservice"); @@ -358,7 +358,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.apiVersions.length === 0, "Root apiVersions should be empty for multiservice"); }); @@ -415,7 +415,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok( root.apiVersions.length === 0, diff --git a/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts b/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts index 595e936d2f5..3730f08dbdd 100644 --- a/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts @@ -33,7 +33,7 @@ describe("Name for constant type", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel); const propertyType = testModel.properties[0].type; @@ -64,7 +64,7 @@ describe("Name for constant type", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const testModel1 = root.models.find((m) => m.name === "TestModel1"); ok(testModel1); const testModel2 = root.models.find((m) => m.name === "TestModel2"); @@ -103,7 +103,7 @@ describe("Constant enum conversion", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel); const propertyType = testModel.properties[0].type; @@ -134,7 +134,7 @@ describe("Constant enum conversion", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel); const propertyType = testModel.properties[0].type; diff --git a/packages/http-client-csharp/emitter/test/Unit/decorator-list.test.ts b/packages/http-client-csharp/emitter/test/Unit/decorator-list.test.ts index 37aaa5ad3fc..53df8aa6e86 100644 --- a/packages/http-client-csharp/emitter/test/Unit/decorator-list.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/decorator-list.test.ts @@ -34,7 +34,7 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const clients = root.clients; strictEqual(clients.length, 1); ok(clients[0].children); @@ -66,7 +66,7 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const methods = root.clients[0].methods; strictEqual(methods.length, 1); const operation = methods[0].operation; @@ -97,7 +97,7 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; strictEqual(models.length, 1); deepStrictEqual(models[0].decorators, [ @@ -127,7 +127,7 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; strictEqual(models.length, 1); deepStrictEqual(models[0].properties[0].decorators, [ @@ -153,7 +153,7 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const methods = root.clients[0].methods; strictEqual(methods.length, 1); const operation = methods[0].operation; diff --git a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts index 6755c79fde1..7509337482f 100644 --- a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts @@ -58,7 +58,7 @@ describe("$onEmit tests", () => { })); vi.mock("../../src/lib/client-model-builder.js", () => ({ - createModel: vi.fn().mockReturnValue({ Name: "TestNamespace" }), + createModel: vi.fn().mockReturnValue([{ name: "TestNamespace" }, []]), })); program = { diff --git a/packages/http-client-csharp/emitter/test/Unit/encode.test.ts b/packages/http-client-csharp/emitter/test/Unit/encode.test.ts index 559d2d54dbb..6344bd3f4ca 100644 --- a/packages/http-client-csharp/emitter/test/Unit/encode.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/encode.test.ts @@ -32,7 +32,7 @@ describe("Test encode duration", () => { // validate method parameter const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const methodParamArray = root.clients[0].methods[0].parameters; strictEqual(1, methodParamArray.length); let type = methodParamArray[0].type; @@ -74,7 +74,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // validate method parameter const methodParamArray = root.clients[0].methods[0].parameters; strictEqual(1, methodParamArray.length); @@ -117,7 +117,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // validate method parameter const methodParamArray = root.clients[0].methods[0].parameters; strictEqual(1, methodParamArray.length); @@ -161,7 +161,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const codeModel = createModel(sdkContext); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const durationModel = models.find((m) => m.name === "ISO8601DurationProperty"); ok(durationModel); @@ -190,7 +190,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const codeModel = createModel(sdkContext); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const durationModel = models.find((m) => m.name === "Int32SecondsDurationProperty"); ok(durationModel); @@ -219,7 +219,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const codeModel = createModel(sdkContext); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const durationModel = models.find((m) => m.name === "FloatSecondsDurationProperty"); ok(durationModel); diff --git a/packages/http-client-csharp/emitter/test/Unit/input-parameter.test.ts b/packages/http-client-csharp/emitter/test/Unit/input-parameter.test.ts index d552104b525..1c0977497ef 100644 --- a/packages/http-client-csharp/emitter/test/Unit/input-parameter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/input-parameter.test.ts @@ -42,7 +42,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -72,7 +72,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -102,7 +102,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -136,7 +136,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -166,7 +166,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -196,7 +196,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -228,7 +228,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -258,7 +258,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -288,7 +288,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -320,7 +320,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -350,7 +350,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -380,7 +380,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -414,7 +414,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -445,7 +445,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -476,7 +476,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -509,7 +509,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -540,7 +540,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -571,7 +571,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -686,7 +686,7 @@ describe("Endpoint parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const codeModel = createModel(sdkContext); + const [codeModel] = createModel(sdkContext); const client = codeModel.clients[0]; ok(client); ok(client.parameters); @@ -722,7 +722,7 @@ describe("Endpoint parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const codeModel = createModel(sdkContext); + const [codeModel] = createModel(sdkContext); const client = codeModel.clients[0]; ok(client); ok(client.parameters); @@ -759,7 +759,7 @@ describe("Test Spread Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root); // validate service method @@ -802,7 +802,7 @@ describe("Test Spread Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root); // validate service method @@ -855,7 +855,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const queryParam = operation.parameters.find((p) => p.name === "queryParam"); @@ -881,7 +881,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const pathParam = operation.parameters.find((p) => p.name === "pathParam"); @@ -910,7 +910,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const headerParam = operation.parameters.find((p) => p.name === "headerParam"); @@ -940,7 +940,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const contentTypeParam = operation.parameters.find((p) => p.name === "contentType"); @@ -970,7 +970,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const bodyParam = operation.parameters.find((p) => p.name === "bodyParam"); diff --git a/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts b/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts index c343dab4823..556cda60305 100644 --- a/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts @@ -51,7 +51,7 @@ op test(@body input: Pet): Pet; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const petModel = models.find((m) => m.name === "Pet"); const catModel = models.find((m) => m.name === "Cat"); @@ -135,7 +135,7 @@ op test(@body input: Pet): Pet; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const codeModel = createModel(sdkContext); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const pet = models.find((m) => m.name === "Pet"); assert(pet !== undefined); @@ -229,7 +229,7 @@ op test(@body input: Pet): Pet; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const codeModel = createModel(sdkContext); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const pet = models.find((m) => m.name === "Pet"); assert(pet !== undefined); @@ -350,7 +350,7 @@ op op5(@body body: ExtendsFooArray): ExtendsFooArray; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const extendsUnknownModel = models.find((m) => m.name === "ExtendsUnknown"); const extendsStringModel = models.find((m) => m.name === "ExtendsString"); @@ -442,7 +442,7 @@ op op5(@body body: IsFooArray): IsFooArray; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const isUnknownModel = models.find((m) => m.name === "IsUnknown"); const isStringModel = models.find((m) => m.name === "IsString"); @@ -493,7 +493,7 @@ op op1(): void; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const isEmptyModel = models.find((m) => m.name === "Empty"); ok(isEmptyModel); @@ -522,7 +522,7 @@ model Foo { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const model = models.find((m) => m.name === "Foo"); ok(model); @@ -560,7 +560,7 @@ describe("Anonymous models should be included in library", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root); // validate service method @@ -602,7 +602,7 @@ op testOperation(@bodyRoot body: HeaderModel): void; const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const isEmptyModel = models.find((m) => m.name === "HeaderModel"); ok(isEmptyModel); @@ -647,7 +647,7 @@ op testOperation(@bodyRoot body: HeaderModel): void; const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const isEmptyModel = models.find((m) => m.name === "HeaderModel"); ok(isEmptyModel); @@ -680,7 +680,7 @@ op testOperation(@bodyRoot body: HeaderModel): void; const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const isEmptyModel = models.find((m) => m.name === "HeaderModel"); ok(isEmptyModel); @@ -707,7 +707,7 @@ op testOperation(@bodyRoot body: HeaderModel): void; const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const isEmptyModel = models.find((m) => m.name === "HeaderModel"); ok(isEmptyModel); @@ -757,7 +757,7 @@ describe("typespec-client-generator-core: general decorators list", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; strictEqual(models.length, 1); deepStrictEqual(models[0].decorators, [ @@ -799,7 +799,7 @@ describe("Access decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const enums = root.enums; const colorEnum = enums.find((e) => e.name === "Color"); @@ -830,7 +830,7 @@ describe("Access decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const enums = root.enums; const colorEnum = enums.find((e) => e.name === "Color"); @@ -860,7 +860,7 @@ describe("Access decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const enums = root.enums; const statusEnum = enums.find((e) => e.name === "Status"); @@ -898,7 +898,7 @@ describe("Usage decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const enums = root.enums; const colorEnum = enums.find((e) => e.name === "Color"); @@ -928,7 +928,7 @@ describe("Usage decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const enums = root.enums; const colorEnum = enums.find((e) => e.name === "Color"); diff --git a/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts index d91f0cd31c1..08ac1f48adf 100644 --- a/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts @@ -35,7 +35,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -106,7 +106,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -176,7 +176,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -231,7 +231,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -275,7 +275,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -314,7 +314,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -346,7 +346,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); diff --git a/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts b/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts index d5f5cd66d54..37290c74147 100644 --- a/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts @@ -39,7 +39,7 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -93,7 +93,7 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -138,7 +138,7 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -174,7 +174,7 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -208,7 +208,7 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -256,7 +256,7 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -291,7 +291,7 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -327,7 +327,7 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -363,7 +363,7 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -399,7 +399,7 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -453,7 +453,7 @@ describe("PageSize parameter operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -491,7 +491,7 @@ describe("PageSize parameter operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); diff --git a/packages/http-client-csharp/emitter/test/Unit/property-type.test.ts b/packages/http-client-csharp/emitter/test/Unit/property-type.test.ts index 5de9f067d45..e3589538bd3 100644 --- a/packages/http-client-csharp/emitter/test/Unit/property-type.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/property-type.test.ts @@ -28,7 +28,7 @@ describe("Test GetInputType for array", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "input", ); @@ -49,7 +49,7 @@ describe("Test GetInputType for array", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const bodyType = root.clients[0].methods[0].operation.responses[0].bodyType; strictEqual(bodyType?.kind, "array"); strictEqual(bodyType.crossLanguageDefinitionId, "TypeSpec.Array"); @@ -86,7 +86,7 @@ describe("Test GetInputType for enum", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "input", ); @@ -130,7 +130,7 @@ describe("Test GetInputType for enum", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "input", ); @@ -169,7 +169,7 @@ describe("Test GetInputType for enum", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "input", ); diff --git a/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts b/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts index 015d4ca3c43..cea35899816 100644 --- a/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts @@ -28,7 +28,7 @@ describe("Test GetInputType for scalar", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "location", ); diff --git a/packages/http-client-csharp/emitter/test/Unit/string-format.test.ts b/packages/http-client-csharp/emitter/test/Unit/string-format.test.ts index 2f86dc8409e..3eea60ca0d1 100644 --- a/packages/http-client-csharp/emitter/test/Unit/string-format.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/string-format.test.ts @@ -27,7 +27,7 @@ describe("Test string format", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "sourceUrl", ); @@ -51,7 +51,7 @@ describe("Test string format", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const codeModel = createModel(sdkContext); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const foo = models.find((m) => m.name === "Foo"); ok(foo); diff --git a/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts index 081708c903f..b6fb88bbdf9 100644 --- a/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts @@ -69,7 +69,7 @@ describe("Enum value references", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const enumType = root.enums.find((e) => e.name === "TestEnum"); ok(enumType, "TestEnum should exist in the enums list"); strictEqual(enumType.values.length, 3, "TestEnum should have 3 values"); @@ -119,7 +119,7 @@ describe("External types", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel, "TestModel should exist"); @@ -164,7 +164,7 @@ describe("External types", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel, "TestModel should exist"); diff --git a/packages/http-client-csharp/emitter/test/Unit/usage.test.ts b/packages/http-client-csharp/emitter/test/Unit/usage.test.ts index a20ee81a401..ee0923b0174 100644 --- a/packages/http-client-csharp/emitter/test/Unit/usage.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/usage.test.ts @@ -33,7 +33,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -54,7 +54,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -75,7 +75,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -97,7 +97,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -124,7 +124,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); const templateModel = root.models.find((model) => model.name === "TemplateModelFoo"); @@ -154,7 +154,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const baseModel = root.models.find((model) => model.name === "BaseModel"); const fooModel = root.models.find((model) => model.name === "Foo"); @@ -191,7 +191,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const baseModel = root.models.find((model) => model.name === "BaseModel"); const fooModel = root.models.find((model) => model.name === "Foo"); const propertyModel = root.models.find((model) => model.name === "PropertyModel"); @@ -218,7 +218,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const fooAlias = root.models.find((model) => model.name === "TestRequest"); ok(fooAlias); @@ -266,7 +266,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const fooInfo = root.models.find((model) => model.name === "FooInfo"); const batchCreateFooListItemsRequest = root.models.find( (model) => model.name === "BatchCreateFooListItemsRequest", @@ -312,7 +312,7 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -371,7 +371,7 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const baseModel = root.models.find((model) => model.name === "BaseModelWithDiscriminator"); const derivedModel = root.models.find( (model) => model.name === "DerivedModelWithDiscriminatorA", @@ -441,7 +441,7 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const baseModel = root.models.find((model) => model.name === "BaseModelWithDiscriminator"); const derivedModel = root.models.find( (model) => model.name === "DerivedModelWithDiscriminatorA", @@ -478,7 +478,7 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const simpleEnumRenamed = root.enums.find((enumType) => enumType.name === "SimpleEnumRenamed"); ok(simpleEnumRenamed); @@ -502,7 +502,7 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const renamedModel = root.models.find((model) => model.name === "RenamedModel"); ok(renamedModel); @@ -660,7 +660,7 @@ interface LegacyLro { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const radiologyInsightsInferenceResult = root.models.find( (model) => model.name === "RadiologyInsightsInferenceResult", ); From 7c36f320c8e1fc00870120c5083a9023a1d1b0ba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 20 Jan 2026 07:09:57 +0000 Subject: [PATCH 03/22] Add tests for new diagnostic collection API Co-authored-by: ArcturusZhang <10554446+ArcturusZhang@users.noreply.github.com> --- .../test/Unit/client-model-builder.test.ts | 59 +++++++++++++++++++ .../emitter/test/Unit/emitter.test.ts | 53 +++++++++++++++++ .../emitter/test/Unit/utils/test-util.ts | 25 +++++--- 3 files changed, 130 insertions(+), 7 deletions(-) diff --git a/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts b/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts index dbd26ede69c..b3cdb30e8ae 100644 --- a/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts @@ -441,3 +441,62 @@ describe("parseApiVersions", () => { ok(barClient.apiVersions.includes("bv2"), "Bar client should include bv2"); }); }); + +describe("createModel diagnostic collection", () => { + let runner: TestHost; + + beforeEach(async () => { + runner = await createEmitterTestHost(); + }); + + it("should return a tuple with CodeModel and diagnostics array", async () => { + const program = await typeSpecCompile( + ` + model TestModel { + name: string; + } + + @route("/test") + op test(): TestModel; + `, + runner, + ); + const context = createEmitterContext(program); + const sdkContext = await createCSharpSdkContext(context, true); // Enable diagnostic collection + const result = createModel(sdkContext); + + // Verify the result is a tuple + ok(Array.isArray(result), "Result should be an array (tuple)"); + strictEqual(result.length, 2, "Result should have exactly 2 elements"); + + const [codeModel, diagnostics] = result; + + // Verify the code model + ok(codeModel, "CodeModel should be defined"); + strictEqual(codeModel.name, "Azure.Csharp.Testing", "CodeModel name should be Azure.Csharp.Testing"); + + // Verify diagnostics is an array + ok(Array.isArray(diagnostics), "Diagnostics should be an array"); + }); + + it("should collect diagnostics when using diagnostic collection mode", async () => { + const program = await typeSpecCompile( + ` + model TestModel { + name: string; + } + + @route("/test") + op test(): TestModel; + `, + runner, + ); + const context = createEmitterContext(program); + const sdkContext = await createCSharpSdkContext(context, true); // Enable diagnostic collection + const [, diagnostics] = createModel(sdkContext); + + // Verify diagnostics array exists (may be empty or contain diagnostics) + ok(diagnostics !== undefined, "Diagnostics should not be undefined"); + ok(Array.isArray(diagnostics), "Diagnostics should be an array"); + }); +}); diff --git a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts index 7509337482f..7246e73cf2e 100644 --- a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts @@ -179,6 +179,59 @@ describe("$onEmit tests", () => { }); }); +describe("createCodeModel tests", () => { + let runner: TestHost; + let program: Program; + + beforeEach(async () => { + vi.restoreAllMocks(); + runner = await createEmitterTestHost(); + }); + + it("should return diagnostics array from createCodeModel", async () => { + program = await typeSpecCompile( + ` + model TestModel { + name: string; + } + + @route("/test") + op test(): TestModel; + `, + runner, + ); + const context = createEmitterContext(program); + const { createCodeModel } = await import("../../src/emitter.js"); + const [, diagnostics] = await createCodeModel(context); + + // Verify that diagnostics is an array + expect(Array.isArray(diagnostics)).toBe(true); + // Diagnostics array should be defined (may be empty or have diagnostics) + expect(diagnostics).toBeDefined(); + }); + + it("should collect diagnostics from createModel in createCodeModel", async () => { + program = await typeSpecCompile( + ` + model TestModel { + name: string; + } + + @route("/test") + op test(): TestModel; + `, + runner, + ); + const context = createEmitterContext(program); + const { createCodeModel } = await import("../../src/emitter.js"); + const [, diagnostics] = await createCodeModel(context); + + // The function should return diagnostics even if empty + expect(diagnostics).toBeDefined(); + expect(Array.isArray(diagnostics)).toBe(true); + }); +}); + describe("Test _validateDotNetSdk", () => { let runner: TestHost; let program: Program; diff --git a/packages/http-client-csharp/emitter/test/Unit/utils/test-util.ts b/packages/http-client-csharp/emitter/test/Unit/utils/test-util.ts index 3b6d60db3ca..8dd90fc57e0 100644 --- a/packages/http-client-csharp/emitter/test/Unit/utils/test-util.ts +++ b/packages/http-client-csharp/emitter/test/Unit/utils/test-util.ts @@ -119,14 +119,25 @@ export function createEmitterContext( /* We always need to pass in the emitter name now that it is required so making a helper to do this. */ export async function createCSharpSdkContext( program: EmitContext, - sdkContextOptions: CreateSdkContextOptions = {}, + sdkContextOptionsOrCollectDiagnostics: CreateSdkContextOptions | boolean = {}, + sdkContextOptions?: CreateSdkContextOptions, ): Promise { + // Handle backward compatibility - if second param is boolean, it's collectDiagnostics + let collectDiagnostics = false; + let options: CreateSdkContextOptions = {}; + + if (typeof sdkContextOptionsOrCollectDiagnostics === "boolean") { + collectDiagnostics = sdkContextOptionsOrCollectDiagnostics; + options = sdkContextOptions ?? {}; + } else { + options = sdkContextOptionsOrCollectDiagnostics; + } + const createSdkContext = await getCreateSdkContext(); - const context = await createSdkContext( - program, - "@typespec/http-client-csharp", - sdkContextOptions, - ); + const context = await createSdkContext(program, "@typespec/http-client-csharp", options); const Logger = await getLogger(); - return createCSharpEmitterContext(context, new Logger(program.program, LoggerLevel.INFO)); + return createCSharpEmitterContext( + context, + new Logger(program.program, LoggerLevel.INFO, collectDiagnostics), + ); } From d368866ec9c29309dd0a7db6b9653be908e6e550 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 20 Jan 2026 07:11:02 +0000 Subject: [PATCH 04/22] Add documentation for new diagnostic collection API Co-authored-by: ArcturusZhang <10554446+ArcturusZhang@users.noreply.github.com> --- packages/http-client-csharp/emitter/src/emitter.ts | 12 ++++++++++++ .../emitter/src/lib/client-model-builder.ts | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index 4ab553d2f69..60a26c0ed91 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -51,6 +51,18 @@ function findProjectRoot(path: string): string | undefined { /** * Creates a code model by executing the full emission logic. * This function can be called by downstream emitters to generate a code model and collect diagnostics. + * + * @example + * ```typescript + * import { createCodeModel } from "@typespec/http-client-csharp"; + * + * export async function $onEmit(context: EmitContext) { + * const [, diagnostics] = await createCodeModel(context); + * // Process diagnostics as needed + * context.program.reportDiagnostics(diagnostics); + * } + * ``` + * * @param context - The emit context * @returns A tuple containing void and any diagnostics that were generated during the emission * @beta diff --git a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts index 33c88272158..1462b7e9953 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts @@ -23,6 +23,19 @@ import { /** * Creates the code model from the SDK context. + * This function follows TypeSpec best practices by returning diagnostics alongside the result. + * + * @example + * ```typescript + * import { createModel } from "@typespec/http-client-csharp"; + * import { Logger } from "@typespec/http-client-csharp/lib/logger"; + * + * const logger = new Logger(program, LoggerLevel.INFO, true); // Enable diagnostic collection + * const sdkContext = createCSharpEmitterContext(context, logger); + * const [codeModel, diagnostics] = createModel(sdkContext); + * // Process the code model and handle diagnostics + * ``` + * * @param sdkContext - The SDK context * @returns A tuple containing the code model and any diagnostics that were generated * @beta From feb41b6030f3273e91d2cd11e4508d406b65c64b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 07:49:28 +0000 Subject: [PATCH 05/22] Refactor: move update-code-model callback from options to createCodeModel parameter Co-authored-by: ArcturusZhang <10554446+ArcturusZhang@users.noreply.github.com> --- .../http-client-csharp/emitter/src/emitter.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index 60a26c0ed91..53c1bd94b3f 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -26,6 +26,7 @@ import { Logger } from "./lib/logger.js"; import { execAsync, execCSharpGenerator } from "./lib/utils.js"; import { CSharpEmitterOptions, resolveOptions } from "./options.js"; import { createCSharpEmitterContext, CSharpEmitterContext } from "./sdk-context.js"; +import { CodeModel } from "./type/code-model.js"; import { Configuration } from "./type/configuration.js"; /** @@ -57,18 +58,24 @@ function findProjectRoot(path: string): string | undefined { * import { createCodeModel } from "@typespec/http-client-csharp"; * * export async function $onEmit(context: EmitContext) { - * const [, diagnostics] = await createCodeModel(context); + * const updateCodeModel = (model: CodeModel, context: CSharpEmitterContext) => { + * // Customize the code model here + * return model; + * }; + * const [, diagnostics] = await createCodeModel(context, updateCodeModel); * // Process diagnostics as needed * context.program.reportDiagnostics(diagnostics); * } * ``` * * @param context - The emit context + * @param updateCodeModel - Optional callback to modify the code model before emission. Defaults to identity function. * @returns A tuple containing void and any diagnostics that were generated during the emission * @beta */ export async function createCodeModel( context: EmitContext, + updateCodeModel?: (model: CodeModel, context: CSharpEmitterContext) => CodeModel, ): Promise<[void, readonly Diagnostic[]]> { const program: Program = context.program; const options = resolveOptions(context); @@ -95,7 +102,9 @@ export async function createCodeModel( diagnostics.push(...modelDiagnostics); if (root) { - const updatedRoot = options["update-code-model"](root, sdkContext); + // Use the provided callback or default to identity function + const updateCodeModelFn = updateCodeModel ?? ((model: CodeModel) => model); + const updatedRoot = updateCodeModelFn(root, sdkContext); diagnostics.push(...logger.getDiagnostics()); const generatedFolder = resolvePath(outputFolder, "src", "Generated"); @@ -165,7 +174,8 @@ export async function createCodeModel( * @beta */ export async function $onEmit(context: EmitContext) { - const [, diagnostics] = await createCodeModel(context); + const options = resolveOptions(context); + const [, diagnostics] = await createCodeModel(context, options["update-code-model"]); context.program.reportDiagnostics(diagnostics); } From 750acf0eb76a0dedb1abcf8a3e1481e624568410 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 08:45:00 +0000 Subject: [PATCH 06/22] Remove reportDiagnostic from Logger and use diagnostic collection pattern - Removed reportDiagnostic, warn, and error methods from Logger class - Added __diagnostics array to CSharpEmitterContext for collecting diagnostics - Updated all code to use createDiagnostic and push to sdkContext.__diagnostics - Updated createModel to return diagnostics from __diagnostics instead of logger - Updated all tests to report returned diagnostics to program for assertions - Simplified Logger to only handle tracing, removed diagnostic collection mode Co-authored-by: ArcturusZhang <10554446+ArcturusZhang@users.noreply.github.com> --- .../emitter/src/code-model-writer.ts | 11 ++- .../http-client-csharp/emitter/src/emitter.ts | 54 +++++++----- .../emitter/src/lib/client-converter.ts | 13 +-- .../emitter/src/lib/client-model-builder.ts | 2 +- .../emitter/src/lib/logger.ts | 42 +-------- .../emitter/src/lib/operation-converter.ts | 81 +++++++++-------- .../emitter/src/lib/service-authentication.ts | 65 ++++++++------ .../emitter/src/lib/type-converter.ts | 25 +++--- .../emitter/src/sdk-context.ts | 4 +- .../emitter/test/Unit/auth.test.ts | 45 ++++++---- .../test/Unit/client-converter.test.ts | 12 ++- .../test/Unit/client-initialization.test.ts | 12 ++- .../test/Unit/client-model-builder.test.ts | 24 +++-- .../emitter/test/Unit/constant-type.test.ts | 12 ++- .../emitter/test/Unit/decorator-list.test.ts | 15 ++-- .../emitter/test/Unit/emitter.test.ts | 4 + .../emitter/test/Unit/encode.test.ts | 18 ++-- .../emitter/test/Unit/input-parameter.test.ts | 87 ++++++++++++------- .../emitter/test/Unit/model-type.test.ts | 54 ++++++++---- .../test/Unit/operation-converter.test.ts | 21 +++-- .../test/Unit/operation-paging.test.ts | 36 +++++--- .../emitter/test/Unit/property-type.test.ts | 15 ++-- .../emitter/test/Unit/scalar.test.ts | 3 +- .../emitter/test/Unit/string-format.test.ts | 6 +- .../emitter/test/Unit/type-converter.test.ts | 9 +- .../emitter/test/Unit/usage.test.ts | 45 ++++++---- 26 files changed, 438 insertions(+), 277 deletions(-) diff --git a/packages/http-client-csharp/emitter/src/code-model-writer.ts b/packages/http-client-csharp/emitter/src/code-model-writer.ts index 994d0bad2e7..0810ce0ba74 100644 --- a/packages/http-client-csharp/emitter/src/code-model-writer.ts +++ b/packages/http-client-csharp/emitter/src/code-model-writer.ts @@ -2,8 +2,9 @@ // Licensed under the MIT License. See License.txt in the project root for license information. import { UsageFlags } from "@azure-tools/typespec-client-generator-core"; -import { resolvePath } from "@typespec/compiler"; +import { NoTarget, resolvePath } from "@typespec/compiler"; import { configurationFileName, tspOutputFileName } from "./constants.js"; +import { createDiagnostic } from "./lib/lib.js"; import { CSharpEmitterContext } from "./sdk-context.js"; import { CodeModel } from "./type/code-model.js"; import { Configuration } from "./type/configuration.js"; @@ -73,7 +74,13 @@ function buildJson(context: CSharpEmitterContext, codeModel: CodeModel): any { function handleObject(obj: any, id: string | undefined, stack: any[]): any { if (stack.includes(obj)) { // we have a cyclical reference, we should not continue - context.logger.warn(`Cyclical reference detected in the code model (id: ${id}).`); + context.__diagnostics.push( + createDiagnostic({ + code: "general-warning", + format: { message: `Cyclical reference detected in the code model (id: ${id}).` }, + target: NoTarget, + }), + ); return undefined; } diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index 53c1bd94b3f..6bb32dbf53d 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -21,6 +21,7 @@ import { tspOutputFileName, } from "./constants.js"; import { createModel } from "./lib/client-model-builder.js"; +import { createDiagnostic } from "./lib/lib.js"; import { LoggerLevel } from "./lib/logger-level.js"; import { Logger } from "./lib/logger.js"; import { execAsync, execCSharpGenerator } from "./lib/utils.js"; @@ -105,7 +106,8 @@ export async function createCodeModel( // Use the provided callback or default to identity function const updateCodeModelFn = updateCodeModel ?? ((model: CodeModel) => model); const updatedRoot = updateCodeModelFn(root, sdkContext); - diagnostics.push(...logger.getDiagnostics()); + // Collect any diagnostics added during the callback execution + diagnostics.push(...sdkContext.__diagnostics); const generatedFolder = resolvePath(outputFolder, "src", "Generated"); @@ -228,15 +230,17 @@ export async function _validateDotNetSdk( return validateDotNetSdkVersionCore(sdkContext, result.stdout, minMajorVersion); } catch (error: any) { if (error && "code" in error && error["code"] === "ENOENT") { - sdkContext.logger.reportDiagnostic({ - code: "invalid-dotnet-sdk-dependency", - messageId: "missing", - format: { - dotnetMajorVersion: `${minMajorVersion}`, - downloadUrl: "https://dotnet.microsoft.com/", - }, - target: NoTarget, - }); + sdkContext.__diagnostics.push( + createDiagnostic({ + code: "invalid-dotnet-sdk-dependency", + messageId: "missing", + format: { + dotnetMajorVersion: `${minMajorVersion}`, + downloadUrl: "https://dotnet.microsoft.com/", + }, + target: NoTarget, + }), + ); } return false; } @@ -256,21 +260,29 @@ function validateDotNetSdkVersionCore( return false; } if (major < minMajorVersion) { - sdkContext.logger.reportDiagnostic({ - code: "invalid-dotnet-sdk-dependency", - messageId: "invalidVersion", - format: { - installedVersion: version, - dotnetMajorVersion: `${minMajorVersion}`, - downloadUrl: "https://dotnet.microsoft.com/", - }, - target: NoTarget, - }); + sdkContext.__diagnostics.push( + createDiagnostic({ + code: "invalid-dotnet-sdk-dependency", + messageId: "invalidVersion", + format: { + installedVersion: version, + dotnetMajorVersion: `${minMajorVersion}`, + downloadUrl: "https://dotnet.microsoft.com/", + }, + target: NoTarget, + }), + ); return false; } return true; } else { - sdkContext.logger.error("Cannot get the installed .NET SDK version."); + sdkContext.__diagnostics.push( + createDiagnostic({ + code: "general-error", + format: { message: "Cannot get the installed .NET SDK version." }, + target: NoTarget, + }), + ); return false; } } diff --git a/packages/http-client-csharp/emitter/src/lib/client-converter.ts b/packages/http-client-csharp/emitter/src/lib/client-converter.ts index ff75e604ce2..893f7170c6e 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-converter.ts @@ -11,6 +11,7 @@ import { } from "@azure-tools/typespec-client-generator-core"; import { NoTarget } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; +import { createDiagnostic } from "./lib.js"; import { InputParameterScope } from "../type/input-parameter-scope.js"; import { InputClient, @@ -136,11 +137,13 @@ function fromSdkClient( .replace("http://", "") .split("/")[0]; if (!/^\{\w+\}$/.test(endpointExpr)) { - sdkContext.logger.reportDiagnostic({ - code: "unsupported-endpoint-url", - format: { endpoint: type.serverUrl }, - target: NoTarget, - }); + sdkContext.__diagnostics.push( + createDiagnostic({ + code: "unsupported-endpoint-url", + format: { endpoint: type.serverUrl }, + target: NoTarget, + }), + ); return []; } const endpointVariableName = endpointExpr.substring(1, endpointExpr.length - 1); diff --git a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts index 1462b7e9953..36834af643e 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts @@ -74,7 +74,7 @@ export function createModel(sdkContext: CSharpEmitterContext): [CodeModel, reado auth: processServiceAuthentication(sdkContext, sdkPackage), }; - return [clientModel, sdkContext.logger.getDiagnostics()]; + return [clientModel, sdkContext.__diagnostics]; } /** diff --git a/packages/http-client-csharp/emitter/src/lib/logger.ts b/packages/http-client-csharp/emitter/src/lib/logger.ts index f99f726c061..f9461e37d83 100644 --- a/packages/http-client-csharp/emitter/src/lib/logger.ts +++ b/packages/http-client-csharp/emitter/src/lib/logger.ts @@ -1,13 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -import { Diagnostic, DiagnosticReport, NoTarget, Program, Tracer } from "@typespec/compiler"; -import { - createDiagnostic, - DiagnosticMessagesMap, - getTracer, - reportDiagnostic as libReportDiagnostic, -} from "./lib.js"; +import { Diagnostic, Program, Tracer } from "@typespec/compiler"; +import { getTracer } from "./lib.js"; import { LoggerLevel } from "./logger-level.js"; /** @@ -18,22 +13,21 @@ export class Logger { private tracer: Tracer; private level: LoggerLevel; private program: Program; - private collectedDiagnostics: Diagnostic[] | undefined; public constructor(program: Program, level: LoggerLevel, collectDiagnostics: boolean = false) { this.tracer = getTracer(program); this.level = level; this.program = program; - this.collectedDiagnostics = collectDiagnostics ? [] : undefined; } /** * Get collected diagnostics. Only available if the logger was created with collectDiagnostics=true. * @returns The collected diagnostics. * @beta + * @deprecated This method is deprecated and will be removed. Use sdkContext.__diagnostics instead. */ public getDiagnostics(): readonly Diagnostic[] { - return this.collectedDiagnostics ?? []; + return []; } trace(level: LoggerLevel, message: string): void { @@ -71,32 +65,4 @@ export class Logger { this.tracer.trace(LoggerLevel.VERBOSE, message); } } - - reportDiagnostic( - diag: DiagnosticReport, - ): void { - if (this.collectedDiagnostics) { - // In collecting mode, store the diagnostic instead of reporting it - this.collectedDiagnostics.push(createDiagnostic(diag)); - } else { - // In normal mode, report the diagnostic directly - libReportDiagnostic(this.program, diag); - } - } - - warn(message: string): void { - this.reportDiagnostic({ - code: "general-warning", - format: { message: message }, - target: NoTarget, - }); - } - - error(message: string): void { - this.reportDiagnostic({ - code: "general-error", - format: { message: message }, - target: NoTarget, - }); - } } diff --git a/packages/http-client-csharp/emitter/src/lib/operation-converter.ts b/packages/http-client-csharp/emitter/src/lib/operation-converter.ts index 566bdb7ba64..8f0cc7e9f6a 100644 --- a/packages/http-client-csharp/emitter/src/lib/operation-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/operation-converter.ts @@ -29,6 +29,7 @@ import { getDeprecated, isErrorModel, NoTarget } from "@typespec/compiler"; import { HttpStatusCodeRange } from "@typespec/http"; import { getResourceOperation } from "@typespec/rest"; import { CSharpEmitterContext } from "../sdk-context.js"; +import { createDiagnostic } from "./lib.js"; import { collectionFormatToDelimMap } from "../type/collection-format.js"; import { HttpResponseHeader } from "../type/http-response-header.js"; import { InputConstant } from "../type/input-constant.js"; @@ -135,11 +136,13 @@ export function fromSdkServiceMethod( method = lroPagingMethod; break; default: - sdkContext.logger.reportDiagnostic({ - code: "unsupported-service-method", - format: { methodKind: methodKind }, - target: NoTarget, - }); + sdkContext.__diagnostics.push( + createDiagnostic({ + code: "unsupported-service-method", + format: { methodKind: methodKind }, + target: NoTarget, + }), + ); method = undefined; break; } @@ -164,13 +167,15 @@ export function fromSdkServiceMethodOperation( let generateConvenience = shouldGenerateConvenient(sdkContext, method.operation.__raw.operation); if (method.operation.verb === "patch" && generateConvenience) { - sdkContext.logger.reportDiagnostic({ - code: "unsupported-patch-convenience-method", - format: { - methodCrossLanguageDefinitionId: method.crossLanguageDefinitionId, - }, - target: method.__raw ?? NoTarget, - }); + sdkContext.__diagnostics.push( + createDiagnostic({ + code: "unsupported-patch-convenience-method", + format: { + methodCrossLanguageDefinitionId: method.crossLanguageDefinitionId, + }, + target: method.__raw ?? NoTarget, + }), + ); generateConvenience = false; } @@ -268,11 +273,13 @@ function getValueType(sdkContext: CSharpEmitterContext, value: any): SdkBuiltInK case "bigint": return "int64"; default: - sdkContext.logger.reportDiagnostic({ - code: "unsupported-default-value-type", - format: { valueType: typeof value }, - target: NoTarget, - }); + sdkContext.__diagnostics.push( + createDiagnostic({ + code: "unsupported-default-value-type", + format: { valueType: typeof value }, + target: NoTarget, + }), + ); return "unknown"; } } @@ -348,11 +355,13 @@ function fromSdkOperationParameters( const parameters: InputHttpParameter[] = []; for (const p of operation.parameters) { if (p.kind === "cookie") { - sdkContext.logger.reportDiagnostic({ - code: "unsupported-cookie-parameter", - format: { parameterName: p.name, path: operation.path }, - target: NoTarget, - }); + sdkContext.__diagnostics.push( + createDiagnostic({ + code: "unsupported-cookie-parameter", + format: { parameterName: p.name, path: operation.path }, + target: NoTarget, + }), + ); return parameters; } const param = fromParameter(sdkContext, p, rootApiVersions); @@ -395,11 +404,13 @@ export function fromParameter( parameter = fromBodyParameter(sdkContext, p, rootApiVersions); break; default: - sdkContext.logger.reportDiagnostic({ - code: "unsupported-parameter-kind", - format: { parameterKind }, - target: p.__raw ?? NoTarget, - }); + sdkContext.__diagnostics.push( + createDiagnostic({ + code: "unsupported-parameter-kind", + format: { parameterKind }, + target: p.__raw ?? NoTarget, + }), + ); parameter = undefined; break; } @@ -794,13 +805,15 @@ function getResponseLocation( } if (isHttpMetadata(context, p)) { - context.logger.reportDiagnostic({ - code: "unsupported-continuation-location", - format: { - crossLanguageDefinitionId: method.crossLanguageDefinitionId, - }, - target: NoTarget, - }); + context.__diagnostics.push( + createDiagnostic({ + code: "unsupported-continuation-location", + format: { + crossLanguageDefinitionId: method.crossLanguageDefinitionId, + }, + target: NoTarget, + }), + ); return ResponseLocation.None; } diff --git a/packages/http-client-csharp/emitter/src/lib/service-authentication.ts b/packages/http-client-csharp/emitter/src/lib/service-authentication.ts index 728cd30c5f2..3ac777c6587 100644 --- a/packages/http-client-csharp/emitter/src/lib/service-authentication.ts +++ b/packages/http-client-csharp/emitter/src/lib/service-authentication.ts @@ -10,6 +10,7 @@ import { import { NoTarget } from "@typespec/compiler"; import { Oauth2Auth, OAuth2Flow } from "@typespec/http"; import { CSharpEmitterContext } from "../sdk-context.js"; +import { createDiagnostic } from "./lib.js"; import { InputAuth } from "../type/input-auth.js"; import { InputOAuth2Flow } from "../type/input-oauth2-auth.js"; @@ -36,11 +37,13 @@ export function processServiceAuthentication( if (authClientParameter.type.kind === "credential") { const auth = processAuthType(sdkContext, authClientParameter.type); if (!auth && authClientParameter.type.scheme.type !== "noAuth") { - sdkContext.logger.reportDiagnostic({ - code: "unsupported-auth", - messageId: "onlyUnsupportedAuthProvided", - target: authClientParameter.type.__raw ?? NoTarget, - }); + sdkContext.__diagnostics.push( + createDiagnostic({ + code: "unsupported-auth", + messageId: "onlyUnsupportedAuthProvided", + target: authClientParameter.type.__raw ?? NoTarget, + }), + ); return inputAuth; } @@ -64,11 +67,13 @@ export function processServiceAuthentication( } if (!inputAuth?.apiKey && !inputAuth?.oAuth2) { - sdkContext.logger.reportDiagnostic({ - code: "unsupported-auth", - messageId: "onlyUnsupportedAuthProvided", - target: authClientParameter.type.__raw ?? NoTarget, - }); + sdkContext.__diagnostics.push( + createDiagnostic({ + code: "unsupported-auth", + messageId: "onlyUnsupportedAuthProvided", + target: authClientParameter.type.__raw ?? NoTarget, + }), + ); } return inputAuth; @@ -82,13 +87,15 @@ function processAuthType( switch (scheme.type) { case "apiKey": if (scheme.in !== "header") { - sdkContext.logger.reportDiagnostic({ - code: "unsupported-auth", - format: { - message: `Only header is supported for ApiKey authentication. ${scheme.in} is not supported.`, - }, - target: credentialType.__raw ?? NoTarget, - }); + sdkContext.__diagnostics.push( + createDiagnostic({ + code: "unsupported-auth", + format: { + message: `Only header is supported for ApiKey authentication. ${scheme.in} is not supported.`, + }, + target: credentialType.__raw ?? NoTarget, + }), + ); return undefined; } return { apiKey: { name: scheme.name, in: scheme.in } } as InputAuth; @@ -98,11 +105,13 @@ function processAuthType( const schemeOrApiKeyPrefix = scheme.scheme; switch (schemeOrApiKeyPrefix) { case "Basic": - sdkContext.logger.reportDiagnostic({ - code: "unsupported-auth", - format: { message: `${schemeOrApiKeyPrefix} auth method is currently not supported.` }, - target: credentialType.__raw ?? NoTarget, - }); + sdkContext.__diagnostics.push( + createDiagnostic({ + code: "unsupported-auth", + format: { message: `${schemeOrApiKeyPrefix} auth method is currently not supported.` }, + target: credentialType.__raw ?? NoTarget, + }), + ); return undefined; case "Bearer": return { @@ -123,11 +132,13 @@ function processAuthType( } } default: - sdkContext.logger.reportDiagnostic({ - code: "unsupported-auth", - format: { message: `un-supported authentication scheme ${scheme.type}` }, - target: credentialType.__raw ?? NoTarget, - }); + sdkContext.__diagnostics.push( + createDiagnostic({ + code: "unsupported-auth", + format: { message: `un-supported authentication scheme ${scheme.type}` }, + target: credentialType.__raw ?? NoTarget, + }), + ); return undefined; } } diff --git a/packages/http-client-csharp/emitter/src/lib/type-converter.ts b/packages/http-client-csharp/emitter/src/lib/type-converter.ts index f6b31db5578..d9eca44ca99 100644 --- a/packages/http-client-csharp/emitter/src/lib/type-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/type-converter.ts @@ -21,6 +21,7 @@ import { isHttpMetadata, } from "@azure-tools/typespec-client-generator-core"; import { Model, NoTarget } from "@typespec/compiler"; +import { createDiagnostic } from "./lib.js"; import { CSharpEmitterContext } from "../sdk-context.js"; import { InputArrayType, @@ -130,11 +131,13 @@ export function fromSdkType( retVar = fromSdkDurationType(sdkContext, sdkType); break; case "tuple": - sdkContext.logger.reportDiagnostic({ - code: "unsupported-sdk-type", - format: { sdkType: "tuple" }, - target: sdkType.__raw ?? NoTarget, - }); + sdkContext.__diagnostics.push( + createDiagnostic({ + code: "unsupported-sdk-type", + format: { sdkType: "tuple" }, + target: sdkType.__raw ?? NoTarget, + }), + ); const tupleType: InputPrimitiveType = { kind: "unknown", name: "tuple", @@ -150,11 +153,13 @@ export function fromSdkType( retVar = fromSdkEndpointType(); break; case "credential": - sdkContext.logger.reportDiagnostic({ - code: "unsupported-sdk-type", - format: { sdkType: "credential" }, - target: sdkType.__raw ?? NoTarget, - }); + sdkContext.__diagnostics.push( + createDiagnostic({ + code: "unsupported-sdk-type", + format: { sdkType: "credential" }, + target: sdkType.__raw ?? NoTarget, + }), + ); const credentialType: InputPrimitiveType = { kind: "unknown", name: "credential", diff --git a/packages/http-client-csharp/emitter/src/sdk-context.ts b/packages/http-client-csharp/emitter/src/sdk-context.ts index 1c8d3b03162..cdaae318ee2 100644 --- a/packages/http-client-csharp/emitter/src/sdk-context.ts +++ b/packages/http-client-csharp/emitter/src/sdk-context.ts @@ -14,7 +14,7 @@ import { SdkServiceMethod, SdkType, } from "@azure-tools/typespec-client-generator-core"; -import { Type } from "@typespec/compiler"; +import { Diagnostic, Type } from "@typespec/compiler"; import { Logger } from "./lib/logger.js"; import { CSharpEmitterOptions } from "./options.js"; import { InputOperation } from "./type/input-operation.js"; @@ -37,6 +37,7 @@ import { OperationResponse } from "./type/operation-response.js"; export interface CSharpEmitterContext extends SdkContext { logger: Logger; __typeCache: SdkTypeCache; + __diagnostics: Diagnostic[]; } /** @@ -53,6 +54,7 @@ export function createCSharpEmitterContext< ...context, logger, __typeCache: new SdkTypeCache(), + __diagnostics: [], }; } diff --git a/packages/http-client-csharp/emitter/test/Unit/auth.test.ts b/packages/http-client-csharp/emitter/test/Unit/auth.test.ts index 4aff753d192..f910fb1798e 100644 --- a/packages/http-client-csharp/emitter/test/Unit/auth.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/auth.test.ts @@ -30,7 +30,8 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const diagnostics = context.program.diagnostics; const noAuthDiagnostics = diagnostics.filter( @@ -72,7 +73,8 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const diagnostics = context.program.diagnostics; const noAuthDiagnostics = diagnostics.filter( @@ -114,7 +116,8 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const diagnostics = context.program.diagnostics; const noAuthDiagnostics = diagnostics.filter( @@ -154,7 +157,8 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const diagnostics = context.program.diagnostics; const noAuthDiagnostic = diagnostics.find( @@ -182,7 +186,8 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const diagnostics = context.program.diagnostics; const noAuthDiagnostics = diagnostics.filter( @@ -216,7 +221,8 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const diagnostics = context.program.diagnostics; const noAuthDiagnostics = diagnostics.filter( @@ -249,7 +255,8 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const diagnostics = context.program.diagnostics; // Should have no auth-related diagnostics @@ -290,7 +297,8 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -322,7 +330,8 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -352,7 +361,8 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -389,7 +399,8 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 2); @@ -426,7 +437,8 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -454,7 +466,8 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -482,7 +495,8 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); // Should have both OAuth2 and API key auth ok(root.auth?.oAuth2); @@ -513,7 +527,8 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); diff --git a/packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts index 3e2546f9626..6b78d8ee905 100644 --- a/packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts @@ -28,7 +28,8 @@ describe("isMultiServiceClient", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const client = root.clients[0]; ok(client, "Client should exist"); @@ -81,7 +82,8 @@ describe("isMultiServiceClient", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); strictEqual(root.name, "Service.MultiService", "Root namespace should be Service.MultiService"); const client = root.clients[0]; @@ -147,7 +149,8 @@ describe("isMultiServiceClient", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); strictEqual(root.name, "Service.MultiService", "Root namespace should be Service.MultiService"); const client = root.clients[0]; @@ -219,7 +222,8 @@ describe("isMultiServiceClient", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); strictEqual(root.name, "Service.MultiService", "Root namespace should be Service.MultiService"); const clients = root.clients; diff --git a/packages/http-client-csharp/emitter/test/Unit/client-initialization.test.ts b/packages/http-client-csharp/emitter/test/Unit/client-initialization.test.ts index d9b508b9a16..8d6aedeaa96 100644 --- a/packages/http-client-csharp/emitter/test/Unit/client-initialization.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/client-initialization.test.ts @@ -34,7 +34,8 @@ describe("ClientInitialization", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const client = root.clients[0]; ok(client, "Client should exist"); @@ -59,7 +60,8 @@ describe("ClientInitialization", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const client = root.clients[0]; // initializedBy field should exist on the client (may be undefined or have a value) @@ -84,7 +86,8 @@ describe("ClientInitialization", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const client = root.clients[0]; ok(client.parameters, "Client should have parameters"); @@ -113,7 +116,8 @@ describe("ClientInitialization", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const client = root.clients[0]; ok("initializedBy" in client, "Parent client should have initializedBy field"); diff --git a/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts b/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts index b3cdb30e8ae..d36ca4ffe3e 100644 --- a/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts @@ -58,7 +58,8 @@ describe("fixNamingConflicts", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); // Find the real enum const realEnum = root.enums.find( @@ -142,7 +143,8 @@ describe("fixNamingConflicts", () => { namespace: targetNamespace, } as any); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); // Get all ErrorResponse models - fixNamingConflicts should have resolved the conflicts const errorModels = root.models.filter( @@ -194,7 +196,8 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); // The root apiVersions should include the version from the Versions enum // which is defined in the default namespace with version "2023-01-01-preview" @@ -228,7 +231,8 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); // The root apiVersions should include all versions from the TestVersions enum strictEqual(root.apiVersions.length, 3, "Root apiVersions should have 3 versions"); @@ -247,7 +251,8 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); // Single service client should have apiVersions from the @versioned decorator ok(root.apiVersions.length > 0, "Root apiVersions should not be empty for single service"); @@ -299,7 +304,8 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); ok(root.apiVersions.length === 0, "Root apiVersions should be empty for multiservice"); @@ -358,7 +364,8 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); ok(root.apiVersions.length === 0, "Root apiVersions should be empty for multiservice"); }); @@ -415,7 +422,8 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); ok( root.apiVersions.length === 0, diff --git a/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts b/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts index 3730f08dbdd..51617d0817c 100644 --- a/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts @@ -33,7 +33,8 @@ describe("Name for constant type", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel); const propertyType = testModel.properties[0].type; @@ -64,7 +65,8 @@ describe("Name for constant type", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const testModel1 = root.models.find((m) => m.name === "TestModel1"); ok(testModel1); const testModel2 = root.models.find((m) => m.name === "TestModel2"); @@ -103,7 +105,8 @@ describe("Constant enum conversion", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel); const propertyType = testModel.properties[0].type; @@ -134,7 +137,8 @@ describe("Constant enum conversion", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel); const propertyType = testModel.properties[0].type; diff --git a/packages/http-client-csharp/emitter/test/Unit/decorator-list.test.ts b/packages/http-client-csharp/emitter/test/Unit/decorator-list.test.ts index 53df8aa6e86..a1dd9175862 100644 --- a/packages/http-client-csharp/emitter/test/Unit/decorator-list.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/decorator-list.test.ts @@ -34,7 +34,8 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const clients = root.clients; strictEqual(clients.length, 1); ok(clients[0].children); @@ -66,7 +67,8 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const methods = root.clients[0].methods; strictEqual(methods.length, 1); const operation = methods[0].operation; @@ -97,7 +99,8 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = root.models; strictEqual(models.length, 1); deepStrictEqual(models[0].decorators, [ @@ -127,7 +130,8 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = root.models; strictEqual(models.length, 1); deepStrictEqual(models[0].properties[0].decorators, [ @@ -153,7 +157,8 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const methods = root.clients[0].methods; strictEqual(methods.length, 1); const operation = methods[0].operation; diff --git a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts index 7246e73cf2e..fde2a575e65 100644 --- a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts @@ -271,6 +271,8 @@ describe("Test _validateDotNetSdk", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); const result = await _validateDotNetSdk(sdkContext, minVersion); + // Report collected diagnostics to program + program.reportDiagnostics(sdkContext.__diagnostics); expect(result).toBe(false); strictEqual(program.diagnostics.length, 1); strictEqual( @@ -329,6 +331,8 @@ describe("Test _validateDotNetSdk", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); const result = await _validateDotNetSdk(sdkContext, minVersion); + // Report collected diagnostics to program + program.reportDiagnostics(sdkContext.__diagnostics); expect(result).toBe(false); strictEqual(program.diagnostics.length, 1); strictEqual( diff --git a/packages/http-client-csharp/emitter/test/Unit/encode.test.ts b/packages/http-client-csharp/emitter/test/Unit/encode.test.ts index 6344bd3f4ca..767fa13177f 100644 --- a/packages/http-client-csharp/emitter/test/Unit/encode.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/encode.test.ts @@ -32,7 +32,8 @@ describe("Test encode duration", () => { // validate method parameter const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const methodParamArray = root.clients[0].methods[0].parameters; strictEqual(1, methodParamArray.length); let type = methodParamArray[0].type; @@ -74,7 +75,8 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); // validate method parameter const methodParamArray = root.clients[0].methods[0].parameters; strictEqual(1, methodParamArray.length); @@ -117,7 +119,8 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); // validate method parameter const methodParamArray = root.clients[0].methods[0].parameters; strictEqual(1, methodParamArray.length); @@ -161,7 +164,8 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [codeModel] = createModel(sdkContext); + const [codeModel, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = codeModel.models; const durationModel = models.find((m) => m.name === "ISO8601DurationProperty"); ok(durationModel); @@ -190,7 +194,8 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [codeModel] = createModel(sdkContext); + const [codeModel, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = codeModel.models; const durationModel = models.find((m) => m.name === "Int32SecondsDurationProperty"); ok(durationModel); @@ -219,7 +224,8 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [codeModel] = createModel(sdkContext); + const [codeModel, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = codeModel.models; const durationModel = models.find((m) => m.name === "FloatSecondsDurationProperty"); ok(durationModel); diff --git a/packages/http-client-csharp/emitter/test/Unit/input-parameter.test.ts b/packages/http-client-csharp/emitter/test/Unit/input-parameter.test.ts index 1c0977497ef..a4e55ebad61 100644 --- a/packages/http-client-csharp/emitter/test/Unit/input-parameter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/input-parameter.test.ts @@ -42,7 +42,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -72,7 +73,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -102,7 +104,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -136,7 +139,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -166,7 +170,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -196,7 +201,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -228,7 +234,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -258,7 +265,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -288,7 +296,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -320,7 +329,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -350,7 +360,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -380,7 +391,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -414,7 +426,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -445,7 +458,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -476,7 +490,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -509,7 +524,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -540,7 +556,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -571,7 +588,8 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -609,8 +627,9 @@ describe("Test Cookie Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); + const [, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const diagnostics = context.program.diagnostics; - createModel(sdkContext); const unsupportedCookie = diagnostics.find( (d) => d.code === "@typespec/http-client-csharp/unsupported-cookie-parameter", @@ -653,8 +672,9 @@ describe("Endpoint parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); + const [, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const diagnostics = context.program.diagnostics; - createModel(sdkContext); const unsupportedCookie = diagnostics.find( (d) => d.code === "@typespec/http-client-csharp/unsupported-endpoint-url", @@ -686,7 +706,8 @@ describe("Endpoint parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [codeModel] = createModel(sdkContext); + const [codeModel, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const client = codeModel.clients[0]; ok(client); ok(client.parameters); @@ -722,7 +743,8 @@ describe("Endpoint parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [codeModel] = createModel(sdkContext); + const [codeModel, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const client = codeModel.clients[0]; ok(client); ok(client.parameters); @@ -759,7 +781,8 @@ describe("Test Spread Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); ok(root); // validate service method @@ -802,7 +825,8 @@ describe("Test Spread Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); ok(root); // validate service method @@ -855,7 +879,8 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const operation = root.clients[0].methods[0].operation; const queryParam = operation.parameters.find((p) => p.name === "queryParam"); @@ -881,7 +906,8 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const operation = root.clients[0].methods[0].operation; const pathParam = operation.parameters.find((p) => p.name === "pathParam"); @@ -910,7 +936,8 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const operation = root.clients[0].methods[0].operation; const headerParam = operation.parameters.find((p) => p.name === "headerParam"); @@ -940,7 +967,8 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const operation = root.clients[0].methods[0].operation; const contentTypeParam = operation.parameters.find((p) => p.name === "contentType"); @@ -970,7 +998,8 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const operation = root.clients[0].methods[0].operation; const bodyParam = operation.parameters.find((p) => p.name === "bodyParam"); diff --git a/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts b/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts index 556cda60305..fdab7a03cdc 100644 --- a/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts @@ -51,7 +51,8 @@ op test(@body input: Pet): Pet; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = root.models; const petModel = models.find((m) => m.name === "Pet"); const catModel = models.find((m) => m.name === "Cat"); @@ -135,7 +136,8 @@ op test(@body input: Pet): Pet; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [codeModel] = createModel(sdkContext); + const [codeModel, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = codeModel.models; const pet = models.find((m) => m.name === "Pet"); assert(pet !== undefined); @@ -229,7 +231,8 @@ op test(@body input: Pet): Pet; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [codeModel] = createModel(sdkContext); + const [codeModel, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = codeModel.models; const pet = models.find((m) => m.name === "Pet"); assert(pet !== undefined); @@ -350,7 +353,8 @@ op op5(@body body: ExtendsFooArray): ExtendsFooArray; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = root.models; const extendsUnknownModel = models.find((m) => m.name === "ExtendsUnknown"); const extendsStringModel = models.find((m) => m.name === "ExtendsString"); @@ -442,7 +446,8 @@ op op5(@body body: IsFooArray): IsFooArray; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = root.models; const isUnknownModel = models.find((m) => m.name === "IsUnknown"); const isStringModel = models.find((m) => m.name === "IsString"); @@ -493,7 +498,8 @@ op op1(): void; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = root.models; const isEmptyModel = models.find((m) => m.name === "Empty"); ok(isEmptyModel); @@ -522,7 +528,8 @@ model Foo { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = root.models; const model = models.find((m) => m.name === "Foo"); ok(model); @@ -560,7 +567,8 @@ describe("Anonymous models should be included in library", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); ok(root); // validate service method @@ -602,7 +610,8 @@ op testOperation(@bodyRoot body: HeaderModel): void; const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = root.models; const isEmptyModel = models.find((m) => m.name === "HeaderModel"); ok(isEmptyModel); @@ -647,7 +656,8 @@ op testOperation(@bodyRoot body: HeaderModel): void; const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = root.models; const isEmptyModel = models.find((m) => m.name === "HeaderModel"); ok(isEmptyModel); @@ -680,7 +690,8 @@ op testOperation(@bodyRoot body: HeaderModel): void; const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = root.models; const isEmptyModel = models.find((m) => m.name === "HeaderModel"); ok(isEmptyModel); @@ -707,7 +718,8 @@ op testOperation(@bodyRoot body: HeaderModel): void; const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = root.models; const isEmptyModel = models.find((m) => m.name === "HeaderModel"); ok(isEmptyModel); @@ -757,7 +769,8 @@ describe("typespec-client-generator-core: general decorators list", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = root.models; strictEqual(models.length, 1); deepStrictEqual(models[0].decorators, [ @@ -799,7 +812,8 @@ describe("Access decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const enums = root.enums; const colorEnum = enums.find((e) => e.name === "Color"); @@ -830,7 +844,8 @@ describe("Access decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const enums = root.enums; const colorEnum = enums.find((e) => e.name === "Color"); @@ -860,7 +875,8 @@ describe("Access decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const enums = root.enums; const statusEnum = enums.find((e) => e.name === "Status"); @@ -898,7 +914,8 @@ describe("Usage decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const enums = root.enums; const colorEnum = enums.find((e) => e.name === "Color"); @@ -928,7 +945,8 @@ describe("Usage decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const enums = root.enums; const colorEnum = enums.find((e) => e.name === "Color"); diff --git a/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts index 08ac1f48adf..899cb96199b 100644 --- a/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts @@ -35,7 +35,8 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -106,7 +107,8 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -176,7 +178,8 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -231,7 +234,8 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -275,7 +279,8 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -314,7 +319,8 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -346,7 +352,8 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); diff --git a/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts b/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts index 37290c74147..7bcf7dc40d1 100644 --- a/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts @@ -39,7 +39,8 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -93,7 +94,8 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -138,7 +140,8 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -174,7 +177,8 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -208,7 +212,8 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -256,7 +261,8 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -291,7 +297,8 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -327,7 +334,8 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -363,7 +371,8 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -399,7 +408,8 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -453,7 +463,8 @@ describe("PageSize parameter operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -491,7 +502,8 @@ describe("PageSize parameter operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); diff --git a/packages/http-client-csharp/emitter/test/Unit/property-type.test.ts b/packages/http-client-csharp/emitter/test/Unit/property-type.test.ts index e3589538bd3..dca2060117f 100644 --- a/packages/http-client-csharp/emitter/test/Unit/property-type.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/property-type.test.ts @@ -28,7 +28,8 @@ describe("Test GetInputType for array", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "input", ); @@ -49,7 +50,8 @@ describe("Test GetInputType for array", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const bodyType = root.clients[0].methods[0].operation.responses[0].bodyType; strictEqual(bodyType?.kind, "array"); strictEqual(bodyType.crossLanguageDefinitionId, "TypeSpec.Array"); @@ -86,7 +88,8 @@ describe("Test GetInputType for enum", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "input", ); @@ -130,7 +133,8 @@ describe("Test GetInputType for enum", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "input", ); @@ -169,7 +173,8 @@ describe("Test GetInputType for enum", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "input", ); diff --git a/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts b/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts index cea35899816..0391012f63e 100644 --- a/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts @@ -28,7 +28,8 @@ describe("Test GetInputType for scalar", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "location", ); diff --git a/packages/http-client-csharp/emitter/test/Unit/string-format.test.ts b/packages/http-client-csharp/emitter/test/Unit/string-format.test.ts index 3eea60ca0d1..62e9d943a60 100644 --- a/packages/http-client-csharp/emitter/test/Unit/string-format.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/string-format.test.ts @@ -27,7 +27,8 @@ describe("Test string format", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "sourceUrl", ); @@ -51,7 +52,8 @@ describe("Test string format", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [codeModel] = createModel(sdkContext); + const [codeModel, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const models = codeModel.models; const foo = models.find((m) => m.name === "Foo"); ok(foo); diff --git a/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts index b6fb88bbdf9..131994a0b99 100644 --- a/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts @@ -69,7 +69,8 @@ describe("Enum value references", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const enumType = root.enums.find((e) => e.name === "TestEnum"); ok(enumType, "TestEnum should exist in the enums list"); strictEqual(enumType.values.length, 3, "TestEnum should have 3 values"); @@ -119,7 +120,8 @@ describe("External types", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel, "TestModel should exist"); @@ -164,7 +166,8 @@ describe("External types", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel, "TestModel should exist"); diff --git a/packages/http-client-csharp/emitter/test/Unit/usage.test.ts b/packages/http-client-csharp/emitter/test/Unit/usage.test.ts index ee0923b0174..77df29eb1cc 100644 --- a/packages/http-client-csharp/emitter/test/Unit/usage.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/usage.test.ts @@ -33,7 +33,8 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -54,7 +55,8 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -75,7 +77,8 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -97,7 +100,8 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -124,7 +128,8 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const fooModel = root.models.find((model) => model.name === "Foo"); const templateModel = root.models.find((model) => model.name === "TemplateModelFoo"); @@ -154,7 +159,8 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const baseModel = root.models.find((model) => model.name === "BaseModel"); const fooModel = root.models.find((model) => model.name === "Foo"); @@ -191,7 +197,8 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const baseModel = root.models.find((model) => model.name === "BaseModel"); const fooModel = root.models.find((model) => model.name === "Foo"); const propertyModel = root.models.find((model) => model.name === "PropertyModel"); @@ -218,7 +225,8 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const fooAlias = root.models.find((model) => model.name === "TestRequest"); ok(fooAlias); @@ -266,7 +274,8 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const fooInfo = root.models.find((model) => model.name === "FooInfo"); const batchCreateFooListItemsRequest = root.models.find( (model) => model.name === "BatchCreateFooListItemsRequest", @@ -312,7 +321,8 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -371,7 +381,8 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const baseModel = root.models.find((model) => model.name === "BaseModelWithDiscriminator"); const derivedModel = root.models.find( (model) => model.name === "DerivedModelWithDiscriminatorA", @@ -441,7 +452,8 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const baseModel = root.models.find((model) => model.name === "BaseModelWithDiscriminator"); const derivedModel = root.models.find( (model) => model.name === "DerivedModelWithDiscriminatorA", @@ -478,7 +490,8 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const simpleEnumRenamed = root.enums.find((enumType) => enumType.name === "SimpleEnumRenamed"); ok(simpleEnumRenamed); @@ -502,7 +515,8 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const renamedModel = root.models.find((model) => model.name === "RenamedModel"); ok(renamedModel); @@ -660,7 +674,8 @@ interface LegacyLro { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root] = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); + context.program.reportDiagnostics(modelDiagnostics); const radiologyInsightsInferenceResult = root.models.find( (model) => model.name === "RadiologyInsightsInferenceResult", ); From 4ba9cd266077d960a528f8f5035963f22fdb1c42 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 09:07:17 +0000 Subject: [PATCH 07/22] Use createDiagnosticCollector following TypeSpec best practices - Replaced __diagnostics array with DiagnosticCollector from TypeSpec - Updated createModel to use diagnostics.wrap() for returning tuple - Functions now access diagnostics via sdkContext.__diagnostics.add() - Updated tests to set up diagnostic collector when needed - Follows the pattern from TCGC: createDiagnosticCollector at top level Co-authored-by: ArcturusZhang <10554446+ArcturusZhang@users.noreply.github.com> --- .../emitter/src/code-model-writer.ts | 3 ++- .../http-client-csharp/emitter/src/emitter.ts | 12 ++++++++---- .../emitter/src/lib/client-converter.ts | 3 ++- .../emitter/src/lib/client-model-builder.ts | 10 ++++++---- .../emitter/src/lib/operation-converter.ts | 18 ++++++++++++------ .../emitter/src/lib/service-authentication.ts | 18 ++++++++++-------- .../emitter/src/lib/type-converter.ts | 6 ++++-- .../emitter/src/sdk-context.ts | 5 ++--- .../emitter/test/Unit/emitter.test.ts | 10 +++++++--- 9 files changed, 53 insertions(+), 32 deletions(-) diff --git a/packages/http-client-csharp/emitter/src/code-model-writer.ts b/packages/http-client-csharp/emitter/src/code-model-writer.ts index 0810ce0ba74..da6f09f94a4 100644 --- a/packages/http-client-csharp/emitter/src/code-model-writer.ts +++ b/packages/http-client-csharp/emitter/src/code-model-writer.ts @@ -33,6 +33,7 @@ export async function writeCodeModel( * @param codeModel - The code model to build */ function buildJson(context: CSharpEmitterContext, codeModel: CodeModel): any { + const diagnostics = context.__diagnostics!; const objectsIds = new Map(); const stack: any[] = []; @@ -74,7 +75,7 @@ function buildJson(context: CSharpEmitterContext, codeModel: CodeModel): any { function handleObject(obj: any, id: string | undefined, stack: any[]): any { if (stack.includes(obj)) { // we have a cyclical reference, we should not continue - context.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "general-warning", format: { message: `Cyclical reference detected in the code model (id: ${id}).` }, diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index 6bb32dbf53d..ece5efff4a9 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -107,7 +107,9 @@ export async function createCodeModel( const updateCodeModelFn = updateCodeModel ?? ((model: CodeModel) => model); const updatedRoot = updateCodeModelFn(root, sdkContext); // Collect any diagnostics added during the callback execution - diagnostics.push(...sdkContext.__diagnostics); + if (sdkContext.__diagnostics) { + diagnostics.push(...sdkContext.__diagnostics.diagnostics); + } const generatedFolder = resolvePath(outputFolder, "src", "Generated"); @@ -225,12 +227,13 @@ export async function _validateDotNetSdk( sdkContext: CSharpEmitterContext, minMajorVersion: number, ): Promise { + const diagnostics = sdkContext.__diagnostics!; try { const result = await execAsync("dotnet", ["--version"], { stdio: "pipe" }); return validateDotNetSdkVersionCore(sdkContext, result.stdout, minMajorVersion); } catch (error: any) { if (error && "code" in error && error["code"] === "ENOENT") { - sdkContext.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "invalid-dotnet-sdk-dependency", messageId: "missing", @@ -251,6 +254,7 @@ function validateDotNetSdkVersionCore( version: string, minMajorVersion: number, ): boolean { + const diagnostics = sdkContext.__diagnostics!; if (version) { const dotIndex = version.indexOf("."); const firstPart = dotIndex === -1 ? version : version.substring(0, dotIndex); @@ -260,7 +264,7 @@ function validateDotNetSdkVersionCore( return false; } if (major < minMajorVersion) { - sdkContext.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "invalid-dotnet-sdk-dependency", messageId: "invalidVersion", @@ -276,7 +280,7 @@ function validateDotNetSdkVersionCore( } return true; } else { - sdkContext.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "general-error", format: { message: "Cannot get the installed .NET SDK version." }, diff --git a/packages/http-client-csharp/emitter/src/lib/client-converter.ts b/packages/http-client-csharp/emitter/src/lib/client-converter.ts index 893f7170c6e..f32e76f5f0d 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-converter.ts @@ -131,13 +131,14 @@ function fromSdkClient( } function fromSdkEndpointType(type: SdkEndpointType): InputEndpointParameter[] { + const diagnostics = sdkContext.__diagnostics!; // TODO: support free-style endpoint url with multiple parameters const endpointExpr = type.serverUrl .replace("https://", "") .replace("http://", "") .split("/")[0]; if (!/^\{\w+\}$/.test(endpointExpr)) { - sdkContext.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "unsupported-endpoint-url", format: { endpoint: type.serverUrl }, diff --git a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts index 36834af643e..470b3e82a7d 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts @@ -7,7 +7,7 @@ import { SdkHttpOperation, UsageFlags, } from "@azure-tools/typespec-client-generator-core"; -import { Diagnostic } from "@typespec/compiler"; +import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { CodeModel } from "../type/code-model.js"; import { InputEnumType, InputLiteralType, InputModelType } from "../type/input-type.js"; @@ -28,9 +28,7 @@ import { * @example * ```typescript * import { createModel } from "@typespec/http-client-csharp"; - * import { Logger } from "@typespec/http-client-csharp/lib/logger"; * - * const logger = new Logger(program, LoggerLevel.INFO, true); // Enable diagnostic collection * const sdkContext = createCSharpEmitterContext(context, logger); * const [codeModel, diagnostics] = createModel(sdkContext); * // Process the code model and handle diagnostics @@ -41,6 +39,10 @@ import { * @beta */ export function createModel(sdkContext: CSharpEmitterContext): [CodeModel, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + // Attach diagnostics collector to context for helper functions to use + sdkContext.__diagnostics = diagnostics; + const sdkPackage = sdkContext.sdkPackage; // TO-DO: Consider exposing the namespace hierarchy in the code model https://github.com/microsoft/typespec/issues/8332 @@ -74,7 +76,7 @@ export function createModel(sdkContext: CSharpEmitterContext): [CodeModel, reado auth: processServiceAuthentication(sdkContext, sdkPackage), }; - return [clientModel, sdkContext.__diagnostics]; + return diagnostics.wrap(clientModel); } /** diff --git a/packages/http-client-csharp/emitter/src/lib/operation-converter.ts b/packages/http-client-csharp/emitter/src/lib/operation-converter.ts index 8f0cc7e9f6a..1dda74bae45 100644 --- a/packages/http-client-csharp/emitter/src/lib/operation-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/operation-converter.ts @@ -73,6 +73,7 @@ export function fromSdkServiceMethod( rootApiVersions: string[], namespace: string, ): InputServiceMethod | undefined { + const diagnostics = sdkContext.__diagnostics!; let method = sdkContext.__typeCache.methods.get(sdkMethod); if (method) { return method; @@ -136,7 +137,7 @@ export function fromSdkServiceMethod( method = lroPagingMethod; break; default: - sdkContext.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "unsupported-service-method", format: { methodKind: methodKind }, @@ -160,6 +161,7 @@ export function fromSdkServiceMethodOperation( uri: string, rootApiVersions: string[], ): InputOperation { + const diagnostics = sdkContext.__diagnostics!; let operation = sdkContext.__typeCache.operations.get(method.operation); if (operation) { return operation; @@ -167,7 +169,7 @@ export function fromSdkServiceMethodOperation( let generateConvenience = shouldGenerateConvenient(sdkContext, method.operation.__raw.operation); if (method.operation.verb === "patch" && generateConvenience) { - sdkContext.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "unsupported-patch-convenience-method", format: { @@ -263,6 +265,7 @@ function createServiceMethod( } function getValueType(sdkContext: CSharpEmitterContext, value: any): SdkBuiltInKinds { + const diagnostics = sdkContext.__diagnostics!; switch (typeof value) { case "string": return "string"; @@ -273,7 +276,7 @@ function getValueType(sdkContext: CSharpEmitterContext, value: any): SdkBuiltInK case "bigint": return "int64"; default: - sdkContext.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "unsupported-default-value-type", format: { valueType: typeof value }, @@ -352,10 +355,11 @@ function fromSdkOperationParameters( operation: SdkHttpOperation, rootApiVersions: string[], ): InputHttpParameter[] { + const diagnostics = sdkContext.__diagnostics!; const parameters: InputHttpParameter[] = []; for (const p of operation.parameters) { if (p.kind === "cookie") { - sdkContext.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "unsupported-cookie-parameter", format: { parameterName: p.name, path: operation.path }, @@ -384,6 +388,7 @@ export function fromParameter( p: SdkHttpParameter | SdkModelPropertyType, rootApiVersions: string[], ): InputHttpParameter | undefined { + const diagnostics = sdkContext.__diagnostics!; let parameter = sdkContext.__typeCache.operationParameters.get(p); if (parameter) { return parameter; @@ -404,7 +409,7 @@ export function fromParameter( parameter = fromBodyParameter(sdkContext, p, rootApiVersions); break; default: - sdkContext.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "unsupported-parameter-kind", format: { parameterKind }, @@ -800,12 +805,13 @@ function getResponseLocation( method: SdkPagingServiceMethod | SdkLroPagingServiceMethod, p: SdkServiceResponseHeader | SdkModelPropertyType, ): ResponseLocation { + const diagnostics = context.__diagnostics!; if (p.kind === "responseheader") { return ResponseLocation.Header; } if (isHttpMetadata(context, p)) { - context.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "unsupported-continuation-location", format: { diff --git a/packages/http-client-csharp/emitter/src/lib/service-authentication.ts b/packages/http-client-csharp/emitter/src/lib/service-authentication.ts index 3ac777c6587..e5d50fbff71 100644 --- a/packages/http-client-csharp/emitter/src/lib/service-authentication.ts +++ b/packages/http-client-csharp/emitter/src/lib/service-authentication.ts @@ -7,7 +7,7 @@ import { SdkHttpOperation, SdkPackage, } from "@azure-tools/typespec-client-generator-core"; -import { NoTarget } from "@typespec/compiler"; +import { createDiagnosticCollector, DiagnosticCollector, NoTarget } from "@typespec/compiler"; import { Oauth2Auth, OAuth2Flow } from "@typespec/http"; import { CSharpEmitterContext } from "../sdk-context.js"; import { createDiagnostic } from "./lib.js"; @@ -18,6 +18,7 @@ export function processServiceAuthentication( sdkContext: CSharpEmitterContext, sdkPackage: SdkPackage, ): InputAuth | undefined { + const diagnostics = sdkContext.__diagnostics!; let authClientParameter: SdkCredentialParameter | undefined = undefined; for (const client of sdkPackage.clients) { for (const parameter of client.clientInitialization.parameters) { @@ -35,9 +36,9 @@ export function processServiceAuthentication( const inputAuth: InputAuth = {}; if (authClientParameter.type.kind === "credential") { - const auth = processAuthType(sdkContext, authClientParameter.type); + const auth = processAuthType(sdkContext, authClientParameter.type, diagnostics); if (!auth && authClientParameter.type.scheme.type !== "noAuth") { - sdkContext.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "unsupported-auth", messageId: "onlyUnsupportedAuthProvided", @@ -53,7 +54,7 @@ export function processServiceAuthentication( let containsNoAuth = false; for (const authType of authClientParameter.type.variantTypes) { containsNoAuth = containsNoAuth || authType.scheme.type === "noAuth"; - const auth = processAuthType(sdkContext, authType); + const auth = processAuthType(sdkContext, authType, diagnostics); if (auth?.apiKey) { inputAuth.apiKey = auth.apiKey; } @@ -67,7 +68,7 @@ export function processServiceAuthentication( } if (!inputAuth?.apiKey && !inputAuth?.oAuth2) { - sdkContext.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "unsupported-auth", messageId: "onlyUnsupportedAuthProvided", @@ -82,12 +83,13 @@ export function processServiceAuthentication( function processAuthType( sdkContext: CSharpEmitterContext, credentialType: SdkCredentialType, + diagnostics: DiagnosticCollector, ): InputAuth | undefined { const scheme = credentialType.scheme; switch (scheme.type) { case "apiKey": if (scheme.in !== "header") { - sdkContext.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "unsupported-auth", format: { @@ -105,7 +107,7 @@ function processAuthType( const schemeOrApiKeyPrefix = scheme.scheme; switch (schemeOrApiKeyPrefix) { case "Basic": - sdkContext.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "unsupported-auth", format: { message: `${schemeOrApiKeyPrefix} auth method is currently not supported.` }, @@ -132,7 +134,7 @@ function processAuthType( } } default: - sdkContext.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "unsupported-auth", format: { message: `un-supported authentication scheme ${scheme.type}` }, diff --git a/packages/http-client-csharp/emitter/src/lib/type-converter.ts b/packages/http-client-csharp/emitter/src/lib/type-converter.ts index d9eca44ca99..668c1e32e15 100644 --- a/packages/http-client-csharp/emitter/src/lib/type-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/type-converter.ts @@ -22,6 +22,7 @@ import { } from "@azure-tools/typespec-client-generator-core"; import { Model, NoTarget } from "@typespec/compiler"; import { createDiagnostic } from "./lib.js"; +import { DiagnosticCollector } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { InputArrayType, @@ -77,6 +78,7 @@ export function fromSdkType( sdkProperty?: SdkModelPropertyTypeBase, namespace?: string, ): InputReturnType { + const diagnostics = sdkContext.__diagnostics!; let retVar = sdkContext.__typeCache.types.get(sdkType); if (retVar) { return retVar as any; @@ -131,7 +133,7 @@ export function fromSdkType( retVar = fromSdkDurationType(sdkContext, sdkType); break; case "tuple": - sdkContext.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "unsupported-sdk-type", format: { sdkType: "tuple" }, @@ -153,7 +155,7 @@ export function fromSdkType( retVar = fromSdkEndpointType(); break; case "credential": - sdkContext.__diagnostics.push( + diagnostics.add( createDiagnostic({ code: "unsupported-sdk-type", format: { sdkType: "credential" }, diff --git a/packages/http-client-csharp/emitter/src/sdk-context.ts b/packages/http-client-csharp/emitter/src/sdk-context.ts index cdaae318ee2..c2063dff0b4 100644 --- a/packages/http-client-csharp/emitter/src/sdk-context.ts +++ b/packages/http-client-csharp/emitter/src/sdk-context.ts @@ -14,7 +14,7 @@ import { SdkServiceMethod, SdkType, } from "@azure-tools/typespec-client-generator-core"; -import { Diagnostic, Type } from "@typespec/compiler"; +import { DiagnosticCollector, Type } from "@typespec/compiler"; import { Logger } from "./lib/logger.js"; import { CSharpEmitterOptions } from "./options.js"; import { InputOperation } from "./type/input-operation.js"; @@ -37,7 +37,7 @@ import { OperationResponse } from "./type/operation-response.js"; export interface CSharpEmitterContext extends SdkContext { logger: Logger; __typeCache: SdkTypeCache; - __diagnostics: Diagnostic[]; + __diagnostics?: DiagnosticCollector; } /** @@ -54,7 +54,6 @@ export function createCSharpEmitterContext< ...context, logger, __typeCache: new SdkTypeCache(), - __diagnostics: [], }; } diff --git a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts index fde2a575e65..a02ca4e8848 100644 --- a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts @@ -1,6 +1,6 @@ vi.resetModules(); -import { EmitContext, Program } from "@typespec/compiler"; +import { createDiagnosticCollector, EmitContext, Program } from "@typespec/compiler"; import { TestHost } from "@typespec/compiler/testing"; import { strictEqual } from "assert"; import { statSync } from "fs"; @@ -270,9 +270,11 @@ describe("Test _validateDotNetSdk", () => { (execAsync as Mock).mockRejectedValueOnce(error); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); + // Set up diagnostic collector for the test + sdkContext.__diagnostics = createDiagnosticCollector(); const result = await _validateDotNetSdk(sdkContext, minVersion); // Report collected diagnostics to program - program.reportDiagnostics(sdkContext.__diagnostics); + program.reportDiagnostics(sdkContext.__diagnostics.diagnostics); expect(result).toBe(false); strictEqual(program.diagnostics.length, 1); strictEqual( @@ -330,9 +332,11 @@ describe("Test _validateDotNetSdk", () => { }); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); + // Set up diagnostic collector for the test + sdkContext.__diagnostics = createDiagnosticCollector(); const result = await _validateDotNetSdk(sdkContext, minVersion); // Report collected diagnostics to program - program.reportDiagnostics(sdkContext.__diagnostics); + program.reportDiagnostics(sdkContext.__diagnostics.diagnostics); expect(result).toBe(false); strictEqual(program.diagnostics.length, 1); strictEqual( From 97af9e88d924340aaeb13a0b46cdee189fc765ad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 09:11:14 +0000 Subject: [PATCH 08/22] Fix: use void 0 instead of undefined as void cast Co-authored-by: ArcturusZhang <10554446+ArcturusZhang@users.noreply.github.com> --- packages/http-client-csharp/emitter/src/emitter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index ece5efff4a9..7c5d78a8575 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -169,7 +169,7 @@ export async function createCodeModel( } } - return [undefined as void, diagnostics]; + return [void 0, diagnostics]; } /** From bde168c39f5dc40d725155a618e974037fd8f64a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 09:41:28 +0000 Subject: [PATCH 09/22] Refactor http-client-csharp emitter to follow TypeSpec diagnostic collection pattern Co-authored-by: ArcturusZhang <10554446+ArcturusZhang@users.noreply.github.com> --- .../emitter/src/code-model-writer.ts | 9 +- .../http-client-csharp/emitter/src/emitter.ts | 28 +++-- .../emitter/src/lib/client-converter.ts | 30 +++-- .../emitter/src/lib/client-model-builder.ts | 19 ++- .../emitter/src/lib/example-converter.ts | 25 ++-- .../emitter/src/lib/namespace-converter.ts | 2 + .../emitter/src/lib/operation-converter.ts | 112 +++++++++++------- .../emitter/src/lib/service-authentication.ts | 16 +-- .../emitter/src/lib/type-converter.ts | 87 ++++++++------ .../emitter/src/lib/typespec-server.ts | 7 +- .../emitter/src/sdk-context.ts | 1 - .../emitter/test/Unit/emitter.test.ts | 16 +-- .../test/Unit/namespace-converter.test.ts | 4 +- 13 files changed, 202 insertions(+), 154 deletions(-) diff --git a/packages/http-client-csharp/emitter/src/code-model-writer.ts b/packages/http-client-csharp/emitter/src/code-model-writer.ts index da6f09f94a4..92366b37161 100644 --- a/packages/http-client-csharp/emitter/src/code-model-writer.ts +++ b/packages/http-client-csharp/emitter/src/code-model-writer.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. import { UsageFlags } from "@azure-tools/typespec-client-generator-core"; -import { NoTarget, resolvePath } from "@typespec/compiler"; +import { DiagnosticCollector, NoTarget, resolvePath } from "@typespec/compiler"; import { configurationFileName, tspOutputFileName } from "./constants.js"; import { createDiagnostic } from "./lib/lib.js"; import { CSharpEmitterContext } from "./sdk-context.js"; @@ -20,10 +20,11 @@ export async function writeCodeModel( context: CSharpEmitterContext, codeModel: CodeModel, outputFolder: string, + diagnostics: DiagnosticCollector, ) { await context.program.host.writeFile( resolvePath(outputFolder, tspOutputFileName), - prettierOutput(JSON.stringify(buildJson(context, codeModel), transformJSONProperties, 2)), + prettierOutput(JSON.stringify(buildJson(context, codeModel, diagnostics), transformJSONProperties, 2)), ); } @@ -31,9 +32,9 @@ export async function writeCodeModel( * This function builds a json from code model with refs and ids in it. * @param context - The CSharp emitter context * @param codeModel - The code model to build + * @param diagnostics - The diagnostic collector */ -function buildJson(context: CSharpEmitterContext, codeModel: CodeModel): any { - const diagnostics = context.__diagnostics!; +function buildJson(context: CSharpEmitterContext, codeModel: CodeModel, diagnostics: DiagnosticCollector): any { const objectsIds = new Map(); const stack: any[] = []; diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index 7c5d78a8575..ced465fd2ec 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -3,7 +3,9 @@ import { createSdkContext, SdkContext } from "@azure-tools/typespec-client-generator-core"; import { + createDiagnosticCollector, Diagnostic, + DiagnosticCollector, EmitContext, getDirectoryPath, joinPaths, @@ -106,10 +108,6 @@ export async function createCodeModel( // Use the provided callback or default to identity function const updateCodeModelFn = updateCodeModel ?? ((model: CodeModel) => model); const updatedRoot = updateCodeModelFn(root, sdkContext); - // Collect any diagnostics added during the callback execution - if (sdkContext.__diagnostics) { - diagnostics.push(...sdkContext.__diagnostics.diagnostics); - } const generatedFolder = resolvePath(outputFolder, "src", "Generated"); @@ -118,7 +116,9 @@ export async function createCodeModel( } // emit tspCodeModel.json - await writeCodeModel(sdkContext, updatedRoot, outputFolder); + const writeDiagnostics = createDiagnosticCollector(); + await writeCodeModel(sdkContext, updatedRoot, outputFolder, writeDiagnostics); + diagnostics.push(...writeDiagnostics.diagnostics); const namespace = updatedRoot.name; const configurations: Configuration = createConfiguration(options, namespace, sdkContext); @@ -148,7 +148,8 @@ export async function createCodeModel( debug: options.debug ?? false, }); if (result.exitCode !== 0) { - const isValid = await _validateDotNetSdk(sdkContext, _minSupportedDotNetSdkVersion); + const [isValid, validationDiagnostics] = await _validateDotNetSdk(sdkContext, _minSupportedDotNetSdkVersion); + diagnostics.push(...validationDiagnostics); // if the dotnet sdk is valid, the error is not dependency issue, log it as normal if (isValid) { throw new Error( @@ -157,7 +158,8 @@ export async function createCodeModel( } } } catch (error: any) { - const isValid = await _validateDotNetSdk(sdkContext, _minSupportedDotNetSdkVersion); + const [isValid, validationDiagnostics] = await _validateDotNetSdk(sdkContext, _minSupportedDotNetSdkVersion); + diagnostics.push(...validationDiagnostics); // if the dotnet sdk is valid, the error is not dependency issue, log it as normal if (isValid) throw new Error(error); } @@ -220,17 +222,17 @@ export function createConfiguration( * Report diagnostic if dotnet sdk is not installed or its version does not meet prerequisite * @param sdkContext - The SDK context * @param minVersionRequisite - The minimum required major version - * @param logger - The logger + * @returns A tuple containing whether the SDK is valid and any diagnostics * @internal */ export async function _validateDotNetSdk( sdkContext: CSharpEmitterContext, minMajorVersion: number, -): Promise { - const diagnostics = sdkContext.__diagnostics!; +): Promise<[boolean, readonly Diagnostic[]]> { + const diagnostics = createDiagnosticCollector(); try { const result = await execAsync("dotnet", ["--version"], { stdio: "pipe" }); - return validateDotNetSdkVersionCore(sdkContext, result.stdout, minMajorVersion); + return diagnostics.wrap(validateDotNetSdkVersionCore(sdkContext, result.stdout, minMajorVersion, diagnostics)); } catch (error: any) { if (error && "code" in error && error["code"] === "ENOENT") { diagnostics.add( @@ -245,7 +247,7 @@ export async function _validateDotNetSdk( }), ); } - return false; + return diagnostics.wrap(false); } } @@ -253,8 +255,8 @@ function validateDotNetSdkVersionCore( sdkContext: CSharpEmitterContext, version: string, minMajorVersion: number, + diagnostics: DiagnosticCollector, ): boolean { - const diagnostics = sdkContext.__diagnostics!; if (version) { const dotIndex = version.indexOf("."); const firstPart = dotIndex === -1 ? version : version.substring(0, dotIndex); diff --git a/packages/http-client-csharp/emitter/src/lib/client-converter.ts b/packages/http-client-csharp/emitter/src/lib/client-converter.ts index f32e76f5f0d..1e1b2640c11 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-converter.ts @@ -9,7 +9,7 @@ import { SdkHttpOperation, SdkMethodParameter, } from "@azure-tools/typespec-client-generator-core"; -import { NoTarget } from "@typespec/compiler"; +import { DiagnosticCollector, NoTarget } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { createDiagnostic } from "./lib.js"; import { InputParameterScope } from "../type/input-parameter-scope.js"; @@ -33,10 +33,11 @@ export function fromSdkClients( sdkContext: CSharpEmitterContext, clients: SdkClientType[], rootApiVersions: string[], + diagnostics: DiagnosticCollector, ): InputClient[] { const inputClients: InputClient[] = []; for (const client of clients) { - const inputClient = fromSdkClient(sdkContext, client, rootApiVersions); + const inputClient = fromSdkClient(sdkContext, client, rootApiVersions, diagnostics); inputClients.push(inputClient); } @@ -47,6 +48,7 @@ function fromSdkClient( sdkContext: CSharpEmitterContext, client: SdkClientType, rootApiVersions: string[], + diagnostics: DiagnosticCollector, ): InputClient { let inputClient: InputClient | undefined = sdkContext.__typeCache.clients.get(client); if (inputClient) { @@ -62,6 +64,7 @@ function fromSdkClient( sdkContext, client.clientInitialization.parameters, client.namespace, + diagnostics, ); inputClient = { @@ -71,7 +74,7 @@ function fromSdkClient( doc: client.doc, summary: client.summary, methods: client.methods - .map((m) => fromSdkServiceMethod(sdkContext, m, uri, rootApiVersions, client.namespace)) + .map((m) => fromSdkServiceMethod(sdkContext, m, uri, rootApiVersions, client.namespace, diagnostics)) .filter((m) => m !== undefined), parameters: clientParameters, initializedBy: client.clientInitialization.initializedBy, @@ -87,12 +90,12 @@ function fromSdkClient( // fill parent if (client.parent) { - inputClient.parent = fromSdkClient(sdkContext, client.parent, rootApiVersions); + inputClient.parent = fromSdkClient(sdkContext, client.parent, rootApiVersions, diagnostics); } // fill children if (client.children) { inputClient.children = client.children.map((c) => - fromSdkClient(sdkContext, c, rootApiVersions), + fromSdkClient(sdkContext, c, rootApiVersions, diagnostics), ); } @@ -102,17 +105,18 @@ function fromSdkClient( sdkContext: CSharpEmitterContext, parameters: (SdkEndpointParameter | SdkCredentialParameter | SdkMethodParameter)[], namespace: string, + diagnostics: DiagnosticCollector, ): InputParameter[] { const inputParameters: InputParameter[] = []; for (const param of parameters) { if (param.kind === "endpoint") { // Convert endpoint parameters - const endpointParams = fromSdkEndpointParameter(param); + const endpointParams = fromSdkEndpointParameter(param, diagnostics); inputParameters.push(...endpointParams); } else if (param.kind === "method") { // Convert method parameters - const methodParam = fromMethodParameter(sdkContext, param, namespace); + const methodParam = fromMethodParameter(sdkContext, param, namespace, diagnostics); inputParameters.push(methodParam); } // Note: credential parameters are handled separately in service-authentication.ts @@ -122,16 +126,15 @@ function fromSdkClient( return inputParameters; } - function fromSdkEndpointParameter(p: SdkEndpointParameter): InputEndpointParameter[] { + function fromSdkEndpointParameter(p: SdkEndpointParameter, diagnostics: DiagnosticCollector): InputEndpointParameter[] { if (p.type.kind === "union") { - return fromSdkEndpointType(p.type.variantTypes[0]); + return fromSdkEndpointType(p.type.variantTypes[0], diagnostics); } else { - return fromSdkEndpointType(p.type); + return fromSdkEndpointType(p.type, diagnostics); } } - function fromSdkEndpointType(type: SdkEndpointType): InputEndpointParameter[] { - const diagnostics = sdkContext.__diagnostics!; + function fromSdkEndpointType(type: SdkEndpointType, diagnostics: DiagnosticCollector): InputEndpointParameter[] { // TODO: support free-style endpoint url with multiple parameters const endpointExpr = type.serverUrl .replace("https://", "") @@ -159,7 +162,7 @@ function fromSdkClient( crossLanguageDefinitionId: parameter.type.kind === "string" ? "TypeSpec.string" : "TypeSpec.url", } - : fromSdkType(sdkContext, parameter.type); // TODO: consolidate with converter.fromSdkEndpointType + : fromSdkType(sdkContext, parameter.type, diagnostics); // TODO: consolidate with converter.fromSdkEndpointType parameters.push({ kind: "endpoint", name: parameter.name, @@ -175,6 +178,7 @@ function fromSdkClient( sdkContext, parameter.clientDefaultValue, parameterType, + diagnostics, ), serverUrlTemplate: type.serverUrl, skipUrlEncoding: false, diff --git a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts index 470b3e82a7d..ba317e411e5 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts @@ -7,7 +7,7 @@ import { SdkHttpOperation, UsageFlags, } from "@azure-tools/typespec-client-generator-core"; -import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler"; +import { createDiagnosticCollector, Diagnostic, DiagnosticCollector } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { CodeModel } from "../type/code-model.js"; import { InputEnumType, InputLiteralType, InputModelType } from "../type/input-type.js"; @@ -40,15 +40,12 @@ import { */ export function createModel(sdkContext: CSharpEmitterContext): [CodeModel, readonly Diagnostic[]] { const diagnostics = createDiagnosticCollector(); - // Attach diagnostics collector to context for helper functions to use - sdkContext.__diagnostics = diagnostics; - const sdkPackage = sdkContext.sdkPackage; // TO-DO: Consider exposing the namespace hierarchy in the code model https://github.com/microsoft/typespec/issues/8332 - fromSdkNamespaces(sdkContext, sdkPackage.namespaces); + fromSdkNamespaces(sdkContext, sdkPackage.namespaces, diagnostics); // TO-DO: Consider using the TCGC model + enum cache once https://github.com/Azure/typespec-azure/issues/3180 is resolved - navigateModels(sdkContext); + navigateModels(sdkContext, diagnostics); const types = Array.from(sdkContext.__typeCache.types.values()); const [models, enums] = [ @@ -58,7 +55,7 @@ export function createModel(sdkContext: CSharpEmitterContext): [CodeModel, reado const rootClients = sdkPackage.clients; const rootApiVersions = parseApiVersions(sdkPackage.enums, rootClients); - const inputClients = fromSdkClients(sdkContext, rootClients, rootApiVersions); + const inputClients = fromSdkClients(sdkContext, rootClients, rootApiVersions, diagnostics); // TODO -- TCGC now does not have constants field in its sdkPackage, they might add it in the future. const constants = Array.from(sdkContext.__typeCache.constants.values()); @@ -73,7 +70,7 @@ export function createModel(sdkContext: CSharpEmitterContext): [CodeModel, reado constants: constants, models: models, clients: inputClients, - auth: processServiceAuthentication(sdkContext, sdkPackage), + auth: diagnostics.pipe(processServiceAuthentication(sdkContext, sdkPackage)), }; return diagnostics.wrap(clientModel); @@ -168,11 +165,11 @@ function fixNamingConflicts(models: InputModelType[], constants: InputLiteralTyp } } -function navigateModels(sdkContext: CSharpEmitterContext) { +function navigateModels(sdkContext: CSharpEmitterContext, diagnostics: DiagnosticCollector) { for (const m of sdkContext.sdkPackage.models) { - fromSdkType(sdkContext, m); + fromSdkType(sdkContext, m, diagnostics); } for (const e of sdkContext.sdkPackage.enums) { - fromSdkType(sdkContext, e); + fromSdkType(sdkContext, e, diagnostics); } } diff --git a/packages/http-client-csharp/emitter/src/lib/example-converter.ts b/packages/http-client-csharp/emitter/src/lib/example-converter.ts index 74b20a8c479..bdd78190b05 100644 --- a/packages/http-client-csharp/emitter/src/lib/example-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/example-converter.ts @@ -16,6 +16,7 @@ import { SdkUnionExampleValue, SdkUnknownExampleValue, } from "@azure-tools/typespec-client-generator-core"; +import { createDiagnosticCollector, DiagnosticCollector } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { InputArrayExampleValue, @@ -48,6 +49,10 @@ export function fromSdkHttpExamples( sdkContext: CSharpEmitterContext, examples: SdkHttpOperationExample[], ): InputHttpOperationExample[] { + // Create a diagnostics collector for internal use + // Any errors in examples won't prevent the code model from being generated + const diagnostics = createDiagnosticCollector(); + return examples.map((example) => fromSdkHttpExample(example)); function fromSdkHttpExample(example: SdkHttpOperationExample): InputHttpOperationExample { @@ -76,7 +81,7 @@ export function fromSdkHttpExamples( responseValue: SdkHttpResponseExampleValue, ): OperationResponseExample { return { - response: fromSdkHttpOperationResponse(sdkContext, responseValue.response), + response: fromSdkHttpOperationResponse(sdkContext, responseValue.response, diagnostics), statusCode: responseValue.statusCode, bodyValue: responseValue.bodyValue ? fromSdkExample(responseValue.bodyValue) : undefined, }; @@ -108,7 +113,7 @@ export function fromSdkHttpExamples( function fromSdkStringExample(example: SdkStringExampleValue): InputStringExampleValue { return { kind: "string", - type: fromSdkType(sdkContext, example.type), + type: fromSdkType(sdkContext, example.type, diagnostics), value: example.value, }; } @@ -116,7 +121,7 @@ export function fromSdkHttpExamples( function fromSdkNumberExample(example: SdkNumberExampleValue): InputNumberExampleValue { return { kind: "number", - type: fromSdkType(sdkContext, example.type), + type: fromSdkType(sdkContext, example.type, diagnostics), value: example.value, }; } @@ -124,7 +129,7 @@ export function fromSdkHttpExamples( function fromSdkBooleanExample(example: SdkBooleanExampleValue): InputBooleanExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputPrimitiveType, + type: fromSdkType(sdkContext, example.type, diagnostics) as InputPrimitiveType, value: example.value, }; } @@ -132,7 +137,7 @@ export function fromSdkHttpExamples( function fromSdkUnionExample(example: SdkUnionExampleValue): InputUnionExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputUnionType, + type: fromSdkType(sdkContext, example.type, diagnostics) as InputUnionType, value: example.value, }; } @@ -140,7 +145,7 @@ export function fromSdkHttpExamples( function fromSdkArrayExample(example: SdkArrayExampleValue): InputArrayExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputArrayType, + type: fromSdkType(sdkContext, example.type, diagnostics) as InputArrayType, value: example.value.map((v) => fromSdkExample(v)), }; } @@ -150,7 +155,7 @@ export function fromSdkHttpExamples( ): InputDictionaryExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputDictionaryType, + type: fromSdkType(sdkContext, example.type, diagnostics) as InputDictionaryType, value: fromExampleRecord(example.value), }; } @@ -158,7 +163,7 @@ export function fromSdkHttpExamples( function fromSdkModelExample(example: SdkModelExampleValue): InputModelExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputModelType, + type: fromSdkType(sdkContext, example.type, diagnostics) as InputModelType, value: fromExampleRecord(example.value), additionalPropertiesValue: example.additionalPropertiesValue ? fromExampleRecord(example.additionalPropertiesValue) @@ -169,7 +174,7 @@ export function fromSdkHttpExamples( function fromSdkAnyExample(example: SdkUnknownExampleValue): InputUnknownExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputPrimitiveType, + type: fromSdkType(sdkContext, example.type, diagnostics) as InputPrimitiveType, value: example.value, }; } @@ -177,7 +182,7 @@ export function fromSdkHttpExamples( function fromSdkNullExample(example: SdkNullExampleValue): InputNullExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputNullableType, + type: fromSdkType(sdkContext, example.type, diagnostics) as InputNullableType, value: example.value, }; } diff --git a/packages/http-client-csharp/emitter/src/lib/namespace-converter.ts b/packages/http-client-csharp/emitter/src/lib/namespace-converter.ts index ef3f8f38195..fdb1cc0d0bd 100644 --- a/packages/http-client-csharp/emitter/src/lib/namespace-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/namespace-converter.ts @@ -1,10 +1,12 @@ import { SdkHttpOperation, SdkNamespace } from "@azure-tools/typespec-client-generator-core"; +import { DiagnosticCollector } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { InputNamespace } from "../type/input-type.js"; export function fromSdkNamespaces( sdkContext: CSharpEmitterContext, namespaces: SdkNamespace[], + diagnostics: DiagnosticCollector, ): InputNamespace[] { const inputNamespaces: InputNamespace[] = []; for (const namespace of namespaces) { diff --git a/packages/http-client-csharp/emitter/src/lib/operation-converter.ts b/packages/http-client-csharp/emitter/src/lib/operation-converter.ts index 1dda74bae45..3359a17e2eb 100644 --- a/packages/http-client-csharp/emitter/src/lib/operation-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/operation-converter.ts @@ -25,7 +25,7 @@ import { shouldGenerateConvenient, shouldGenerateProtocol, } from "@azure-tools/typespec-client-generator-core"; -import { getDeprecated, isErrorModel, NoTarget } from "@typespec/compiler"; +import { DiagnosticCollector, getDeprecated, isErrorModel, NoTarget } from "@typespec/compiler"; import { HttpStatusCodeRange } from "@typespec/http"; import { getResourceOperation } from "@typespec/rest"; import { CSharpEmitterContext } from "../sdk-context.js"; @@ -72,8 +72,8 @@ export function fromSdkServiceMethod( uri: string, rootApiVersions: string[], namespace: string, + diagnostics: DiagnosticCollector, ): InputServiceMethod | undefined { - const diagnostics = sdkContext.__diagnostics!; let method = sdkContext.__typeCache.methods.get(sdkMethod); if (method) { return method; @@ -88,6 +88,7 @@ export function fromSdkServiceMethod( uri, rootApiVersions, namespace, + diagnostics, ); break; case "paging": @@ -97,6 +98,7 @@ export function fromSdkServiceMethod( uri, rootApiVersions, namespace, + diagnostics, ); pagingServiceMethod.pagingMetadata = loadPagingServiceMetadata( sdkContext, @@ -104,6 +106,7 @@ export function fromSdkServiceMethod( rootApiVersions, uri, namespace, + diagnostics, ); method = pagingServiceMethod; break; @@ -114,8 +117,9 @@ export function fromSdkServiceMethod( uri, rootApiVersions, namespace, + diagnostics, ); - lroServiceMethod.lroMetadata = loadLongRunningMetadata(sdkContext, sdkMethod); + lroServiceMethod.lroMetadata = loadLongRunningMetadata(sdkContext, sdkMethod, diagnostics); method = lroServiceMethod; break; case "lropaging": @@ -125,14 +129,16 @@ export function fromSdkServiceMethod( uri, rootApiVersions, namespace, + diagnostics, ); - lroPagingMethod.lroMetadata = loadLongRunningMetadata(sdkContext, sdkMethod); + lroPagingMethod.lroMetadata = loadLongRunningMetadata(sdkContext, sdkMethod, diagnostics); lroPagingMethod.pagingMetadata = loadPagingServiceMetadata( sdkContext, sdkMethod, rootApiVersions, uri, namespace, + diagnostics, ); method = lroPagingMethod; break; @@ -160,8 +166,8 @@ export function fromSdkServiceMethodOperation( method: SdkServiceMethod, uri: string, rootApiVersions: string[], + diagnostics: DiagnosticCollector, ): InputOperation { - const diagnostics = sdkContext.__diagnostics!; let operation = sdkContext.__typeCache.operations.get(method.operation); if (operation) { return operation; @@ -191,8 +197,8 @@ export function fromSdkServiceMethodOperation( summary: method.summary, doc: method.doc, accessibility: method.access, - parameters: fromSdkOperationParameters(sdkContext, method.operation, rootApiVersions), - responses: fromSdkHttpOperationResponses(sdkContext, method.operation.responses), + parameters: fromSdkOperationParameters(sdkContext, method.operation, rootApiVersions, diagnostics), + responses: fromSdkHttpOperationResponses(sdkContext, method.operation.responses, diagnostics), httpMethod: parseHttpRequestMethod(method.operation.verb), uri: uri, path: method.operation.path, @@ -217,6 +223,7 @@ export function getParameterDefaultValue( sdkContext: CSharpEmitterContext, clientDefaultValue: any, parameterType: InputType, + diagnostics: DiagnosticCollector, ): InputConstant | undefined { if ( clientDefaultValue === undefined || @@ -226,7 +233,7 @@ export function getParameterDefaultValue( return undefined; } - const kind = getValueType(sdkContext, clientDefaultValue); + const kind = getValueType(sdkContext, clientDefaultValue, diagnostics); return { type: { kind: kind, @@ -243,6 +250,7 @@ function createServiceMethod( uri: string, rootApiVersions: string[], namespace: string, + diagnostics: DiagnosticCollector, ): T { return { kind: method.kind, @@ -251,11 +259,11 @@ function createServiceMethod( apiVersions: method.apiVersions, doc: method.doc, summary: method.summary, - operation: fromSdkServiceMethodOperation(sdkContext, method, uri, rootApiVersions), - parameters: fromSdkServiceMethodParameters(sdkContext, method, rootApiVersions, namespace), - response: fromSdkServiceMethodResponse(sdkContext, method.response), + operation: fromSdkServiceMethodOperation(sdkContext, method, uri, rootApiVersions, diagnostics), + parameters: fromSdkServiceMethodParameters(sdkContext, method, rootApiVersions, namespace, diagnostics), + response: fromSdkServiceMethodResponse(sdkContext, method.response, diagnostics), exception: method.exception - ? fromSdkServiceMethodResponse(sdkContext, method.exception) + ? fromSdkServiceMethodResponse(sdkContext, method.exception, diagnostics) : undefined, isOverride: method.isOverride, generateConvenient: method.generateConvenient, @@ -264,8 +272,7 @@ function createServiceMethod( } as T; } -function getValueType(sdkContext: CSharpEmitterContext, value: any): SdkBuiltInKinds { - const diagnostics = sdkContext.__diagnostics!; +function getValueType(sdkContext: CSharpEmitterContext, value: any, diagnostics: DiagnosticCollector): SdkBuiltInKinds { switch (typeof value) { case "string": return "string"; @@ -292,11 +299,12 @@ function fromSdkServiceMethodParameters( method: SdkServiceMethod, rootApiVersions: string[], namespace: string, + diagnostics: DiagnosticCollector, ): InputMethodParameter[] { const parameters: InputMethodParameter[] = []; for (const p of method.parameters) { - const methodInputParameter = fromMethodParameter(sdkContext, p, namespace); + const methodInputParameter = fromMethodParameter(sdkContext, p, namespace, diagnostics); const operationHttpParameter = getHttpOperationParameter(method, p); if (!operationHttpParameter) { @@ -310,6 +318,7 @@ function fromSdkServiceMethodParameters( methodInputParameter, operationHttpParameter, rootApiVersions, + diagnostics, ); parameters.push(methodInputParameter); } @@ -322,6 +331,7 @@ function updateMethodParameter( methodParameter: InputMethodParameter, operationHttpParameter: SdkHttpParameter | SdkModelPropertyType, rootApiVersions: string[], + diagnostics: DiagnosticCollector, ): void { methodParameter.serializedName = getNameInRequest(operationHttpParameter); methodParameter.location = getParameterLocation(operationHttpParameter); @@ -333,7 +343,7 @@ function updateMethodParameter( if (methodParameter.location === RequestLocation.Body) { // Convert constants to enums if (methodParameter.type.kind === "constant") { - methodParameter.type = fromSdkType(sdkContext, operationHttpParameter.type); + methodParameter.type = fromSdkType(sdkContext, operationHttpParameter.type, diagnostics); } } } @@ -341,9 +351,10 @@ function updateMethodParameter( function fromSdkServiceMethodResponse( sdkContext: CSharpEmitterContext, methodResponse: SdkMethodResponse, + diagnostics: DiagnosticCollector, ): InputServiceMethodResponse { return { - type: getResponseType(sdkContext, methodResponse.type), + type: getResponseType(sdkContext, methodResponse.type, diagnostics), resultSegments: methodResponse.resultSegments?.map((segment) => getResponseSegmentName(segment), ), @@ -354,8 +365,8 @@ function fromSdkOperationParameters( sdkContext: CSharpEmitterContext, operation: SdkHttpOperation, rootApiVersions: string[], + diagnostics: DiagnosticCollector, ): InputHttpParameter[] { - const diagnostics = sdkContext.__diagnostics!; const parameters: InputHttpParameter[] = []; for (const p of operation.parameters) { if (p.kind === "cookie") { @@ -368,14 +379,14 @@ function fromSdkOperationParameters( ); return parameters; } - const param = fromParameter(sdkContext, p, rootApiVersions); + const param = fromParameter(sdkContext, p, rootApiVersions, diagnostics); if (param) { parameters.push(param); } } if (operation.bodyParam) { - const bodyParam = fromParameter(sdkContext, operation.bodyParam, rootApiVersions); + const bodyParam = fromParameter(sdkContext, operation.bodyParam, rootApiVersions, diagnostics); if (bodyParam) { parameters.push(bodyParam); } @@ -387,8 +398,8 @@ export function fromParameter( sdkContext: CSharpEmitterContext, p: SdkHttpParameter | SdkModelPropertyType, rootApiVersions: string[], + diagnostics: DiagnosticCollector, ): InputHttpParameter | undefined { - const diagnostics = sdkContext.__diagnostics!; let parameter = sdkContext.__typeCache.operationParameters.get(p); if (parameter) { return parameter; @@ -397,16 +408,16 @@ export function fromParameter( switch (parameterKind) { case "query": - parameter = fromQueryParameter(sdkContext, p, rootApiVersions); + parameter = fromQueryParameter(sdkContext, p, rootApiVersions, diagnostics); break; case "path": - parameter = fromPathParameter(sdkContext, p, rootApiVersions); + parameter = fromPathParameter(sdkContext, p, rootApiVersions, diagnostics); break; case "header": - parameter = fromHeaderParameter(sdkContext, p, rootApiVersions); + parameter = fromHeaderParameter(sdkContext, p, rootApiVersions, diagnostics); break; case "body": - parameter = fromBodyParameter(sdkContext, p, rootApiVersions); + parameter = fromBodyParameter(sdkContext, p, rootApiVersions, diagnostics); break; default: diagnostics.add( @@ -430,8 +441,9 @@ function fromQueryParameter( sdkContext: CSharpEmitterContext, p: SdkQueryParameter, rootApiVersions: string[], + diagnostics: DiagnosticCollector, ): InputQueryParameter { - const parameterType = fromSdkType(sdkContext, p.type); + const parameterType = fromSdkType(sdkContext, p.type, diagnostics); const retVar: InputQueryParameter = { kind: "query", @@ -442,7 +454,7 @@ function fromQueryParameter( type: parameterType, isApiVersion: p.isApiVersionParam, explode: isExploded(p), - defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType), + defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType, diagnostics), arraySerializationDelimiter: getArraySerializationDelimiter(p), optional: p.optional, scope: getParameterScope(p, parameterType, rootApiVersions.length > 0), @@ -459,8 +471,9 @@ function fromPathParameter( sdkContext: CSharpEmitterContext, p: SdkPathParameter, rootApiVersions: string[], + diagnostics: DiagnosticCollector, ): InputPathParameter { - const parameterType = fromSdkType(sdkContext, p.type); + const parameterType = fromSdkType(sdkContext, p.type, diagnostics); const retVar: InputPathParameter = { kind: "path", @@ -474,7 +487,7 @@ function fromPathParameter( style: p.style, allowReserved: p.allowReserved, skipUrlEncoding: p.allowReserved, - defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType), + defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType, diagnostics), optional: p.optional, scope: getParameterScope(p, parameterType, rootApiVersions.length > 0), decorators: p.decorators, @@ -490,8 +503,9 @@ function fromHeaderParameter( sdkContext: CSharpEmitterContext, p: SdkHeaderParameter, rootApiVersions: string[], + diagnostics: DiagnosticCollector, ): InputHeaderParameter { - const parameterType = fromSdkType(sdkContext, p.type); + const parameterType = fromSdkType(sdkContext, p.type, diagnostics); const retVar: InputHeaderParameter = { kind: "header", @@ -503,7 +517,7 @@ function fromHeaderParameter( isApiVersion: p.isApiVersionParam, collectionFormat: p.collectionFormat, arraySerializationDelimiter: getArraySerializationDelimiter(p), - defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType), + defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType, diagnostics), optional: p.optional, isContentType: isContentType(p), scope: getParameterScope(p, parameterType, rootApiVersions.length > 0), @@ -520,8 +534,9 @@ function fromBodyParameter( sdkContext: CSharpEmitterContext, p: SdkBodyParameter, rootApiVersions: string[], + diagnostics: DiagnosticCollector, ): InputBodyParameter { - const parameterType = fromSdkType(sdkContext, p.type); + const parameterType = fromSdkType(sdkContext, p.type, diagnostics); const retVar: InputBodyParameter = { kind: "body", @@ -548,13 +563,14 @@ export function fromMethodParameter( sdkContext: CSharpEmitterContext, p: SdkMethodParameter, namespace: string, + diagnostics: DiagnosticCollector, ): InputMethodParameter { let retVar = sdkContext.__typeCache.methodParmeters.get(p); if (retVar) { return retVar as InputMethodParameter; } - const parameterType = fromSdkType(sdkContext, p.type, p, namespace); + const parameterType = fromSdkType(sdkContext, p.type, diagnostics, p, namespace); retVar = { kind: "method", @@ -565,7 +581,7 @@ export function fromMethodParameter( type: parameterType, location: RequestLocation.None, isApiVersion: p.isApiVersionParam, - defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType), + defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType, diagnostics), optional: p.optional, scope: InputParameterScope.Method, crossLanguageDefinitionId: p.crossLanguageDefinitionId, @@ -581,6 +597,7 @@ export function fromMethodParameter( function loadLongRunningMetadata( sdkContext: CSharpEmitterContext, method: SdkLroServiceMethod | SdkLroPagingServiceMethod, + diagnostics: DiagnosticCollector, ): InputLongRunningServiceMetadata { return { finalStateVia: convertLroFinalStateVia(method.lroMetadata.finalStateVia), @@ -590,7 +607,7 @@ function loadLongRunningMetadata( statusCodes: method.operation.verb === "delete" ? [204] : [200], bodyType: method.lroMetadata.finalResponse?.envelopeResult !== undefined - ? fromSdkType(sdkContext, method.lroMetadata.finalResponse.envelopeResult) + ? fromSdkType(sdkContext, method.lroMetadata.finalResponse.envelopeResult, diagnostics) : undefined, } as OperationResponse, resultPath: method.lroMetadata.finalResultPath, @@ -600,10 +617,11 @@ function loadLongRunningMetadata( function fromSdkHttpOperationResponses( sdkContext: CSharpEmitterContext, operationResponses: SdkHttpResponse[], + diagnostics: DiagnosticCollector, ): OperationResponse[] { const responses: OperationResponse[] = []; for (const r of operationResponses) { - responses.push(fromSdkHttpOperationResponse(sdkContext, r)); + responses.push(fromSdkHttpOperationResponse(sdkContext, r, diagnostics)); } return responses; } @@ -611,6 +629,7 @@ function fromSdkHttpOperationResponses( export function fromSdkHttpOperationResponse( sdkContext: CSharpEmitterContext, sdkResponse: SdkHttpResponse, + diagnostics: DiagnosticCollector, ): OperationResponse { let retVar = sdkContext.__typeCache.responses.get(sdkResponse); if (retVar) { @@ -620,8 +639,8 @@ export function fromSdkHttpOperationResponse( const range = sdkResponse.statusCodes; retVar = { statusCodes: toStatusCodesArray(range), - bodyType: getResponseType(sdkContext, sdkResponse.type), - headers: fromSdkServiceResponseHeaders(sdkContext, sdkResponse.headers), + bodyType: getResponseType(sdkContext, sdkResponse.type, diagnostics), + headers: fromSdkServiceResponseHeaders(sdkContext, sdkResponse.headers, diagnostics), isErrorResponse: sdkResponse.type !== undefined && isErrorModel(sdkContext.program, sdkResponse.type.__raw!), contentTypes: sdkResponse.contentTypes, @@ -634,6 +653,7 @@ export function fromSdkHttpOperationResponse( function fromSdkServiceResponseHeaders( sdkContext: CSharpEmitterContext, headers: SdkServiceResponseHeader[], + diagnostics: DiagnosticCollector, ): HttpResponseHeader[] { return headers.map( (h) => @@ -642,7 +662,7 @@ function fromSdkServiceResponseHeaders( nameInResponse: h.serializedName, summary: h.summary, doc: h.doc, - type: fromSdkType(sdkContext, h.type), + type: fromSdkType(sdkContext, h.type, diagnostics), }) as HttpResponseHeader, ); } @@ -696,6 +716,7 @@ function loadPagingServiceMetadata( rootApiVersions: string[], uri: string, namespace: string, + diagnostics: DiagnosticCollector, ): InputPagingServiceMetadata { let nextLink: InputNextLink | undefined; if (method.pagingMetadata.nextLinkSegments) { @@ -707,6 +728,7 @@ function loadPagingServiceMetadata( context, method, method.pagingMetadata.nextLinkSegments[0], + diagnostics, ), }; @@ -717,6 +739,7 @@ function loadPagingServiceMetadata( uri, rootApiVersions, namespace, + diagnostics, ); } @@ -732,7 +755,7 @@ function loadPagingServiceMetadata( ] as SdkModelPropertyType; const operationParameter = getHttpOperationParameter(method, lastParameterSegment); if (operationParameter) { - const parameter = fromParameter(context, operationParameter, rootApiVersions); + const parameter = fromParameter(context, operationParameter, rootApiVersions, diagnostics); if (parameter) { nextLinkReInjectedParameters.push(parameter); } @@ -757,6 +780,7 @@ function loadPagingServiceMetadata( context, getHttpOperationParameter(method, lastParameterSegment)!, rootApiVersions, + diagnostics, ); if (continuationTokenParameter) { continuationToken = { @@ -768,6 +792,7 @@ function loadPagingServiceMetadata( context, method, method.pagingMetadata.continuationTokenResponseSegments?.[0], + diagnostics, ), }; } @@ -804,8 +829,8 @@ function getResponseLocation( context: CSharpEmitterContext, method: SdkPagingServiceMethod | SdkLroPagingServiceMethod, p: SdkServiceResponseHeader | SdkModelPropertyType, + diagnostics: DiagnosticCollector, ): ResponseLocation { - const diagnostics = context.__diagnostics!; if (p.kind === "responseheader") { return ResponseLocation.Header; } @@ -934,6 +959,7 @@ function getArraySerializationDelimiter( function getResponseType( sdkContext: CSharpEmitterContext, type: SdkType | undefined, + diagnostics: DiagnosticCollector, ): InputType | undefined { if (!type) { return undefined; @@ -941,8 +967,8 @@ function getResponseType( // handle anonymous union enum response types by defaulting to the enum value type in the case of if (type.kind === "enum" && type.isUnionAsEnum && type.isGeneratedName) { - return fromSdkType(sdkContext, type.valueType); + return fromSdkType(sdkContext, type.valueType, diagnostics); } - return fromSdkType(sdkContext, type); + return fromSdkType(sdkContext, type, diagnostics); } diff --git a/packages/http-client-csharp/emitter/src/lib/service-authentication.ts b/packages/http-client-csharp/emitter/src/lib/service-authentication.ts index e5d50fbff71..2800e4d1c8f 100644 --- a/packages/http-client-csharp/emitter/src/lib/service-authentication.ts +++ b/packages/http-client-csharp/emitter/src/lib/service-authentication.ts @@ -7,7 +7,7 @@ import { SdkHttpOperation, SdkPackage, } from "@azure-tools/typespec-client-generator-core"; -import { createDiagnosticCollector, DiagnosticCollector, NoTarget } from "@typespec/compiler"; +import { createDiagnosticCollector, Diagnostic, DiagnosticCollector, NoTarget } from "@typespec/compiler"; import { Oauth2Auth, OAuth2Flow } from "@typespec/http"; import { CSharpEmitterContext } from "../sdk-context.js"; import { createDiagnostic } from "./lib.js"; @@ -17,8 +17,8 @@ import { InputOAuth2Flow } from "../type/input-oauth2-auth.js"; export function processServiceAuthentication( sdkContext: CSharpEmitterContext, sdkPackage: SdkPackage, -): InputAuth | undefined { - const diagnostics = sdkContext.__diagnostics!; +): [InputAuth | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); let authClientParameter: SdkCredentialParameter | undefined = undefined; for (const client of sdkPackage.clients) { for (const parameter of client.clientInitialization.parameters) { @@ -30,7 +30,7 @@ export function processServiceAuthentication( } if (!authClientParameter) { - return undefined; + return diagnostics.wrap(undefined); } const inputAuth: InputAuth = {}; @@ -46,9 +46,9 @@ export function processServiceAuthentication( }), ); - return inputAuth; + return diagnostics.wrap(inputAuth); } - return auth; + return diagnostics.wrap(auth); } let containsNoAuth = false; @@ -64,7 +64,7 @@ export function processServiceAuthentication( } if (containsNoAuth && !inputAuth.apiKey && !inputAuth.oAuth2) { - return undefined; + return diagnostics.wrap(undefined); } if (!inputAuth?.apiKey && !inputAuth?.oAuth2) { @@ -77,7 +77,7 @@ export function processServiceAuthentication( ); } - return inputAuth; + return diagnostics.wrap(inputAuth); } function processAuthType( diff --git a/packages/http-client-csharp/emitter/src/lib/type-converter.ts b/packages/http-client-csharp/emitter/src/lib/type-converter.ts index 668c1e32e15..0c97f2ae0b5 100644 --- a/packages/http-client-csharp/emitter/src/lib/type-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/type-converter.ts @@ -75,10 +75,10 @@ type InputReturnType = T extends { kind: "nullable" } export function fromSdkType( sdkContext: CSharpEmitterContext, sdkType: T, + diagnostics: DiagnosticCollector, sdkProperty?: SdkModelPropertyTypeBase, namespace?: string, ): InputReturnType { - const diagnostics = sdkContext.__diagnostics!; let retVar = sdkContext.__typeCache.types.get(sdkType); if (retVar) { return retVar as any; @@ -88,26 +88,26 @@ export function fromSdkType( case "nullable": const nullableType: InputNullableType = { kind: "nullable", - type: fromSdkType(sdkContext, sdkType.type, sdkProperty, namespace), + type: fromSdkType(sdkContext, sdkType.type, diagnostics, sdkProperty, namespace), namespace: sdkType.namespace, external: fromSdkExternalTypeInfo(sdkType), }; retVar = nullableType; break; case "model": - retVar = fromSdkModelType(sdkContext, sdkType); + retVar = fromSdkModelType(sdkContext, sdkType, diagnostics); break; case "enum": - retVar = fromSdkEnumType(sdkContext, sdkType); + retVar = fromSdkEnumType(sdkContext, sdkType, diagnostics); break; case "enumvalue": - retVar = fromSdkEnumValueType(sdkContext, sdkType); + retVar = fromSdkEnumValueType(sdkContext, sdkType, diagnostics); break; case "dict": - retVar = fromSdkDictionaryType(sdkContext, sdkType); + retVar = fromSdkDictionaryType(sdkContext, sdkType, diagnostics); break; case "array": - retVar = fromSdkArrayType(sdkContext, sdkType); + retVar = fromSdkArrayType(sdkContext, sdkType, diagnostics); break; case "constant": if ( @@ -117,20 +117,20 @@ export function fromSdkType( sdkType.valueType.kind !== "boolean" ) { // turn the constant into an extensible enum - retVar = createEnumType(sdkContext, sdkType, namespace!); + retVar = createEnumType(sdkContext, sdkType, namespace!, diagnostics); } else { - retVar = fromSdkConstantType(sdkContext, sdkType); + retVar = fromSdkConstantType(sdkContext, sdkType, diagnostics); } break; case "union": - retVar = fromUnionType(sdkContext, sdkType); + retVar = fromUnionType(sdkContext, sdkType, diagnostics); break; case "utcDateTime": case "offsetDateTime": - retVar = fromSdkDateTimeType(sdkContext, sdkType); + retVar = fromSdkDateTimeType(sdkContext, sdkType, diagnostics); break; case "duration": - retVar = fromSdkDurationType(sdkContext, sdkType); + retVar = fromSdkDurationType(sdkContext, sdkType, diagnostics); break; case "tuple": diagnostics.add( @@ -172,7 +172,7 @@ export function fromSdkType( retVar = credentialType; break; default: - retVar = fromSdkBuiltInType(sdkContext, sdkType); + retVar = fromSdkBuiltInType(sdkContext, sdkType, diagnostics); break; } @@ -184,6 +184,7 @@ export function fromSdkType( function fromSdkModelType( sdkContext: CSharpEmitterContext, modelType: SdkModelType, + diagnostics: DiagnosticCollector, ): InputModelType { // get all unique decorators for the model type from the namespace level and the model level let decorators: DecoratorInfo[] = modelType.decorators; @@ -209,12 +210,12 @@ function fromSdkModelType( sdkContext.__typeCache.updateSdkTypeReferences(modelType, inputModelType); inputModelType.additionalProperties = modelType.additionalProperties - ? fromSdkType(sdkContext, modelType.additionalProperties) + ? fromSdkType(sdkContext, modelType.additionalProperties, diagnostics) : undefined; const properties: InputModelProperty[] = []; for (const property of modelType.properties) { - const ourProperty = fromSdkModelProperty(sdkContext, property, modelType); + const ourProperty = fromSdkModelProperty(sdkContext, property, modelType, diagnostics); if (ourProperty) { properties.push(ourProperty); @@ -222,11 +223,11 @@ function fromSdkModelType( } inputModelType.discriminatorProperty = modelType.discriminatorProperty - ? fromSdkModelProperty(sdkContext, modelType.discriminatorProperty, modelType) + ? fromSdkModelProperty(sdkContext, modelType.discriminatorProperty, modelType, diagnostics) : undefined; inputModelType.baseModel = modelType.baseModel - ? fromSdkType(sdkContext, modelType.baseModel) + ? fromSdkType(sdkContext, modelType.baseModel, diagnostics) : undefined; inputModelType.properties = properties; @@ -235,7 +236,7 @@ function fromSdkModelType( const discriminatedSubtypes: Record = {}; for (const key in modelType.discriminatedSubtypes) { const subtype = modelType.discriminatedSubtypes[key]; - discriminatedSubtypes[key] = fromSdkType(sdkContext, subtype); + discriminatedSubtypes[key] = fromSdkType(sdkContext, subtype, diagnostics); } inputModelType.discriminatedSubtypes = discriminatedSubtypes; } @@ -247,6 +248,7 @@ function fromSdkModelProperty( sdkContext: CSharpEmitterContext, sdkProperty: SdkModelPropertyType, sdkModel: SdkModelType, + diagnostics: DiagnosticCollector, ): InputModelProperty | undefined { // TODO -- this returns undefined because some properties we do not support yet. let property = sdkContext.__typeCache.properties.get(sdkProperty) as @@ -266,7 +268,7 @@ function fromSdkModelProperty( serializedName: serializedName, summary: sdkProperty.summary, doc: sdkProperty.doc, - type: fromSdkType(sdkContext, sdkProperty.type, sdkProperty, sdkModel.namespace), + type: fromSdkType(sdkContext, sdkProperty.type, diagnostics, sdkProperty, sdkModel.namespace), optional: sdkProperty.optional, readOnly: isReadOnly(sdkProperty), discriminator: sdkProperty.discriminator, @@ -285,14 +287,15 @@ function fromSdkModelProperty( return property; } -function fromSdkEnumType(sdkContext: CSharpEmitterContext, enumType: SdkEnumType): InputEnumType { - return createEnumType(sdkContext, enumType, enumType.namespace); +function fromSdkEnumType(sdkContext: CSharpEmitterContext, enumType: SdkEnumType, diagnostics: DiagnosticCollector): InputEnumType { + return createEnumType(sdkContext, enumType, enumType.namespace, diagnostics); } function createEnumType( sdkContext: CSharpEmitterContext, sdkType: SdkConstantType | SdkEnumType, namespace: string, + diagnostics: DiagnosticCollector, ): InputEnumType { const values: InputEnumValueType[] = []; @@ -302,8 +305,8 @@ function createEnumType( crossLanguageDefinitionId: sdkType.kind === "enum" ? sdkType.crossLanguageDefinitionId : "", valueType: sdkType.kind === "enum" - ? (fromSdkType(sdkContext, sdkType.valueType) as InputPrimitiveType) - : fromSdkBuiltInType(sdkContext, sdkType.valueType), + ? (fromSdkType(sdkContext, sdkType.valueType, diagnostics) as InputPrimitiveType) + : fromSdkBuiltInType(sdkContext, sdkType.valueType, diagnostics), values: values, // constantType.access, TODO - constant type now does not have access. TCGC will add it later access: @@ -324,10 +327,10 @@ function createEnumType( if (sdkType.kind === "enum") { for (const v of sdkType.values) { - values.push(createEnumValueType(sdkContext, v, inputEnumType)); + values.push(createEnumValueType(sdkContext, v, inputEnumType, diagnostics)); } } else { - values.push(createEnumValueType(sdkContext, sdkType, inputEnumType)); + values.push(createEnumValueType(sdkContext, sdkType, inputEnumType, diagnostics)); } return inputEnumType; @@ -336,14 +339,15 @@ function createEnumType( function fromSdkDateTimeType( sdkContext: CSharpEmitterContext, dateTimeType: SdkDateTimeType, + diagnostics: DiagnosticCollector, ): InputDateTimeType { return { kind: dateTimeType.kind, name: dateTimeType.name, encode: dateTimeType.encode, - wireType: fromSdkType(sdkContext, dateTimeType.wireType), + wireType: fromSdkType(sdkContext, dateTimeType.wireType, diagnostics), crossLanguageDefinitionId: dateTimeType.crossLanguageDefinitionId, - baseType: dateTimeType.baseType ? fromSdkType(sdkContext, dateTimeType.baseType) : undefined, + baseType: dateTimeType.baseType ? fromSdkType(sdkContext, dateTimeType.baseType, diagnostics) : undefined, decorators: dateTimeType.decorators, external: fromSdkExternalTypeInfo(dateTimeType), }; @@ -352,14 +356,15 @@ function fromSdkDateTimeType( function fromSdkDurationType( sdkContext: CSharpEmitterContext, durationType: SdkDurationType, + diagnostics: DiagnosticCollector, ): InputDurationType { return { kind: durationType.kind, name: durationType.name, encode: durationType.encode, - wireType: fromSdkType(sdkContext, durationType.wireType), + wireType: fromSdkType(sdkContext, durationType.wireType, diagnostics), crossLanguageDefinitionId: durationType.crossLanguageDefinitionId, - baseType: durationType.baseType ? fromSdkType(sdkContext, durationType.baseType) : undefined, + baseType: durationType.baseType ? fromSdkType(sdkContext, durationType.baseType, diagnostics) : undefined, decorators: durationType.decorators, external: fromSdkExternalTypeInfo(durationType), }; @@ -368,22 +373,23 @@ function fromSdkDurationType( function fromSdkBuiltInType( sdkContext: CSharpEmitterContext, builtInType: SdkBuiltInType, + diagnostics: DiagnosticCollector, ): InputPrimitiveType { return { kind: builtInType.kind, name: builtInType.name, encode: builtInType.encode !== builtInType.kind ? builtInType.encode : undefined, crossLanguageDefinitionId: builtInType.crossLanguageDefinitionId, - baseType: builtInType.baseType ? fromSdkType(sdkContext, builtInType.baseType) : undefined, + baseType: builtInType.baseType ? fromSdkType(sdkContext, builtInType.baseType, diagnostics) : undefined, decorators: builtInType.decorators, external: fromSdkExternalTypeInfo(builtInType), }; } -function fromUnionType(sdkContext: CSharpEmitterContext, union: SdkUnionType): InputUnionType { +function fromUnionType(sdkContext: CSharpEmitterContext, union: SdkUnionType, diagnostics: DiagnosticCollector): InputUnionType { const variantTypes: InputType[] = []; for (const value of union.variantTypes) { - const variantType = fromSdkType(sdkContext, value); + const variantType = fromSdkType(sdkContext, value, diagnostics); variantTypes.push(variantType); } @@ -400,6 +406,7 @@ function fromUnionType(sdkContext: CSharpEmitterContext, union: SdkUnionType): I function fromSdkConstantType( sdkContext: CSharpEmitterContext, constantType: SdkConstantType, + diagnostics: DiagnosticCollector, ): InputLiteralType { const literalType = { kind: constantType.kind, @@ -407,7 +414,7 @@ function fromSdkConstantType( namespace: "", // constantType.namespace, TODO - constant type now does not have namespace. TCGC will add it later access: undefined, // constantType.access, TODO - constant type now does not have access. TCGC will add it later usage: UsageFlags.None, // constantType.usage, TODO - constant type now does not have usage. TCGC will add it later - valueType: fromSdkType(sdkContext, constantType.valueType), + valueType: fromSdkType(sdkContext, constantType.valueType, diagnostics), value: constantType.value, decorators: constantType.decorators, }; @@ -420,14 +427,16 @@ function fromSdkConstantType( function fromSdkEnumValueType( sdkContext: CSharpEmitterContext, enumValueType: SdkEnumValueType, + diagnostics: DiagnosticCollector, ): InputEnumValueType { - return createEnumValueType(sdkContext, enumValueType, enumValueType.enumType); + return createEnumValueType(sdkContext, enumValueType, enumValueType.enumType, diagnostics); } function createEnumValueType( sdkContext: CSharpEmitterContext, sdkType: SdkEnumValueType | SdkConstantType, enumType: InputEnumType, + diagnostics: DiagnosticCollector, ): InputEnumValueType { return { kind: "enumvalue", @@ -439,7 +448,7 @@ function createEnumValueType( : sdkType.name, value: typeof sdkType.value === "boolean" ? (sdkType.value ? 1 : 0) : sdkType.value, valueType: - sdkType.kind === "constant" ? sdkType.valueType : fromSdkType(sdkContext, sdkType.valueType), + sdkType.kind === "constant" ? sdkType.valueType : fromSdkType(sdkContext, sdkType.valueType, diagnostics), enumType: enumType, summary: sdkType.summary, doc: sdkType.doc, @@ -450,11 +459,12 @@ function createEnumValueType( function fromSdkDictionaryType( sdkContext: CSharpEmitterContext, dictionaryType: SdkDictionaryType, + diagnostics: DiagnosticCollector, ): InputDictionaryType { return { kind: "dict", - keyType: fromSdkType(sdkContext, dictionaryType.keyType), - valueType: fromSdkType(sdkContext, dictionaryType.valueType), + keyType: fromSdkType(sdkContext, dictionaryType.keyType, diagnostics), + valueType: fromSdkType(sdkContext, dictionaryType.valueType, diagnostics), decorators: dictionaryType.decorators, external: fromSdkExternalTypeInfo(dictionaryType), }; @@ -463,11 +473,12 @@ function fromSdkDictionaryType( function fromSdkArrayType( sdkContext: CSharpEmitterContext, arrayType: SdkArrayType, + diagnostics: DiagnosticCollector, ): InputArrayType { return { kind: "array", name: arrayType.name, - valueType: fromSdkType(sdkContext, arrayType.valueType), + valueType: fromSdkType(sdkContext, arrayType.valueType, diagnostics), crossLanguageDefinitionId: arrayType.crossLanguageDefinitionId, decorators: arrayType.decorators, external: fromSdkExternalTypeInfo(arrayType), diff --git a/packages/http-client-csharp/emitter/src/lib/typespec-server.ts b/packages/http-client-csharp/emitter/src/lib/typespec-server.ts index 77cfe50b341..d5e76b8cb49 100644 --- a/packages/http-client-csharp/emitter/src/lib/typespec-server.ts +++ b/packages/http-client-csharp/emitter/src/lib/typespec-server.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. import { getClientType } from "@azure-tools/typespec-client-generator-core"; -import { getDoc, getSummary, Value } from "@typespec/compiler"; +import { createDiagnosticCollector, getDoc, getSummary, Value } from "@typespec/compiler"; import { HttpServer } from "@typespec/http"; import { getExtensions } from "@typespec/openapi"; import { CSharpEmitterContext } from "../sdk-context.js"; @@ -21,6 +21,9 @@ export function resolveServers( sdkContext: CSharpEmitterContext, servers: HttpServer[], ): TypeSpecServer[] { + // Create a diagnostics collector for internal use + const diagnostics = createDiagnosticCollector(); + return servers.map((server) => { const parameters: InputEndpointParameter[] = []; let url: string = server.url; @@ -35,7 +38,7 @@ export function resolveServers( name: "url", crossLanguageDefinitionId: "TypeSpec.url", } - : fromSdkType(sdkContext, getClientType(sdkContext, prop)); + : fromSdkType(sdkContext, getClientType(sdkContext, prop), diagnostics); if (value) { defaultValue = { diff --git a/packages/http-client-csharp/emitter/src/sdk-context.ts b/packages/http-client-csharp/emitter/src/sdk-context.ts index c2063dff0b4..2718ebfe32d 100644 --- a/packages/http-client-csharp/emitter/src/sdk-context.ts +++ b/packages/http-client-csharp/emitter/src/sdk-context.ts @@ -37,7 +37,6 @@ import { OperationResponse } from "./type/operation-response.js"; export interface CSharpEmitterContext extends SdkContext { logger: Logger; __typeCache: SdkTypeCache; - __diagnostics?: DiagnosticCollector; } /** diff --git a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts index a02ca4e8848..40e1a78578e 100644 --- a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts @@ -1,6 +1,6 @@ vi.resetModules(); -import { createDiagnosticCollector, EmitContext, Program } from "@typespec/compiler"; +import { createDiagnosticCollector, Diagnostic, EmitContext, Program } from "@typespec/compiler"; import { TestHost } from "@typespec/compiler/testing"; import { strictEqual } from "assert"; import { statSync } from "fs"; @@ -236,7 +236,7 @@ describe("Test _validateDotNetSdk", () => { let runner: TestHost; let program: Program; const minVersion = 8; - let _validateDotNetSdk: (arg0: any, arg1: number) => Promise; + let _validateDotNetSdk: (arg0: any, arg1: number) => Promise<[boolean, readonly Diagnostic[]]>; beforeEach(async () => { vi.resetModules(); @@ -270,11 +270,9 @@ describe("Test _validateDotNetSdk", () => { (execAsync as Mock).mockRejectedValueOnce(error); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - // Set up diagnostic collector for the test - sdkContext.__diagnostics = createDiagnosticCollector(); - const result = await _validateDotNetSdk(sdkContext, minVersion); + const [result, diagnostics] = await _validateDotNetSdk(sdkContext, minVersion); // Report collected diagnostics to program - program.reportDiagnostics(sdkContext.__diagnostics.diagnostics); + program.reportDiagnostics(diagnostics); expect(result).toBe(false); strictEqual(program.diagnostics.length, 1); strictEqual( @@ -332,11 +330,9 @@ describe("Test _validateDotNetSdk", () => { }); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - // Set up diagnostic collector for the test - sdkContext.__diagnostics = createDiagnosticCollector(); - const result = await _validateDotNetSdk(sdkContext, minVersion); + const [result, diagnostics] = await _validateDotNetSdk(sdkContext, minVersion); // Report collected diagnostics to program - program.reportDiagnostics(sdkContext.__diagnostics.diagnostics); + program.reportDiagnostics(diagnostics); expect(result).toBe(false); strictEqual(program.diagnostics.length, 1); strictEqual( diff --git a/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts index 95583fb6fc2..7c85bcfbede 100644 --- a/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts @@ -1,4 +1,5 @@ import { TestHost } from "@typespec/compiler/testing"; +import { createDiagnosticCollector } from "@typespec/compiler"; import { ok, strictEqual } from "assert"; import { beforeEach, describe, it } from "vitest"; import { fromSdkNamespaces } from "../../src/lib/namespace-converter.js"; @@ -50,7 +51,8 @@ describe("Namespace Converter", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); const sdkPackage = sdkContext.sdkPackage; - const parsedNamespaces = fromSdkNamespaces(sdkContext, sdkPackage.namespaces); + const diagnostics = createDiagnosticCollector(); + const parsedNamespaces = fromSdkNamespaces(sdkContext, sdkPackage.namespaces, diagnostics); strictEqual(parsedNamespaces.length, 1); From 36617286204df4af8a53ffeb5699efc542a163b1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 09:46:11 +0000 Subject: [PATCH 10/22] Fix remaining tests for diagnostic collection pattern Co-authored-by: ArcturusZhang <10554446+ArcturusZhang@users.noreply.github.com> --- .../http-client-csharp/emitter/test/Unit/emitter.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts index 40e1a78578e..56bf635ff2d 100644 --- a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts @@ -296,10 +296,10 @@ describe("Test _validateDotNetSdk", () => { }); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const result = await _validateDotNetSdk(sdkContext, minVersion); + const [result, diagnostics] = await _validateDotNetSdk(sdkContext, minVersion); expect(result).toBe(true); /* no diagnostics */ - strictEqual(program.diagnostics.length, 0); + strictEqual(diagnostics.length, 0); }); it("should return true for installed SDK version whose major greaters than min supported version", async () => { @@ -313,10 +313,10 @@ describe("Test _validateDotNetSdk", () => { }); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const result = await _validateDotNetSdk(sdkContext, minVersion); + const [result, diagnostics] = await _validateDotNetSdk(sdkContext, minVersion); expect(result).toBe(true); /* no diagnostics */ - strictEqual(program.diagnostics.length, 0); + strictEqual(diagnostics.length, 0); }); it("should return false and report diagnostic for invalid .NET SDK version", async () => { From 433e28f5b18c254aa1523c952e17637b7150c20f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 06:04:00 +0000 Subject: [PATCH 11/22] Fix diagnostic collection pattern to follow TypeSpec best practices This PR refactors the diagnostic collection pattern throughout the http-client-csharp emitter to follow TypeSpec best practices as recommended by code review feedback. Key Changes: - Each function creates its own DiagnosticCollector at the beginning - Functions return tuples [Result, readonly Diagnostic[]] instead of accepting DiagnosticCollector as a parameter - Callers use diagnostics.pipe() to unwrap and collect diagnostics - Functions return with diagnostics.wrap() at the end Files Modified: - lib/type-converter.ts: 13 functions updated - lib/client-converter.ts: 4 functions updated - lib/operation-converter.ts: 20+ functions updated - lib/namespace-converter.ts: 1 function updated - lib/client-model-builder.ts: Updated to use pipe() - code-model-writer.ts: 2 functions updated - emitter.ts: Updated to handle new signatures - lib/example-converter.ts: Updated to use pipe() - lib/typespec-server.ts: Updated to use pipe() Benefits: - Prevents issues with parallel execution - Each function has clear ownership of its diagnostics - More composable and type-safe - Consistent with TypeSpec ecosystem patterns All tests pass successfully. --- .../emitter/src/code-model-writer.ts | 15 +- .../http-client-csharp/emitter/src/emitter.ts | 5 +- .../emitter/src/lib/client-converter.ts | 60 ++-- .../emitter/src/lib/client-model-builder.ts | 16 +- .../emitter/src/lib/example-converter.ts | 20 +- .../emitter/src/lib/namespace-converter.ts | 8 +- .../emitter/src/lib/operation-converter.ts | 309 +++++++++--------- .../emitter/src/lib/type-converter.ts | 169 +++++----- .../emitter/src/lib/typespec-server.ts | 2 +- .../test/Unit/namespace-converter.test.ts | 3 +- 10 files changed, 311 insertions(+), 296 deletions(-) diff --git a/packages/http-client-csharp/emitter/src/code-model-writer.ts b/packages/http-client-csharp/emitter/src/code-model-writer.ts index 92366b37161..4884276778f 100644 --- a/packages/http-client-csharp/emitter/src/code-model-writer.ts +++ b/packages/http-client-csharp/emitter/src/code-model-writer.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. import { UsageFlags } from "@azure-tools/typespec-client-generator-core"; -import { DiagnosticCollector, NoTarget, resolvePath } from "@typespec/compiler"; +import { createDiagnosticCollector, Diagnostic, NoTarget, resolvePath } from "@typespec/compiler"; import { configurationFileName, tspOutputFileName } from "./constants.js"; import { createDiagnostic } from "./lib/lib.js"; import { CSharpEmitterContext } from "./sdk-context.js"; @@ -20,25 +20,26 @@ export async function writeCodeModel( context: CSharpEmitterContext, codeModel: CodeModel, outputFolder: string, - diagnostics: DiagnosticCollector, -) { +): Promise { + const diagnostics = createDiagnosticCollector(); await context.program.host.writeFile( resolvePath(outputFolder, tspOutputFileName), - prettierOutput(JSON.stringify(buildJson(context, codeModel, diagnostics), transformJSONProperties, 2)), + prettierOutput(JSON.stringify(diagnostics.pipe(buildJson(context, codeModel)), transformJSONProperties, 2)), ); + return diagnostics.diagnostics; } /** * This function builds a json from code model with refs and ids in it. * @param context - The CSharp emitter context * @param codeModel - The code model to build - * @param diagnostics - The diagnostic collector */ -function buildJson(context: CSharpEmitterContext, codeModel: CodeModel, diagnostics: DiagnosticCollector): any { +function buildJson(context: CSharpEmitterContext, codeModel: CodeModel): [any, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const objectsIds = new Map(); const stack: any[] = []; - return doBuildJson(codeModel, stack); + return diagnostics.wrap(doBuildJson(codeModel, stack)); function doBuildJson(obj: any, stack: any[]): any { // check if this is a primitive type or null or undefined diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index ced465fd2ec..3f0107f862d 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -116,9 +116,8 @@ export async function createCodeModel( } // emit tspCodeModel.json - const writeDiagnostics = createDiagnosticCollector(); - await writeCodeModel(sdkContext, updatedRoot, outputFolder, writeDiagnostics); - diagnostics.push(...writeDiagnostics.diagnostics); + const writeDiagnostics = await writeCodeModel(sdkContext, updatedRoot, outputFolder); + diagnostics.push(...writeDiagnostics); const namespace = updatedRoot.name; const configurations: Configuration = createConfiguration(options, namespace, sdkContext); diff --git a/packages/http-client-csharp/emitter/src/lib/client-converter.ts b/packages/http-client-csharp/emitter/src/lib/client-converter.ts index 1e1b2640c11..b681251de0a 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-converter.ts @@ -9,7 +9,7 @@ import { SdkHttpOperation, SdkMethodParameter, } from "@azure-tools/typespec-client-generator-core"; -import { DiagnosticCollector, NoTarget } from "@typespec/compiler"; +import { createDiagnosticCollector, Diagnostic, NoTarget } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { createDiagnostic } from "./lib.js"; import { InputParameterScope } from "../type/input-parameter-scope.js"; @@ -33,26 +33,26 @@ export function fromSdkClients( sdkContext: CSharpEmitterContext, clients: SdkClientType[], rootApiVersions: string[], - diagnostics: DiagnosticCollector, -): InputClient[] { +): [InputClient[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const inputClients: InputClient[] = []; for (const client of clients) { - const inputClient = fromSdkClient(sdkContext, client, rootApiVersions, diagnostics); + const inputClient = diagnostics.pipe(fromSdkClient(sdkContext, client, rootApiVersions)); inputClients.push(inputClient); } - return inputClients; + return diagnostics.wrap(inputClients); } function fromSdkClient( sdkContext: CSharpEmitterContext, client: SdkClientType, rootApiVersions: string[], - diagnostics: DiagnosticCollector, -): InputClient { +): [InputClient, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); let inputClient: InputClient | undefined = sdkContext.__typeCache.clients.get(client); if (inputClient) { - return inputClient; + return diagnostics.wrap(inputClient); } const endpointParameter = client.clientInitialization.parameters.find( (p) => p.kind === "endpoint", @@ -60,12 +60,11 @@ function fromSdkClient( const uri = getMethodUri(endpointParameter); // Convert all clientInitialization parameters - const clientParameters = fromSdkClientInitializationParameters( + const clientParameters = diagnostics.pipe(fromSdkClientInitializationParameters( sdkContext, client.clientInitialization.parameters, client.namespace, - diagnostics, - ); + )); inputClient = { kind: "client", @@ -74,7 +73,7 @@ function fromSdkClient( doc: client.doc, summary: client.summary, methods: client.methods - .map((m) => fromSdkServiceMethod(sdkContext, m, uri, rootApiVersions, client.namespace, diagnostics)) + .map((m) => diagnostics.pipe(fromSdkServiceMethod(sdkContext, m, uri, rootApiVersions, client.namespace))) .filter((m) => m !== undefined), parameters: clientParameters, initializedBy: client.clientInitialization.initializedBy, @@ -90,51 +89,53 @@ function fromSdkClient( // fill parent if (client.parent) { - inputClient.parent = fromSdkClient(sdkContext, client.parent, rootApiVersions, diagnostics); + inputClient.parent = diagnostics.pipe(fromSdkClient(sdkContext, client.parent, rootApiVersions)); } // fill children if (client.children) { inputClient.children = client.children.map((c) => - fromSdkClient(sdkContext, c, rootApiVersions, diagnostics), + diagnostics.pipe(fromSdkClient(sdkContext, c, rootApiVersions)), ); } - return inputClient; + return diagnostics.wrap(inputClient); function fromSdkClientInitializationParameters( sdkContext: CSharpEmitterContext, parameters: (SdkEndpointParameter | SdkCredentialParameter | SdkMethodParameter)[], namespace: string, - diagnostics: DiagnosticCollector, - ): InputParameter[] { + ): [InputParameter[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const inputParameters: InputParameter[] = []; for (const param of parameters) { if (param.kind === "endpoint") { // Convert endpoint parameters - const endpointParams = fromSdkEndpointParameter(param, diagnostics); + const endpointParams = diagnostics.pipe(fromSdkEndpointParameter(param)); inputParameters.push(...endpointParams); } else if (param.kind === "method") { // Convert method parameters - const methodParam = fromMethodParameter(sdkContext, param, namespace, diagnostics); + const methodParam = diagnostics.pipe(fromMethodParameter(sdkContext, param, namespace)); inputParameters.push(methodParam); } // Note: credential parameters are handled separately in service-authentication.ts // and are not included in the client parameters list } - return inputParameters; + return diagnostics.wrap(inputParameters); } - function fromSdkEndpointParameter(p: SdkEndpointParameter, diagnostics: DiagnosticCollector): InputEndpointParameter[] { + function fromSdkEndpointParameter(p: SdkEndpointParameter): [InputEndpointParameter[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); if (p.type.kind === "union") { - return fromSdkEndpointType(p.type.variantTypes[0], diagnostics); + return diagnostics.wrap(diagnostics.pipe(fromSdkEndpointType(p.type.variantTypes[0]))); } else { - return fromSdkEndpointType(p.type, diagnostics); + return diagnostics.wrap(diagnostics.pipe(fromSdkEndpointType(p.type))); } } - function fromSdkEndpointType(type: SdkEndpointType, diagnostics: DiagnosticCollector): InputEndpointParameter[] { + function fromSdkEndpointType(type: SdkEndpointType): [InputEndpointParameter[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); // TODO: support free-style endpoint url with multiple parameters const endpointExpr = type.serverUrl .replace("https://", "") @@ -148,7 +149,7 @@ function fromSdkClient( target: NoTarget, }), ); - return []; + return diagnostics.wrap([]); } const endpointVariableName = endpointExpr.substring(1, endpointExpr.length - 1); @@ -162,7 +163,7 @@ function fromSdkClient( crossLanguageDefinitionId: parameter.type.kind === "string" ? "TypeSpec.string" : "TypeSpec.url", } - : fromSdkType(sdkContext, parameter.type, diagnostics); // TODO: consolidate with converter.fromSdkEndpointType + : diagnostics.pipe(fromSdkType(sdkContext, parameter.type)); // TODO: consolidate with converter.fromSdkEndpointType parameters.push({ kind: "endpoint", name: parameter.name, @@ -174,19 +175,18 @@ function fromSdkClient( optional: parameter.optional, scope: InputParameterScope.Client, isEndpoint: isEndpoint, - defaultValue: getParameterDefaultValue( + defaultValue: diagnostics.pipe(getParameterDefaultValue( sdkContext, parameter.clientDefaultValue, parameterType, - diagnostics, - ), + )), serverUrlTemplate: type.serverUrl, skipUrlEncoding: false, readOnly: isReadOnly(parameter), crossLanguageDefinitionId: parameter.crossLanguageDefinitionId, }); } - return parameters; + return diagnostics.wrap(parameters); } } diff --git a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts index ba317e411e5..4f8cd114594 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts @@ -7,7 +7,7 @@ import { SdkHttpOperation, UsageFlags, } from "@azure-tools/typespec-client-generator-core"; -import { createDiagnosticCollector, Diagnostic, DiagnosticCollector } from "@typespec/compiler"; +import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { CodeModel } from "../type/code-model.js"; import { InputEnumType, InputLiteralType, InputModelType } from "../type/input-type.js"; @@ -43,9 +43,9 @@ export function createModel(sdkContext: CSharpEmitterContext): [CodeModel, reado const sdkPackage = sdkContext.sdkPackage; // TO-DO: Consider exposing the namespace hierarchy in the code model https://github.com/microsoft/typespec/issues/8332 - fromSdkNamespaces(sdkContext, sdkPackage.namespaces, diagnostics); + diagnostics.pipe(fromSdkNamespaces(sdkContext, sdkPackage.namespaces)); // TO-DO: Consider using the TCGC model + enum cache once https://github.com/Azure/typespec-azure/issues/3180 is resolved - navigateModels(sdkContext, diagnostics); + diagnostics.pipe(navigateModels(sdkContext)); const types = Array.from(sdkContext.__typeCache.types.values()); const [models, enums] = [ @@ -55,7 +55,7 @@ export function createModel(sdkContext: CSharpEmitterContext): [CodeModel, reado const rootClients = sdkPackage.clients; const rootApiVersions = parseApiVersions(sdkPackage.enums, rootClients); - const inputClients = fromSdkClients(sdkContext, rootClients, rootApiVersions, diagnostics); + const inputClients = diagnostics.pipe(fromSdkClients(sdkContext, rootClients, rootApiVersions)); // TODO -- TCGC now does not have constants field in its sdkPackage, they might add it in the future. const constants = Array.from(sdkContext.__typeCache.constants.values()); @@ -165,11 +165,13 @@ function fixNamingConflicts(models: InputModelType[], constants: InputLiteralTyp } } -function navigateModels(sdkContext: CSharpEmitterContext, diagnostics: DiagnosticCollector) { +function navigateModels(sdkContext: CSharpEmitterContext): [void, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); for (const m of sdkContext.sdkPackage.models) { - fromSdkType(sdkContext, m, diagnostics); + diagnostics.pipe(fromSdkType(sdkContext, m)); } for (const e of sdkContext.sdkPackage.enums) { - fromSdkType(sdkContext, e, diagnostics); + diagnostics.pipe(fromSdkType(sdkContext, e)); } + return diagnostics.wrap(undefined as void); } diff --git a/packages/http-client-csharp/emitter/src/lib/example-converter.ts b/packages/http-client-csharp/emitter/src/lib/example-converter.ts index bdd78190b05..c1300ee2082 100644 --- a/packages/http-client-csharp/emitter/src/lib/example-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/example-converter.ts @@ -81,7 +81,7 @@ export function fromSdkHttpExamples( responseValue: SdkHttpResponseExampleValue, ): OperationResponseExample { return { - response: fromSdkHttpOperationResponse(sdkContext, responseValue.response, diagnostics), + response: diagnostics.pipe(fromSdkHttpOperationResponse(sdkContext, responseValue.response)), statusCode: responseValue.statusCode, bodyValue: responseValue.bodyValue ? fromSdkExample(responseValue.bodyValue) : undefined, }; @@ -113,7 +113,7 @@ export function fromSdkHttpExamples( function fromSdkStringExample(example: SdkStringExampleValue): InputStringExampleValue { return { kind: "string", - type: fromSdkType(sdkContext, example.type, diagnostics), + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)), value: example.value, }; } @@ -121,7 +121,7 @@ export function fromSdkHttpExamples( function fromSdkNumberExample(example: SdkNumberExampleValue): InputNumberExampleValue { return { kind: "number", - type: fromSdkType(sdkContext, example.type, diagnostics), + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)), value: example.value, }; } @@ -129,7 +129,7 @@ export function fromSdkHttpExamples( function fromSdkBooleanExample(example: SdkBooleanExampleValue): InputBooleanExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type, diagnostics) as InputPrimitiveType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputPrimitiveType, value: example.value, }; } @@ -137,7 +137,7 @@ export function fromSdkHttpExamples( function fromSdkUnionExample(example: SdkUnionExampleValue): InputUnionExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type, diagnostics) as InputUnionType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputUnionType, value: example.value, }; } @@ -145,7 +145,7 @@ export function fromSdkHttpExamples( function fromSdkArrayExample(example: SdkArrayExampleValue): InputArrayExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type, diagnostics) as InputArrayType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputArrayType, value: example.value.map((v) => fromSdkExample(v)), }; } @@ -155,7 +155,7 @@ export function fromSdkHttpExamples( ): InputDictionaryExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type, diagnostics) as InputDictionaryType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputDictionaryType, value: fromExampleRecord(example.value), }; } @@ -163,7 +163,7 @@ export function fromSdkHttpExamples( function fromSdkModelExample(example: SdkModelExampleValue): InputModelExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type, diagnostics) as InputModelType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputModelType, value: fromExampleRecord(example.value), additionalPropertiesValue: example.additionalPropertiesValue ? fromExampleRecord(example.additionalPropertiesValue) @@ -174,7 +174,7 @@ export function fromSdkHttpExamples( function fromSdkAnyExample(example: SdkUnknownExampleValue): InputUnknownExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type, diagnostics) as InputPrimitiveType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputPrimitiveType, value: example.value, }; } @@ -182,7 +182,7 @@ export function fromSdkHttpExamples( function fromSdkNullExample(example: SdkNullExampleValue): InputNullExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type, diagnostics) as InputNullableType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputNullableType, value: example.value, }; } diff --git a/packages/http-client-csharp/emitter/src/lib/namespace-converter.ts b/packages/http-client-csharp/emitter/src/lib/namespace-converter.ts index fdb1cc0d0bd..cfc2eb761a0 100644 --- a/packages/http-client-csharp/emitter/src/lib/namespace-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/namespace-converter.ts @@ -1,20 +1,20 @@ import { SdkHttpOperation, SdkNamespace } from "@azure-tools/typespec-client-generator-core"; -import { DiagnosticCollector } from "@typespec/compiler"; +import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { InputNamespace } from "../type/input-type.js"; export function fromSdkNamespaces( sdkContext: CSharpEmitterContext, namespaces: SdkNamespace[], - diagnostics: DiagnosticCollector, -): InputNamespace[] { +): [InputNamespace[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const inputNamespaces: InputNamespace[] = []; for (const namespace of namespaces) { const inputNamespace = fromSdkNamespace(sdkContext, namespace); inputNamespaces.push(inputNamespace); } - return inputNamespaces; + return diagnostics.wrap(inputNamespaces); } function fromSdkNamespace( diff --git a/packages/http-client-csharp/emitter/src/lib/operation-converter.ts b/packages/http-client-csharp/emitter/src/lib/operation-converter.ts index 3359a17e2eb..ac1dcf22406 100644 --- a/packages/http-client-csharp/emitter/src/lib/operation-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/operation-converter.ts @@ -25,7 +25,13 @@ import { shouldGenerateConvenient, shouldGenerateProtocol, } from "@azure-tools/typespec-client-generator-core"; -import { DiagnosticCollector, getDeprecated, isErrorModel, NoTarget } from "@typespec/compiler"; +import { + createDiagnosticCollector, + Diagnostic, + getDeprecated, + isErrorModel, + NoTarget, +} from "@typespec/compiler"; import { HttpStatusCodeRange } from "@typespec/http"; import { getResourceOperation } from "@typespec/rest"; import { CSharpEmitterContext } from "../sdk-context.js"; @@ -72,74 +78,69 @@ export function fromSdkServiceMethod( uri: string, rootApiVersions: string[], namespace: string, - diagnostics: DiagnosticCollector, -): InputServiceMethod | undefined { +): [InputServiceMethod | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + let method = sdkContext.__typeCache.methods.get(sdkMethod); if (method) { - return method; + return diagnostics.wrap(method); } const methodKind = sdkMethod.kind; switch (methodKind) { case "basic": - method = createServiceMethod( + method = diagnostics.pipe(createServiceMethod( sdkContext, sdkMethod, uri, rootApiVersions, namespace, - diagnostics, - ); + )); break; case "paging": - const pagingServiceMethod = createServiceMethod( + const pagingServiceMethod = diagnostics.pipe(createServiceMethod( sdkContext, sdkMethod, uri, rootApiVersions, namespace, - diagnostics, - ); - pagingServiceMethod.pagingMetadata = loadPagingServiceMetadata( + )); + pagingServiceMethod.pagingMetadata = diagnostics.pipe(loadPagingServiceMetadata( sdkContext, sdkMethod, rootApiVersions, uri, namespace, - diagnostics, - ); + )); method = pagingServiceMethod; break; case "lro": - const lroServiceMethod = createServiceMethod( + const lroServiceMethod = diagnostics.pipe(createServiceMethod( sdkContext, sdkMethod, uri, rootApiVersions, namespace, - diagnostics, - ); - lroServiceMethod.lroMetadata = loadLongRunningMetadata(sdkContext, sdkMethod, diagnostics); + )); + lroServiceMethod.lroMetadata = diagnostics.pipe(loadLongRunningMetadata(sdkContext, sdkMethod)); method = lroServiceMethod; break; case "lropaging": - const lroPagingMethod = createServiceMethod( + const lroPagingMethod = diagnostics.pipe(createServiceMethod( sdkContext, sdkMethod, uri, rootApiVersions, namespace, - diagnostics, - ); - lroPagingMethod.lroMetadata = loadLongRunningMetadata(sdkContext, sdkMethod, diagnostics); - lroPagingMethod.pagingMetadata = loadPagingServiceMetadata( + )); + lroPagingMethod.lroMetadata = diagnostics.pipe(loadLongRunningMetadata(sdkContext, sdkMethod)); + lroPagingMethod.pagingMetadata = diagnostics.pipe(loadPagingServiceMetadata( sdkContext, sdkMethod, rootApiVersions, uri, namespace, - diagnostics, - ); + )); method = lroPagingMethod; break; default: @@ -158,7 +159,7 @@ export function fromSdkServiceMethod( sdkContext.__typeCache.updateSdkMethodReferences(sdkMethod, method); } - return method; + return diagnostics.wrap(method); } export function fromSdkServiceMethodOperation( @@ -166,11 +167,12 @@ export function fromSdkServiceMethodOperation( method: SdkServiceMethod, uri: string, rootApiVersions: string[], - diagnostics: DiagnosticCollector, -): InputOperation { +): [InputOperation, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + let operation = sdkContext.__typeCache.operations.get(method.operation); if (operation) { - return operation; + return diagnostics.wrap(operation); } let generateConvenience = shouldGenerateConvenient(sdkContext, method.operation.__raw.operation); @@ -197,8 +199,8 @@ export function fromSdkServiceMethodOperation( summary: method.summary, doc: method.doc, accessibility: method.access, - parameters: fromSdkOperationParameters(sdkContext, method.operation, rootApiVersions, diagnostics), - responses: fromSdkHttpOperationResponses(sdkContext, method.operation.responses, diagnostics), + parameters: diagnostics.pipe(fromSdkOperationParameters(sdkContext, method.operation, rootApiVersions)), + responses: diagnostics.pipe(fromSdkHttpOperationResponses(sdkContext, method.operation.responses)), httpMethod: parseHttpRequestMethod(method.operation.verb), uri: uri, path: method.operation.path, @@ -216,32 +218,33 @@ export function fromSdkServiceMethodOperation( sdkContext.__typeCache.updateSdkOperationReferences(method.operation, operation); - return operation; + return diagnostics.wrap(operation); } export function getParameterDefaultValue( sdkContext: CSharpEmitterContext, clientDefaultValue: any, parameterType: InputType, - diagnostics: DiagnosticCollector, -): InputConstant | undefined { +): [InputConstant | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + if ( clientDefaultValue === undefined || // a constant parameter should overwrite client default value parameterType.kind === "constant" ) { - return undefined; + return diagnostics.wrap(undefined); } - const kind = getValueType(sdkContext, clientDefaultValue, diagnostics); - return { + const kind = diagnostics.pipe(getValueType(sdkContext, clientDefaultValue)); + return diagnostics.wrap({ type: { kind: kind, name: kind, crossLanguageDefinitionId: `TypeSpec.${kind}`, }, value: clientDefaultValue, - }; + }); } function createServiceMethod( @@ -250,38 +253,41 @@ function createServiceMethod( uri: string, rootApiVersions: string[], namespace: string, - diagnostics: DiagnosticCollector, -): T { - return { +): [T, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + + return diagnostics.wrap({ kind: method.kind, name: method.name, accessibility: method.access, apiVersions: method.apiVersions, doc: method.doc, summary: method.summary, - operation: fromSdkServiceMethodOperation(sdkContext, method, uri, rootApiVersions, diagnostics), - parameters: fromSdkServiceMethodParameters(sdkContext, method, rootApiVersions, namespace, diagnostics), - response: fromSdkServiceMethodResponse(sdkContext, method.response, diagnostics), + operation: diagnostics.pipe(fromSdkServiceMethodOperation(sdkContext, method, uri, rootApiVersions)), + parameters: diagnostics.pipe(fromSdkServiceMethodParameters(sdkContext, method, rootApiVersions, namespace)), + response: diagnostics.pipe(fromSdkServiceMethodResponse(sdkContext, method.response)), exception: method.exception - ? fromSdkServiceMethodResponse(sdkContext, method.exception, diagnostics) + ? diagnostics.pipe(fromSdkServiceMethodResponse(sdkContext, method.exception)) : undefined, isOverride: method.isOverride, generateConvenient: method.generateConvenient, generateProtocol: method.generateProtocol, crossLanguageDefinitionId: method.crossLanguageDefinitionId, - } as T; + } as T); } -function getValueType(sdkContext: CSharpEmitterContext, value: any, diagnostics: DiagnosticCollector): SdkBuiltInKinds { +function getValueType(sdkContext: CSharpEmitterContext, value: any): [SdkBuiltInKinds, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + switch (typeof value) { case "string": - return "string"; + return diagnostics.wrap("string"); case "number": - return "int32"; + return diagnostics.wrap("int32"); case "boolean": - return "boolean"; + return diagnostics.wrap("boolean"); case "bigint": - return "int64"; + return diagnostics.wrap("int64"); default: diagnostics.add( createDiagnostic({ @@ -290,7 +296,7 @@ function getValueType(sdkContext: CSharpEmitterContext, value: any, diagnostics: target: NoTarget, }), ); - return "unknown"; + return diagnostics.wrap("unknown"); } } @@ -299,12 +305,12 @@ function fromSdkServiceMethodParameters( method: SdkServiceMethod, rootApiVersions: string[], namespace: string, - diagnostics: DiagnosticCollector, -): InputMethodParameter[] { +): [InputMethodParameter[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const parameters: InputMethodParameter[] = []; for (const p of method.parameters) { - const methodInputParameter = fromMethodParameter(sdkContext, p, namespace, diagnostics); + const methodInputParameter = diagnostics.pipe(fromMethodParameter(sdkContext, p, namespace)); const operationHttpParameter = getHttpOperationParameter(method, p); if (!operationHttpParameter) { @@ -323,7 +329,7 @@ function fromSdkServiceMethodParameters( parameters.push(methodInputParameter); } - return parameters; + return diagnostics.wrap(parameters); } function updateMethodParameter( @@ -331,7 +337,7 @@ function updateMethodParameter( methodParameter: InputMethodParameter, operationHttpParameter: SdkHttpParameter | SdkModelPropertyType, rootApiVersions: string[], - diagnostics: DiagnosticCollector, + diagnostics: ReturnType, ): void { methodParameter.serializedName = getNameInRequest(operationHttpParameter); methodParameter.location = getParameterLocation(operationHttpParameter); @@ -343,7 +349,7 @@ function updateMethodParameter( if (methodParameter.location === RequestLocation.Body) { // Convert constants to enums if (methodParameter.type.kind === "constant") { - methodParameter.type = fromSdkType(sdkContext, operationHttpParameter.type, diagnostics); + methodParameter.type = diagnostics.pipe(fromSdkType(sdkContext, operationHttpParameter.type)); } } } @@ -351,23 +357,25 @@ function updateMethodParameter( function fromSdkServiceMethodResponse( sdkContext: CSharpEmitterContext, methodResponse: SdkMethodResponse, - diagnostics: DiagnosticCollector, -): InputServiceMethodResponse { - return { - type: getResponseType(sdkContext, methodResponse.type, diagnostics), +): [InputServiceMethodResponse, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + + return diagnostics.wrap({ + type: diagnostics.pipe(getResponseType(sdkContext, methodResponse.type)), resultSegments: methodResponse.resultSegments?.map((segment) => getResponseSegmentName(segment), ), - }; + }); } function fromSdkOperationParameters( sdkContext: CSharpEmitterContext, operation: SdkHttpOperation, rootApiVersions: string[], - diagnostics: DiagnosticCollector, -): InputHttpParameter[] { +): [InputHttpParameter[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const parameters: InputHttpParameter[] = []; + for (const p of operation.parameters) { if (p.kind === "cookie") { diagnostics.add( @@ -377,47 +385,48 @@ function fromSdkOperationParameters( target: NoTarget, }), ); - return parameters; + return diagnostics.wrap(parameters); } - const param = fromParameter(sdkContext, p, rootApiVersions, diagnostics); + const param = diagnostics.pipe(fromParameter(sdkContext, p, rootApiVersions)); if (param) { parameters.push(param); } } if (operation.bodyParam) { - const bodyParam = fromParameter(sdkContext, operation.bodyParam, rootApiVersions, diagnostics); + const bodyParam = diagnostics.pipe(fromParameter(sdkContext, operation.bodyParam, rootApiVersions)); if (bodyParam) { parameters.push(bodyParam); } } - return parameters; + return diagnostics.wrap(parameters); } export function fromParameter( sdkContext: CSharpEmitterContext, p: SdkHttpParameter | SdkModelPropertyType, rootApiVersions: string[], - diagnostics: DiagnosticCollector, -): InputHttpParameter | undefined { +): [InputHttpParameter | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + let parameter = sdkContext.__typeCache.operationParameters.get(p); if (parameter) { - return parameter; + return diagnostics.wrap(parameter); } const parameterKind = p.kind; switch (parameterKind) { case "query": - parameter = fromQueryParameter(sdkContext, p, rootApiVersions, diagnostics); + parameter = diagnostics.pipe(fromQueryParameter(sdkContext, p, rootApiVersions)); break; case "path": - parameter = fromPathParameter(sdkContext, p, rootApiVersions, diagnostics); + parameter = diagnostics.pipe(fromPathParameter(sdkContext, p, rootApiVersions)); break; case "header": - parameter = fromHeaderParameter(sdkContext, p, rootApiVersions, diagnostics); + parameter = diagnostics.pipe(fromHeaderParameter(sdkContext, p, rootApiVersions)); break; case "body": - parameter = fromBodyParameter(sdkContext, p, rootApiVersions, diagnostics); + parameter = diagnostics.pipe(fromBodyParameter(sdkContext, p, rootApiVersions)); break; default: diagnostics.add( @@ -434,16 +443,16 @@ export function fromParameter( if (parameter) { sdkContext.__typeCache.operationParameters.set(p, parameter); } - return parameter; + return diagnostics.wrap(parameter); } function fromQueryParameter( sdkContext: CSharpEmitterContext, p: SdkQueryParameter, rootApiVersions: string[], - diagnostics: DiagnosticCollector, -): InputQueryParameter { - const parameterType = fromSdkType(sdkContext, p.type, diagnostics); +): [InputQueryParameter, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + const parameterType = diagnostics.pipe(fromSdkType(sdkContext, p.type)); const retVar: InputQueryParameter = { kind: "query", @@ -454,7 +463,7 @@ function fromQueryParameter( type: parameterType, isApiVersion: p.isApiVersionParam, explode: isExploded(p), - defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType, diagnostics), + defaultValue: diagnostics.pipe(getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType)), arraySerializationDelimiter: getArraySerializationDelimiter(p), optional: p.optional, scope: getParameterScope(p, parameterType, rootApiVersions.length > 0), @@ -464,16 +473,16 @@ function fromQueryParameter( }; sdkContext.__typeCache.updateSdkOperationParameterReferences(p, retVar); - return retVar; + return diagnostics.wrap(retVar); } function fromPathParameter( sdkContext: CSharpEmitterContext, p: SdkPathParameter, rootApiVersions: string[], - diagnostics: DiagnosticCollector, -): InputPathParameter { - const parameterType = fromSdkType(sdkContext, p.type, diagnostics); +): [InputPathParameter, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + const parameterType = diagnostics.pipe(fromSdkType(sdkContext, p.type)); const retVar: InputPathParameter = { kind: "path", @@ -487,7 +496,7 @@ function fromPathParameter( style: p.style, allowReserved: p.allowReserved, skipUrlEncoding: p.allowReserved, - defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType, diagnostics), + defaultValue: diagnostics.pipe(getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType)), optional: p.optional, scope: getParameterScope(p, parameterType, rootApiVersions.length > 0), decorators: p.decorators, @@ -496,16 +505,16 @@ function fromPathParameter( }; sdkContext.__typeCache.updateSdkOperationParameterReferences(p, retVar); - return retVar; + return diagnostics.wrap(retVar); } function fromHeaderParameter( sdkContext: CSharpEmitterContext, p: SdkHeaderParameter, rootApiVersions: string[], - diagnostics: DiagnosticCollector, -): InputHeaderParameter { - const parameterType = fromSdkType(sdkContext, p.type, diagnostics); +): [InputHeaderParameter, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + const parameterType = diagnostics.pipe(fromSdkType(sdkContext, p.type)); const retVar: InputHeaderParameter = { kind: "header", @@ -517,7 +526,7 @@ function fromHeaderParameter( isApiVersion: p.isApiVersionParam, collectionFormat: p.collectionFormat, arraySerializationDelimiter: getArraySerializationDelimiter(p), - defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType, diagnostics), + defaultValue: diagnostics.pipe(getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType)), optional: p.optional, isContentType: isContentType(p), scope: getParameterScope(p, parameterType, rootApiVersions.length > 0), @@ -527,16 +536,16 @@ function fromHeaderParameter( }; sdkContext.__typeCache.updateSdkOperationParameterReferences(p, retVar); - return retVar; + return diagnostics.wrap(retVar); } function fromBodyParameter( sdkContext: CSharpEmitterContext, p: SdkBodyParameter, rootApiVersions: string[], - diagnostics: DiagnosticCollector, -): InputBodyParameter { - const parameterType = fromSdkType(sdkContext, p.type, diagnostics); +): [InputBodyParameter, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + const parameterType = diagnostics.pipe(fromSdkType(sdkContext, p.type)); const retVar: InputBodyParameter = { kind: "body", @@ -556,21 +565,22 @@ function fromBodyParameter( }; sdkContext.__typeCache.updateSdkOperationParameterReferences(p, retVar); - return retVar; + return diagnostics.wrap(retVar); } export function fromMethodParameter( sdkContext: CSharpEmitterContext, p: SdkMethodParameter, namespace: string, - diagnostics: DiagnosticCollector, -): InputMethodParameter { +): [InputMethodParameter, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + let retVar = sdkContext.__typeCache.methodParmeters.get(p); if (retVar) { - return retVar as InputMethodParameter; + return diagnostics.wrap(retVar as InputMethodParameter); } - const parameterType = fromSdkType(sdkContext, p.type, diagnostics, p, namespace); + const parameterType = diagnostics.pipe(fromSdkType(sdkContext, p.type, p, namespace)); retVar = { kind: "method", @@ -581,7 +591,7 @@ export function fromMethodParameter( type: parameterType, location: RequestLocation.None, isApiVersion: p.isApiVersionParam, - defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType, diagnostics), + defaultValue: diagnostics.pipe(getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType)), optional: p.optional, scope: InputParameterScope.Method, crossLanguageDefinitionId: p.crossLanguageDefinitionId, @@ -591,15 +601,16 @@ export function fromMethodParameter( }; sdkContext.__typeCache.updateSdkMethodParameterReferences(p, retVar); - return retVar; + return diagnostics.wrap(retVar); } function loadLongRunningMetadata( sdkContext: CSharpEmitterContext, method: SdkLroServiceMethod | SdkLroPagingServiceMethod, - diagnostics: DiagnosticCollector, -): InputLongRunningServiceMetadata { - return { +): [InputLongRunningServiceMetadata, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + + return diagnostics.wrap({ finalStateVia: convertLroFinalStateVia(method.lroMetadata.finalStateVia), finalResponse: { // in swagger, we allow delete to return some meaningful body content @@ -607,64 +618,67 @@ function loadLongRunningMetadata( statusCodes: method.operation.verb === "delete" ? [204] : [200], bodyType: method.lroMetadata.finalResponse?.envelopeResult !== undefined - ? fromSdkType(sdkContext, method.lroMetadata.finalResponse.envelopeResult, diagnostics) + ? diagnostics.pipe(fromSdkType(sdkContext, method.lroMetadata.finalResponse.envelopeResult)) : undefined, } as OperationResponse, resultPath: method.lroMetadata.finalResultPath, - }; + }); } function fromSdkHttpOperationResponses( sdkContext: CSharpEmitterContext, operationResponses: SdkHttpResponse[], - diagnostics: DiagnosticCollector, -): OperationResponse[] { +): [OperationResponse[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const responses: OperationResponse[] = []; + for (const r of operationResponses) { - responses.push(fromSdkHttpOperationResponse(sdkContext, r, diagnostics)); + responses.push(diagnostics.pipe(fromSdkHttpOperationResponse(sdkContext, r))); } - return responses; + return diagnostics.wrap(responses); } export function fromSdkHttpOperationResponse( sdkContext: CSharpEmitterContext, sdkResponse: SdkHttpResponse, - diagnostics: DiagnosticCollector, -): OperationResponse { +): [OperationResponse, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + let retVar = sdkContext.__typeCache.responses.get(sdkResponse); if (retVar) { - return retVar; + return diagnostics.wrap(retVar); } const range = sdkResponse.statusCodes; retVar = { statusCodes: toStatusCodesArray(range), - bodyType: getResponseType(sdkContext, sdkResponse.type, diagnostics), - headers: fromSdkServiceResponseHeaders(sdkContext, sdkResponse.headers, diagnostics), + bodyType: diagnostics.pipe(getResponseType(sdkContext, sdkResponse.type)), + headers: diagnostics.pipe(fromSdkServiceResponseHeaders(sdkContext, sdkResponse.headers)), isErrorResponse: sdkResponse.type !== undefined && isErrorModel(sdkContext.program, sdkResponse.type.__raw!), contentTypes: sdkResponse.contentTypes, }; sdkContext.__typeCache.updateSdkResponseReferences(sdkResponse, retVar); - return retVar; + return diagnostics.wrap(retVar); } function fromSdkServiceResponseHeaders( sdkContext: CSharpEmitterContext, headers: SdkServiceResponseHeader[], - diagnostics: DiagnosticCollector, -): HttpResponseHeader[] { - return headers.map( +): [HttpResponseHeader[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + + return diagnostics.wrap(headers.map( (h) => ({ name: h.__raw!.name, nameInResponse: h.serializedName, summary: h.summary, doc: h.doc, - type: fromSdkType(sdkContext, h.type, diagnostics), + type: diagnostics.pipe(fromSdkType(sdkContext, h.type)), }) as HttpResponseHeader, - ); + )); } function toStatusCodesArray(range: number | HttpStatusCodeRange): number[] { @@ -716,31 +730,30 @@ function loadPagingServiceMetadata( rootApiVersions: string[], uri: string, namespace: string, - diagnostics: DiagnosticCollector, -): InputPagingServiceMetadata { +): [InputPagingServiceMetadata, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + let nextLink: InputNextLink | undefined; if (method.pagingMetadata.nextLinkSegments) { nextLink = { responseSegments: method.pagingMetadata.nextLinkSegments.map((segment) => getResponseSegmentName(segment), ), - responseLocation: getResponseLocation( + responseLocation: diagnostics.pipe(getResponseLocation( context, method, method.pagingMetadata.nextLinkSegments[0], - diagnostics, - ), + )), }; if (method.pagingMetadata.nextLinkOperation) { - nextLink.operation = fromSdkServiceMethod( + nextLink.operation = diagnostics.pipe(fromSdkServiceMethod( context, method.pagingMetadata.nextLinkOperation, uri, rootApiVersions, namespace, - diagnostics, - ); + )); } if ( @@ -755,7 +768,7 @@ function loadPagingServiceMetadata( ] as SdkModelPropertyType; const operationParameter = getHttpOperationParameter(method, lastParameterSegment); if (operationParameter) { - const parameter = fromParameter(context, operationParameter, rootApiVersions, diagnostics); + const parameter = diagnostics.pipe(fromParameter(context, operationParameter, rootApiVersions)); if (parameter) { nextLinkReInjectedParameters.push(parameter); } @@ -776,24 +789,22 @@ function loadPagingServiceMetadata( const lastParameterSegment = method.pagingMetadata.continuationTokenParameterSegments[ method.pagingMetadata.continuationTokenParameterSegments.length - 1 ] as SdkModelPropertyType; - const continuationTokenParameter = fromParameter( + const continuationTokenParameter = diagnostics.pipe(fromParameter( context, getHttpOperationParameter(method, lastParameterSegment)!, rootApiVersions, - diagnostics, - ); + )); if (continuationTokenParameter) { continuationToken = { parameter: continuationTokenParameter, responseSegments: method.pagingMetadata.continuationTokenResponseSegments!.map((segment) => getResponseSegmentName(segment), ), - responseLocation: getResponseLocation( + responseLocation: diagnostics.pipe(getResponseLocation( context, method, method.pagingMetadata.continuationTokenResponseSegments?.[0], - diagnostics, - ), + )), }; } } @@ -805,12 +816,12 @@ function loadPagingServiceMetadata( ); } - return { + return diagnostics.wrap({ itemPropertySegments: method.response.resultSegments!.map((s) => getResponseSegmentName(s)), nextLink: nextLink, continuationToken: continuationToken, pageSizeParameterSegments: pageSizeParameterSegments, - }; + }); } function getResponseSegmentName(segment: SdkServiceResponseHeader | SdkModelPropertyType): string { @@ -829,10 +840,11 @@ function getResponseLocation( context: CSharpEmitterContext, method: SdkPagingServiceMethod | SdkLroPagingServiceMethod, p: SdkServiceResponseHeader | SdkModelPropertyType, - diagnostics: DiagnosticCollector, -): ResponseLocation { +): [ResponseLocation, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + if (p.kind === "responseheader") { - return ResponseLocation.Header; + return diagnostics.wrap(ResponseLocation.Header); } if (isHttpMetadata(context, p)) { @@ -845,10 +857,10 @@ function getResponseLocation( target: NoTarget, }), ); - return ResponseLocation.None; + return diagnostics.wrap(ResponseLocation.None); } - return ResponseLocation.Body; + return diagnostics.wrap(ResponseLocation.Body); } // TODO: https://github.com/Azure/typespec-azure/issues/1441 @@ -959,16 +971,17 @@ function getArraySerializationDelimiter( function getResponseType( sdkContext: CSharpEmitterContext, type: SdkType | undefined, - diagnostics: DiagnosticCollector, -): InputType | undefined { +): [InputType | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + if (!type) { - return undefined; + return diagnostics.wrap(undefined); } // handle anonymous union enum response types by defaulting to the enum value type in the case of if (type.kind === "enum" && type.isUnionAsEnum && type.isGeneratedName) { - return fromSdkType(sdkContext, type.valueType, diagnostics); + return fromSdkType(sdkContext, type.valueType); } - return fromSdkType(sdkContext, type, diagnostics); + return fromSdkType(sdkContext, type); } diff --git a/packages/http-client-csharp/emitter/src/lib/type-converter.ts b/packages/http-client-csharp/emitter/src/lib/type-converter.ts index 0c97f2ae0b5..81cc99620a1 100644 --- a/packages/http-client-csharp/emitter/src/lib/type-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/type-converter.ts @@ -20,9 +20,8 @@ import { getAccessOverride, isHttpMetadata, } from "@azure-tools/typespec-client-generator-core"; -import { Model, NoTarget } from "@typespec/compiler"; +import { createDiagnosticCollector, Diagnostic, Model, NoTarget } from "@typespec/compiler"; import { createDiagnostic } from "./lib.js"; -import { DiagnosticCollector } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { InputArrayType, @@ -75,39 +74,39 @@ type InputReturnType = T extends { kind: "nullable" } export function fromSdkType( sdkContext: CSharpEmitterContext, sdkType: T, - diagnostics: DiagnosticCollector, sdkProperty?: SdkModelPropertyTypeBase, namespace?: string, -): InputReturnType { +): [InputReturnType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); let retVar = sdkContext.__typeCache.types.get(sdkType); if (retVar) { - return retVar as any; + return diagnostics.wrap(retVar as any); } switch (sdkType.kind) { case "nullable": const nullableType: InputNullableType = { kind: "nullable", - type: fromSdkType(sdkContext, sdkType.type, diagnostics, sdkProperty, namespace), + type: diagnostics.pipe(fromSdkType(sdkContext, sdkType.type, sdkProperty, namespace)), namespace: sdkType.namespace, external: fromSdkExternalTypeInfo(sdkType), }; retVar = nullableType; break; case "model": - retVar = fromSdkModelType(sdkContext, sdkType, diagnostics); + retVar = diagnostics.pipe(fromSdkModelType(sdkContext, sdkType)); break; case "enum": - retVar = fromSdkEnumType(sdkContext, sdkType, diagnostics); + retVar = diagnostics.pipe(fromSdkEnumType(sdkContext, sdkType)); break; case "enumvalue": - retVar = fromSdkEnumValueType(sdkContext, sdkType, diagnostics); + retVar = diagnostics.pipe(fromSdkEnumValueType(sdkContext, sdkType)); break; case "dict": - retVar = fromSdkDictionaryType(sdkContext, sdkType, diagnostics); + retVar = diagnostics.pipe(fromSdkDictionaryType(sdkContext, sdkType)); break; case "array": - retVar = fromSdkArrayType(sdkContext, sdkType, diagnostics); + retVar = diagnostics.pipe(fromSdkArrayType(sdkContext, sdkType)); break; case "constant": if ( @@ -117,20 +116,20 @@ export function fromSdkType( sdkType.valueType.kind !== "boolean" ) { // turn the constant into an extensible enum - retVar = createEnumType(sdkContext, sdkType, namespace!, diagnostics); + retVar = diagnostics.pipe(createEnumType(sdkContext, sdkType, namespace!)); } else { - retVar = fromSdkConstantType(sdkContext, sdkType, diagnostics); + retVar = diagnostics.pipe(fromSdkConstantType(sdkContext, sdkType)); } break; case "union": - retVar = fromUnionType(sdkContext, sdkType, diagnostics); + retVar = diagnostics.pipe(fromUnionType(sdkContext, sdkType)); break; case "utcDateTime": case "offsetDateTime": - retVar = fromSdkDateTimeType(sdkContext, sdkType, diagnostics); + retVar = diagnostics.pipe(fromSdkDateTimeType(sdkContext, sdkType)); break; case "duration": - retVar = fromSdkDurationType(sdkContext, sdkType, diagnostics); + retVar = diagnostics.pipe(fromSdkDurationType(sdkContext, sdkType)); break; case "tuple": diagnostics.add( @@ -172,20 +171,20 @@ export function fromSdkType( retVar = credentialType; break; default: - retVar = fromSdkBuiltInType(sdkContext, sdkType, diagnostics); + retVar = diagnostics.pipe(fromSdkBuiltInType(sdkContext, sdkType)); break; } sdkContext.__typeCache.updateSdkTypeReferences(sdkType, retVar); // we have to cast to any because TypeScript's type narrowing does not automatically infer the return type for conditional types - return retVar as any; + return diagnostics.wrap(retVar as any); } function fromSdkModelType( sdkContext: CSharpEmitterContext, modelType: SdkModelType, - diagnostics: DiagnosticCollector, -): InputModelType { +): [InputModelType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); // get all unique decorators for the model type from the namespace level and the model level let decorators: DecoratorInfo[] = modelType.decorators; const namespace = sdkContext.__typeCache.namespaces.get(modelType.namespace); @@ -210,12 +209,12 @@ function fromSdkModelType( sdkContext.__typeCache.updateSdkTypeReferences(modelType, inputModelType); inputModelType.additionalProperties = modelType.additionalProperties - ? fromSdkType(sdkContext, modelType.additionalProperties, diagnostics) + ? diagnostics.pipe(fromSdkType(sdkContext, modelType.additionalProperties)) : undefined; const properties: InputModelProperty[] = []; for (const property of modelType.properties) { - const ourProperty = fromSdkModelProperty(sdkContext, property, modelType, diagnostics); + const ourProperty = diagnostics.pipe(fromSdkModelProperty(sdkContext, property, modelType)); if (ourProperty) { properties.push(ourProperty); @@ -223,11 +222,11 @@ function fromSdkModelType( } inputModelType.discriminatorProperty = modelType.discriminatorProperty - ? fromSdkModelProperty(sdkContext, modelType.discriminatorProperty, modelType, diagnostics) + ? diagnostics.pipe(fromSdkModelProperty(sdkContext, modelType.discriminatorProperty, modelType)) : undefined; inputModelType.baseModel = modelType.baseModel - ? fromSdkType(sdkContext, modelType.baseModel, diagnostics) + ? diagnostics.pipe(fromSdkType(sdkContext, modelType.baseModel)) : undefined; inputModelType.properties = properties; @@ -236,26 +235,26 @@ function fromSdkModelType( const discriminatedSubtypes: Record = {}; for (const key in modelType.discriminatedSubtypes) { const subtype = modelType.discriminatedSubtypes[key]; - discriminatedSubtypes[key] = fromSdkType(sdkContext, subtype, diagnostics); + discriminatedSubtypes[key] = diagnostics.pipe(fromSdkType(sdkContext, subtype)); } inputModelType.discriminatedSubtypes = discriminatedSubtypes; } - return inputModelType; + return diagnostics.wrap(inputModelType); } function fromSdkModelProperty( sdkContext: CSharpEmitterContext, sdkProperty: SdkModelPropertyType, sdkModel: SdkModelType, - diagnostics: DiagnosticCollector, -): InputModelProperty | undefined { +): [InputModelProperty | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); // TODO -- this returns undefined because some properties we do not support yet. let property = sdkContext.__typeCache.properties.get(sdkProperty) as | InputModelProperty | undefined; if (property) { - return property; + return diagnostics.wrap(property); } const serializedName = @@ -268,7 +267,7 @@ function fromSdkModelProperty( serializedName: serializedName, summary: sdkProperty.summary, doc: sdkProperty.doc, - type: fromSdkType(sdkContext, sdkProperty.type, diagnostics, sdkProperty, sdkModel.namespace), + type: diagnostics.pipe(fromSdkType(sdkContext, sdkProperty.type, sdkProperty, sdkModel.namespace)), optional: sdkProperty.optional, readOnly: isReadOnly(sdkProperty), discriminator: sdkProperty.discriminator, @@ -284,19 +283,20 @@ function fromSdkModelProperty( sdkContext.__typeCache.updateSdkPropertyReferences(sdkProperty, property); } - return property; + return diagnostics.wrap(property); } -function fromSdkEnumType(sdkContext: CSharpEmitterContext, enumType: SdkEnumType, diagnostics: DiagnosticCollector): InputEnumType { - return createEnumType(sdkContext, enumType, enumType.namespace, diagnostics); +function fromSdkEnumType(sdkContext: CSharpEmitterContext, enumType: SdkEnumType): [InputEnumType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap(diagnostics.pipe(createEnumType(sdkContext, enumType, enumType.namespace))); } function createEnumType( sdkContext: CSharpEmitterContext, sdkType: SdkConstantType | SdkEnumType, namespace: string, - diagnostics: DiagnosticCollector, -): InputEnumType { +): [InputEnumType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const values: InputEnumValueType[] = []; const inputEnumType: InputEnumType = { @@ -305,8 +305,8 @@ function createEnumType( crossLanguageDefinitionId: sdkType.kind === "enum" ? sdkType.crossLanguageDefinitionId : "", valueType: sdkType.kind === "enum" - ? (fromSdkType(sdkContext, sdkType.valueType, diagnostics) as InputPrimitiveType) - : fromSdkBuiltInType(sdkContext, sdkType.valueType, diagnostics), + ? (diagnostics.pipe(fromSdkType(sdkContext, sdkType.valueType)) as InputPrimitiveType) + : diagnostics.pipe(fromSdkBuiltInType(sdkContext, sdkType.valueType)), values: values, // constantType.access, TODO - constant type now does not have access. TCGC will add it later access: @@ -327,118 +327,119 @@ function createEnumType( if (sdkType.kind === "enum") { for (const v of sdkType.values) { - values.push(createEnumValueType(sdkContext, v, inputEnumType, diagnostics)); + values.push(diagnostics.pipe(createEnumValueType(sdkContext, v, inputEnumType))); } } else { - values.push(createEnumValueType(sdkContext, sdkType, inputEnumType, diagnostics)); + values.push(diagnostics.pipe(createEnumValueType(sdkContext, sdkType, inputEnumType))); } - return inputEnumType; + return diagnostics.wrap(inputEnumType); } function fromSdkDateTimeType( sdkContext: CSharpEmitterContext, dateTimeType: SdkDateTimeType, - diagnostics: DiagnosticCollector, -): InputDateTimeType { - return { +): [InputDateTimeType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap({ kind: dateTimeType.kind, name: dateTimeType.name, encode: dateTimeType.encode, - wireType: fromSdkType(sdkContext, dateTimeType.wireType, diagnostics), + wireType: diagnostics.pipe(fromSdkType(sdkContext, dateTimeType.wireType)), crossLanguageDefinitionId: dateTimeType.crossLanguageDefinitionId, - baseType: dateTimeType.baseType ? fromSdkType(sdkContext, dateTimeType.baseType, diagnostics) : undefined, + baseType: dateTimeType.baseType ? diagnostics.pipe(fromSdkType(sdkContext, dateTimeType.baseType)) : undefined, decorators: dateTimeType.decorators, external: fromSdkExternalTypeInfo(dateTimeType), - }; + }); } function fromSdkDurationType( sdkContext: CSharpEmitterContext, durationType: SdkDurationType, - diagnostics: DiagnosticCollector, -): InputDurationType { - return { +): [InputDurationType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap({ kind: durationType.kind, name: durationType.name, encode: durationType.encode, - wireType: fromSdkType(sdkContext, durationType.wireType, diagnostics), + wireType: diagnostics.pipe(fromSdkType(sdkContext, durationType.wireType)), crossLanguageDefinitionId: durationType.crossLanguageDefinitionId, - baseType: durationType.baseType ? fromSdkType(sdkContext, durationType.baseType, diagnostics) : undefined, + baseType: durationType.baseType ? diagnostics.pipe(fromSdkType(sdkContext, durationType.baseType)) : undefined, decorators: durationType.decorators, external: fromSdkExternalTypeInfo(durationType), - }; + }); } function fromSdkBuiltInType( sdkContext: CSharpEmitterContext, builtInType: SdkBuiltInType, - diagnostics: DiagnosticCollector, -): InputPrimitiveType { - return { +): [InputPrimitiveType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap({ kind: builtInType.kind, name: builtInType.name, encode: builtInType.encode !== builtInType.kind ? builtInType.encode : undefined, crossLanguageDefinitionId: builtInType.crossLanguageDefinitionId, - baseType: builtInType.baseType ? fromSdkType(sdkContext, builtInType.baseType, diagnostics) : undefined, + baseType: builtInType.baseType ? diagnostics.pipe(fromSdkType(sdkContext, builtInType.baseType)) : undefined, decorators: builtInType.decorators, external: fromSdkExternalTypeInfo(builtInType), - }; + }); } -function fromUnionType(sdkContext: CSharpEmitterContext, union: SdkUnionType, diagnostics: DiagnosticCollector): InputUnionType { +function fromUnionType(sdkContext: CSharpEmitterContext, union: SdkUnionType): [InputUnionType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const variantTypes: InputType[] = []; for (const value of union.variantTypes) { - const variantType = fromSdkType(sdkContext, value, diagnostics); + const variantType = diagnostics.pipe(fromSdkType(sdkContext, value)); variantTypes.push(variantType); } - return { + return diagnostics.wrap({ kind: "union", name: union.name, variantTypes: variantTypes, namespace: union.namespace, decorators: union.decorators, external: fromSdkExternalTypeInfo(union), - }; + }); } function fromSdkConstantType( sdkContext: CSharpEmitterContext, constantType: SdkConstantType, - diagnostics: DiagnosticCollector, -): InputLiteralType { +): [InputLiteralType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const literalType = { kind: constantType.kind, name: constantType.name, namespace: "", // constantType.namespace, TODO - constant type now does not have namespace. TCGC will add it later access: undefined, // constantType.access, TODO - constant type now does not have access. TCGC will add it later usage: UsageFlags.None, // constantType.usage, TODO - constant type now does not have usage. TCGC will add it later - valueType: fromSdkType(sdkContext, constantType.valueType, diagnostics), + valueType: diagnostics.pipe(fromSdkType(sdkContext, constantType.valueType)), value: constantType.value, decorators: constantType.decorators, }; sdkContext.__typeCache.updateConstantCache(constantType, literalType); - return literalType; + return diagnostics.wrap(literalType); } function fromSdkEnumValueType( sdkContext: CSharpEmitterContext, enumValueType: SdkEnumValueType, - diagnostics: DiagnosticCollector, -): InputEnumValueType { - return createEnumValueType(sdkContext, enumValueType, enumValueType.enumType, diagnostics); +): [InputEnumValueType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap(diagnostics.pipe(createEnumValueType(sdkContext, enumValueType, enumValueType.enumType))); } function createEnumValueType( sdkContext: CSharpEmitterContext, sdkType: SdkEnumValueType | SdkConstantType, enumType: InputEnumType, - diagnostics: DiagnosticCollector, -): InputEnumValueType { - return { +): [InputEnumValueType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap({ kind: "enumvalue", name: sdkType.kind === "constant" @@ -448,41 +449,41 @@ function createEnumValueType( : sdkType.name, value: typeof sdkType.value === "boolean" ? (sdkType.value ? 1 : 0) : sdkType.value, valueType: - sdkType.kind === "constant" ? sdkType.valueType : fromSdkType(sdkContext, sdkType.valueType, diagnostics), + sdkType.kind === "constant" ? sdkType.valueType : diagnostics.pipe(fromSdkType(sdkContext, sdkType.valueType)), enumType: enumType, summary: sdkType.summary, doc: sdkType.doc, decorators: sdkType.decorators, - }; + }); } function fromSdkDictionaryType( sdkContext: CSharpEmitterContext, dictionaryType: SdkDictionaryType, - diagnostics: DiagnosticCollector, -): InputDictionaryType { - return { +): [InputDictionaryType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap({ kind: "dict", - keyType: fromSdkType(sdkContext, dictionaryType.keyType, diagnostics), - valueType: fromSdkType(sdkContext, dictionaryType.valueType, diagnostics), + keyType: diagnostics.pipe(fromSdkType(sdkContext, dictionaryType.keyType)), + valueType: diagnostics.pipe(fromSdkType(sdkContext, dictionaryType.valueType)), decorators: dictionaryType.decorators, external: fromSdkExternalTypeInfo(dictionaryType), - }; + }); } function fromSdkArrayType( sdkContext: CSharpEmitterContext, arrayType: SdkArrayType, - diagnostics: DiagnosticCollector, -): InputArrayType { - return { +): [InputArrayType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap({ kind: "array", name: arrayType.name, - valueType: fromSdkType(sdkContext, arrayType.valueType, diagnostics), + valueType: diagnostics.pipe(fromSdkType(sdkContext, arrayType.valueType)), crossLanguageDefinitionId: arrayType.crossLanguageDefinitionId, decorators: arrayType.decorators, external: fromSdkExternalTypeInfo(arrayType), - }; + }); } function fromSdkEndpointType(): InputPrimitiveType { diff --git a/packages/http-client-csharp/emitter/src/lib/typespec-server.ts b/packages/http-client-csharp/emitter/src/lib/typespec-server.ts index d5e76b8cb49..e1f5e5c8a84 100644 --- a/packages/http-client-csharp/emitter/src/lib/typespec-server.ts +++ b/packages/http-client-csharp/emitter/src/lib/typespec-server.ts @@ -38,7 +38,7 @@ export function resolveServers( name: "url", crossLanguageDefinitionId: "TypeSpec.url", } - : fromSdkType(sdkContext, getClientType(sdkContext, prop), diagnostics); + : diagnostics.pipe(fromSdkType(sdkContext, getClientType(sdkContext, prop))); if (value) { defaultValue = { diff --git a/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts index 7c85bcfbede..96359b0d73c 100644 --- a/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts @@ -51,8 +51,7 @@ describe("Namespace Converter", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); const sdkPackage = sdkContext.sdkPackage; - const diagnostics = createDiagnosticCollector(); - const parsedNamespaces = fromSdkNamespaces(sdkContext, sdkPackage.namespaces, diagnostics); + const [parsedNamespaces, diagnostics] = fromSdkNamespaces(sdkContext, sdkPackage.namespaces); strictEqual(parsedNamespaces.length, 1); From 1bde6f845b684731bcd97f9c8e06486d0d3b0331 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 07:41:34 +0000 Subject: [PATCH 12/22] Refactor createCodeModel to use diagnostic collector pattern properly - Use createDiagnosticCollector() instead of manual Diagnostic[] array - Use diagnostics.pipe() to unwrap tuple-returning functions - Use diagnostics.wrap() for return value - Use for loops to add arrays of diagnostics instead of spread - Follows the same pattern as createModel and other functions Co-authored-by: ArcturusZhang <10554446+ArcturusZhang@users.noreply.github.com> --- .../http-client-csharp/emitter/src/emitter.ts | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index 3f0107f862d..98edd1c0c28 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -80,6 +80,7 @@ export async function createCodeModel( context: EmitContext, updateCodeModel?: (model: CodeModel, context: CSharpEmitterContext) => CodeModel, ): Promise<[void, readonly Diagnostic[]]> { + const diagnostics = createDiagnosticCollector(); const program: Program = context.program; const options = resolveOptions(context); const outputFolder = context.emitterOutputDir; @@ -87,8 +88,6 @@ export async function createCodeModel( /* set the log level. */ const logger = new Logger(program, options.logLevel ?? LoggerLevel.INFO, true); - const diagnostics: Diagnostic[] = []; - if (!program.compilerOptions.noEmit && !program.hasError()) { // Write out the dotnet model to the output path const sdkContext = createCSharpEmitterContext( @@ -99,10 +98,11 @@ export async function createCodeModel( ), logger, ); - diagnostics.push(...sdkContext.diagnostics); + for (const diag of sdkContext.diagnostics) { + diagnostics.add(diag); + } - const [root, modelDiagnostics] = createModel(sdkContext); - diagnostics.push(...modelDiagnostics); + const root = diagnostics.pipe(createModel(sdkContext)); if (root) { // Use the provided callback or default to identity function @@ -116,8 +116,9 @@ export async function createCodeModel( } // emit tspCodeModel.json - const writeDiagnostics = await writeCodeModel(sdkContext, updatedRoot, outputFolder); - diagnostics.push(...writeDiagnostics); + for (const diag of await writeCodeModel(sdkContext, updatedRoot, outputFolder)) { + diagnostics.add(diag); + } const namespace = updatedRoot.name; const configurations: Configuration = createConfiguration(options, namespace, sdkContext); @@ -147,8 +148,7 @@ export async function createCodeModel( debug: options.debug ?? false, }); if (result.exitCode !== 0) { - const [isValid, validationDiagnostics] = await _validateDotNetSdk(sdkContext, _minSupportedDotNetSdkVersion); - diagnostics.push(...validationDiagnostics); + const isValid = diagnostics.pipe(await _validateDotNetSdk(sdkContext, _minSupportedDotNetSdkVersion)); // if the dotnet sdk is valid, the error is not dependency issue, log it as normal if (isValid) { throw new Error( @@ -157,8 +157,7 @@ export async function createCodeModel( } } } catch (error: any) { - const [isValid, validationDiagnostics] = await _validateDotNetSdk(sdkContext, _minSupportedDotNetSdkVersion); - diagnostics.push(...validationDiagnostics); + const isValid = diagnostics.pipe(await _validateDotNetSdk(sdkContext, _minSupportedDotNetSdkVersion)); // if the dotnet sdk is valid, the error is not dependency issue, log it as normal if (isValid) throw new Error(error); } @@ -170,7 +169,7 @@ export async function createCodeModel( } } - return [void 0, diagnostics]; + return diagnostics.wrap(void 0); } /** From 1422867209987f2efb84d0177107c1a4d6761fe7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 08:28:28 +0000 Subject: [PATCH 13/22] Co-authored-by: ArcturusZhang <10554446+ArcturusZhang@users.noreply.github.com> --- .../emitter/src/code-model-writer.ts | 19 ++++--------- .../http-client-csharp/emitter/src/emitter.ts | 23 +++++++-------- .../emitter/src/lib/example-converter.ts | 7 +++-- .../emitter/src/lib/logger.ts | 16 ++++------- .../emitter/src/lib/operation-converter.ts | 2 +- .../emitter/src/lib/service-authentication.ts | 28 +++++++++---------- .../emitter/src/lib/typespec-server.ts | 8 ++++-- .../test/Unit/client-model-builder.test.ts | 4 +-- .../emitter/test/Unit/utils/test-util.ts | 18 ++---------- 9 files changed, 50 insertions(+), 75 deletions(-) diff --git a/packages/http-client-csharp/emitter/src/code-model-writer.ts b/packages/http-client-csharp/emitter/src/code-model-writer.ts index 4884276778f..d6a46edf1e5 100644 --- a/packages/http-client-csharp/emitter/src/code-model-writer.ts +++ b/packages/http-client-csharp/emitter/src/code-model-writer.ts @@ -20,13 +20,13 @@ export async function writeCodeModel( context: CSharpEmitterContext, codeModel: CodeModel, outputFolder: string, -): Promise { +): Promise<[void, readonly Diagnostic[]]> { const diagnostics = createDiagnosticCollector(); await context.program.host.writeFile( resolvePath(outputFolder, tspOutputFileName), - prettierOutput(JSON.stringify(diagnostics.pipe(buildJson(context, codeModel)), transformJSONProperties, 2)), + prettierOutput(JSON.stringify(buildJson(context, codeModel), transformJSONProperties, 2)), ); - return diagnostics.diagnostics; + return diagnostics.wrap(undefined); } /** @@ -34,12 +34,11 @@ export async function writeCodeModel( * @param context - The CSharp emitter context * @param codeModel - The code model to build */ -function buildJson(context: CSharpEmitterContext, codeModel: CodeModel): [any, readonly Diagnostic[]] { - const diagnostics = createDiagnosticCollector(); +function buildJson(context: CSharpEmitterContext, codeModel: CodeModel): any { const objectsIds = new Map(); const stack: any[] = []; - return diagnostics.wrap(doBuildJson(codeModel, stack)); + return doBuildJson(codeModel, stack); function doBuildJson(obj: any, stack: any[]): any { // check if this is a primitive type or null or undefined @@ -77,13 +76,7 @@ function buildJson(context: CSharpEmitterContext, codeModel: CodeModel): [any, r function handleObject(obj: any, id: string | undefined, stack: any[]): any { if (stack.includes(obj)) { // we have a cyclical reference, we should not continue - diagnostics.add( - createDiagnostic({ - code: "general-warning", - format: { message: `Cyclical reference detected in the code model (id: ${id}).` }, - target: NoTarget, - }), - ); + context.logger.warn(`Cyclical reference detected in the code model (id: ${id}).`); return undefined; } diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index 98edd1c0c28..c48230b6d78 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -5,7 +5,6 @@ import { createSdkContext, SdkContext } from "@azure-tools/typespec-client-gener import { createDiagnosticCollector, Diagnostic, - DiagnosticCollector, EmitContext, getDirectoryPath, joinPaths, @@ -86,7 +85,7 @@ export async function createCodeModel( const outputFolder = context.emitterOutputDir; /* set the log level. */ - const logger = new Logger(program, options.logLevel ?? LoggerLevel.INFO, true); + const logger = new Logger(program, options.logLevel ?? LoggerLevel.INFO); if (!program.compilerOptions.noEmit && !program.hasError()) { // Write out the dotnet model to the output path @@ -116,9 +115,7 @@ export async function createCodeModel( } // emit tspCodeModel.json - for (const diag of await writeCodeModel(sdkContext, updatedRoot, outputFolder)) { - diagnostics.add(diag); - } + diagnostics.pipe(await writeCodeModel(sdkContext, updatedRoot, outputFolder)); const namespace = updatedRoot.name; const configurations: Configuration = createConfiguration(options, namespace, sdkContext); @@ -169,7 +166,7 @@ export async function createCodeModel( } } - return diagnostics.wrap(void 0); + return diagnostics.wrap(undefined); } /** @@ -230,7 +227,7 @@ export async function _validateDotNetSdk( const diagnostics = createDiagnosticCollector(); try { const result = await execAsync("dotnet", ["--version"], { stdio: "pipe" }); - return diagnostics.wrap(validateDotNetSdkVersionCore(sdkContext, result.stdout, minMajorVersion, diagnostics)); + return diagnostics.wrap(diagnostics.pipe(validateDotNetSdkVersionCore(sdkContext, result.stdout, minMajorVersion))); } catch (error: any) { if (error && "code" in error && error["code"] === "ENOENT") { diagnostics.add( @@ -253,15 +250,15 @@ function validateDotNetSdkVersionCore( sdkContext: CSharpEmitterContext, version: string, minMajorVersion: number, - diagnostics: DiagnosticCollector, -): boolean { +): [boolean, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); if (version) { const dotIndex = version.indexOf("."); const firstPart = dotIndex === -1 ? version : version.substring(0, dotIndex); const major = Number(firstPart); if (isNaN(major)) { - return false; + return diagnostics.wrap(false); } if (major < minMajorVersion) { diagnostics.add( @@ -276,9 +273,9 @@ function validateDotNetSdkVersionCore( target: NoTarget, }), ); - return false; + return diagnostics.wrap(false); } - return true; + return diagnostics.wrap(true); } else { diagnostics.add( createDiagnostic({ @@ -287,7 +284,7 @@ function validateDotNetSdkVersionCore( target: NoTarget, }), ); - return false; + return diagnostics.wrap(false); } } diff --git a/packages/http-client-csharp/emitter/src/lib/example-converter.ts b/packages/http-client-csharp/emitter/src/lib/example-converter.ts index c1300ee2082..e4f61940d43 100644 --- a/packages/http-client-csharp/emitter/src/lib/example-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/example-converter.ts @@ -16,7 +16,7 @@ import { SdkUnionExampleValue, SdkUnknownExampleValue, } from "@azure-tools/typespec-client-generator-core"; -import { createDiagnosticCollector, DiagnosticCollector } from "@typespec/compiler"; +import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { InputArrayExampleValue, @@ -48,12 +48,13 @@ import { fromSdkType } from "./type-converter.js"; export function fromSdkHttpExamples( sdkContext: CSharpEmitterContext, examples: SdkHttpOperationExample[], -): InputHttpOperationExample[] { +): [InputHttpOperationExample[], readonly Diagnostic[]] { // Create a diagnostics collector for internal use // Any errors in examples won't prevent the code model from being generated const diagnostics = createDiagnosticCollector(); - return examples.map((example) => fromSdkHttpExample(example)); + const result = examples.map((example) => fromSdkHttpExample(example)); + return diagnostics.wrap(result); function fromSdkHttpExample(example: SdkHttpOperationExample): InputHttpOperationExample { return { diff --git a/packages/http-client-csharp/emitter/src/lib/logger.ts b/packages/http-client-csharp/emitter/src/lib/logger.ts index f9461e37d83..654f0bf1aea 100644 --- a/packages/http-client-csharp/emitter/src/lib/logger.ts +++ b/packages/http-client-csharp/emitter/src/lib/logger.ts @@ -14,22 +14,12 @@ export class Logger { private level: LoggerLevel; private program: Program; - public constructor(program: Program, level: LoggerLevel, collectDiagnostics: boolean = false) { + public constructor(program: Program, level: LoggerLevel) { this.tracer = getTracer(program); this.level = level; this.program = program; } - /** - * Get collected diagnostics. Only available if the logger was created with collectDiagnostics=true. - * @returns The collected diagnostics. - * @beta - * @deprecated This method is deprecated and will be removed. Use sdkContext.__diagnostics instead. - */ - public getDiagnostics(): readonly Diagnostic[] { - return []; - } - trace(level: LoggerLevel, message: string): void { switch (level) { case LoggerLevel.INFO: @@ -65,4 +55,8 @@ export class Logger { this.tracer.trace(LoggerLevel.VERBOSE, message); } } + + warn(message: string): void { + this.tracer.trace("warning", message); + } } diff --git a/packages/http-client-csharp/emitter/src/lib/operation-converter.ts b/packages/http-client-csharp/emitter/src/lib/operation-converter.ts index ac1dcf22406..990043f9dc7 100644 --- a/packages/http-client-csharp/emitter/src/lib/operation-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/operation-converter.ts @@ -212,7 +212,7 @@ export function fromSdkServiceMethodOperation( crossLanguageDefinitionId: method.crossLanguageDefinitionId, decorators: method.decorators, examples: method.operation.examples - ? fromSdkHttpExamples(sdkContext, method.operation.examples) + ? diagnostics.pipe(fromSdkHttpExamples(sdkContext, method.operation.examples)) : undefined, }; diff --git a/packages/http-client-csharp/emitter/src/lib/service-authentication.ts b/packages/http-client-csharp/emitter/src/lib/service-authentication.ts index 2800e4d1c8f..7f7b73e17f0 100644 --- a/packages/http-client-csharp/emitter/src/lib/service-authentication.ts +++ b/packages/http-client-csharp/emitter/src/lib/service-authentication.ts @@ -7,7 +7,7 @@ import { SdkHttpOperation, SdkPackage, } from "@azure-tools/typespec-client-generator-core"; -import { createDiagnosticCollector, Diagnostic, DiagnosticCollector, NoTarget } from "@typespec/compiler"; +import { createDiagnosticCollector, Diagnostic, NoTarget } from "@typespec/compiler"; import { Oauth2Auth, OAuth2Flow } from "@typespec/http"; import { CSharpEmitterContext } from "../sdk-context.js"; import { createDiagnostic } from "./lib.js"; @@ -36,7 +36,7 @@ export function processServiceAuthentication( const inputAuth: InputAuth = {}; if (authClientParameter.type.kind === "credential") { - const auth = processAuthType(sdkContext, authClientParameter.type, diagnostics); + const auth = diagnostics.pipe(processAuthType(sdkContext, authClientParameter.type)); if (!auth && authClientParameter.type.scheme.type !== "noAuth") { diagnostics.add( createDiagnostic({ @@ -54,7 +54,7 @@ export function processServiceAuthentication( let containsNoAuth = false; for (const authType of authClientParameter.type.variantTypes) { containsNoAuth = containsNoAuth || authType.scheme.type === "noAuth"; - const auth = processAuthType(sdkContext, authType, diagnostics); + const auth = diagnostics.pipe(processAuthType(sdkContext, authType)); if (auth?.apiKey) { inputAuth.apiKey = auth.apiKey; } @@ -83,8 +83,8 @@ export function processServiceAuthentication( function processAuthType( sdkContext: CSharpEmitterContext, credentialType: SdkCredentialType, - diagnostics: DiagnosticCollector, -): InputAuth | undefined { +): [InputAuth | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const scheme = credentialType.scheme; switch (scheme.type) { case "apiKey": @@ -98,11 +98,11 @@ function processAuthType( target: credentialType.__raw ?? NoTarget, }), ); - return undefined; + return diagnostics.wrap(undefined); } - return { apiKey: { name: scheme.name, in: scheme.in } } as InputAuth; + return diagnostics.wrap({ apiKey: { name: scheme.name, in: scheme.in } } as InputAuth); case "oauth2": - return processOAuth2(scheme); + return diagnostics.wrap(processOAuth2(scheme)); case "http": { const schemeOrApiKeyPrefix = scheme.scheme; switch (schemeOrApiKeyPrefix) { @@ -114,23 +114,23 @@ function processAuthType( target: credentialType.__raw ?? NoTarget, }), ); - return undefined; + return diagnostics.wrap(undefined); case "Bearer": - return { + return diagnostics.wrap({ apiKey: { name: "Authorization", in: "header", prefix: "Bearer", }, - }; + }); default: - return { + return diagnostics.wrap({ apiKey: { name: "Authorization", in: "header", prefix: schemeOrApiKeyPrefix, }, - }; + }); } } default: @@ -141,7 +141,7 @@ function processAuthType( target: credentialType.__raw ?? NoTarget, }), ); - return undefined; + return diagnostics.wrap(undefined); } } diff --git a/packages/http-client-csharp/emitter/src/lib/typespec-server.ts b/packages/http-client-csharp/emitter/src/lib/typespec-server.ts index e1f5e5c8a84..b7afef9f730 100644 --- a/packages/http-client-csharp/emitter/src/lib/typespec-server.ts +++ b/packages/http-client-csharp/emitter/src/lib/typespec-server.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. import { getClientType } from "@azure-tools/typespec-client-generator-core"; -import { createDiagnosticCollector, getDoc, getSummary, Value } from "@typespec/compiler"; +import { createDiagnosticCollector, Diagnostic, getDoc, getSummary, Value } from "@typespec/compiler"; import { HttpServer } from "@typespec/http"; import { getExtensions } from "@typespec/openapi"; import { CSharpEmitterContext } from "../sdk-context.js"; @@ -20,11 +20,11 @@ export interface TypeSpecServer { export function resolveServers( sdkContext: CSharpEmitterContext, servers: HttpServer[], -): TypeSpecServer[] { +): [TypeSpecServer[], readonly Diagnostic[]] { // Create a diagnostics collector for internal use const diagnostics = createDiagnosticCollector(); - return servers.map((server) => { + const result = servers.map((server) => { const parameters: InputEndpointParameter[] = []; let url: string = server.url; const endpoint: string = url.replace("http://", "").replace("https://", "").split("/")[0]; @@ -104,6 +104,8 @@ export function resolveServers( parameters, }; }); + + return diagnostics.wrap(result); } function getDefaultValue(value: Value): any { diff --git a/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts b/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts index d36ca4ffe3e..d314421880d 100644 --- a/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts @@ -470,7 +470,7 @@ describe("createModel diagnostic collection", () => { runner, ); const context = createEmitterContext(program); - const sdkContext = await createCSharpSdkContext(context, true); // Enable diagnostic collection + const sdkContext = await createCSharpSdkContext(context); const result = createModel(sdkContext); // Verify the result is a tuple @@ -500,7 +500,7 @@ describe("createModel diagnostic collection", () => { runner, ); const context = createEmitterContext(program); - const sdkContext = await createCSharpSdkContext(context, true); // Enable diagnostic collection + const sdkContext = await createCSharpSdkContext(context); const [, diagnostics] = createModel(sdkContext); // Verify diagnostics array exists (may be empty or contain diagnostics) diff --git a/packages/http-client-csharp/emitter/test/Unit/utils/test-util.ts b/packages/http-client-csharp/emitter/test/Unit/utils/test-util.ts index 8dd90fc57e0..e1e2541800f 100644 --- a/packages/http-client-csharp/emitter/test/Unit/utils/test-util.ts +++ b/packages/http-client-csharp/emitter/test/Unit/utils/test-util.ts @@ -119,25 +119,13 @@ export function createEmitterContext( /* We always need to pass in the emitter name now that it is required so making a helper to do this. */ export async function createCSharpSdkContext( program: EmitContext, - sdkContextOptionsOrCollectDiagnostics: CreateSdkContextOptions | boolean = {}, - sdkContextOptions?: CreateSdkContextOptions, + sdkContextOptions: CreateSdkContextOptions = {}, ): Promise { - // Handle backward compatibility - if second param is boolean, it's collectDiagnostics - let collectDiagnostics = false; - let options: CreateSdkContextOptions = {}; - - if (typeof sdkContextOptionsOrCollectDiagnostics === "boolean") { - collectDiagnostics = sdkContextOptionsOrCollectDiagnostics; - options = sdkContextOptions ?? {}; - } else { - options = sdkContextOptionsOrCollectDiagnostics; - } - const createSdkContext = await getCreateSdkContext(); - const context = await createSdkContext(program, "@typespec/http-client-csharp", options); + const context = await createSdkContext(program, "@typespec/http-client-csharp", sdkContextOptions); const Logger = await getLogger(); return createCSharpEmitterContext( context, - new Logger(program.program, LoggerLevel.INFO, collectDiagnostics), + new Logger(program.program, LoggerLevel.INFO), ); } From 59709e0372dc007877baf5eb27d617c447b3a948 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 08:51:32 +0000 Subject: [PATCH 14/22] Final cleanup: revert writeCodeModel and remove update-code-model option - writeCodeModel no longer returns diagnostics (buildJson uses logger.warn, not diagnostics) - Removed update-code-model option from CSharpEmitterOptions completely - Removed update-code-model from schema, defaultOptions, and skipKeys - Removed updateCodeModel parameter from createCodeModel function - Removed test for update-code-model callback - Kept for loop for adding TCGC diagnostics (no bulk add method exists) Co-authored-by: ArcturusZhang <10554446+ArcturusZhang@users.noreply.github.com> --- .../emitter/src/code-model-writer.ts | 4 +--- .../http-client-csharp/emitter/src/emitter.ts | 20 ++++--------------- .../http-client-csharp/emitter/src/options.ts | 10 ---------- .../emitter/test/Unit/emitter.test.ts | 10 ---------- 4 files changed, 5 insertions(+), 39 deletions(-) diff --git a/packages/http-client-csharp/emitter/src/code-model-writer.ts b/packages/http-client-csharp/emitter/src/code-model-writer.ts index d6a46edf1e5..cb58710ea2b 100644 --- a/packages/http-client-csharp/emitter/src/code-model-writer.ts +++ b/packages/http-client-csharp/emitter/src/code-model-writer.ts @@ -20,13 +20,11 @@ export async function writeCodeModel( context: CSharpEmitterContext, codeModel: CodeModel, outputFolder: string, -): Promise<[void, readonly Diagnostic[]]> { - const diagnostics = createDiagnosticCollector(); +) { await context.program.host.writeFile( resolvePath(outputFolder, tspOutputFileName), prettierOutput(JSON.stringify(buildJson(context, codeModel), transformJSONProperties, 2)), ); - return diagnostics.wrap(undefined); } /** diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index c48230b6d78..e9d19779049 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -60,24 +60,18 @@ function findProjectRoot(path: string): string | undefined { * import { createCodeModel } from "@typespec/http-client-csharp"; * * export async function $onEmit(context: EmitContext) { - * const updateCodeModel = (model: CodeModel, context: CSharpEmitterContext) => { - * // Customize the code model here - * return model; - * }; - * const [, diagnostics] = await createCodeModel(context, updateCodeModel); + * const [, diagnostics] = await createCodeModel(context); * // Process diagnostics as needed * context.program.reportDiagnostics(diagnostics); * } * ``` * * @param context - The emit context - * @param updateCodeModel - Optional callback to modify the code model before emission. Defaults to identity function. * @returns A tuple containing void and any diagnostics that were generated during the emission * @beta */ export async function createCodeModel( context: EmitContext, - updateCodeModel?: (model: CodeModel, context: CSharpEmitterContext) => CodeModel, ): Promise<[void, readonly Diagnostic[]]> { const diagnostics = createDiagnosticCollector(); const program: Program = context.program; @@ -104,10 +98,6 @@ export async function createCodeModel( const root = diagnostics.pipe(createModel(sdkContext)); if (root) { - // Use the provided callback or default to identity function - const updateCodeModelFn = updateCodeModel ?? ((model: CodeModel) => model); - const updatedRoot = updateCodeModelFn(root, sdkContext); - const generatedFolder = resolvePath(outputFolder, "src", "Generated"); if (!fs.existsSync(generatedFolder)) { @@ -115,9 +105,9 @@ export async function createCodeModel( } // emit tspCodeModel.json - diagnostics.pipe(await writeCodeModel(sdkContext, updatedRoot, outputFolder)); + await writeCodeModel(sdkContext, root, outputFolder); - const namespace = updatedRoot.name; + const namespace = root.name; const configurations: Configuration = createConfiguration(options, namespace, sdkContext); //emit configuration.json @@ -175,8 +165,7 @@ export async function createCodeModel( * @beta */ export async function $onEmit(context: EmitContext) { - const options = resolveOptions(context); - const [, diagnostics] = await createCodeModel(context, options["update-code-model"]); + const [, diagnostics] = await createCodeModel(context); context.program.reportDiagnostics(diagnostics); } @@ -187,7 +176,6 @@ export function createConfiguration( ): Configuration { const skipKeys = [ "new-project", - "update-code-model", "sdk-context-options", "save-inputs", "generator-name", diff --git a/packages/http-client-csharp/emitter/src/options.ts b/packages/http-client-csharp/emitter/src/options.ts index 42196440cac..9ae08884a9e 100644 --- a/packages/http-client-csharp/emitter/src/options.ts +++ b/packages/http-client-csharp/emitter/src/options.ts @@ -1,10 +1,8 @@ import { CreateSdkContextOptions } from "@azure-tools/typespec-client-generator-core"; import { EmitContext, JSONSchemaType } from "@typespec/compiler"; import { _defaultGeneratorName } from "./constants.js"; -import { CSharpEmitterContext } from "./index.js"; import { DYNAMIC_MODEL_DECORATOR_PATTERN } from "./lib/decorators.js"; import { LoggerLevel } from "./lib/logger-level.js"; -import { CodeModel } from "./type/code-model.js"; /** * The emitter options for the CSharp emitter. @@ -20,7 +18,6 @@ export interface CSharpEmitterOptions { "disable-xml-docs"?: boolean; "generator-name"?: string; "emitter-extension-path"?: string; - "update-code-model"?: (model: CodeModel, context: CSharpEmitterContext) => CodeModel; "sdk-context-options"?: CreateSdkContextOptions; "generate-protocol-methods"?: boolean; "generate-convenience-methods"?: boolean; @@ -116,12 +113,6 @@ export const CSharpEmitterOptionsSchema: JSONSchemaType = description: "Allows emitter authors to specify the path to a custom emitter package, allowing you to extend the emitter behavior. This should be set to `import.meta.url` if you are using a custom emitter.", }, - "update-code-model": { - type: "object", - nullable: true, - description: - "Allows emitter authors to specify a custom function to modify the generated code model before emitting. This is useful for modifying the code model before it is passed to the generator.", - }, license: { type: "object", additionalProperties: false, @@ -160,7 +151,6 @@ export const defaultOptions = { debug: undefined, logLevel: LoggerLevel.INFO, "generator-name": _defaultGeneratorName, - "update-code-model": (model: CodeModel, context: CSharpEmitterContext) => model, "sdk-context-options": { additionalDecorators: [DYNAMIC_MODEL_DECORATOR_PATTERN], }, diff --git a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts index 56bf635ff2d..1f8bc49bbba 100644 --- a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts @@ -84,16 +84,6 @@ describe("$onEmit tests", () => { $onEmit = (await import("../../src/emitter.js")).$onEmit; }); - it("should apply the update-code-model callback just once", async () => { - const context: EmitContext = createEmitterContext(program); - const updateCallback = vi.fn().mockImplementation((model: CodeModel) => { - return model; - }); - context.options["update-code-model"] = updateCallback; - await $onEmit(context); - expect(updateCallback).toHaveBeenCalledTimes(1); - }); - it("should apply sdk-context-options", async () => { const context: EmitContext = createEmitterContext(program); const additionalDecorators = ["Decorator1", "Decorator2"]; From 239dba66e4840a3bcf936e7cc201c0f76323db6f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 09:29:06 +0000 Subject: [PATCH 15/22] Add back updateCodeModel callback parameter to createCodeModel - Restored updateCodeModel optional parameter to createCodeModel function - Parameter allows downstream emitters to customize code model - Updated documentation with usage example - Added test for updateCodeModel callback functionality - Callback NOT added to options - only available as function parameter Co-authored-by: ArcturusZhang <10554446+ArcturusZhang@users.noreply.github.com> --- .../http-client-csharp/emitter/src/emitter.ts | 15 ++++++++++++--- .../emitter/test/Unit/emitter.test.ts | 19 +++++++++++++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index e9d19779049..6d2664df72a 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -60,18 +60,24 @@ function findProjectRoot(path: string): string | undefined { * import { createCodeModel } from "@typespec/http-client-csharp"; * * export async function $onEmit(context: EmitContext) { - * const [, diagnostics] = await createCodeModel(context); + * const updateCodeModel = (model: CodeModel, context: CSharpEmitterContext) => { + * // Customize the code model here + * return model; + * }; + * const [, diagnostics] = await createCodeModel(context, updateCodeModel); * // Process diagnostics as needed * context.program.reportDiagnostics(diagnostics); * } * ``` * * @param context - The emit context + * @param updateCodeModel - Optional callback to modify the code model before emission * @returns A tuple containing void and any diagnostics that were generated during the emission * @beta */ export async function createCodeModel( context: EmitContext, + updateCodeModel?: (model: CodeModel, context: CSharpEmitterContext) => CodeModel, ): Promise<[void, readonly Diagnostic[]]> { const diagnostics = createDiagnosticCollector(); const program: Program = context.program; @@ -98,6 +104,9 @@ export async function createCodeModel( const root = diagnostics.pipe(createModel(sdkContext)); if (root) { + // Apply optional code model update callback + const updatedRoot = updateCodeModel ? updateCodeModel(root, sdkContext) : root; + const generatedFolder = resolvePath(outputFolder, "src", "Generated"); if (!fs.existsSync(generatedFolder)) { @@ -105,9 +114,9 @@ export async function createCodeModel( } // emit tspCodeModel.json - await writeCodeModel(sdkContext, root, outputFolder); + await writeCodeModel(sdkContext, updatedRoot, outputFolder); - const namespace = root.name; + const namespace = updatedRoot.name; const configurations: Configuration = createConfiguration(options, namespace, sdkContext); //emit configuration.json diff --git a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts index 1f8bc49bbba..941da354792 100644 --- a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts @@ -19,6 +19,10 @@ import { describe("$onEmit tests", () => { let program: Program; let $onEmit: (arg0: EmitContext) => any; + let createCodeModel: ( + context: EmitContext, + updateCodeModel?: (model: CodeModel, context: any) => CodeModel, + ) => any; beforeEach(async () => { // Reset the dynamically imported module to ensure a clean state vi.resetModules(); @@ -79,9 +83,20 @@ describe("$onEmit tests", () => { reportDiagnostics: vi.fn(), } as unknown as Program; - // dynamically import the module to get the $onEmit function + // dynamically import the module to get the $onEmit and createCodeModel functions // we avoid importing it at the top to allow mocking of dependencies - $onEmit = (await import("../../src/emitter.js")).$onEmit; + const emitterModule = await import("../../src/emitter.js"); + $onEmit = emitterModule.$onEmit; + createCodeModel = emitterModule.createCodeModel; + }); + + it("should apply the updateCodeModel callback", async () => { + const context: EmitContext = createEmitterContext(program); + const updateCallback = vi.fn().mockImplementation((model: CodeModel) => { + return model; + }); + await createCodeModel(context, updateCallback); + expect(updateCallback).toHaveBeenCalledTimes(1); }); it("should apply sdk-context-options", async () => { From 4569ec6ac2c05b885f64597190a7df66223ceb3a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 03:39:57 +0000 Subject: [PATCH 16/22] Rename createCodeModel to $emitCodeModel and export it - Renamed createCodeModel to $emitCodeModel (better reflects that it performs emission and returns void) - Exported $emitCodeModel in index.ts for downstream emitters - Updated all references in emitter.ts and tests - Updated documentation with new function name - All tests pass (174 TypeScript + 998 generator tests) Co-authored-by: ArcturusZhang <10554446+ArcturusZhang@users.noreply.github.com> --- .../http-client-csharp/emitter/src/emitter.ts | 8 +++---- .../http-client-csharp/emitter/src/index.ts | 2 +- .../emitter/test/Unit/emitter.test.ts | 22 +++++++++---------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index 6d2664df72a..663fa55ac01 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -57,14 +57,14 @@ function findProjectRoot(path: string): string | undefined { * * @example * ```typescript - * import { createCodeModel } from "@typespec/http-client-csharp"; + * import { $emitCodeModel } from "@typespec/http-client-csharp"; * * export async function $onEmit(context: EmitContext) { * const updateCodeModel = (model: CodeModel, context: CSharpEmitterContext) => { * // Customize the code model here * return model; * }; - * const [, diagnostics] = await createCodeModel(context, updateCodeModel); + * const [, diagnostics] = await $emitCodeModel(context, updateCodeModel); * // Process diagnostics as needed * context.program.reportDiagnostics(diagnostics); * } @@ -75,7 +75,7 @@ function findProjectRoot(path: string): string | undefined { * @returns A tuple containing void and any diagnostics that were generated during the emission * @beta */ -export async function createCodeModel( +export async function $emitCodeModel( context: EmitContext, updateCodeModel?: (model: CodeModel, context: CSharpEmitterContext) => CodeModel, ): Promise<[void, readonly Diagnostic[]]> { @@ -174,7 +174,7 @@ export async function createCodeModel( * @beta */ export async function $onEmit(context: EmitContext) { - const [, diagnostics] = await createCodeModel(context); + const [, diagnostics] = await $emitCodeModel(context); context.program.reportDiagnostics(diagnostics); } diff --git a/packages/http-client-csharp/emitter/src/index.ts b/packages/http-client-csharp/emitter/src/index.ts index 5a0b24094c7..060fee5e0fc 100644 --- a/packages/http-client-csharp/emitter/src/index.ts +++ b/packages/http-client-csharp/emitter/src/index.ts @@ -3,7 +3,7 @@ export { writeCodeModel } from "./code-model-writer.js"; export { configurationFileName, tspOutputFileName } from "./constants.js"; -export { $onEmit } from "./emitter.js"; +export { $emitCodeModel, $onEmit } from "./emitter.js"; // we export `createModel` only for autorest.csharp because it uses the emitter to generate the code model file but not calling the dll here // we could remove this export when in the future we deprecate autorest.csharp export { createModel } from "./lib/client-model-builder.js"; diff --git a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts index 941da354792..b06de888cbd 100644 --- a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts @@ -19,7 +19,7 @@ import { describe("$onEmit tests", () => { let program: Program; let $onEmit: (arg0: EmitContext) => any; - let createCodeModel: ( + let $emitCodeModel: ( context: EmitContext, updateCodeModel?: (model: CodeModel, context: any) => CodeModel, ) => any; @@ -83,11 +83,11 @@ describe("$onEmit tests", () => { reportDiagnostics: vi.fn(), } as unknown as Program; - // dynamically import the module to get the $onEmit and createCodeModel functions + // dynamically import the module to get the $onEmit and $emitCodeModel functions // we avoid importing it at the top to allow mocking of dependencies const emitterModule = await import("../../src/emitter.js"); $onEmit = emitterModule.$onEmit; - createCodeModel = emitterModule.createCodeModel; + $emitCodeModel = emitterModule.$emitCodeModel; }); it("should apply the updateCodeModel callback", async () => { @@ -95,7 +95,7 @@ describe("$onEmit tests", () => { const updateCallback = vi.fn().mockImplementation((model: CodeModel) => { return model; }); - await createCodeModel(context, updateCallback); + await $emitCodeModel(context, updateCallback); expect(updateCallback).toHaveBeenCalledTimes(1); }); @@ -184,7 +184,7 @@ describe("$onEmit tests", () => { }); }); -describe("createCodeModel tests", () => { +describe("$emitCodeModel tests", () => { let runner: TestHost; let program: Program; @@ -193,7 +193,7 @@ describe("createCodeModel tests", () => { runner = await createEmitterTestHost(); }); - it("should return diagnostics array from createCodeModel", async () => { + it("should return diagnostics array from $emitCodeModel", async () => { program = await typeSpecCompile( ` model TestModel { @@ -206,8 +206,8 @@ describe("createCodeModel tests", () => { runner, ); const context = createEmitterContext(program); - const { createCodeModel } = await import("../../src/emitter.js"); - const [, diagnostics] = await createCodeModel(context); + const { $emitCodeModel } = await import("../../src/emitter.js"); + const [, diagnostics] = await $emitCodeModel(context); // Verify that diagnostics is an array expect(Array.isArray(diagnostics)).toBe(true); @@ -215,7 +215,7 @@ describe("createCodeModel tests", () => { expect(diagnostics).toBeDefined(); }); - it("should collect diagnostics from createModel in createCodeModel", async () => { + it("should collect diagnostics from createModel in $emitCodeModel", async () => { program = await typeSpecCompile( ` model TestModel { @@ -228,8 +228,8 @@ describe("createCodeModel tests", () => { runner, ); const context = createEmitterContext(program); - const { createCodeModel } = await import("../../src/emitter.js"); - const [, diagnostics] = await createCodeModel(context); + const { $emitCodeModel } = await import("../../src/emitter.js"); + const [, diagnostics] = await $emitCodeModel(context); // The function should return diagnostics even if empty expect(diagnostics).toBeDefined(); From d0c4c82fbfdd94ee02091267bff0878f84ce2334 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 08:47:45 +0000 Subject: [PATCH 17/22] Add comprehensive documentation for downstream emitter usage - Created new downstream-emitters.md guide with detailed usage instructions - Covers basic usage, custom code model transformations, diagnostic handling - Includes complete examples and best practices - Added API reference and troubleshooting section - Updated main README to link to new downstream emitters guide - Removed obsolete update-code-model option from README - All tests pass (1,172 total tests) Co-authored-by: ArcturusZhang <10554446+ArcturusZhang@users.noreply.github.com> --- .../.tspd/docs/downstream-emitters.md | 390 ++++++++++++++++++ packages/http-client-csharp/readme.md | 10 +- 2 files changed, 394 insertions(+), 6 deletions(-) create mode 100644 packages/http-client-csharp/.tspd/docs/downstream-emitters.md diff --git a/packages/http-client-csharp/.tspd/docs/downstream-emitters.md b/packages/http-client-csharp/.tspd/docs/downstream-emitters.md new file mode 100644 index 00000000000..72b33975fbd --- /dev/null +++ b/packages/http-client-csharp/.tspd/docs/downstream-emitters.md @@ -0,0 +1,390 @@ +# Using @typespec/http-client-csharp as a Downstream Emitter + +This guide explains how to use `@typespec/http-client-csharp` as a library in your own TypeSpec emitter to generate C# HTTP client code with custom modifications. + +## Overview + +The `@typespec/http-client-csharp` emitter provides exported APIs that follow TypeSpec best practices for diagnostic collection. This allows downstream emitters to: + +1. Generate C# HTTP client code programmatically +2. Customize the code model before code generation +3. Collect and handle diagnostics properly +4. Compose multiple emitters together + +## Installation + +```bash +npm install @typespec/http-client-csharp +``` + +## Basic Usage + +### Simple Emission + +The simplest way to use the emitter is to call `$emitCodeModel` directly: + +```typescript +import { $emitCodeModel } from "@typespec/http-client-csharp"; +import { EmitContext } from "@typespec/compiler"; + +export async function $onEmit(context: EmitContext) { + // Emit C# code and collect diagnostics + const [, diagnostics] = await $emitCodeModel(context); + + // Report diagnostics to the TypeSpec program + context.program.reportDiagnostics(diagnostics); +} +``` + +### Custom Code Model Transformation + +You can provide a callback to modify the code model before code generation: + +```typescript +import { $emitCodeModel, CodeModel, CSharpEmitterContext } from "@typespec/http-client-csharp"; +import { EmitContext } from "@typespec/compiler"; + +export async function $onEmit(context: EmitContext) { + // Define a function to customize the code model + const updateCodeModel = ( + model: CodeModel, + sdkContext: CSharpEmitterContext + ): CodeModel => { + // Modify the code model + // For example: add custom properties, change naming conventions, etc. + + // Customize client names + for (const client of model.clients) { + client.name = `Custom${client.name}`; + } + + // Customize model names + for (const modelType of model.models) { + modelType.name = `Generated${modelType.name}`; + } + + return model; + }; + + // Emit with custom transformation + const [, diagnostics] = await $emitCodeModel(context, updateCodeModel); + context.program.reportDiagnostics(diagnostics); +} +``` + +## Advanced Usage + +### Generating Code Model Only + +If you only need the code model without performing the full emission: + +```typescript +import { createModel, createCSharpEmitterContext } from "@typespec/http-client-csharp"; +import { createSdkContext } from "@azure-tools/typespec-client-generator-core"; +import { EmitContext } from "@typespec/compiler"; + +export async function $onEmit(context: EmitContext) { + // Create SDK context + const tcgcContext = await createSdkContext( + context, + "@typespec/http-client-csharp" + ); + + // Create C# emitter context + const sdkContext = createCSharpEmitterContext(tcgcContext, logger); + + // Generate the code model + const [codeModel, diagnostics] = createModel(sdkContext); + + // Use the code model for your purposes + console.log(`Generated ${codeModel.clients.length} clients`); + console.log(`Generated ${codeModel.models.length} models`); + + // Report diagnostics + context.program.reportDiagnostics(diagnostics); +} +``` + +## Code Model Structure + +The `CodeModel` interface contains the following key properties: + +```typescript +interface CodeModel { + // Namespace name for the generated code + name: string; + + // API versions supported + apiVersions: string[]; + + // Enum types + enums: InputEnumType[]; + + // Constant values + constants: InputConstant[]; + + // Model types (classes/structs) + models: InputModelType[]; + + // HTTP clients + clients: InputClient[]; + + // Authentication configuration + auth?: InputAuth; +} +``` + +### Customization Examples + +#### Example 1: Adding Custom Metadata + +```typescript +const updateCodeModel = (model: CodeModel, context: CSharpEmitterContext): CodeModel => { + // Add version information to all models + for (const modelType of model.models) { + // Access model properties and customize + modelType.description = `${modelType.description}\n\nGenerated for API version: ${model.apiVersions[0]}`; + } + + return model; +}; +``` + +#### Example 2: Filtering Clients + +```typescript +const updateCodeModel = (model: CodeModel, context: CSharpEmitterContext): CodeModel => { + // Only keep clients with specific names + model.clients = model.clients.filter(client => + client.name.startsWith("Public") + ); + + return model; +}; +``` + +#### Example 3: Custom Authentication + +```typescript +const updateCodeModel = (model: CodeModel, context: CSharpEmitterContext): CodeModel => { + // Customize authentication configuration + if (model.auth) { + // Modify auth settings + if (model.auth.apiKey) { + model.auth.apiKey.name = "X-Custom-API-Key"; + } + } + + return model; +}; +``` + +## Diagnostic Handling + +The emitter follows TypeSpec best practices for diagnostic collection: + +### Diagnostic Collection Pattern + +All functions that return diagnostics follow the pattern: `[Result, readonly Diagnostic[]]` + +```typescript +import { createModel } from "@typespec/http-client-csharp"; + +// createModel returns a tuple: [CodeModel, readonly Diagnostic[]] +const [codeModel, diagnostics] = createModel(sdkContext); + +// Check for errors +const hasErrors = diagnostics.some(d => d.severity === "error"); +if (hasErrors) { + console.error("Code model generation failed with errors"); +} + +// Report all diagnostics to TypeSpec +context.program.reportDiagnostics(diagnostics); +``` + +### Custom Diagnostic Handling + +You can filter or transform diagnostics before reporting: + +```typescript +const [, diagnostics] = await $emitCodeModel(context, updateCodeModel); + +// Filter out warnings +const errorsOnly = diagnostics.filter(d => d.severity === "error"); + +// Add custom context to diagnostics +const customDiagnostics = diagnostics.map(d => ({ + ...d, + message: `[CustomEmitter] ${d.message}` +})); + +// Report filtered/transformed diagnostics +context.program.reportDiagnostics(customDiagnostics); +``` + +## Emitter Options + +When using `$emitCodeModel`, you can configure options through the `EmitContext`: + +```typescript +export async function $onEmit(context: EmitContext) { + // Access options from context + const options = context.options; + + // Options will be passed through to the C# emitter + // including: package-name, api-version, generate-protocol-methods, etc. + + const [, diagnostics] = await $emitCodeModel(context, updateCodeModel); + context.program.reportDiagnostics(diagnostics); +} +``` + +## Complete Example: Custom Emitter Package + +Here's a complete example of a custom emitter package: + +**package.json:** +```json +{ + "name": "my-custom-csharp-emitter", + "version": "1.0.0", + "type": "module", + "main": "dist/index.js", + "dependencies": { + "@typespec/http-client-csharp": "^1.0.0", + "@typespec/compiler": "^0.60.0" + } +} +``` + +**src/index.ts:** +```typescript +import { $emitCodeModel, CodeModel, CSharpEmitterContext } from "@typespec/http-client-csharp"; +import { EmitContext } from "@typespec/compiler"; + +export interface MyEmitterOptions { + // Your custom options + customPrefix?: string; + includeVersion?: boolean; +} + +export async function $onEmit(context: EmitContext) { + const customPrefix = context.options.customPrefix ?? "Generated"; + const includeVersion = context.options.includeVersion ?? true; + + const updateCodeModel = ( + model: CodeModel, + sdkContext: CSharpEmitterContext + ): CodeModel => { + // Apply custom transformations + for (const client of model.clients) { + client.name = `${customPrefix}${client.name}`; + } + + if (includeVersion && model.apiVersions.length > 0) { + for (const modelType of model.models) { + modelType.description = `${modelType.description || ""}\n\nAPI Version: ${model.apiVersions[0]}`; + } + } + + return model; + }; + + // Emit with customizations + const [, diagnostics] = await $emitCodeModel(context, updateCodeModel); + + // Report diagnostics + context.program.reportDiagnostics(diagnostics); +} +``` + +**TypeSpec project using the custom emitter:** + +```yaml +# tspconfig.yaml +emit: + - "my-custom-csharp-emitter" +options: + my-custom-csharp-emitter: + customPrefix: "Contoso" + includeVersion: true +``` + +## Best Practices + +1. **Always handle diagnostics**: Report all diagnostics from the emitter to ensure users see errors and warnings. + +2. **Preserve immutability**: When modifying the code model, avoid mutating nested objects directly. Create new objects when needed. + +3. **Validate transformations**: Check that your code model transformations produce valid output. + +4. **Document customizations**: Clearly document what customizations your emitter applies. + +5. **Test thoroughly**: Test your emitter with various TypeSpec specifications to ensure robustness. + +6. **Follow TypeSpec patterns**: Use `createDiagnosticCollector()` and the tuple return pattern in your own functions that generate diagnostics. + +## API Reference + +### Exported Functions + +- **`$emitCodeModel(context, updateCodeModel?)`**: Main emission function + - Returns: `Promise<[void, readonly Diagnostic[]]>` + - Parameters: + - `context: EmitContext` - The emit context + - `updateCodeModel?: (model: CodeModel, context: CSharpEmitterContext) => CodeModel` - Optional callback to modify code model + +- **`createModel(sdkContext)`**: Generate code model only + - Returns: `[CodeModel, readonly Diagnostic[]]` + - Parameters: + - `sdkContext: CSharpEmitterContext` - The C# emitter context + +### Exported Types + +- `CodeModel` - The code model interface +- `CSharpEmitterContext` - The emitter context interface +- `CSharpEmitterOptions` - Configuration options interface +- `InputClient` - HTTP client interface +- `InputModelType` - Model type interface + +## Support and Resources + +- [Main README](../../readme.md) +- [Customization Guide](./customization.md) +- [TypeSpec Documentation](https://typespec.io/) +- [Report Issues](https://github.com/microsoft/typespec/issues) + +## Migration Guide + +If you were previously using internal APIs or patterns, here's how to migrate: + +### Before (accessing internal APIs): +```typescript +// ❌ Don't use internal implementation details +import { createModel } from "@typespec/http-client-csharp/lib/client-model-builder"; +``` + +### After (using exported APIs): +```typescript +// ✅ Use public exported APIs +import { $emitCodeModel, createModel } from "@typespec/http-client-csharp"; +``` + +## Troubleshooting + +### Issue: Diagnostics not appearing + +**Solution**: Make sure you're calling `context.program.reportDiagnostics(diagnostics)` with the diagnostics returned from `$emitCodeModel`. + +### Issue: Code model is empty + +**Solution**: Check that your TypeSpec specification is valid and that you've properly configured the SDK context options. + +### Issue: Generated code has errors + +**Solution**: Validate your code model transformations. The C# generator expects specific structures - avoid breaking required properties. + +### Issue: TypeScript compilation errors + +**Solution**: Ensure you have the correct type definitions installed and are using compatible versions of `@typespec/compiler` and `@typespec/http-client-csharp`. diff --git a/packages/http-client-csharp/readme.md b/packages/http-client-csharp/readme.md index 77f8efc84b9..db8fecf7b85 100644 --- a/packages/http-client-csharp/readme.md +++ b/packages/http-client-csharp/readme.md @@ -15,6 +15,10 @@ npm install @typespec/http-client-csharp - Install [Node.js](https://nodejs.org/download/) 20 or above. (Verify by running `node --version`) - Install [**.NET 8.0 SDK**](https://dotnet.microsoft.com/download/dotnet/8.0) for your specific platform. (or a higher version) +### Using as a Downstream Emitter + +For detailed instructions on how to use this package as a library in your own emitter, see the [Downstream Emitters Guide](https://github.com/microsoft/typespec/blob/main/packages/http-client-csharp/.tspd/docs/downstream-emitters.md). + ### Customizing Generated Code For detailed instructions on how to customize the generated C# code, see the [Customization Guide](https://github.com/microsoft/typespec/blob/main/packages/http-client-csharp/.tspd/docs/customization.md). @@ -125,12 +129,6 @@ The name of the generator. By default this is set to `ScmCodeModelGenerator`. Ge Allows emitter authors to specify the path to a custom emitter package, allowing you to extend the emitter behavior. This should be set to `import.meta.url` if you are using a custom emitter. -### `update-code-model` - -**Type:** `object` - -Allows emitter authors to specify a custom function to modify the generated code model before emitting. This is useful for modifying the code model before it is passed to the generator. - ### `license` **Type:** `object` From cad76cc29a19e5523ad0d77cf2bc23b2e4a9f927 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 08:57:52 +0000 Subject: [PATCH 18/22] Remove documentation file and rename $emitCodeModel to emitCodeModel - Removed .tspd/docs/downstream-emitters.md file per reviewer feedback - Renamed $emitCodeModel to emitCodeModel (removed $ prefix) - Updated all references in emitter.ts, index.ts, tests, and README - Removed link to deleted documentation from README - All tests pass (1,172 total tests) Co-authored-by: ArcturusZhang <10554446+ArcturusZhang@users.noreply.github.com> --- .../.tspd/docs/downstream-emitters.md | 390 ------------------ .../http-client-csharp/emitter/src/emitter.ts | 8 +- .../http-client-csharp/emitter/src/index.ts | 2 +- .../emitter/test/Unit/emitter.test.ts | 22 +- packages/http-client-csharp/readme.md | 4 - 5 files changed, 16 insertions(+), 410 deletions(-) delete mode 100644 packages/http-client-csharp/.tspd/docs/downstream-emitters.md diff --git a/packages/http-client-csharp/.tspd/docs/downstream-emitters.md b/packages/http-client-csharp/.tspd/docs/downstream-emitters.md deleted file mode 100644 index 72b33975fbd..00000000000 --- a/packages/http-client-csharp/.tspd/docs/downstream-emitters.md +++ /dev/null @@ -1,390 +0,0 @@ -# Using @typespec/http-client-csharp as a Downstream Emitter - -This guide explains how to use `@typespec/http-client-csharp` as a library in your own TypeSpec emitter to generate C# HTTP client code with custom modifications. - -## Overview - -The `@typespec/http-client-csharp` emitter provides exported APIs that follow TypeSpec best practices for diagnostic collection. This allows downstream emitters to: - -1. Generate C# HTTP client code programmatically -2. Customize the code model before code generation -3. Collect and handle diagnostics properly -4. Compose multiple emitters together - -## Installation - -```bash -npm install @typespec/http-client-csharp -``` - -## Basic Usage - -### Simple Emission - -The simplest way to use the emitter is to call `$emitCodeModel` directly: - -```typescript -import { $emitCodeModel } from "@typespec/http-client-csharp"; -import { EmitContext } from "@typespec/compiler"; - -export async function $onEmit(context: EmitContext) { - // Emit C# code and collect diagnostics - const [, diagnostics] = await $emitCodeModel(context); - - // Report diagnostics to the TypeSpec program - context.program.reportDiagnostics(diagnostics); -} -``` - -### Custom Code Model Transformation - -You can provide a callback to modify the code model before code generation: - -```typescript -import { $emitCodeModel, CodeModel, CSharpEmitterContext } from "@typespec/http-client-csharp"; -import { EmitContext } from "@typespec/compiler"; - -export async function $onEmit(context: EmitContext) { - // Define a function to customize the code model - const updateCodeModel = ( - model: CodeModel, - sdkContext: CSharpEmitterContext - ): CodeModel => { - // Modify the code model - // For example: add custom properties, change naming conventions, etc. - - // Customize client names - for (const client of model.clients) { - client.name = `Custom${client.name}`; - } - - // Customize model names - for (const modelType of model.models) { - modelType.name = `Generated${modelType.name}`; - } - - return model; - }; - - // Emit with custom transformation - const [, diagnostics] = await $emitCodeModel(context, updateCodeModel); - context.program.reportDiagnostics(diagnostics); -} -``` - -## Advanced Usage - -### Generating Code Model Only - -If you only need the code model without performing the full emission: - -```typescript -import { createModel, createCSharpEmitterContext } from "@typespec/http-client-csharp"; -import { createSdkContext } from "@azure-tools/typespec-client-generator-core"; -import { EmitContext } from "@typespec/compiler"; - -export async function $onEmit(context: EmitContext) { - // Create SDK context - const tcgcContext = await createSdkContext( - context, - "@typespec/http-client-csharp" - ); - - // Create C# emitter context - const sdkContext = createCSharpEmitterContext(tcgcContext, logger); - - // Generate the code model - const [codeModel, diagnostics] = createModel(sdkContext); - - // Use the code model for your purposes - console.log(`Generated ${codeModel.clients.length} clients`); - console.log(`Generated ${codeModel.models.length} models`); - - // Report diagnostics - context.program.reportDiagnostics(diagnostics); -} -``` - -## Code Model Structure - -The `CodeModel` interface contains the following key properties: - -```typescript -interface CodeModel { - // Namespace name for the generated code - name: string; - - // API versions supported - apiVersions: string[]; - - // Enum types - enums: InputEnumType[]; - - // Constant values - constants: InputConstant[]; - - // Model types (classes/structs) - models: InputModelType[]; - - // HTTP clients - clients: InputClient[]; - - // Authentication configuration - auth?: InputAuth; -} -``` - -### Customization Examples - -#### Example 1: Adding Custom Metadata - -```typescript -const updateCodeModel = (model: CodeModel, context: CSharpEmitterContext): CodeModel => { - // Add version information to all models - for (const modelType of model.models) { - // Access model properties and customize - modelType.description = `${modelType.description}\n\nGenerated for API version: ${model.apiVersions[0]}`; - } - - return model; -}; -``` - -#### Example 2: Filtering Clients - -```typescript -const updateCodeModel = (model: CodeModel, context: CSharpEmitterContext): CodeModel => { - // Only keep clients with specific names - model.clients = model.clients.filter(client => - client.name.startsWith("Public") - ); - - return model; -}; -``` - -#### Example 3: Custom Authentication - -```typescript -const updateCodeModel = (model: CodeModel, context: CSharpEmitterContext): CodeModel => { - // Customize authentication configuration - if (model.auth) { - // Modify auth settings - if (model.auth.apiKey) { - model.auth.apiKey.name = "X-Custom-API-Key"; - } - } - - return model; -}; -``` - -## Diagnostic Handling - -The emitter follows TypeSpec best practices for diagnostic collection: - -### Diagnostic Collection Pattern - -All functions that return diagnostics follow the pattern: `[Result, readonly Diagnostic[]]` - -```typescript -import { createModel } from "@typespec/http-client-csharp"; - -// createModel returns a tuple: [CodeModel, readonly Diagnostic[]] -const [codeModel, diagnostics] = createModel(sdkContext); - -// Check for errors -const hasErrors = diagnostics.some(d => d.severity === "error"); -if (hasErrors) { - console.error("Code model generation failed with errors"); -} - -// Report all diagnostics to TypeSpec -context.program.reportDiagnostics(diagnostics); -``` - -### Custom Diagnostic Handling - -You can filter or transform diagnostics before reporting: - -```typescript -const [, diagnostics] = await $emitCodeModel(context, updateCodeModel); - -// Filter out warnings -const errorsOnly = diagnostics.filter(d => d.severity === "error"); - -// Add custom context to diagnostics -const customDiagnostics = diagnostics.map(d => ({ - ...d, - message: `[CustomEmitter] ${d.message}` -})); - -// Report filtered/transformed diagnostics -context.program.reportDiagnostics(customDiagnostics); -``` - -## Emitter Options - -When using `$emitCodeModel`, you can configure options through the `EmitContext`: - -```typescript -export async function $onEmit(context: EmitContext) { - // Access options from context - const options = context.options; - - // Options will be passed through to the C# emitter - // including: package-name, api-version, generate-protocol-methods, etc. - - const [, diagnostics] = await $emitCodeModel(context, updateCodeModel); - context.program.reportDiagnostics(diagnostics); -} -``` - -## Complete Example: Custom Emitter Package - -Here's a complete example of a custom emitter package: - -**package.json:** -```json -{ - "name": "my-custom-csharp-emitter", - "version": "1.0.0", - "type": "module", - "main": "dist/index.js", - "dependencies": { - "@typespec/http-client-csharp": "^1.0.0", - "@typespec/compiler": "^0.60.0" - } -} -``` - -**src/index.ts:** -```typescript -import { $emitCodeModel, CodeModel, CSharpEmitterContext } from "@typespec/http-client-csharp"; -import { EmitContext } from "@typespec/compiler"; - -export interface MyEmitterOptions { - // Your custom options - customPrefix?: string; - includeVersion?: boolean; -} - -export async function $onEmit(context: EmitContext) { - const customPrefix = context.options.customPrefix ?? "Generated"; - const includeVersion = context.options.includeVersion ?? true; - - const updateCodeModel = ( - model: CodeModel, - sdkContext: CSharpEmitterContext - ): CodeModel => { - // Apply custom transformations - for (const client of model.clients) { - client.name = `${customPrefix}${client.name}`; - } - - if (includeVersion && model.apiVersions.length > 0) { - for (const modelType of model.models) { - modelType.description = `${modelType.description || ""}\n\nAPI Version: ${model.apiVersions[0]}`; - } - } - - return model; - }; - - // Emit with customizations - const [, diagnostics] = await $emitCodeModel(context, updateCodeModel); - - // Report diagnostics - context.program.reportDiagnostics(diagnostics); -} -``` - -**TypeSpec project using the custom emitter:** - -```yaml -# tspconfig.yaml -emit: - - "my-custom-csharp-emitter" -options: - my-custom-csharp-emitter: - customPrefix: "Contoso" - includeVersion: true -``` - -## Best Practices - -1. **Always handle diagnostics**: Report all diagnostics from the emitter to ensure users see errors and warnings. - -2. **Preserve immutability**: When modifying the code model, avoid mutating nested objects directly. Create new objects when needed. - -3. **Validate transformations**: Check that your code model transformations produce valid output. - -4. **Document customizations**: Clearly document what customizations your emitter applies. - -5. **Test thoroughly**: Test your emitter with various TypeSpec specifications to ensure robustness. - -6. **Follow TypeSpec patterns**: Use `createDiagnosticCollector()` and the tuple return pattern in your own functions that generate diagnostics. - -## API Reference - -### Exported Functions - -- **`$emitCodeModel(context, updateCodeModel?)`**: Main emission function - - Returns: `Promise<[void, readonly Diagnostic[]]>` - - Parameters: - - `context: EmitContext` - The emit context - - `updateCodeModel?: (model: CodeModel, context: CSharpEmitterContext) => CodeModel` - Optional callback to modify code model - -- **`createModel(sdkContext)`**: Generate code model only - - Returns: `[CodeModel, readonly Diagnostic[]]` - - Parameters: - - `sdkContext: CSharpEmitterContext` - The C# emitter context - -### Exported Types - -- `CodeModel` - The code model interface -- `CSharpEmitterContext` - The emitter context interface -- `CSharpEmitterOptions` - Configuration options interface -- `InputClient` - HTTP client interface -- `InputModelType` - Model type interface - -## Support and Resources - -- [Main README](../../readme.md) -- [Customization Guide](./customization.md) -- [TypeSpec Documentation](https://typespec.io/) -- [Report Issues](https://github.com/microsoft/typespec/issues) - -## Migration Guide - -If you were previously using internal APIs or patterns, here's how to migrate: - -### Before (accessing internal APIs): -```typescript -// ❌ Don't use internal implementation details -import { createModel } from "@typespec/http-client-csharp/lib/client-model-builder"; -``` - -### After (using exported APIs): -```typescript -// ✅ Use public exported APIs -import { $emitCodeModel, createModel } from "@typespec/http-client-csharp"; -``` - -## Troubleshooting - -### Issue: Diagnostics not appearing - -**Solution**: Make sure you're calling `context.program.reportDiagnostics(diagnostics)` with the diagnostics returned from `$emitCodeModel`. - -### Issue: Code model is empty - -**Solution**: Check that your TypeSpec specification is valid and that you've properly configured the SDK context options. - -### Issue: Generated code has errors - -**Solution**: Validate your code model transformations. The C# generator expects specific structures - avoid breaking required properties. - -### Issue: TypeScript compilation errors - -**Solution**: Ensure you have the correct type definitions installed and are using compatible versions of `@typespec/compiler` and `@typespec/http-client-csharp`. diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index 663fa55ac01..c324ad666d6 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -57,14 +57,14 @@ function findProjectRoot(path: string): string | undefined { * * @example * ```typescript - * import { $emitCodeModel } from "@typespec/http-client-csharp"; + * import { emitCodeModel } from "@typespec/http-client-csharp"; * * export async function $onEmit(context: EmitContext) { * const updateCodeModel = (model: CodeModel, context: CSharpEmitterContext) => { * // Customize the code model here * return model; * }; - * const [, diagnostics] = await $emitCodeModel(context, updateCodeModel); + * const [, diagnostics] = await emitCodeModel(context, updateCodeModel); * // Process diagnostics as needed * context.program.reportDiagnostics(diagnostics); * } @@ -75,7 +75,7 @@ function findProjectRoot(path: string): string | undefined { * @returns A tuple containing void and any diagnostics that were generated during the emission * @beta */ -export async function $emitCodeModel( +export async function emitCodeModel( context: EmitContext, updateCodeModel?: (model: CodeModel, context: CSharpEmitterContext) => CodeModel, ): Promise<[void, readonly Diagnostic[]]> { @@ -174,7 +174,7 @@ export async function $emitCodeModel( * @beta */ export async function $onEmit(context: EmitContext) { - const [, diagnostics] = await $emitCodeModel(context); + const [, diagnostics] = await emitCodeModel(context); context.program.reportDiagnostics(diagnostics); } diff --git a/packages/http-client-csharp/emitter/src/index.ts b/packages/http-client-csharp/emitter/src/index.ts index 060fee5e0fc..ce40cf1b1db 100644 --- a/packages/http-client-csharp/emitter/src/index.ts +++ b/packages/http-client-csharp/emitter/src/index.ts @@ -3,7 +3,7 @@ export { writeCodeModel } from "./code-model-writer.js"; export { configurationFileName, tspOutputFileName } from "./constants.js"; -export { $emitCodeModel, $onEmit } from "./emitter.js"; +export { emitCodeModel, $onEmit } from "./emitter.js"; // we export `createModel` only for autorest.csharp because it uses the emitter to generate the code model file but not calling the dll here // we could remove this export when in the future we deprecate autorest.csharp export { createModel } from "./lib/client-model-builder.js"; diff --git a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts index b06de888cbd..4ceeddda514 100644 --- a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts @@ -19,7 +19,7 @@ import { describe("$onEmit tests", () => { let program: Program; let $onEmit: (arg0: EmitContext) => any; - let $emitCodeModel: ( + let emitCodeModel: ( context: EmitContext, updateCodeModel?: (model: CodeModel, context: any) => CodeModel, ) => any; @@ -83,11 +83,11 @@ describe("$onEmit tests", () => { reportDiagnostics: vi.fn(), } as unknown as Program; - // dynamically import the module to get the $onEmit and $emitCodeModel functions + // dynamically import the module to get the $onEmit and emitCodeModel functions // we avoid importing it at the top to allow mocking of dependencies const emitterModule = await import("../../src/emitter.js"); $onEmit = emitterModule.$onEmit; - $emitCodeModel = emitterModule.$emitCodeModel; + emitCodeModel = emitterModule.emitCodeModel; }); it("should apply the updateCodeModel callback", async () => { @@ -95,7 +95,7 @@ describe("$onEmit tests", () => { const updateCallback = vi.fn().mockImplementation((model: CodeModel) => { return model; }); - await $emitCodeModel(context, updateCallback); + await emitCodeModel(context, updateCallback); expect(updateCallback).toHaveBeenCalledTimes(1); }); @@ -184,7 +184,7 @@ describe("$onEmit tests", () => { }); }); -describe("$emitCodeModel tests", () => { +describe("emitCodeModel tests", () => { let runner: TestHost; let program: Program; @@ -193,7 +193,7 @@ describe("$emitCodeModel tests", () => { runner = await createEmitterTestHost(); }); - it("should return diagnostics array from $emitCodeModel", async () => { + it("should return diagnostics array from emitCodeModel", async () => { program = await typeSpecCompile( ` model TestModel { @@ -206,8 +206,8 @@ describe("$emitCodeModel tests", () => { runner, ); const context = createEmitterContext(program); - const { $emitCodeModel } = await import("../../src/emitter.js"); - const [, diagnostics] = await $emitCodeModel(context); + const { emitCodeModel } = await import("../../src/emitter.js"); + const [, diagnostics] = await emitCodeModel(context); // Verify that diagnostics is an array expect(Array.isArray(diagnostics)).toBe(true); @@ -215,7 +215,7 @@ describe("$emitCodeModel tests", () => { expect(diagnostics).toBeDefined(); }); - it("should collect diagnostics from createModel in $emitCodeModel", async () => { + it("should collect diagnostics from createModel in emitCodeModel", async () => { program = await typeSpecCompile( ` model TestModel { @@ -228,8 +228,8 @@ describe("$emitCodeModel tests", () => { runner, ); const context = createEmitterContext(program); - const { $emitCodeModel } = await import("../../src/emitter.js"); - const [, diagnostics] = await $emitCodeModel(context); + const { emitCodeModel } = await import("../../src/emitter.js"); + const [, diagnostics] = await emitCodeModel(context); // The function should return diagnostics even if empty expect(diagnostics).toBeDefined(); diff --git a/packages/http-client-csharp/readme.md b/packages/http-client-csharp/readme.md index db8fecf7b85..30e8bea7724 100644 --- a/packages/http-client-csharp/readme.md +++ b/packages/http-client-csharp/readme.md @@ -15,10 +15,6 @@ npm install @typespec/http-client-csharp - Install [Node.js](https://nodejs.org/download/) 20 or above. (Verify by running `node --version`) - Install [**.NET 8.0 SDK**](https://dotnet.microsoft.com/download/dotnet/8.0) for your specific platform. (or a higher version) -### Using as a Downstream Emitter - -For detailed instructions on how to use this package as a library in your own emitter, see the [Downstream Emitters Guide](https://github.com/microsoft/typespec/blob/main/packages/http-client-csharp/.tspd/docs/downstream-emitters.md). - ### Customizing Generated Code For detailed instructions on how to customize the generated C# code, see the [Customization Guide](https://github.com/microsoft/typespec/blob/main/packages/http-client-csharp/.tspd/docs/customization.md). From 7fff0f3145efcaf3eade0192a2befc274825e0b0 Mon Sep 17 00:00:00 2001 From: Dapeng Zhang Date: Thu, 19 Mar 2026 15:30:55 +0800 Subject: [PATCH 19/22] chore: clean up unused imports and dead code Remove unused imports from code-model-writer.ts, logger.ts, sdk-context.ts, emitter.test.ts, and namespace-converter.test.ts. Remove dead 'program' field from Logger class (only user was the removed reportDiagnostic method). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../http-client-csharp/emitter/src/code-model-writer.ts | 3 +-- packages/http-client-csharp/emitter/src/lib/logger.ts | 4 +--- packages/http-client-csharp/emitter/src/sdk-context.ts | 2 +- .../http-client-csharp/emitter/test/Unit/emitter.test.ts | 6 +++--- .../emitter/test/Unit/namespace-converter.test.ts | 3 +-- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/http-client-csharp/emitter/src/code-model-writer.ts b/packages/http-client-csharp/emitter/src/code-model-writer.ts index cb58710ea2b..994d0bad2e7 100644 --- a/packages/http-client-csharp/emitter/src/code-model-writer.ts +++ b/packages/http-client-csharp/emitter/src/code-model-writer.ts @@ -2,9 +2,8 @@ // Licensed under the MIT License. See License.txt in the project root for license information. import { UsageFlags } from "@azure-tools/typespec-client-generator-core"; -import { createDiagnosticCollector, Diagnostic, NoTarget, resolvePath } from "@typespec/compiler"; +import { resolvePath } from "@typespec/compiler"; import { configurationFileName, tspOutputFileName } from "./constants.js"; -import { createDiagnostic } from "./lib/lib.js"; import { CSharpEmitterContext } from "./sdk-context.js"; import { CodeModel } from "./type/code-model.js"; import { Configuration } from "./type/configuration.js"; diff --git a/packages/http-client-csharp/emitter/src/lib/logger.ts b/packages/http-client-csharp/emitter/src/lib/logger.ts index 654f0bf1aea..8b1682f0c27 100644 --- a/packages/http-client-csharp/emitter/src/lib/logger.ts +++ b/packages/http-client-csharp/emitter/src/lib/logger.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -import { Diagnostic, Program, Tracer } from "@typespec/compiler"; +import { Program, Tracer } from "@typespec/compiler"; import { getTracer } from "./lib.js"; import { LoggerLevel } from "./logger-level.js"; @@ -12,12 +12,10 @@ import { LoggerLevel } from "./logger-level.js"; export class Logger { private tracer: Tracer; private level: LoggerLevel; - private program: Program; public constructor(program: Program, level: LoggerLevel) { this.tracer = getTracer(program); this.level = level; - this.program = program; } trace(level: LoggerLevel, message: string): void { diff --git a/packages/http-client-csharp/emitter/src/sdk-context.ts b/packages/http-client-csharp/emitter/src/sdk-context.ts index 2718ebfe32d..1c8d3b03162 100644 --- a/packages/http-client-csharp/emitter/src/sdk-context.ts +++ b/packages/http-client-csharp/emitter/src/sdk-context.ts @@ -14,7 +14,7 @@ import { SdkServiceMethod, SdkType, } from "@azure-tools/typespec-client-generator-core"; -import { DiagnosticCollector, Type } from "@typespec/compiler"; +import { Type } from "@typespec/compiler"; import { Logger } from "./lib/logger.js"; import { CSharpEmitterOptions } from "./options.js"; import { InputOperation } from "./type/input-operation.js"; diff --git a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts index 4ceeddda514..d3078c9dc59 100644 --- a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts @@ -1,6 +1,6 @@ vi.resetModules(); -import { createDiagnosticCollector, Diagnostic, EmitContext, Program } from "@typespec/compiler"; +import { Diagnostic, EmitContext, Program } from "@typespec/compiler"; import { TestHost } from "@typespec/compiler/testing"; import { strictEqual } from "assert"; import { statSync } from "fs"; @@ -208,7 +208,7 @@ describe("emitCodeModel tests", () => { const context = createEmitterContext(program); const { emitCodeModel } = await import("../../src/emitter.js"); const [, diagnostics] = await emitCodeModel(context); - + // Verify that diagnostics is an array expect(Array.isArray(diagnostics)).toBe(true); // Diagnostics array should be defined (may be empty or have diagnostics) @@ -230,7 +230,7 @@ describe("emitCodeModel tests", () => { const context = createEmitterContext(program); const { emitCodeModel } = await import("../../src/emitter.js"); const [, diagnostics] = await emitCodeModel(context); - + // The function should return diagnostics even if empty expect(diagnostics).toBeDefined(); expect(Array.isArray(diagnostics)).toBe(true); diff --git a/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts index 96359b0d73c..8179f78486b 100644 --- a/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts @@ -1,5 +1,4 @@ import { TestHost } from "@typespec/compiler/testing"; -import { createDiagnosticCollector } from "@typespec/compiler"; import { ok, strictEqual } from "assert"; import { beforeEach, describe, it } from "vitest"; import { fromSdkNamespaces } from "../../src/lib/namespace-converter.js"; @@ -51,7 +50,7 @@ describe("Namespace Converter", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); const sdkPackage = sdkContext.sdkPackage; - const [parsedNamespaces, diagnostics] = fromSdkNamespaces(sdkContext, sdkPackage.namespaces); + const [parsedNamespaces] = fromSdkNamespaces(sdkContext, sdkPackage.namespaces); strictEqual(parsedNamespaces.length, 1); From 051db8e56b520e45f6a6261b9760e4de6729295d Mon Sep 17 00:00:00 2001 From: Dapeng Zhang Date: Thu, 19 Mar 2026 15:46:19 +0800 Subject: [PATCH 20/22] refactor: remove unnecessary diagnostic reporting in tests Tests that don't validate diagnostics no longer destructure or report modelDiagnostics. Tests that DO validate diagnostics now check the returned modelDiagnostics directly instead of program.diagnostics. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../emitter/test/Unit/auth.test.ts | 52 +++------- .../test/Unit/client-converter.test.ts | 12 +-- .../test/Unit/client-initialization.test.ts | 12 +-- .../test/Unit/client-model-builder.test.ts | 24 ++--- .../emitter/test/Unit/constant-type.test.ts | 12 +-- .../emitter/test/Unit/decorator-list.test.ts | 15 +-- .../emitter/test/Unit/encode.test.ts | 18 ++-- .../emitter/test/Unit/input-parameter.test.ts | 94 ++++++------------- .../emitter/test/Unit/model-type.test.ts | 54 ++++------- .../test/Unit/operation-converter.test.ts | 21 ++--- .../test/Unit/operation-paging.test.ts | 44 ++++----- .../emitter/test/Unit/property-type.test.ts | 15 +-- .../emitter/test/Unit/scalar.test.ts | 3 +- .../emitter/test/Unit/string-format.test.ts | 6 +- .../emitter/test/Unit/type-converter.test.ts | 9 +- .../emitter/test/Unit/usage.test.ts | 45 +++------ 16 files changed, 142 insertions(+), 294 deletions(-) diff --git a/packages/http-client-csharp/emitter/test/Unit/auth.test.ts b/packages/http-client-csharp/emitter/test/Unit/auth.test.ts index f910fb1798e..ceac4514668 100644 --- a/packages/http-client-csharp/emitter/test/Unit/auth.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/auth.test.ts @@ -30,9 +30,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); - const diagnostics = context.program.diagnostics; + const [root, diagnostics] = createModel(sdkContext); const noAuthDiagnostics = diagnostics.filter( (d) => @@ -73,9 +71,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); - const diagnostics = context.program.diagnostics; + const [root, diagnostics] = createModel(sdkContext); const noAuthDiagnostics = diagnostics.filter( (d) => @@ -116,9 +112,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); - const diagnostics = context.program.diagnostics; + const [root, diagnostics] = createModel(sdkContext); const noAuthDiagnostics = diagnostics.filter( (d) => @@ -157,9 +151,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); - const diagnostics = context.program.diagnostics; + const [root, diagnostics] = createModel(sdkContext); const noAuthDiagnostic = diagnostics.find( (d) => d.code === "@typespec/http-client-csharp/unsupported-auth", @@ -186,9 +178,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); - const diagnostics = context.program.diagnostics; + const [root, diagnostics] = createModel(sdkContext); const noAuthDiagnostics = diagnostics.filter( (d) => d.code === "@typespec/http-client-csharp/unsupported-auth", @@ -221,9 +211,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); - const diagnostics = context.program.diagnostics; + const [root, diagnostics] = createModel(sdkContext); const noAuthDiagnostics = diagnostics.filter( (d) => @@ -255,9 +243,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); - const diagnostics = context.program.diagnostics; + const [root, diagnostics] = createModel(sdkContext); // Should have no auth-related diagnostics const authDiagnostics = diagnostics.filter( @@ -297,8 +283,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -330,8 +315,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -361,8 +345,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -399,8 +382,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 2); @@ -437,8 +419,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -466,8 +447,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -495,8 +475,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); // Should have both OAuth2 and API key auth ok(root.auth?.oAuth2); @@ -527,8 +506,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); diff --git a/packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts index 92392185e72..8aa7561cbf7 100644 --- a/packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts @@ -28,8 +28,7 @@ describe("isMultiServiceClient", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok(client, "Client should exist"); @@ -82,8 +81,7 @@ describe("isMultiServiceClient", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); strictEqual(root.name, "Service.MultiService", "Root namespace should be Service.MultiService"); const client = root.clients[0]; @@ -149,8 +147,7 @@ describe("isMultiServiceClient", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); strictEqual(root.name, "Service.MultiService", "Root namespace should be Service.MultiService"); const client = root.clients[0]; @@ -222,8 +219,7 @@ describe("isMultiServiceClient", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); strictEqual(root.name, "Service.MultiService", "Root namespace should be Service.MultiService"); const clients = root.clients; diff --git a/packages/http-client-csharp/emitter/test/Unit/client-initialization.test.ts b/packages/http-client-csharp/emitter/test/Unit/client-initialization.test.ts index f194996e998..e03f8fcffb5 100644 --- a/packages/http-client-csharp/emitter/test/Unit/client-initialization.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/client-initialization.test.ts @@ -35,8 +35,7 @@ describe("ClientInitialization", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok(client, "Client should exist"); @@ -61,8 +60,7 @@ describe("ClientInitialization", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const client = root.clients[0]; // initializedBy field should exist on the client (may be undefined or have a value) @@ -87,8 +85,7 @@ describe("ClientInitialization", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok(client.parameters, "Client should have parameters"); @@ -117,8 +114,7 @@ describe("ClientInitialization", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok("initializedBy" in client, "Parent client should have initializedBy field"); diff --git a/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts b/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts index e2d8bbe072d..908ed25ab19 100644 --- a/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts @@ -58,8 +58,7 @@ describe("fixNamingConflicts", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); // Find the real enum const realEnum = root.enums.find( @@ -143,8 +142,7 @@ describe("fixNamingConflicts", () => { namespace: targetNamespace, } as any); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); // Get all ErrorResponse models - fixNamingConflicts should have resolved the conflicts const errorModels = root.models.filter( @@ -196,8 +194,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); // The root apiVersions should include the version from the Versions enum // which is defined in the default namespace with version "2023-01-01-preview" @@ -231,8 +228,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); // The root apiVersions should include all versions from the TestVersions enum strictEqual(root.apiVersions.length, 3, "Root apiVersions should have 3 versions"); @@ -251,8 +247,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); // Single service client should have apiVersions from the @versioned decorator ok(root.apiVersions.length > 0, "Root apiVersions should not be empty for single service"); @@ -304,8 +299,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); ok(root.apiVersions.length === 0, "Root apiVersions should be empty for multiservice"); @@ -364,8 +358,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); ok(root.apiVersions.length === 0, "Root apiVersions should be empty for multiservice"); }); @@ -422,8 +415,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); ok( root.apiVersions.length === 0, diff --git a/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts b/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts index 51617d0817c..3730f08dbdd 100644 --- a/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts @@ -33,8 +33,7 @@ describe("Name for constant type", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel); const propertyType = testModel.properties[0].type; @@ -65,8 +64,7 @@ describe("Name for constant type", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const testModel1 = root.models.find((m) => m.name === "TestModel1"); ok(testModel1); const testModel2 = root.models.find((m) => m.name === "TestModel2"); @@ -105,8 +103,7 @@ describe("Constant enum conversion", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel); const propertyType = testModel.properties[0].type; @@ -137,8 +134,7 @@ describe("Constant enum conversion", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel); const propertyType = testModel.properties[0].type; diff --git a/packages/http-client-csharp/emitter/test/Unit/decorator-list.test.ts b/packages/http-client-csharp/emitter/test/Unit/decorator-list.test.ts index a1dd9175862..53df8aa6e86 100644 --- a/packages/http-client-csharp/emitter/test/Unit/decorator-list.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/decorator-list.test.ts @@ -34,8 +34,7 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const clients = root.clients; strictEqual(clients.length, 1); ok(clients[0].children); @@ -67,8 +66,7 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const methods = root.clients[0].methods; strictEqual(methods.length, 1); const operation = methods[0].operation; @@ -99,8 +97,7 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const models = root.models; strictEqual(models.length, 1); deepStrictEqual(models[0].decorators, [ @@ -130,8 +127,7 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const models = root.models; strictEqual(models.length, 1); deepStrictEqual(models[0].properties[0].decorators, [ @@ -157,8 +153,7 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const methods = root.clients[0].methods; strictEqual(methods.length, 1); const operation = methods[0].operation; diff --git a/packages/http-client-csharp/emitter/test/Unit/encode.test.ts b/packages/http-client-csharp/emitter/test/Unit/encode.test.ts index 767fa13177f..6344bd3f4ca 100644 --- a/packages/http-client-csharp/emitter/test/Unit/encode.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/encode.test.ts @@ -32,8 +32,7 @@ describe("Test encode duration", () => { // validate method parameter const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const methodParamArray = root.clients[0].methods[0].parameters; strictEqual(1, methodParamArray.length); let type = methodParamArray[0].type; @@ -75,8 +74,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); // validate method parameter const methodParamArray = root.clients[0].methods[0].parameters; strictEqual(1, methodParamArray.length); @@ -119,8 +117,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); // validate method parameter const methodParamArray = root.clients[0].methods[0].parameters; strictEqual(1, methodParamArray.length); @@ -164,8 +161,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [codeModel, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const durationModel = models.find((m) => m.name === "ISO8601DurationProperty"); ok(durationModel); @@ -194,8 +190,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [codeModel, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const durationModel = models.find((m) => m.name === "Int32SecondsDurationProperty"); ok(durationModel); @@ -224,8 +219,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [codeModel, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const durationModel = models.find((m) => m.name === "FloatSecondsDurationProperty"); ok(durationModel); diff --git a/packages/http-client-csharp/emitter/test/Unit/input-parameter.test.ts b/packages/http-client-csharp/emitter/test/Unit/input-parameter.test.ts index 4b2610493da..7e4db98cba1 100644 --- a/packages/http-client-csharp/emitter/test/Unit/input-parameter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/input-parameter.test.ts @@ -42,8 +42,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -73,8 +72,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -104,8 +102,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -139,8 +136,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -170,8 +166,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -201,8 +196,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -234,8 +228,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -265,8 +258,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -296,8 +288,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -329,8 +320,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -360,8 +350,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -391,8 +380,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -426,8 +414,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -458,8 +445,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -490,8 +476,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -524,8 +509,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -556,8 +540,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -588,8 +571,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -627,9 +609,7 @@ describe("Test Cookie Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); - const diagnostics = context.program.diagnostics; + const [, diagnostics] = createModel(sdkContext); const unsupportedCookie = diagnostics.find( (d) => d.code === "@typespec/http-client-csharp/unsupported-cookie-parameter", @@ -672,9 +652,7 @@ describe("Endpoint parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); - const diagnostics = context.program.diagnostics; + const [, diagnostics] = createModel(sdkContext); const unsupportedCookie = diagnostics.find( (d) => d.code === "@typespec/http-client-csharp/unsupported-endpoint-url", @@ -706,8 +684,7 @@ describe("Endpoint parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [codeModel, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [codeModel] = createModel(sdkContext); const client = codeModel.clients[0]; ok(client); ok(client.parameters); @@ -743,8 +720,7 @@ describe("Endpoint parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [codeModel, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [codeModel] = createModel(sdkContext); const client = codeModel.clients[0]; ok(client); ok(client.parameters); @@ -781,8 +757,7 @@ describe("Test Spread Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); ok(root); // validate service method @@ -825,8 +800,7 @@ describe("Test Spread Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); ok(root); // validate service method @@ -879,9 +853,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); - + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const queryParam = operation.parameters.find((p) => p.name === "queryParam"); @@ -906,9 +878,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); - + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const pathParam = operation.parameters.find((p) => p.name === "pathParam"); @@ -936,9 +906,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); - + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const headerParam = operation.parameters.find((p) => p.name === "headerParam"); @@ -967,9 +935,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); - + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const contentTypeParam = operation.parameters.find((p) => p.name === "contentType"); @@ -1092,9 +1058,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); - + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const bodyParam = operation.parameters.find((p) => p.name === "bodyParam"); diff --git a/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts b/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts index 4addb3c4c8e..025e8f5ef91 100644 --- a/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts @@ -51,8 +51,7 @@ op test(@body input: Pet): Pet; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const models = root.models; const petModel = models.find((m) => m.name === "Pet"); const catModel = models.find((m) => m.name === "Cat"); @@ -136,8 +135,7 @@ op test(@body input: Pet): Pet; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [codeModel, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const pet = models.find((m) => m.name === "Pet"); assert(pet !== undefined); @@ -231,8 +229,7 @@ op test(@body input: Pet): Pet; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [codeModel, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const pet = models.find((m) => m.name === "Pet"); assert(pet !== undefined); @@ -353,8 +350,7 @@ op op5(@body body: ExtendsFooArray): ExtendsFooArray; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const models = root.models; const extendsUnknownModel = models.find((m) => m.name === "ExtendsUnknown"); const extendsStringModel = models.find((m) => m.name === "ExtendsString"); @@ -446,8 +442,7 @@ op op5(@body body: IsFooArray): IsFooArray; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const models = root.models; const isUnknownModel = models.find((m) => m.name === "IsUnknown"); const isStringModel = models.find((m) => m.name === "IsString"); @@ -498,8 +493,7 @@ op op1(): void; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const models = root.models; const isEmptyModel = models.find((m) => m.name === "Empty"); ok(isEmptyModel); @@ -528,8 +522,7 @@ model Foo { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const models = root.models; const model = models.find((m) => m.name === "Foo"); ok(model); @@ -567,8 +560,7 @@ describe("Anonymous models should be included in library", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); ok(root); // validate service method @@ -610,8 +602,7 @@ op testOperation(@bodyRoot body: HeaderModel): void; const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const models = root.models; const isEmptyModel = models.find((m) => m.name === "HeaderModel"); ok(isEmptyModel); @@ -656,8 +647,7 @@ op testOperation(@bodyRoot body: HeaderModel): void; const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const models = root.models; const isEmptyModel = models.find((m) => m.name === "HeaderModel"); ok(isEmptyModel); @@ -690,8 +680,7 @@ op testOperation(@bodyRoot body: HeaderModel): void; const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const models = root.models; const isEmptyModel = models.find((m) => m.name === "HeaderModel"); ok(isEmptyModel); @@ -718,8 +707,7 @@ op testOperation(@bodyRoot body: HeaderModel): void; const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const models = root.models; const isEmptyModel = models.find((m) => m.name === "HeaderModel"); ok(isEmptyModel); @@ -769,8 +757,7 @@ describe("typespec-client-generator-core: general decorators list", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const models = root.models; strictEqual(models.length, 1); deepStrictEqual(models[0].decorators, [ @@ -812,8 +799,7 @@ describe("Access decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const enums = root.enums; const colorEnum = enums.find((e) => e.name === "Color"); @@ -844,8 +830,7 @@ describe("Access decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const enums = root.enums; const colorEnum = enums.find((e) => e.name === "Color"); @@ -875,8 +860,7 @@ describe("Access decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const enums = root.enums; const statusEnum = enums.find((e) => e.name === "Status"); @@ -914,8 +898,7 @@ describe("Usage decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const enums = root.enums; const colorEnum = enums.find((e) => e.name === "Color"); @@ -945,8 +928,7 @@ describe("Usage decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const enums = root.enums; const colorEnum = enums.find((e) => e.name === "Color"); diff --git a/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts index ad130f773d3..af8d27f6fc0 100644 --- a/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts @@ -35,8 +35,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -107,8 +106,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -178,8 +176,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -234,8 +231,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -279,8 +275,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -319,8 +314,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -352,8 +346,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); diff --git a/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts b/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts index 7bcf7dc40d1..5338c2f7f53 100644 --- a/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts @@ -39,8 +39,7 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -94,8 +93,7 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -140,8 +138,7 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -177,8 +174,7 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -213,7 +209,6 @@ describe("Next link operations", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -225,13 +220,13 @@ describe("Next link operations", () => { strictEqual(paging.nextLink?.responseSegments.length, 1); strictEqual(paging.nextLink?.responseSegments[0], "next"); - strictEqual(program.diagnostics.length, 1); + strictEqual(modelDiagnostics.length, 1); strictEqual( - program.diagnostics[0].code, + modelDiagnostics[0].code, "@typespec/http-client-csharp/unsupported-continuation-location", ); strictEqual( - program.diagnostics[0].message, + modelDiagnostics[0].message, `Unsupported continuation location for operation ${root.clients[0].methods[0].operation.crossLanguageDefinitionId}.`, ); }); @@ -261,8 +256,7 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -297,8 +291,7 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -334,8 +327,7 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -371,8 +363,7 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -409,7 +400,6 @@ describe("Continuation token operations", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -425,13 +415,13 @@ describe("Continuation token operations", () => { strictEqual(continuationToken.responseLocation, ResponseLocation.None); strictEqual(continuationToken.responseSegments.length, 1); strictEqual(continuationToken.responseSegments[0], "nextToken"); - strictEqual(program.diagnostics.length, 1); + strictEqual(modelDiagnostics.length, 1); strictEqual( - program.diagnostics[0].code, + modelDiagnostics[0].code, "@typespec/http-client-csharp/unsupported-continuation-location", ); strictEqual( - program.diagnostics[0].message, + modelDiagnostics[0].message, `Unsupported continuation location for operation ${root.clients[0].methods[0].operation.crossLanguageDefinitionId}.`, ); }); @@ -463,8 +453,7 @@ describe("PageSize parameter operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -502,8 +491,7 @@ describe("PageSize parameter operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); diff --git a/packages/http-client-csharp/emitter/test/Unit/property-type.test.ts b/packages/http-client-csharp/emitter/test/Unit/property-type.test.ts index dca2060117f..e3589538bd3 100644 --- a/packages/http-client-csharp/emitter/test/Unit/property-type.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/property-type.test.ts @@ -28,8 +28,7 @@ describe("Test GetInputType for array", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "input", ); @@ -50,8 +49,7 @@ describe("Test GetInputType for array", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const bodyType = root.clients[0].methods[0].operation.responses[0].bodyType; strictEqual(bodyType?.kind, "array"); strictEqual(bodyType.crossLanguageDefinitionId, "TypeSpec.Array"); @@ -88,8 +86,7 @@ describe("Test GetInputType for enum", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "input", ); @@ -133,8 +130,7 @@ describe("Test GetInputType for enum", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "input", ); @@ -173,8 +169,7 @@ describe("Test GetInputType for enum", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "input", ); diff --git a/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts b/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts index 0391012f63e..cea35899816 100644 --- a/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts @@ -28,8 +28,7 @@ describe("Test GetInputType for scalar", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "location", ); diff --git a/packages/http-client-csharp/emitter/test/Unit/string-format.test.ts b/packages/http-client-csharp/emitter/test/Unit/string-format.test.ts index 62e9d943a60..3eea60ca0d1 100644 --- a/packages/http-client-csharp/emitter/test/Unit/string-format.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/string-format.test.ts @@ -27,8 +27,7 @@ describe("Test string format", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "sourceUrl", ); @@ -52,8 +51,7 @@ describe("Test string format", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [codeModel, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const foo = models.find((m) => m.name === "Foo"); ok(foo); diff --git a/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts index 131994a0b99..b6fb88bbdf9 100644 --- a/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts @@ -69,8 +69,7 @@ describe("Enum value references", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const enumType = root.enums.find((e) => e.name === "TestEnum"); ok(enumType, "TestEnum should exist in the enums list"); strictEqual(enumType.values.length, 3, "TestEnum should have 3 values"); @@ -120,8 +119,7 @@ describe("External types", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel, "TestModel should exist"); @@ -166,8 +164,7 @@ describe("External types", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel, "TestModel should exist"); diff --git a/packages/http-client-csharp/emitter/test/Unit/usage.test.ts b/packages/http-client-csharp/emitter/test/Unit/usage.test.ts index 77df29eb1cc..ee0923b0174 100644 --- a/packages/http-client-csharp/emitter/test/Unit/usage.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/usage.test.ts @@ -33,8 +33,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -55,8 +54,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -77,8 +75,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -100,8 +97,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -128,8 +124,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); const templateModel = root.models.find((model) => model.name === "TemplateModelFoo"); @@ -159,8 +154,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const baseModel = root.models.find((model) => model.name === "BaseModel"); const fooModel = root.models.find((model) => model.name === "Foo"); @@ -197,8 +191,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const baseModel = root.models.find((model) => model.name === "BaseModel"); const fooModel = root.models.find((model) => model.name === "Foo"); const propertyModel = root.models.find((model) => model.name === "PropertyModel"); @@ -225,8 +218,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const fooAlias = root.models.find((model) => model.name === "TestRequest"); ok(fooAlias); @@ -274,8 +266,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const fooInfo = root.models.find((model) => model.name === "FooInfo"); const batchCreateFooListItemsRequest = root.models.find( (model) => model.name === "BatchCreateFooListItemsRequest", @@ -321,8 +312,7 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -381,8 +371,7 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const baseModel = root.models.find((model) => model.name === "BaseModelWithDiscriminator"); const derivedModel = root.models.find( (model) => model.name === "DerivedModelWithDiscriminatorA", @@ -452,8 +441,7 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const baseModel = root.models.find((model) => model.name === "BaseModelWithDiscriminator"); const derivedModel = root.models.find( (model) => model.name === "DerivedModelWithDiscriminatorA", @@ -490,8 +478,7 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const simpleEnumRenamed = root.enums.find((enumType) => enumType.name === "SimpleEnumRenamed"); ok(simpleEnumRenamed); @@ -515,8 +502,7 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const renamedModel = root.models.find((model) => model.name === "RenamedModel"); ok(renamedModel); @@ -674,8 +660,7 @@ interface LegacyLro { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const [root, modelDiagnostics] = createModel(sdkContext); - context.program.reportDiagnostics(modelDiagnostics); + const [root] = createModel(sdkContext); const radiologyInsightsInferenceResult = root.models.find( (model) => model.name === "RadiologyInsightsInferenceResult", ); From 2daaeeeadb2bf99224f331c80ea7ab91b23507ce Mon Sep 17 00:00:00 2001 From: Dapeng Zhang Date: Thu, 19 Mar 2026 17:29:09 +0800 Subject: [PATCH 21/22] chore: regenerate reference docs and format code - Run 'npm run regen-docs' to remove stale 'update-code-model' option from generated emitter reference documentation - Run 'pnpm format:dir' to fix formatting across all emitter files Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../http-client-csharp/emitter/src/emitter.ts | 18 ++++--- .../http-client-csharp/emitter/src/index.ts | 2 +- .../emitter/src/lib/client-converter.ts | 2 +- .../emitter/src/lib/client-model-builder.ts | 6 +-- .../emitter/src/lib/example-converter.ts | 2 +- .../emitter/src/lib/operation-converter.ts | 2 +- .../emitter/src/lib/service-authentication.ts | 6 ++- .../emitter/src/lib/type-converter.ts | 6 +-- .../emitter/src/lib/typespec-server.ts | 12 +++-- .../emitter/test/Unit/utils/test-util.ts | 11 +++-- .../reference/decorators.md | 5 +- .../http-client-csharp/reference/emitter.md | 48 +------------------ .../http-client-csharp/reference/index.mdx | 10 +--- 13 files changed, 45 insertions(+), 85 deletions(-) diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index e91ecbcf65d..936f79dd191 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -54,11 +54,11 @@ function findProjectRoot(path: string): string | undefined { /** * Creates a code model by executing the full emission logic. * This function can be called by downstream emitters to generate a code model and collect diagnostics. - * + * * @example * ```typescript * import { emitCodeModel } from "@typespec/http-client-csharp"; - * + * * export async function $onEmit(context: EmitContext) { * const updateCodeModel = (model: CodeModel, context: CSharpEmitterContext) => { * // Customize the code model here @@ -69,7 +69,7 @@ function findProjectRoot(path: string): string | undefined { * context.program.reportDiagnostics(diagnostics); * } * ``` - * + * * @param context - The emit context * @param updateCodeModel - Optional callback to modify the code model before emission * @returns A tuple containing void and any diagnostics that were generated during the emission @@ -144,7 +144,9 @@ export async function emitCodeModel( debug: options.debug ?? false, }); if (result.exitCode !== 0) { - const isValid = diagnostics.pipe(await _validateDotNetSdk(sdkContext, _minSupportedDotNetSdkVersion)); + const isValid = diagnostics.pipe( + await _validateDotNetSdk(sdkContext, _minSupportedDotNetSdkVersion), + ); // if the dotnet sdk is valid, the error is not dependency issue, log it as normal if (isValid) { throw new Error( @@ -153,7 +155,9 @@ export async function emitCodeModel( } } } catch (error: any) { - const isValid = diagnostics.pipe(await _validateDotNetSdk(sdkContext, _minSupportedDotNetSdkVersion)); + const isValid = diagnostics.pipe( + await _validateDotNetSdk(sdkContext, _minSupportedDotNetSdkVersion), + ); // if the dotnet sdk is valid, the error is not dependency issue, log it as normal if (isValid) throw new Error(error, { cause: error }); } @@ -224,7 +228,9 @@ export async function _validateDotNetSdk( const diagnostics = createDiagnosticCollector(); try { const result = await execAsync("dotnet", ["--version"], { stdio: "pipe" }); - return diagnostics.wrap(diagnostics.pipe(validateDotNetSdkVersionCore(sdkContext, result.stdout, minMajorVersion))); + return diagnostics.wrap( + diagnostics.pipe(validateDotNetSdkVersionCore(sdkContext, result.stdout, minMajorVersion)), + ); } catch (error: any) { if (error && "code" in error && error["code"] === "ENOENT") { diagnostics.add( diff --git a/packages/http-client-csharp/emitter/src/index.ts b/packages/http-client-csharp/emitter/src/index.ts index ce40cf1b1db..fb26b5a8b09 100644 --- a/packages/http-client-csharp/emitter/src/index.ts +++ b/packages/http-client-csharp/emitter/src/index.ts @@ -3,7 +3,7 @@ export { writeCodeModel } from "./code-model-writer.js"; export { configurationFileName, tspOutputFileName } from "./constants.js"; -export { emitCodeModel, $onEmit } from "./emitter.js"; +export { $onEmit, emitCodeModel } from "./emitter.js"; // we export `createModel` only for autorest.csharp because it uses the emitter to generate the code model file but not calling the dll here // we could remove this export when in the future we deprecate autorest.csharp export { createModel } from "./lib/client-model-builder.js"; diff --git a/packages/http-client-csharp/emitter/src/lib/client-converter.ts b/packages/http-client-csharp/emitter/src/lib/client-converter.ts index 91048922e4a..52586f2ddf0 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-converter.ts @@ -11,7 +11,6 @@ import { } from "@azure-tools/typespec-client-generator-core"; import { createDiagnosticCollector, Diagnostic, NoTarget } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; -import { createDiagnostic } from "./lib.js"; import { InputParameterScope } from "../type/input-parameter-scope.js"; import { InputClient, @@ -19,6 +18,7 @@ import { InputParameter, InputType, } from "../type/input-type.js"; +import { createDiagnostic } from "./lib.js"; import { fromMethodParameter, fromSdkServiceMethod, diff --git a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts index fa0e68592c5..f0fc8413bff 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts @@ -19,16 +19,16 @@ import { firstLetterToUpperCase, getClientNamespaceString } from "./utils.js"; /** * Creates the code model from the SDK context. * This function follows TypeSpec best practices by returning diagnostics alongside the result. - * + * * @example * ```typescript * import { createModel } from "@typespec/http-client-csharp"; - * + * * const sdkContext = createCSharpEmitterContext(context, logger); * const [codeModel, diagnostics] = createModel(sdkContext); * // Process the code model and handle diagnostics * ``` - * + * * @param sdkContext - The SDK context * @returns A tuple containing the code model and any diagnostics that were generated * @beta diff --git a/packages/http-client-csharp/emitter/src/lib/example-converter.ts b/packages/http-client-csharp/emitter/src/lib/example-converter.ts index e4f61940d43..ce23c24eb70 100644 --- a/packages/http-client-csharp/emitter/src/lib/example-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/example-converter.ts @@ -52,7 +52,7 @@ export function fromSdkHttpExamples( // Create a diagnostics collector for internal use // Any errors in examples won't prevent the code model from being generated const diagnostics = createDiagnosticCollector(); - + const result = examples.map((example) => fromSdkHttpExample(example)); return diagnostics.wrap(result); diff --git a/packages/http-client-csharp/emitter/src/lib/operation-converter.ts b/packages/http-client-csharp/emitter/src/lib/operation-converter.ts index 4b74a7f436a..decc442eb79 100644 --- a/packages/http-client-csharp/emitter/src/lib/operation-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/operation-converter.ts @@ -38,7 +38,6 @@ import { import { HttpStatusCodeRange } from "@typespec/http"; import { getResourceOperation } from "@typespec/rest"; import { CSharpEmitterContext } from "../sdk-context.js"; -import { createDiagnostic } from "./lib.js"; import { collectionFormatToDelimMap } from "../type/collection-format.js"; import { HttpResponseHeader } from "../type/http-response-header.js"; import { InputConstant } from "../type/input-constant.js"; @@ -72,6 +71,7 @@ import { parseHttpRequestMethod } from "../type/request-method.js"; import { ResponseLocation } from "../type/response-location.js"; import { getExternalDocs, getOperationId } from "./decorators.js"; import { fromSdkHttpExamples } from "./example-converter.js"; +import { createDiagnostic } from "./lib.js"; import { fromSdkType } from "./type-converter.js"; import { getClientNamespaceString, isReadOnly } from "./utils.js"; diff --git a/packages/http-client-csharp/emitter/src/lib/service-authentication.ts b/packages/http-client-csharp/emitter/src/lib/service-authentication.ts index 7f7b73e17f0..52b1b44de5a 100644 --- a/packages/http-client-csharp/emitter/src/lib/service-authentication.ts +++ b/packages/http-client-csharp/emitter/src/lib/service-authentication.ts @@ -10,9 +10,9 @@ import { import { createDiagnosticCollector, Diagnostic, NoTarget } from "@typespec/compiler"; import { Oauth2Auth, OAuth2Flow } from "@typespec/http"; import { CSharpEmitterContext } from "../sdk-context.js"; -import { createDiagnostic } from "./lib.js"; import { InputAuth } from "../type/input-auth.js"; import { InputOAuth2Flow } from "../type/input-oauth2-auth.js"; +import { createDiagnostic } from "./lib.js"; export function processServiceAuthentication( sdkContext: CSharpEmitterContext, @@ -110,7 +110,9 @@ function processAuthType( diagnostics.add( createDiagnostic({ code: "unsupported-auth", - format: { message: `${schemeOrApiKeyPrefix} auth method is currently not supported.` }, + format: { + message: `${schemeOrApiKeyPrefix} auth method is currently not supported.`, + }, target: credentialType.__raw ?? NoTarget, }), ); diff --git a/packages/http-client-csharp/emitter/src/lib/type-converter.ts b/packages/http-client-csharp/emitter/src/lib/type-converter.ts index 0f4a5c46cfc..ded35eb85e8 100644 --- a/packages/http-client-csharp/emitter/src/lib/type-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/type-converter.ts @@ -3,6 +3,8 @@ import { DecoratorInfo, + getAccessOverride, + isHttpMetadata, SdkArrayType, SdkBuiltInType, SdkConstantType, @@ -17,11 +19,8 @@ import { SdkType, SdkUnionType, UsageFlags, - getAccessOverride, - isHttpMetadata, } from "@azure-tools/typespec-client-generator-core"; import { createDiagnosticCollector, Diagnostic, Model, NoTarget } from "@typespec/compiler"; -import { createDiagnostic } from "./lib.js"; import { CSharpEmitterContext } from "../sdk-context.js"; import { InputArrayType, @@ -40,6 +39,7 @@ import { InputType, InputUnionType, } from "../type/input-type.js"; +import { createDiagnostic } from "./lib.js"; import { isReadOnly } from "./utils.js"; // we have this complicated type here to let the caller of fromSdkType could infer the real return type of this function. diff --git a/packages/http-client-csharp/emitter/src/lib/typespec-server.ts b/packages/http-client-csharp/emitter/src/lib/typespec-server.ts index b7afef9f730..f31b9830dda 100644 --- a/packages/http-client-csharp/emitter/src/lib/typespec-server.ts +++ b/packages/http-client-csharp/emitter/src/lib/typespec-server.ts @@ -2,7 +2,13 @@ // Licensed under the MIT License. See License.txt in the project root for license information. import { getClientType } from "@azure-tools/typespec-client-generator-core"; -import { createDiagnosticCollector, Diagnostic, getDoc, getSummary, Value } from "@typespec/compiler"; +import { + createDiagnosticCollector, + Diagnostic, + getDoc, + getSummary, + Value, +} from "@typespec/compiler"; import { HttpServer } from "@typespec/http"; import { getExtensions } from "@typespec/openapi"; import { CSharpEmitterContext } from "../sdk-context.js"; @@ -23,7 +29,7 @@ export function resolveServers( ): [TypeSpecServer[], readonly Diagnostic[]] { // Create a diagnostics collector for internal use const diagnostics = createDiagnosticCollector(); - + const result = servers.map((server) => { const parameters: InputEndpointParameter[] = []; let url: string = server.url; @@ -104,7 +110,7 @@ export function resolveServers( parameters, }; }); - + return diagnostics.wrap(result); } diff --git a/packages/http-client-csharp/emitter/test/Unit/utils/test-util.ts b/packages/http-client-csharp/emitter/test/Unit/utils/test-util.ts index e1e2541800f..3b6d60db3ca 100644 --- a/packages/http-client-csharp/emitter/test/Unit/utils/test-util.ts +++ b/packages/http-client-csharp/emitter/test/Unit/utils/test-util.ts @@ -122,10 +122,11 @@ export async function createCSharpSdkContext( sdkContextOptions: CreateSdkContextOptions = {}, ): Promise { const createSdkContext = await getCreateSdkContext(); - const context = await createSdkContext(program, "@typespec/http-client-csharp", sdkContextOptions); - const Logger = await getLogger(); - return createCSharpEmitterContext( - context, - new Logger(program.program, LoggerLevel.INFO), + const context = await createSdkContext( + program, + "@typespec/http-client-csharp", + sdkContextOptions, ); + const Logger = await getLogger(); + return createCSharpEmitterContext(context, new Logger(program.program, LoggerLevel.INFO)); } diff --git a/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/decorators.md b/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/decorators.md index 46589b06c79..892297a2833 100644 --- a/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/decorators.md +++ b/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/decorators.md @@ -4,14 +4,11 @@ description: "Decorators exported by @typespec/http-client-csharp" toc_min_heading_level: 2 toc_max_heading_level: 3 --- - ## TypeSpec.HttpClient.CSharp - ### `@dynamicModel` {#@TypeSpec.HttpClient.CSharp.dynamicModel} Marks a model or namespace as dynamic, indicating it should generate dynamic model code. Can be applied to Model or Namespace types. - ```typespec @TypeSpec.HttpClient.CSharp.dynamicModel ``` @@ -21,7 +18,6 @@ Can be applied to Model or Namespace types. `Model | Namespace` #### Parameters - None #### Examples @@ -40,3 +36,4 @@ namespace PetStore { } } ``` + diff --git a/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/emitter.md b/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/emitter.md index 55c52dcc6bf..9a8bcdbe92a 100644 --- a/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/emitter.md +++ b/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/emitter.md @@ -1,24 +1,17 @@ --- title: "Emitter usage" --- - ## Emitter usage - 1. Via the command line - ```bash tsp compile . --emit=@typespec/http-client-csharp ``` - 2. Via the config - ```yaml emit: - - "@typespec/http-client-csharp" + - "@typespec/http-client-csharp" ``` - The config can be extended with options as follows: - ```yaml emit: - "@typespec/http-client-csharp" @@ -26,102 +19,65 @@ options: "@typespec/http-client-csharp": option: value ``` - ## Emitter options - ### `emitter-output-dir` - **Type:** `absolutePath` Defines the emitter output directory. Defaults to `{output-dir}/@typespec/http-client-csharp` See [Configuring output directory for more info](https://typespec.io/docs/handbook/configuration/configuration/#configuring-output-directory) - ### `api-version` - **Type:** `string` For TypeSpec files using the [`@versioned`](https://typespec.io/docs/libraries/versioning/reference/decorators/#@TypeSpec.Versioning.versioned) decorator, set this option to the version that should be used to generate against. - ### `generate-protocol-methods` - **Type:** `boolean` Set to `false` to skip generation of protocol methods. The default value is `true`. - ### `generate-convenience-methods` - **Type:** `boolean` Set to `false` to skip generation of convenience methods. The default value is `true`. - ### `unreferenced-types-handling` - **Type:** `"removeOrInternalize" | "internalize" | "keepAll"` Defines the strategy on how to handle unreferenced types. The default value is `removeOrInternalize`. - ### `new-project` - **Type:** `boolean` Set to `true` to overwrite the csproj if it already exists. The default value is `false`. - ### `save-inputs` - **Type:** `boolean` Set to `true` to save the `tspCodeModel.json` and `Configuration.json` files that are emitted and used as inputs to the generator. The default value is `false`. - ### `package-name` - **Type:** `string` Define the package name. If not specified, the first namespace defined in the TypeSpec is used as the package name. - ### `debug` - **Type:** `boolean` Set to `true` to automatically attempt to attach to a debugger when executing the C# generator. The default value is `false`. - ### `logLevel` - **Type:** `"info" | "debug" | "verbose"` Set the log level for which to collect traces. The default value is `info`. - ### `disable-xml-docs` - **Type:** `boolean` Set to `true` to disable XML documentation generation. The default value is `false`. - ### `generator-name` - **Type:** `string` The name of the generator. By default this is set to `ScmCodeModelGenerator`. Generator authors can set this to the name of a generator that inherits from `ScmCodeModelGenerator`. - ### `emitter-extension-path` - **Type:** `string` Allows emitter authors to specify the path to a custom emitter package, allowing you to extend the emitter behavior. This should be set to `import.meta.url` if you are using a custom emitter. - -### `update-code-model` - -**Type:** `object` - -Allows emitter authors to specify a custom function to modify the generated code model before emitting. This is useful for modifying the code model before it is passed to the generator. - ### `license` - **Type:** `object` License information for the generated client code. - ### `sdk-context-options` - **Type:** `object` -The SDK context options that implement the `CreateSdkContextOptions` interface from the [`@azure-tools/typespec-client-generator-core`](https://www.npmjs.com/package/@azure-tools/typespec-client-generator-core) package to be used by the CSharp emitter. +The SDK context options that implement the `CreateSdkContextOptions` interface from the [`@azure-tools/typespec-client-generator-core`](https://www.npmjs.com/package/@azure-tools/typespec-client-generator-core) package to be used by the CSharp emitter. \ No newline at end of file diff --git a/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/index.mdx b/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/index.mdx index e7a4de67da2..98fd44e9a3c 100644 --- a/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/index.mdx +++ b/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/index.mdx @@ -4,13 +4,10 @@ sidebar_position: 0 toc_min_heading_level: 2 toc_max_heading_level: 3 --- - import { Tabs, TabItem } from '@astrojs/starlight/components'; TypeSpec library for emitting Http Client libraries for C#. - ## Install - @@ -29,13 +26,8 @@ npm install --save-peer @typespec/http-client-csharp ## Emitter usage - [See documentation](./emitter.md) - ## TypeSpec.HttpClient - ## TypeSpec.HttpClient.CSharp - ### Decorators - -- [`@dynamicModel`](./decorators.md#@TypeSpec.HttpClient.CSharp.dynamicModel) + - [`@dynamicModel`](./decorators.md#@TypeSpec.HttpClient.CSharp.dynamicModel) \ No newline at end of file From b9da5583c1a036e11e11db96d6dd45b715bf59e3 Mon Sep 17 00:00:00 2001 From: Dapeng Zhang Date: Thu, 19 Mar 2026 18:03:58 +0800 Subject: [PATCH 22/22] fix: manually align reference docs with CI-generated format Add blank lines between markdown sections to match the tspd doc output from CI pipeline. Removes trailing blank line from decorators.md. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../reference/decorators.md | 5 ++- .../http-client-csharp/reference/emitter.md | 42 ++++++++++++++++++- .../http-client-csharp/reference/index.mdx | 10 ++++- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/decorators.md b/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/decorators.md index 892297a2833..46589b06c79 100644 --- a/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/decorators.md +++ b/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/decorators.md @@ -4,11 +4,14 @@ description: "Decorators exported by @typespec/http-client-csharp" toc_min_heading_level: 2 toc_max_heading_level: 3 --- + ## TypeSpec.HttpClient.CSharp + ### `@dynamicModel` {#@TypeSpec.HttpClient.CSharp.dynamicModel} Marks a model or namespace as dynamic, indicating it should generate dynamic model code. Can be applied to Model or Namespace types. + ```typespec @TypeSpec.HttpClient.CSharp.dynamicModel ``` @@ -18,6 +21,7 @@ Can be applied to Model or Namespace types. `Model | Namespace` #### Parameters + None #### Examples @@ -36,4 +40,3 @@ namespace PetStore { } } ``` - diff --git a/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/emitter.md b/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/emitter.md index 9a8bcdbe92a..7a989e85368 100644 --- a/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/emitter.md +++ b/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/emitter.md @@ -1,17 +1,24 @@ --- title: "Emitter usage" --- + ## Emitter usage + 1. Via the command line + ```bash tsp compile . --emit=@typespec/http-client-csharp ``` + 2. Via the config + ```yaml emit: - - "@typespec/http-client-csharp" + - "@typespec/http-client-csharp" ``` + The config can be extended with options as follows: + ```yaml emit: - "@typespec/http-client-csharp" @@ -19,65 +26,96 @@ options: "@typespec/http-client-csharp": option: value ``` + ## Emitter options + ### `emitter-output-dir` + **Type:** `absolutePath` Defines the emitter output directory. Defaults to `{output-dir}/@typespec/http-client-csharp` See [Configuring output directory for more info](https://typespec.io/docs/handbook/configuration/configuration/#configuring-output-directory) + ### `api-version` + **Type:** `string` For TypeSpec files using the [`@versioned`](https://typespec.io/docs/libraries/versioning/reference/decorators/#@TypeSpec.Versioning.versioned) decorator, set this option to the version that should be used to generate against. + ### `generate-protocol-methods` + **Type:** `boolean` Set to `false` to skip generation of protocol methods. The default value is `true`. + ### `generate-convenience-methods` + **Type:** `boolean` Set to `false` to skip generation of convenience methods. The default value is `true`. + ### `unreferenced-types-handling` + **Type:** `"removeOrInternalize" | "internalize" | "keepAll"` Defines the strategy on how to handle unreferenced types. The default value is `removeOrInternalize`. + ### `new-project` + **Type:** `boolean` Set to `true` to overwrite the csproj if it already exists. The default value is `false`. + ### `save-inputs` + **Type:** `boolean` Set to `true` to save the `tspCodeModel.json` and `Configuration.json` files that are emitted and used as inputs to the generator. The default value is `false`. + ### `package-name` + **Type:** `string` Define the package name. If not specified, the first namespace defined in the TypeSpec is used as the package name. + ### `debug` + **Type:** `boolean` Set to `true` to automatically attempt to attach to a debugger when executing the C# generator. The default value is `false`. + ### `logLevel` + **Type:** `"info" | "debug" | "verbose"` Set the log level for which to collect traces. The default value is `info`. + ### `disable-xml-docs` + **Type:** `boolean` Set to `true` to disable XML documentation generation. The default value is `false`. + ### `generator-name` + **Type:** `string` The name of the generator. By default this is set to `ScmCodeModelGenerator`. Generator authors can set this to the name of a generator that inherits from `ScmCodeModelGenerator`. + ### `emitter-extension-path` + **Type:** `string` Allows emitter authors to specify the path to a custom emitter package, allowing you to extend the emitter behavior. This should be set to `import.meta.url` if you are using a custom emitter. + ### `license` + **Type:** `object` License information for the generated client code. + ### `sdk-context-options` + **Type:** `object` -The SDK context options that implement the `CreateSdkContextOptions` interface from the [`@azure-tools/typespec-client-generator-core`](https://www.npmjs.com/package/@azure-tools/typespec-client-generator-core) package to be used by the CSharp emitter. \ No newline at end of file +The SDK context options that implement the `CreateSdkContextOptions` interface from the [`@azure-tools/typespec-client-generator-core`](https://www.npmjs.com/package/@azure-tools/typespec-client-generator-core) package to be used by the CSharp emitter. diff --git a/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/index.mdx b/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/index.mdx index 98fd44e9a3c..e7a4de67da2 100644 --- a/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/index.mdx +++ b/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/index.mdx @@ -4,10 +4,13 @@ sidebar_position: 0 toc_min_heading_level: 2 toc_max_heading_level: 3 --- + import { Tabs, TabItem } from '@astrojs/starlight/components'; TypeSpec library for emitting Http Client libraries for C#. + ## Install + @@ -26,8 +29,13 @@ npm install --save-peer @typespec/http-client-csharp ## Emitter usage + [See documentation](./emitter.md) + ## TypeSpec.HttpClient + ## TypeSpec.HttpClient.CSharp + ### Decorators - - [`@dynamicModel`](./decorators.md#@TypeSpec.HttpClient.CSharp.dynamicModel) \ No newline at end of file + +- [`@dynamicModel`](./decorators.md#@TypeSpec.HttpClient.CSharp.dynamicModel)