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..cb58710ea2b 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 { 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"; import { CodeModel } from "./type/code-model.js"; import { Configuration } from "./type/configuration.js"; diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index 8bd34db8889..c324ad666d6 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -3,6 +3,8 @@ import { createSdkContext, SdkContext } from "@azure-tools/typespec-client-generator-core"; import { + createDiagnosticCollector, + Diagnostic, EmitContext, getDirectoryPath, joinPaths, @@ -20,11 +22,13 @@ 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"; 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"; /** @@ -48,11 +52,34 @@ 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. + * + * @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 + * return model; + * }; + * const [, diagnostics] = await emitCodeModel(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 $onEmit(context: EmitContext) { +export async function emitCodeModel( + 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; @@ -70,12 +97,16 @@ export async function $onEmit(context: EmitContext) { ), logger, ); - program.reportDiagnostics(sdkContext.diagnostics); + for (const diag of sdkContext.diagnostics) { + diagnostics.add(diag); + } - let root = createModel(sdkContext); + const root = diagnostics.pipe(createModel(sdkContext)); if (root) { - root = options["update-code-model"](root, sdkContext); + // Apply optional code model update callback + const updatedRoot = updateCodeModel ? updateCodeModel(root, sdkContext) : root; + const generatedFolder = resolvePath(outputFolder, "src", "Generated"); if (!fs.existsSync(generatedFolder)) { @@ -83,9 +114,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 @@ -113,7 +144,7 @@ export async function $onEmit(context: EmitContext) { debug: options.debug ?? false, }); if (result.exitCode !== 0) { - const isValid = 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( @@ -122,7 +153,7 @@ export async function $onEmit(context: EmitContext) { } } } catch (error: any) { - const isValid = 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); } @@ -133,6 +164,18 @@ export async function $onEmit(context: EmitContext) { } } } + + return diagnostics.wrap(undefined); +} + +/** + * 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 emitCodeModel(context); + context.program.reportDiagnostics(diagnostics); } export function createConfiguration( @@ -142,7 +185,6 @@ export function createConfiguration( ): Configuration { const skipKeys = [ "new-project", - "update-code-model", "sdk-context-options", "save-inputs", "generator-name", @@ -172,29 +214,32 @@ 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 { +): 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(diagnostics.pipe(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, - }); + diagnostics.add( + createDiagnostic({ + code: "invalid-dotnet-sdk-dependency", + messageId: "missing", + format: { + dotnetMajorVersion: `${minMajorVersion}`, + downloadUrl: "https://dotnet.microsoft.com/", + }, + target: NoTarget, + }), + ); } - return false; + return diagnostics.wrap(false); } } @@ -202,32 +247,41 @@ function validateDotNetSdkVersionCore( sdkContext: CSharpEmitterContext, version: string, minMajorVersion: number, -): 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) { - sdkContext.logger.reportDiagnostic({ - code: "invalid-dotnet-sdk-dependency", - messageId: "invalidVersion", - format: { - installedVersion: version, - dotnetMajorVersion: `${minMajorVersion}`, - downloadUrl: "https://dotnet.microsoft.com/", - }, - target: NoTarget, - }); - return false; + diagnostics.add( + createDiagnostic({ + code: "invalid-dotnet-sdk-dependency", + messageId: "invalidVersion", + format: { + installedVersion: version, + dotnetMajorVersion: `${minMajorVersion}`, + downloadUrl: "https://dotnet.microsoft.com/", + }, + target: NoTarget, + }), + ); + return diagnostics.wrap(false); } - return true; + return diagnostics.wrap(true); } else { - sdkContext.logger.error("Cannot get the installed .NET SDK version."); - return false; + diagnostics.add( + createDiagnostic({ + code: "general-error", + format: { message: "Cannot get the installed .NET SDK version." }, + target: NoTarget, + }), + ); + return diagnostics.wrap(false); } } diff --git a/packages/http-client-csharp/emitter/src/index.ts b/packages/http-client-csharp/emitter/src/index.ts index 5a0b24094c7..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 { $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/src/lib/client-converter.ts b/packages/http-client-csharp/emitter/src/lib/client-converter.ts index ff75e604ce2..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,8 +9,9 @@ import { SdkHttpOperation, SdkMethodParameter, } from "@azure-tools/typespec-client-generator-core"; -import { 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"; import { InputClient, @@ -32,24 +33,26 @@ export function fromSdkClients( sdkContext: CSharpEmitterContext, clients: SdkClientType[], rootApiVersions: string[], -): InputClient[] { +): [InputClient[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const inputClients: InputClient[] = []; for (const client of clients) { - const inputClient = fromSdkClient(sdkContext, client, rootApiVersions); + 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[], -): 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", @@ -57,11 +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, - ); + )); inputClient = { kind: "client", @@ -70,7 +73,7 @@ function fromSdkClient( doc: client.doc, summary: client.summary, methods: client.methods - .map((m) => fromSdkServiceMethod(sdkContext, m, uri, rootApiVersions, client.namespace)) + .map((m) => diagnostics.pipe(fromSdkServiceMethod(sdkContext, m, uri, rootApiVersions, client.namespace))) .filter((m) => m !== undefined), parameters: clientParameters, initializedBy: client.clientInitialization.initializedBy, @@ -86,62 +89,67 @@ function fromSdkClient( // fill parent if (client.parent) { - inputClient.parent = fromSdkClient(sdkContext, client.parent, rootApiVersions); + 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.pipe(fromSdkClient(sdkContext, c, rootApiVersions)), ); } - return inputClient; + return diagnostics.wrap(inputClient); function fromSdkClientInitializationParameters( sdkContext: CSharpEmitterContext, parameters: (SdkEndpointParameter | SdkCredentialParameter | SdkMethodParameter)[], namespace: string, - ): 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); + const endpointParams = diagnostics.pipe(fromSdkEndpointParameter(param)); inputParameters.push(...endpointParams); } else if (param.kind === "method") { // Convert method parameters - const methodParam = fromMethodParameter(sdkContext, param, namespace); + 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): InputEndpointParameter[] { + function fromSdkEndpointParameter(p: SdkEndpointParameter): [InputEndpointParameter[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); if (p.type.kind === "union") { - return fromSdkEndpointType(p.type.variantTypes[0]); + return diagnostics.wrap(diagnostics.pipe(fromSdkEndpointType(p.type.variantTypes[0]))); } else { - return fromSdkEndpointType(p.type); + return diagnostics.wrap(diagnostics.pipe(fromSdkEndpointType(p.type))); } } - function fromSdkEndpointType(type: SdkEndpointType): 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://", "") .replace("http://", "") .split("/")[0]; if (!/^\{\w+\}$/.test(endpointExpr)) { - sdkContext.logger.reportDiagnostic({ - code: "unsupported-endpoint-url", - format: { endpoint: type.serverUrl }, - target: NoTarget, - }); - return []; + diagnostics.add( + createDiagnostic({ + code: "unsupported-endpoint-url", + format: { endpoint: type.serverUrl }, + target: NoTarget, + }), + ); + return diagnostics.wrap([]); } const endpointVariableName = endpointExpr.substring(1, endpointExpr.length - 1); @@ -155,7 +163,7 @@ function fromSdkClient( crossLanguageDefinitionId: parameter.type.kind === "string" ? "TypeSpec.string" : "TypeSpec.url", } - : fromSdkType(sdkContext, parameter.type); // TODO: consolidate with converter.fromSdkEndpointType + : diagnostics.pipe(fromSdkType(sdkContext, parameter.type)); // TODO: consolidate with converter.fromSdkEndpointType parameters.push({ kind: "endpoint", name: parameter.name, @@ -167,18 +175,18 @@ function fromSdkClient( optional: parameter.optional, scope: InputParameterScope.Client, isEndpoint: isEndpoint, - defaultValue: getParameterDefaultValue( + defaultValue: diagnostics.pipe(getParameterDefaultValue( sdkContext, parameter.clientDefaultValue, parameterType, - ), + )), 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 94f1bfd767d..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,6 +7,7 @@ import { SdkHttpOperation, UsageFlags, } from "@azure-tools/typespec-client-generator-core"; +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"; @@ -22,17 +23,29 @@ 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"; + * + * const sdkContext = createCSharpEmitterContext(context, logger); + * const [codeModel, diagnostics] = createModel(sdkContext); + * // Process the code model and handle diagnostics + * ``` + * * @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 diagnostics = createDiagnosticCollector(); 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.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.pipe(navigateModels(sdkContext)); const types = Array.from(sdkContext.__typeCache.types.values()); const [models, enums] = [ @@ -42,7 +55,7 @@ export function createModel(sdkContext: CSharpEmitterContext): CodeModel { const rootClients = sdkPackage.clients; const rootApiVersions = parseApiVersions(sdkPackage.enums, rootClients); - const inputClients = fromSdkClients(sdkContext, rootClients, rootApiVersions); + 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()); @@ -57,10 +70,10 @@ export function createModel(sdkContext: CSharpEmitterContext): CodeModel { constants: constants, models: models, clients: inputClients, - auth: processServiceAuthentication(sdkContext, sdkPackage), + auth: diagnostics.pipe(processServiceAuthentication(sdkContext, sdkPackage)), }; - return clientModel; + return diagnostics.wrap(clientModel); } /** @@ -152,11 +165,13 @@ function fixNamingConflicts(models: InputModelType[], constants: InputLiteralTyp } } -function navigateModels(sdkContext: CSharpEmitterContext) { +function navigateModels(sdkContext: CSharpEmitterContext): [void, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); for (const m of sdkContext.sdkPackage.models) { - fromSdkType(sdkContext, m); + diagnostics.pipe(fromSdkType(sdkContext, m)); } for (const e of sdkContext.sdkPackage.enums) { - fromSdkType(sdkContext, e); + 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 74b20a8c479..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,6 +16,7 @@ import { SdkUnionExampleValue, SdkUnknownExampleValue, } from "@azure-tools/typespec-client-generator-core"; +import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { InputArrayExampleValue, @@ -47,8 +48,13 @@ import { fromSdkType } from "./type-converter.js"; export function fromSdkHttpExamples( sdkContext: CSharpEmitterContext, examples: SdkHttpOperationExample[], -): InputHttpOperationExample[] { - return examples.map((example) => fromSdkHttpExample(example)); +): [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(); + + const result = examples.map((example) => fromSdkHttpExample(example)); + return diagnostics.wrap(result); function fromSdkHttpExample(example: SdkHttpOperationExample): InputHttpOperationExample { return { @@ -76,7 +82,7 @@ export function fromSdkHttpExamples( responseValue: SdkHttpResponseExampleValue, ): OperationResponseExample { return { - response: fromSdkHttpOperationResponse(sdkContext, responseValue.response), + response: diagnostics.pipe(fromSdkHttpOperationResponse(sdkContext, responseValue.response)), statusCode: responseValue.statusCode, bodyValue: responseValue.bodyValue ? fromSdkExample(responseValue.bodyValue) : undefined, }; @@ -108,7 +114,7 @@ export function fromSdkHttpExamples( function fromSdkStringExample(example: SdkStringExampleValue): InputStringExampleValue { return { kind: "string", - type: fromSdkType(sdkContext, example.type), + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)), value: example.value, }; } @@ -116,7 +122,7 @@ export function fromSdkHttpExamples( function fromSdkNumberExample(example: SdkNumberExampleValue): InputNumberExampleValue { return { kind: "number", - type: fromSdkType(sdkContext, example.type), + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)), value: example.value, }; } @@ -124,7 +130,7 @@ export function fromSdkHttpExamples( function fromSdkBooleanExample(example: SdkBooleanExampleValue): InputBooleanExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputPrimitiveType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputPrimitiveType, value: example.value, }; } @@ -132,7 +138,7 @@ export function fromSdkHttpExamples( function fromSdkUnionExample(example: SdkUnionExampleValue): InputUnionExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputUnionType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputUnionType, value: example.value, }; } @@ -140,7 +146,7 @@ export function fromSdkHttpExamples( function fromSdkArrayExample(example: SdkArrayExampleValue): InputArrayExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputArrayType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputArrayType, value: example.value.map((v) => fromSdkExample(v)), }; } @@ -150,7 +156,7 @@ export function fromSdkHttpExamples( ): InputDictionaryExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputDictionaryType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputDictionaryType, value: fromExampleRecord(example.value), }; } @@ -158,7 +164,7 @@ export function fromSdkHttpExamples( function fromSdkModelExample(example: SdkModelExampleValue): InputModelExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputModelType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputModelType, value: fromExampleRecord(example.value), additionalPropertiesValue: example.additionalPropertiesValue ? fromExampleRecord(example.additionalPropertiesValue) @@ -169,7 +175,7 @@ export function fromSdkHttpExamples( function fromSdkAnyExample(example: SdkUnknownExampleValue): InputUnknownExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputPrimitiveType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputPrimitiveType, value: example.value, }; } @@ -177,7 +183,7 @@ export function fromSdkHttpExamples( function fromSdkNullExample(example: SdkNullExampleValue): InputNullExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputNullableType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputNullableType, value: example.value, }; } diff --git a/packages/http-client-csharp/emitter/src/lib/logger.ts b/packages/http-client-csharp/emitter/src/lib/logger.ts index 8b087a400f3..654f0bf1aea 100644 --- a/packages/http-client-csharp/emitter/src/lib/logger.ts +++ b/packages/http-client-csharp/emitter/src/lib/logger.ts @@ -1,12 +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 { DiagnosticReport, NoTarget, Program, Tracer } from "@typespec/compiler"; -import { - 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"; /** @@ -60,25 +56,7 @@ export class Logger { } } - reportDiagnostic( - diag: DiagnosticReport, - ): void { - 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, - }); + this.tracer.trace("warning", message); } } 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..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,18 +1,20 @@ import { SdkHttpOperation, SdkNamespace } from "@azure-tools/typespec-client-generator-core"; +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[], -): 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 566bdb7ba64..990043f9dc7 100644 --- a/packages/http-client-csharp/emitter/src/lib/operation-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/operation-converter.ts @@ -25,10 +25,17 @@ import { shouldGenerateConvenient, shouldGenerateProtocol, } from "@azure-tools/typespec-client-generator-core"; -import { 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"; +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"; @@ -71,75 +78,79 @@ export function fromSdkServiceMethod( uri: string, rootApiVersions: string[], namespace: string, -): 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, - ); + )); break; case "paging": - const pagingServiceMethod = createServiceMethod( + const pagingServiceMethod = diagnostics.pipe(createServiceMethod( sdkContext, sdkMethod, uri, rootApiVersions, namespace, - ); - pagingServiceMethod.pagingMetadata = loadPagingServiceMetadata( + )); + pagingServiceMethod.pagingMetadata = diagnostics.pipe(loadPagingServiceMetadata( sdkContext, sdkMethod, rootApiVersions, uri, namespace, - ); + )); method = pagingServiceMethod; break; case "lro": - const lroServiceMethod = createServiceMethod( + const lroServiceMethod = diagnostics.pipe(createServiceMethod( sdkContext, sdkMethod, uri, rootApiVersions, namespace, - ); - lroServiceMethod.lroMetadata = loadLongRunningMetadata(sdkContext, sdkMethod); + )); + 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, - ); - lroPagingMethod.lroMetadata = loadLongRunningMetadata(sdkContext, sdkMethod); - lroPagingMethod.pagingMetadata = loadPagingServiceMetadata( + )); + lroPagingMethod.lroMetadata = diagnostics.pipe(loadLongRunningMetadata(sdkContext, sdkMethod)); + lroPagingMethod.pagingMetadata = diagnostics.pipe(loadPagingServiceMetadata( sdkContext, sdkMethod, rootApiVersions, uri, namespace, - ); + )); method = lroPagingMethod; break; default: - sdkContext.logger.reportDiagnostic({ - code: "unsupported-service-method", - format: { methodKind: methodKind }, - target: NoTarget, - }); + diagnostics.add( + createDiagnostic({ + code: "unsupported-service-method", + format: { methodKind: methodKind }, + target: NoTarget, + }), + ); method = undefined; break; } @@ -148,7 +159,7 @@ export function fromSdkServiceMethod( sdkContext.__typeCache.updateSdkMethodReferences(sdkMethod, method); } - return method; + return diagnostics.wrap(method); } export function fromSdkServiceMethodOperation( @@ -156,21 +167,25 @@ export function fromSdkServiceMethodOperation( method: SdkServiceMethod, uri: string, rootApiVersions: string[], -): 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); if (method.operation.verb === "patch" && generateConvenience) { - sdkContext.logger.reportDiagnostic({ - code: "unsupported-patch-convenience-method", - format: { - methodCrossLanguageDefinitionId: method.crossLanguageDefinitionId, - }, - target: method.__raw ?? NoTarget, - }); + diagnostics.add( + createDiagnostic({ + code: "unsupported-patch-convenience-method", + format: { + methodCrossLanguageDefinitionId: method.crossLanguageDefinitionId, + }, + target: method.__raw ?? NoTarget, + }), + ); generateConvenience = false; } @@ -184,8 +199,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: 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, @@ -197,37 +212,39 @@ 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, }; sdkContext.__typeCache.updateSdkOperationReferences(method.operation, operation); - return operation; + return diagnostics.wrap(operation); } export function getParameterDefaultValue( sdkContext: CSharpEmitterContext, clientDefaultValue: any, parameterType: InputType, -): 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); - return { + const kind = diagnostics.pipe(getValueType(sdkContext, clientDefaultValue)); + return diagnostics.wrap({ type: { kind: kind, name: kind, crossLanguageDefinitionId: `TypeSpec.${kind}`, }, value: clientDefaultValue, - }; + }); } function createServiceMethod( @@ -236,44 +253,50 @@ function createServiceMethod( uri: string, rootApiVersions: string[], namespace: string, -): 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), - parameters: fromSdkServiceMethodParameters(sdkContext, method, rootApiVersions, namespace), - response: fromSdkServiceMethodResponse(sdkContext, method.response), + 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.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): 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: - sdkContext.logger.reportDiagnostic({ - code: "unsupported-default-value-type", - format: { valueType: typeof value }, - target: NoTarget, - }); - return "unknown"; + diagnostics.add( + createDiagnostic({ + code: "unsupported-default-value-type", + format: { valueType: typeof value }, + target: NoTarget, + }), + ); + return diagnostics.wrap("unknown"); } } @@ -282,11 +305,12 @@ function fromSdkServiceMethodParameters( method: SdkServiceMethod, rootApiVersions: string[], namespace: string, -): InputMethodParameter[] { +): [InputMethodParameter[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const parameters: InputMethodParameter[] = []; for (const p of method.parameters) { - const methodInputParameter = fromMethodParameter(sdkContext, p, namespace); + const methodInputParameter = diagnostics.pipe(fromMethodParameter(sdkContext, p, namespace)); const operationHttpParameter = getHttpOperationParameter(method, p); if (!operationHttpParameter) { @@ -300,11 +324,12 @@ function fromSdkServiceMethodParameters( methodInputParameter, operationHttpParameter, rootApiVersions, + diagnostics, ); parameters.push(methodInputParameter); } - return parameters; + return diagnostics.wrap(parameters); } function updateMethodParameter( @@ -312,6 +337,7 @@ function updateMethodParameter( methodParameter: InputMethodParameter, operationHttpParameter: SdkHttpParameter | SdkModelPropertyType, rootApiVersions: string[], + diagnostics: ReturnType, ): void { methodParameter.serializedName = getNameInRequest(operationHttpParameter); methodParameter.location = getParameterLocation(operationHttpParameter); @@ -323,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); + methodParameter.type = diagnostics.pipe(fromSdkType(sdkContext, operationHttpParameter.type)); } } } @@ -331,75 +357,85 @@ function updateMethodParameter( function fromSdkServiceMethodResponse( sdkContext: CSharpEmitterContext, methodResponse: SdkMethodResponse, -): InputServiceMethodResponse { - return { - type: getResponseType(sdkContext, methodResponse.type), +): [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[], -): InputHttpParameter[] { +): [InputHttpParameter[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); 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, - }); - return parameters; + diagnostics.add( + createDiagnostic({ + code: "unsupported-cookie-parameter", + format: { parameterName: p.name, path: operation.path }, + target: NoTarget, + }), + ); + return diagnostics.wrap(parameters); } - const param = fromParameter(sdkContext, p, rootApiVersions); + const param = diagnostics.pipe(fromParameter(sdkContext, p, rootApiVersions)); if (param) { parameters.push(param); } } if (operation.bodyParam) { - const bodyParam = fromParameter(sdkContext, operation.bodyParam, rootApiVersions); + 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[], -): 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); + parameter = diagnostics.pipe(fromQueryParameter(sdkContext, p, rootApiVersions)); break; case "path": - parameter = fromPathParameter(sdkContext, p, rootApiVersions); + parameter = diagnostics.pipe(fromPathParameter(sdkContext, p, rootApiVersions)); break; case "header": - parameter = fromHeaderParameter(sdkContext, p, rootApiVersions); + parameter = diagnostics.pipe(fromHeaderParameter(sdkContext, p, rootApiVersions)); break; case "body": - parameter = fromBodyParameter(sdkContext, p, rootApiVersions); + parameter = diagnostics.pipe(fromBodyParameter(sdkContext, p, rootApiVersions)); break; default: - sdkContext.logger.reportDiagnostic({ - code: "unsupported-parameter-kind", - format: { parameterKind }, - target: p.__raw ?? NoTarget, - }); + diagnostics.add( + createDiagnostic({ + code: "unsupported-parameter-kind", + format: { parameterKind }, + target: p.__raw ?? NoTarget, + }), + ); parameter = undefined; break; } @@ -407,15 +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[], -): InputQueryParameter { - const parameterType = fromSdkType(sdkContext, p.type); +): [InputQueryParameter, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + const parameterType = diagnostics.pipe(fromSdkType(sdkContext, p.type)); const retVar: InputQueryParameter = { kind: "query", @@ -426,7 +463,7 @@ function fromQueryParameter( type: parameterType, isApiVersion: p.isApiVersionParam, explode: isExploded(p), - defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType), + defaultValue: diagnostics.pipe(getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType)), arraySerializationDelimiter: getArraySerializationDelimiter(p), optional: p.optional, scope: getParameterScope(p, parameterType, rootApiVersions.length > 0), @@ -436,15 +473,16 @@ function fromQueryParameter( }; sdkContext.__typeCache.updateSdkOperationParameterReferences(p, retVar); - return retVar; + return diagnostics.wrap(retVar); } function fromPathParameter( sdkContext: CSharpEmitterContext, p: SdkPathParameter, rootApiVersions: string[], -): InputPathParameter { - const parameterType = fromSdkType(sdkContext, p.type); +): [InputPathParameter, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + const parameterType = diagnostics.pipe(fromSdkType(sdkContext, p.type)); const retVar: InputPathParameter = { kind: "path", @@ -458,7 +496,7 @@ function fromPathParameter( style: p.style, allowReserved: p.allowReserved, skipUrlEncoding: p.allowReserved, - defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType), + defaultValue: diagnostics.pipe(getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType)), optional: p.optional, scope: getParameterScope(p, parameterType, rootApiVersions.length > 0), decorators: p.decorators, @@ -467,15 +505,16 @@ function fromPathParameter( }; sdkContext.__typeCache.updateSdkOperationParameterReferences(p, retVar); - return retVar; + return diagnostics.wrap(retVar); } function fromHeaderParameter( sdkContext: CSharpEmitterContext, p: SdkHeaderParameter, rootApiVersions: string[], -): InputHeaderParameter { - const parameterType = fromSdkType(sdkContext, p.type); +): [InputHeaderParameter, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + const parameterType = diagnostics.pipe(fromSdkType(sdkContext, p.type)); const retVar: InputHeaderParameter = { kind: "header", @@ -487,7 +526,7 @@ function fromHeaderParameter( isApiVersion: p.isApiVersionParam, collectionFormat: p.collectionFormat, arraySerializationDelimiter: getArraySerializationDelimiter(p), - defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType), + defaultValue: diagnostics.pipe(getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType)), optional: p.optional, isContentType: isContentType(p), scope: getParameterScope(p, parameterType, rootApiVersions.length > 0), @@ -497,15 +536,16 @@ function fromHeaderParameter( }; sdkContext.__typeCache.updateSdkOperationParameterReferences(p, retVar); - return retVar; + return diagnostics.wrap(retVar); } function fromBodyParameter( sdkContext: CSharpEmitterContext, p: SdkBodyParameter, rootApiVersions: string[], -): InputBodyParameter { - const parameterType = fromSdkType(sdkContext, p.type); +): [InputBodyParameter, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + const parameterType = diagnostics.pipe(fromSdkType(sdkContext, p.type)); const retVar: InputBodyParameter = { kind: "body", @@ -525,20 +565,22 @@ function fromBodyParameter( }; sdkContext.__typeCache.updateSdkOperationParameterReferences(p, retVar); - return retVar; + return diagnostics.wrap(retVar); } export function fromMethodParameter( sdkContext: CSharpEmitterContext, p: SdkMethodParameter, namespace: string, -): 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, p, namespace); + const parameterType = diagnostics.pipe(fromSdkType(sdkContext, p.type, p, namespace)); retVar = { kind: "method", @@ -549,7 +591,7 @@ export function fromMethodParameter( type: parameterType, location: RequestLocation.None, isApiVersion: p.isApiVersionParam, - defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType), + defaultValue: diagnostics.pipe(getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType)), optional: p.optional, scope: InputParameterScope.Method, crossLanguageDefinitionId: p.crossLanguageDefinitionId, @@ -559,14 +601,16 @@ export function fromMethodParameter( }; sdkContext.__typeCache.updateSdkMethodParameterReferences(p, retVar); - return retVar; + return diagnostics.wrap(retVar); } function loadLongRunningMetadata( sdkContext: CSharpEmitterContext, method: SdkLroServiceMethod | SdkLroPagingServiceMethod, -): 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 @@ -574,61 +618,67 @@ function loadLongRunningMetadata( statusCodes: method.operation.verb === "delete" ? [204] : [200], bodyType: method.lroMetadata.finalResponse?.envelopeResult !== undefined - ? fromSdkType(sdkContext, method.lroMetadata.finalResponse.envelopeResult) + ? diagnostics.pipe(fromSdkType(sdkContext, method.lroMetadata.finalResponse.envelopeResult)) : undefined, } as OperationResponse, resultPath: method.lroMetadata.finalResultPath, - }; + }); } function fromSdkHttpOperationResponses( sdkContext: CSharpEmitterContext, operationResponses: SdkHttpResponse[], -): OperationResponse[] { +): [OperationResponse[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const responses: OperationResponse[] = []; + for (const r of operationResponses) { - responses.push(fromSdkHttpOperationResponse(sdkContext, r)); + responses.push(diagnostics.pipe(fromSdkHttpOperationResponse(sdkContext, r))); } - return responses; + return diagnostics.wrap(responses); } export function fromSdkHttpOperationResponse( sdkContext: CSharpEmitterContext, sdkResponse: SdkHttpResponse, -): 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), - headers: fromSdkServiceResponseHeaders(sdkContext, sdkResponse.headers), + 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[], -): 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), + type: diagnostics.pipe(fromSdkType(sdkContext, h.type)), }) as HttpResponseHeader, - ); + )); } function toStatusCodesArray(range: number | HttpStatusCodeRange): number[] { @@ -680,28 +730,30 @@ function loadPagingServiceMetadata( rootApiVersions: string[], uri: string, namespace: string, -): 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], - ), + )), }; if (method.pagingMetadata.nextLinkOperation) { - nextLink.operation = fromSdkServiceMethod( + nextLink.operation = diagnostics.pipe(fromSdkServiceMethod( context, method.pagingMetadata.nextLinkOperation, uri, rootApiVersions, namespace, - ); + )); } if ( @@ -716,7 +768,7 @@ function loadPagingServiceMetadata( ] as SdkModelPropertyType; const operationParameter = getHttpOperationParameter(method, lastParameterSegment); if (operationParameter) { - const parameter = fromParameter(context, operationParameter, rootApiVersions); + const parameter = diagnostics.pipe(fromParameter(context, operationParameter, rootApiVersions)); if (parameter) { nextLinkReInjectedParameters.push(parameter); } @@ -737,22 +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, - ); + )); 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], - ), + )), }; } } @@ -764,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 { @@ -788,23 +840,27 @@ function getResponseLocation( context: CSharpEmitterContext, method: SdkPagingServiceMethod | SdkLroPagingServiceMethod, p: SdkServiceResponseHeader | SdkModelPropertyType, -): ResponseLocation { +): [ResponseLocation, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + if (p.kind === "responseheader") { - return ResponseLocation.Header; + return diagnostics.wrap(ResponseLocation.Header); } if (isHttpMetadata(context, p)) { - context.logger.reportDiagnostic({ - code: "unsupported-continuation-location", - format: { - crossLanguageDefinitionId: method.crossLanguageDefinitionId, - }, - target: NoTarget, - }); - return ResponseLocation.None; + diagnostics.add( + createDiagnostic({ + code: "unsupported-continuation-location", + format: { + crossLanguageDefinitionId: method.crossLanguageDefinitionId, + }, + target: NoTarget, + }), + ); + return diagnostics.wrap(ResponseLocation.None); } - return ResponseLocation.Body; + return diagnostics.wrap(ResponseLocation.Body); } // TODO: https://github.com/Azure/typespec-azure/issues/1441 @@ -915,9 +971,11 @@ function getArraySerializationDelimiter( function getResponseType( sdkContext: CSharpEmitterContext, type: SdkType | undefined, -): 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 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..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,16 +7,18 @@ import { SdkHttpOperation, SdkPackage, } from "@azure-tools/typespec-client-generator-core"; -import { 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"; import { InputAuth } from "../type/input-auth.js"; import { InputOAuth2Flow } from "../type/input-oauth2-auth.js"; export function processServiceAuthentication( sdkContext: CSharpEmitterContext, sdkPackage: SdkPackage, -): InputAuth | undefined { +): [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) { @@ -28,29 +30,31 @@ export function processServiceAuthentication( } if (!authClientParameter) { - return undefined; + return diagnostics.wrap(undefined); } const inputAuth: InputAuth = {}; if (authClientParameter.type.kind === "credential") { - const auth = processAuthType(sdkContext, authClientParameter.type); + const auth = diagnostics.pipe(processAuthType(sdkContext, authClientParameter.type)); if (!auth && authClientParameter.type.scheme.type !== "noAuth") { - sdkContext.logger.reportDiagnostic({ - code: "unsupported-auth", - messageId: "onlyUnsupportedAuthProvided", - target: authClientParameter.type.__raw ?? NoTarget, - }); + diagnostics.add( + createDiagnostic({ + code: "unsupported-auth", + messageId: "onlyUnsupportedAuthProvided", + target: authClientParameter.type.__raw ?? NoTarget, + }), + ); - return inputAuth; + return diagnostics.wrap(inputAuth); } - return auth; + return diagnostics.wrap(auth); } let containsNoAuth = false; for (const authType of authClientParameter.type.variantTypes) { containsNoAuth = containsNoAuth || authType.scheme.type === "noAuth"; - const auth = processAuthType(sdkContext, authType); + const auth = diagnostics.pipe(processAuthType(sdkContext, authType)); if (auth?.apiKey) { inputAuth.apiKey = auth.apiKey; } @@ -60,75 +64,84 @@ export function processServiceAuthentication( } if (containsNoAuth && !inputAuth.apiKey && !inputAuth.oAuth2) { - return undefined; + return diagnostics.wrap(undefined); } if (!inputAuth?.apiKey && !inputAuth?.oAuth2) { - sdkContext.logger.reportDiagnostic({ - code: "unsupported-auth", - messageId: "onlyUnsupportedAuthProvided", - target: authClientParameter.type.__raw ?? NoTarget, - }); + diagnostics.add( + createDiagnostic({ + code: "unsupported-auth", + messageId: "onlyUnsupportedAuthProvided", + target: authClientParameter.type.__raw ?? NoTarget, + }), + ); } - return inputAuth; + return diagnostics.wrap(inputAuth); } function processAuthType( sdkContext: CSharpEmitterContext, credentialType: SdkCredentialType, -): InputAuth | undefined { +): [InputAuth | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const scheme = credentialType.scheme; 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, - }); - return undefined; + diagnostics.add( + createDiagnostic({ + code: "unsupported-auth", + format: { + message: `Only header is supported for ApiKey authentication. ${scheme.in} is not supported.`, + }, + target: credentialType.__raw ?? NoTarget, + }), + ); + 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) { case "Basic": - sdkContext.logger.reportDiagnostic({ - code: "unsupported-auth", - format: { message: `${schemeOrApiKeyPrefix} auth method is currently not supported.` }, - target: credentialType.__raw ?? NoTarget, - }); - return undefined; + diagnostics.add( + createDiagnostic({ + code: "unsupported-auth", + format: { message: `${schemeOrApiKeyPrefix} auth method is currently not supported.` }, + target: credentialType.__raw ?? NoTarget, + }), + ); + 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: - sdkContext.logger.reportDiagnostic({ - code: "unsupported-auth", - format: { message: `un-supported authentication scheme ${scheme.type}` }, - target: credentialType.__raw ?? NoTarget, - }); - return undefined; + diagnostics.add( + createDiagnostic({ + code: "unsupported-auth", + format: { message: `un-supported authentication scheme ${scheme.type}` }, + target: credentialType.__raw ?? NoTarget, + }), + ); + return diagnostics.wrap(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..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,7 +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 { CSharpEmitterContext } from "../sdk-context.js"; import { InputArrayType, @@ -75,36 +76,37 @@ export function fromSdkType( sdkType: T, 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, 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); + retVar = diagnostics.pipe(fromSdkModelType(sdkContext, sdkType)); break; case "enum": - retVar = fromSdkEnumType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromSdkEnumType(sdkContext, sdkType)); break; case "enumvalue": - retVar = fromSdkEnumValueType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromSdkEnumValueType(sdkContext, sdkType)); break; case "dict": - retVar = fromSdkDictionaryType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromSdkDictionaryType(sdkContext, sdkType)); break; case "array": - retVar = fromSdkArrayType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromSdkArrayType(sdkContext, sdkType)); break; case "constant": if ( @@ -114,27 +116,29 @@ export function fromSdkType( sdkType.valueType.kind !== "boolean" ) { // turn the constant into an extensible enum - retVar = createEnumType(sdkContext, sdkType, namespace!); + retVar = diagnostics.pipe(createEnumType(sdkContext, sdkType, namespace!)); } else { - retVar = fromSdkConstantType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromSdkConstantType(sdkContext, sdkType)); } break; case "union": - retVar = fromUnionType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromUnionType(sdkContext, sdkType)); break; case "utcDateTime": case "offsetDateTime": - retVar = fromSdkDateTimeType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromSdkDateTimeType(sdkContext, sdkType)); break; case "duration": - retVar = fromSdkDurationType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromSdkDurationType(sdkContext, sdkType)); break; case "tuple": - sdkContext.logger.reportDiagnostic({ - code: "unsupported-sdk-type", - format: { sdkType: "tuple" }, - target: sdkType.__raw ?? NoTarget, - }); + diagnostics.add( + createDiagnostic({ + code: "unsupported-sdk-type", + format: { sdkType: "tuple" }, + target: sdkType.__raw ?? NoTarget, + }), + ); const tupleType: InputPrimitiveType = { kind: "unknown", name: "tuple", @@ -150,11 +154,13 @@ export function fromSdkType( retVar = fromSdkEndpointType(); break; case "credential": - sdkContext.logger.reportDiagnostic({ - code: "unsupported-sdk-type", - format: { sdkType: "credential" }, - target: sdkType.__raw ?? NoTarget, - }); + diagnostics.add( + createDiagnostic({ + code: "unsupported-sdk-type", + format: { sdkType: "credential" }, + target: sdkType.__raw ?? NoTarget, + }), + ); const credentialType: InputPrimitiveType = { kind: "unknown", name: "credential", @@ -165,19 +171,20 @@ export function fromSdkType( retVar = credentialType; break; default: - retVar = fromSdkBuiltInType(sdkContext, sdkType); + 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, -): 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); @@ -202,12 +209,12 @@ function fromSdkModelType( sdkContext.__typeCache.updateSdkTypeReferences(modelType, inputModelType); inputModelType.additionalProperties = modelType.additionalProperties - ? fromSdkType(sdkContext, modelType.additionalProperties) + ? diagnostics.pipe(fromSdkType(sdkContext, modelType.additionalProperties)) : undefined; const properties: InputModelProperty[] = []; for (const property of modelType.properties) { - const ourProperty = fromSdkModelProperty(sdkContext, property, modelType); + const ourProperty = diagnostics.pipe(fromSdkModelProperty(sdkContext, property, modelType)); if (ourProperty) { properties.push(ourProperty); @@ -215,11 +222,11 @@ function fromSdkModelType( } inputModelType.discriminatorProperty = modelType.discriminatorProperty - ? fromSdkModelProperty(sdkContext, modelType.discriminatorProperty, modelType) + ? diagnostics.pipe(fromSdkModelProperty(sdkContext, modelType.discriminatorProperty, modelType)) : undefined; inputModelType.baseModel = modelType.baseModel - ? fromSdkType(sdkContext, modelType.baseModel) + ? diagnostics.pipe(fromSdkType(sdkContext, modelType.baseModel)) : undefined; inputModelType.properties = properties; @@ -228,25 +235,26 @@ function fromSdkModelType( const discriminatedSubtypes: Record = {}; for (const key in modelType.discriminatedSubtypes) { const subtype = modelType.discriminatedSubtypes[key]; - discriminatedSubtypes[key] = fromSdkType(sdkContext, subtype); + discriminatedSubtypes[key] = diagnostics.pipe(fromSdkType(sdkContext, subtype)); } inputModelType.discriminatedSubtypes = discriminatedSubtypes; } - return inputModelType; + return diagnostics.wrap(inputModelType); } function fromSdkModelProperty( sdkContext: CSharpEmitterContext, sdkProperty: SdkModelPropertyType, sdkModel: SdkModelType, -): 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 = @@ -259,7 +267,7 @@ function fromSdkModelProperty( serializedName: serializedName, summary: sdkProperty.summary, doc: sdkProperty.doc, - type: fromSdkType(sdkContext, sdkProperty.type, sdkProperty, sdkModel.namespace), + type: diagnostics.pipe(fromSdkType(sdkContext, sdkProperty.type, sdkProperty, sdkModel.namespace)), optional: sdkProperty.optional, readOnly: isReadOnly(sdkProperty), discriminator: sdkProperty.discriminator, @@ -275,18 +283,20 @@ function fromSdkModelProperty( sdkContext.__typeCache.updateSdkPropertyReferences(sdkProperty, property); } - return property; + return diagnostics.wrap(property); } -function fromSdkEnumType(sdkContext: CSharpEmitterContext, enumType: SdkEnumType): InputEnumType { - return createEnumType(sdkContext, enumType, enumType.namespace); +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, -): InputEnumType { +): [InputEnumType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const values: InputEnumValueType[] = []; const inputEnumType: InputEnumType = { @@ -295,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), + ? (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: @@ -317,112 +327,119 @@ function createEnumType( if (sdkType.kind === "enum") { for (const v of sdkType.values) { - values.push(createEnumValueType(sdkContext, v, inputEnumType)); + values.push(diagnostics.pipe(createEnumValueType(sdkContext, v, inputEnumType))); } } else { - values.push(createEnumValueType(sdkContext, sdkType, inputEnumType)); + values.push(diagnostics.pipe(createEnumValueType(sdkContext, sdkType, inputEnumType))); } - return inputEnumType; + return diagnostics.wrap(inputEnumType); } function fromSdkDateTimeType( sdkContext: CSharpEmitterContext, dateTimeType: SdkDateTimeType, -): 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), + wireType: diagnostics.pipe(fromSdkType(sdkContext, dateTimeType.wireType)), crossLanguageDefinitionId: dateTimeType.crossLanguageDefinitionId, - baseType: dateTimeType.baseType ? fromSdkType(sdkContext, dateTimeType.baseType) : undefined, + baseType: dateTimeType.baseType ? diagnostics.pipe(fromSdkType(sdkContext, dateTimeType.baseType)) : undefined, decorators: dateTimeType.decorators, external: fromSdkExternalTypeInfo(dateTimeType), - }; + }); } function fromSdkDurationType( sdkContext: CSharpEmitterContext, durationType: SdkDurationType, -): 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), + wireType: diagnostics.pipe(fromSdkType(sdkContext, durationType.wireType)), crossLanguageDefinitionId: durationType.crossLanguageDefinitionId, - baseType: durationType.baseType ? fromSdkType(sdkContext, durationType.baseType) : undefined, + baseType: durationType.baseType ? diagnostics.pipe(fromSdkType(sdkContext, durationType.baseType)) : undefined, decorators: durationType.decorators, external: fromSdkExternalTypeInfo(durationType), - }; + }); } function fromSdkBuiltInType( sdkContext: CSharpEmitterContext, builtInType: SdkBuiltInType, -): 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) : undefined, + baseType: builtInType.baseType ? diagnostics.pipe(fromSdkType(sdkContext, builtInType.baseType)) : undefined, decorators: builtInType.decorators, external: fromSdkExternalTypeInfo(builtInType), - }; + }); } -function fromUnionType(sdkContext: CSharpEmitterContext, union: SdkUnionType): 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); + 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, -): 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), + 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, -): InputEnumValueType { - return createEnumValueType(sdkContext, enumValueType, enumValueType.enumType); +): [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, -): InputEnumValueType { - return { +): [InputEnumValueType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap({ kind: "enumvalue", name: sdkType.kind === "constant" @@ -432,39 +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), + 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, -): InputDictionaryType { - return { +): [InputDictionaryType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap({ kind: "dict", - keyType: fromSdkType(sdkContext, dictionaryType.keyType), - valueType: fromSdkType(sdkContext, dictionaryType.valueType), + 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, -): InputArrayType { - return { +): [InputArrayType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap({ kind: "array", name: arrayType.name, - valueType: fromSdkType(sdkContext, arrayType.valueType), + 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 77cfe50b341..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 { 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,8 +20,11 @@ export interface TypeSpecServer { export function resolveServers( sdkContext: CSharpEmitterContext, servers: HttpServer[], -): TypeSpecServer[] { - return servers.map((server) => { +): [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; const endpoint: string = url.replace("http://", "").replace("https://", "").split("/")[0]; @@ -35,7 +38,7 @@ export function resolveServers( name: "url", crossLanguageDefinitionId: "TypeSpec.url", } - : fromSdkType(sdkContext, getClientType(sdkContext, prop)); + : diagnostics.pipe(fromSdkType(sdkContext, getClientType(sdkContext, prop))); if (value) { defaultValue = { @@ -101,6 +104,8 @@ export function resolveServers( parameters, }; }); + + return diagnostics.wrap(result); } function getDefaultValue(value: Value): any { 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/src/sdk-context.ts b/packages/http-client-csharp/emitter/src/sdk-context.ts index 1c8d3b03162..2718ebfe32d 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 { DiagnosticCollector, 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/auth.test.ts b/packages/http-client-csharp/emitter/test/Unit/auth.test.ts index ee94b078d41..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 ab683388071..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 8715259052f..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 611fafc9ec9..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 @@ -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, @@ -441,3 +449,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); + 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); + 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/constant-type.test.ts b/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts index 595e936d2f5..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 37aaa5ad3fc..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 6755c79fde1..4ceeddda514 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, Diagnostic, EmitContext, Program } from "@typespec/compiler"; import { TestHost } from "@typespec/compiler/testing"; import { strictEqual } from "assert"; import { statSync } from "fs"; @@ -19,6 +19,10 @@ import { describe("$onEmit tests", () => { let program: Program; let $onEmit: (arg0: EmitContext) => any; + let emitCodeModel: ( + context: EmitContext, + updateCodeModel?: (model: CodeModel, context: any) => CodeModel, + ) => any; beforeEach(async () => { // Reset the dynamically imported module to ensure a clean state vi.resetModules(); @@ -58,7 +62,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 = { @@ -79,18 +83,19 @@ 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 emitCodeModel 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; + emitCodeModel = emitterModule.emitCodeModel; }); - it("should apply the update-code-model callback just once", async () => { + it("should apply the updateCodeModel callback", async () => { const context: EmitContext = createEmitterContext(program); const updateCallback = vi.fn().mockImplementation((model: CodeModel) => { return model; }); - context.options["update-code-model"] = updateCallback; - await $onEmit(context); + await emitCodeModel(context, updateCallback); expect(updateCallback).toHaveBeenCalledTimes(1); }); @@ -179,11 +184,64 @@ describe("$onEmit tests", () => { }); }); +describe("emitCodeModel tests", () => { + let runner: TestHost; + let program: Program; + + beforeEach(async () => { + vi.restoreAllMocks(); + runner = await createEmitterTestHost(); + }); + + it("should return diagnostics array from emitCodeModel", async () => { + program = await typeSpecCompile( + ` + model TestModel { + name: string; + } + + @route("/test") + op test(): TestModel; + `, + runner, + ); + 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) + expect(diagnostics).toBeDefined(); + }); + + it("should collect diagnostics from createModel in emitCodeModel", async () => { + program = await typeSpecCompile( + ` + model TestModel { + name: string; + } + + @route("/test") + op test(): TestModel; + `, + runner, + ); + 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); + }); +}); + 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(); @@ -217,7 +275,9 @@ describe("Test _validateDotNetSdk", () => { (execAsync as Mock).mockRejectedValueOnce(error); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const result = await _validateDotNetSdk(sdkContext, minVersion); + const [result, diagnostics] = await _validateDotNetSdk(sdkContext, minVersion); + // Report collected diagnostics to program + program.reportDiagnostics(diagnostics); expect(result).toBe(false); strictEqual(program.diagnostics.length, 1); strictEqual( @@ -241,10 +301,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 () => { @@ -258,10 +318,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 () => { @@ -275,7 +335,9 @@ 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); + // Report collected diagnostics to program + program.reportDiagnostics(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 559d2d54dbb..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 d552104b525..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 c343dab4823..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/namespace-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts index 95583fb6fc2..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 @@ -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,7 @@ describe("Namespace Converter", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); const sdkPackage = sdkContext.sdkPackage; - const parsedNamespaces = fromSdkNamespaces(sdkContext, sdkPackage.namespaces); + const [parsedNamespaces, diagnostics] = fromSdkNamespaces(sdkContext, sdkPackage.namespaces); strictEqual(parsedNamespaces.length, 1); 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..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 d5f5cd66d54..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 5de9f067d45..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 015d4ca3c43..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 2f86dc8409e..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 081708c903f..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 a20ee81a401..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", ); 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..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 @@ -122,11 +122,10 @@ export async function createCSharpSdkContext( sdkContextOptions: CreateSdkContextOptions = {}, ): Promise { const createSdkContext = await getCreateSdkContext(); - const context = await createSdkContext( - program, - "@typespec/http-client-csharp", - sdkContextOptions, - ); + const context = await createSdkContext(program, "@typespec/http-client-csharp", sdkContextOptions); const Logger = await getLogger(); - return createCSharpEmitterContext(context, new Logger(program.program, LoggerLevel.INFO)); + return createCSharpEmitterContext( + context, + new Logger(program.program, LoggerLevel.INFO), + ); } diff --git a/packages/http-client-csharp/readme.md b/packages/http-client-csharp/readme.md index 77f8efc84b9..30e8bea7724 100644 --- a/packages/http-client-csharp/readme.md +++ b/packages/http-client-csharp/readme.md @@ -125,12 +125,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`