diff --git a/.chronus/changes/tcgc-diagnostic-docs-2026-07-16-00-00-00.md b/.chronus/changes/tcgc-diagnostic-docs-2026-07-16-00-00-00.md new file mode 100644 index 0000000000..39babed3e3 --- /dev/null +++ b/.chronus/changes/tcgc-diagnostic-docs-2026-07-16-00-00-00.md @@ -0,0 +1,7 @@ +--- +changeKind: internal +packages: + - "@azure-tools/typespec-client-generator-core" +--- + +Add reference documentation for TCGC diagnostics so each diagnostic gets an auto-generated reference page. \ No newline at end of file diff --git a/.gitignore b/.gitignore index 83c8c22bdf..124e385dc9 100644 --- a/.gitignore +++ b/.gitignore @@ -255,10 +255,12 @@ packages/typespec-java/emitter-tests/node_modules/** packages/typespec-java/emitter-tests/package-lock.json packages/typespec-java/emitter-tests/tsp-spector-coverage-*.json -# Generated linter rule reference pages (produced by `tspd doc` / regen-docs via -# each rule's `docs` field). Regenerated during the website build (build:web), so -# they are not tracked. The rule source docs live in packages/*/src/rules/*.md. +# Generated linter rule and diagnostic reference pages (produced by `tspd doc` / +# regen-docs via each rule's or diagnostic's `docs` field). Regenerated during the +# website build (build:web), so they are not tracked. The source docs live in +# packages/*/src/rules/*.md and packages/*/src/diagnostics/*.md. website/src/content/docs/docs/libraries/*/rules/ +website/src/content/docs/docs/libraries/*/reference/diagnostics/ # Empty prs.external-reviewers.generated.yml diff --git a/packages/typespec-client-generator-core/src/diagnostics/add-parameter-duplicate.md b/packages/typespec-client-generator-core/src/diagnostics/add-parameter-duplicate.md new file mode 100644 index 0000000000..5d3016ec57 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/add-parameter-duplicate.md @@ -0,0 +1,44 @@ +This diagnostic is issued when `addParameter` is called with a model property whose name already exists on the operation. + +## Impact + +- **Area:** Client customization transformations. Blocks `addParameter` from producing a valid customized SDK method signature when the added parameter name already exists. +- **Not affected:** The original service operation remains unchanged. + +## ❌ Incorrect Usage + +```typespec +@service +namespace MyService { + op myOp(name: string): void; +} + +model ExtraParams { + name: string; // duplicates existing parameter `name` on `myOp` +} +alias Modified = addParameter(MyService.myOp, ExtraParams.name); +``` + +## Diagnostic Message + +TCGC reports: + +```text +Parameter "name" already exists in operation "myOp". +``` + +## ✅ How to Fix + +Choose a unique parameter name or use `replaceParameter` when the intent is to replace an existing parameter. + +```typespec +@service +namespace MyService { + op myOp(name: string): void; +} + +model ExtraParams { + description: string; +} +alias Modified = addParameter(MyService.myOp, ExtraParams.description); +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/api-version-undefined.md b/packages/typespec-client-generator-core/src/diagnostics/api-version-undefined.md new file mode 100644 index 0000000000..2d39b3169b --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/api-version-undefined.md @@ -0,0 +1,48 @@ +This diagnostic is issued when the `api-version` emitter option names a version that is not present in the service versioning list. + +## Impact + +- **Area:** API-version option resolution. Generation continues by falling back to the latest defined service version, which can target a different SDK API version than requested. +- **Not affected:** The service's declared version enum and versioned TypeSpec projections are unchanged. + +## ❌ Incorrect Usage + +```typespec +@service(#{ title: "Contoso Widget Manager" }) +@versioned(Contoso.WidgetManager.Versions) +namespace Contoso.WidgetManager; + +enum Versions { + v1, + v2, + v3, +} +``` + +```yaml +options: + "@azure-tools/typespec-python": + api-version: "v4" # v4 is not one of v1/v2/v3 +``` + +## Diagnostic Message + +TCGC reports: + +```text +The API version specified in the config: "v4" is not defined in service versioning list. Fall back to the latest version. +``` + +## ✅ How to Fix + +Set `api-version` to a service version that exists. `latest` and `all` are also valid values when those behaviors are intended. + +```yaml +options: + "@azure-tools/typespec-python": + api-version: "v3" +``` + +## Suppression + +This diagnostic should not be suppressed. Fix the configured `api-version` value in `tspconfig.yaml` so it matches the service versioning list, or use `latest` or `all` intentionally. diff --git a/packages/typespec-client-generator-core/src/diagnostics/auto-merge-service-conflict.md b/packages/typespec-client-generator-core/src/diagnostics/auto-merge-service-conflict.md new file mode 100644 index 0000000000..48b64f9c96 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/auto-merge-service-conflict.md @@ -0,0 +1,77 @@ +This diagnostic is issued when a parent client uses `autoMergeService` and a nested client also specifies its own service configuration. + +## Impact + +- **Area:** Multi-service client hierarchy. Blocks an auto-merged parent client from also containing a nested client with its own explicit service binding. +- **Not affected:** The underlying service namespaces and routes remain valid independently. + +## ❌ Incorrect Usage + +```typespec +@service +namespace ServiceA { + @route("/aTest") + op opA(): void; +} + +@service +namespace ServiceB { + @route("/bTest") + op opB(): void; +} + +@client({ + name: "ParentClient", + service: [ServiceA, ServiceB], + autoMergeService: true, +}) +namespace ParentClient { + @client({ + name: "ChildClient", + service: ServiceA, // child service conflicts with the parent's `autoMergeService` + }) + namespace Child { + + } +} +``` + +## Diagnostic Message + +TCGC reports: + +```text +Auto-merging service client must be empty. +``` + +## ✅ How to Fix + +Leave the nested client's `service` option unset so it inherits from the parent, or remove the parent `autoMergeService` setup. + +```typespec +@service +namespace ServiceA { + @route("/aTest") + op opA(): void; +} + +@service +namespace ServiceB { + @route("/bTest") + op opB(): void; +} + +@client({ + name: "ParentClient", + service: [ServiceA, ServiceB], + autoMergeService: true, +}) +namespace ParentClient { + @client({ + name: "ChildClient", + }) + namespace Child { + + } +} +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/client-location-conflict.md b/packages/typespec-client-generator-core/src/diagnostics/client-location-conflict.md new file mode 100644 index 0000000000..fddc5a3b1d --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/client-location-conflict.md @@ -0,0 +1,187 @@ +This diagnostic is issued when a `@clientLocation` move conflicts with the client structure TCGC is building. It is reported in several situations: + +- **String target with multiple root clients** — a string target cannot be resolved when more than one root client exists, because TCGC cannot decide which root client should own the new sub client. +- **Moving an operation onto another operation** — an operation can only be moved to an interface or namespace, not onto another operation. +- **Moving a model property to a string target** — a model property can only be moved to an interface or namespace, not to a string-named target. +- **Parameter name already used in client initialization** — the parameter produced by the moved model property collides with an existing client initialization parameter. +- **Same parameter moved with different types** — the same parameter name is moved to one client with conflicting types, which commonly happens when `@clientLocation` is applied to a templated parameter that is instantiated with different types. + +## Impact + +- **Area:** Client operation and parameter placement. Generation continues, but the requested `@clientLocation` move cannot be applied safely to the generated client structure. +- **Not affected:** HTTP routes, parameter wire names, and service operation definitions are unchanged. + +## String target with multiple root clients + +### Diagnostic Message + +TCGC reports: + +```text +@clientLocation with string target could not be used for multiple root clients scenario +``` + +### ✅ How to Fix + +Use an interface or namespace target when multiple root clients exist, or define the target sub client explicitly under the intended root client. + +## Operation to operation + +### Diagnostic Message + +TCGC reports: + +```text +`@clientLocation` cannot be used to move an operation to another operation. Operations can only be moved to interfaces or namespaces. +``` + +### ✅ How to Fix + +Move the operation to an interface or namespace instead of another operation. + +## Model property conflicts with client initialization + +### ❌ Incorrect Usage + +```typespec +model ClientOptions { + apiKey: string; +} + +@clientInitialization(ClientOptions) +@service +namespace WidgetService { + model Widget { + @clientLocation(WidgetService) // conflicts with `apiKey` already in the client initialization model + apiKey: string; + } +} +``` + +### Diagnostic Message + +TCGC reports: + +```text +There is already a parameter called 'apiKey' in the client initialization. +``` + +### ✅ How to Fix + +Rename the moved property or the client-initialization parameter so they do not collide. + +```typespec +model ClientOptions { + apiKey: string; +} + +@clientInitialization(ClientOptions) +@service +namespace WidgetService { + model Widget { + @clientLocation(WidgetService) + widgetApiKey: string; + } +} +``` + +## Model property moved to string target + +### ❌ Incorrect Usage + +```typespec +@service +namespace WidgetService { + model Widget { + @clientLocation("SharedClient") // a model property can only be moved to an interface or namespace, not a string target + region: string; + } +} +``` + +### Diagnostic Message + +TCGC reports: + +```text +`@clientLocation` can only move model properties to interfaces or namespaces. +``` + +### ✅ How to Fix + +Move the model property to a concrete interface or namespace target instead of a string-named target. + +```typespec +@service +namespace WidgetService { + namespace SharedClient { + + } + + model Widget { + @clientLocation(SharedClient) + region: string; + } +} +``` + +## Moved parameters with conflicting types + +### ❌ Incorrect Usage + +```typespec +@service +namespace Default; + +union FeatureOptInKeys { + insights: "Insights.V1Preview", + schedules: "Schedules.V1Preview", +} + +alias WithPreviewHeader = { + @clientLocation(Default) // templated parameter moves different concrete types to the same client + @header("x-preview-features") + previewFeatures: T; +}; + +op getInsights(...WithPreviewHeader): void; +op getSchedules(...WithPreviewHeader): void; +``` + +### Diagnostic Message + +TCGC reports: + +```text +@clientLocation cannot move multiple parameters named 'previewFeatures' with different types to the same client. This often happens when @clientLocation is applied to a templated parameter that is instantiated with different types. Move the parameter on each operation instead, so that it has a consistent type on the client. +``` + +### ✅ How to Fix + +Move the parameter on each operation instead of on the templated alias, or ensure every moved `previewFeatures` parameter has the same type. + +```typespec +@service +namespace Default; + +union FeatureOptInKeys { + insights: "Insights.V1Preview", + schedules: "Schedules.V1Preview", +} + +op getInsights( + @clientLocation(Default) + @header("x-preview-features") + previewFeatures: FeatureOptInKeys, +): void; + +op getSchedules( + @clientLocation(Default) + @header("x-preview-features") + previewFeatures: FeatureOptInKeys, +): void; +``` + +## Suppression + +This diagnostic should not be suppressed. Fix the `@clientLocation` usage as described in the cases above. diff --git a/packages/typespec-client-generator-core/src/diagnostics/client-location-wrong-type.md b/packages/typespec-client-generator-core/src/diagnostics/client-location-wrong-type.md new file mode 100644 index 0000000000..bd1362b7c5 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/client-location-wrong-type.md @@ -0,0 +1,48 @@ +This diagnostic is issued when an operation marked with `@clientLocation` points to a target that is not a valid client location under the root namespace decorated with `@service`. + +## Impact + +- **Area:** Client operation placement. Generation continues, but the operation cannot be moved to a target outside the valid service client hierarchy. +- **Not affected:** The operation's HTTP route and protocol metadata are unchanged. + +## ❌ Incorrect Usage + +```typespec +namespace OtherNamespace { + +} + +@service +namespace MyService { + @clientLocation(OtherNamespace) // target is outside the `MyService` service namespace + op list(): void; +} +``` + +## Diagnostic Message + +TCGC reports: + +```text +`@clientLocation` could only move operation to the interface or namespace belong to the root namespace with `@service`. +``` + +## ✅ How to Fix + +Move the operation to an interface or namespace that belongs to the root service namespace, or define the intended target as a valid client under that service. + +```typespec +@service +namespace MyService { + namespace Operations { + + } + + @clientLocation(Operations) + op list(): void; +} +``` + +## Suppression + +This diagnostic should not be suppressed. Move the operation to an interface or namespace that belongs to the root `@service` namespace. diff --git a/packages/typespec-client-generator-core/src/diagnostics/client-name-ineffective.md b/packages/typespec-client-generator-core/src/diagnostics/client-name-ineffective.md new file mode 100644 index 0000000000..f40198209b --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/client-name-ineffective.md @@ -0,0 +1,39 @@ +This diagnostic is issued when a `@clientName` application will not affect the generated name. + +## Impact + +- **Area:** SDK naming customization. Blocks or rejects a `@clientName` placement that TCGC will not use for the generated declaration. +- **Not affected:** The original service operation or type shape is unchanged. + +## ❌ Incorrect Usage + +```typespec +@service +namespace KeyVault { + op getSecret(secretName: string): void; +} + +@clientName("listSecretProperties") // applied to the override method instead of `KeyVault.getSecret` +op getSecretOverride(secretName: string): void; +@@override(KeyVault.getSecret, getSecretOverride); +``` + +## Diagnostic Message + +TCGC reports: + +```text +Application of @clientName decorator to listSecretProperties is not effective because it is applied to the override method. Please apply it on the original method definition "getSecret" instead. +``` + +## ✅ How to Fix + +Move `@clientName` to the declaration TCGC actually generates, such as the original operation when using `@override`: + +```typespec +@service +namespace KeyVault { + @clientName("listSecretProperties") + op getSecret(secretName: string): void; +} +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/client-option-requires-scope.md b/packages/typespec-client-generator-core/src/diagnostics/client-option-requires-scope.md new file mode 100644 index 0000000000..9bf7b497b8 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/client-option-requires-scope.md @@ -0,0 +1,40 @@ +This diagnostic is issued when `@clientOption` is used without a specific language scope. + +## Impact + +- **Area:** Language-scoped client options. Generation continues, but an unscoped `@clientOption` may be exposed to emitters that do not understand it. +- **Not affected:** The option name and value are unchanged; only its emitter applicability is in question. + +## ❌ Incorrect Usage + +```typespec +#suppress "@azure-tools/typespec-client-generator-core/client-option" "temporary workaround" +@clientOption("enableFeatureFoo", true) // missing language scope argument +model Widget {} +``` + +## Diagnostic Message + +TCGC reports: + +```text +@clientOption should be applied with a specific language scope since it is highly likely this is language-specific. +``` + +## ✅ How to Fix + +Pass the intended language scope as the third argument to `@clientOption`: + +```typespec +#suppress "@azure-tools/typespec-client-generator-core/client-option" "temporary Python workaround" +@clientOption("enableFeatureFoo", true, "python") +model Widget {} +``` + +## Suppression + +Suppress this warning only if the client option is intentionally shared by all emitters rather than scoped to one language. + +```typespec +#suppress "@azure-tools/typespec-client-generator-core/client-option-requires-scope" "shared client option for all emitters" +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/client-option.md b/packages/typespec-client-generator-core/src/diagnostics/client-option.md new file mode 100644 index 0000000000..9ce529c98f --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/client-option.md @@ -0,0 +1,41 @@ +This diagnostic is always issued when `@clientOption` is used, because client options are a temporary mechanism intended only for language emitters. + +## Impact + +- **Area:** Targeted language-emitter options. Generation can proceed only with an explicit suppression acknowledging that `@clientOption` is a temporary emitter-specific workaround. +- **Not affected:** The TypeSpec service model and wire protocol are unchanged. + +## ❌ Incorrect Usage + +```typespec +@clientOption("enableFeatureFoo", true, "python") // experimental client option must be suppressed +model Test { + id: string; +} +``` + +## Diagnostic Message + +TCGC reports: + +```text +@clientOption is experimental and should only be used for temporary workarounds. This usage must be suppressed. +``` + +## ✅ How to Fix + +Double-check whether the client option reflects an intended language-emitter behavior. If it does, suppress this diagnostic; otherwise remove `@clientOption`. + +```typespec +model Test { + id: string; +} +``` + +## Suppression + +Because `@clientOption` is a temporary mechanism for language emitters, this diagnostic should always be suppressed with a justification when the usage is intentional. + +```typespec +#suppress "@azure-tools/typespec-client-generator-core/client-option" "preview feature for python" +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/conflict-access-override.md b/packages/typespec-client-generator-core/src/diagnostics/conflict-access-override.md new file mode 100644 index 0000000000..8c8f3054a4 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/conflict-access-override.md @@ -0,0 +1,39 @@ +This diagnostic is issued when an `@access` override conflicts with access already calculated from an operation or another `@access` override. + +## Impact + +- **Area:** Generated SDK visibility. Generation continues using the access level TCGC calculated from operations or earlier overrides, so the conflicting override may not hide the type. +- **Not affected:** Serialization, wire payloads, and service routes are unchanged. + +## ❌ Incorrect Usage + +```typespec +@access(Access.internal) // conflicts with public access inferred from `op test` +model A {} + +op test(@body body: A): void; +``` + +## Diagnostic Message + +TCGC reports: + +```text +@access override conflicts with the access calculated from operation or other @access override. +``` + +## ✅ How to Fix + +Align access settings so each generated type has one consistent access level, or remove the conflicting override. + +```typespec +@access(Access.internal) +model A {} + +@access(Access.internal) +op test(@body body: A): void; +``` + +## Suppression + +This diagnostic should not be suppressed. Make the access settings consistent so the type has a single access level. diff --git a/packages/typespec-client-generator-core/src/diagnostics/conflicting-multipart-model-usage.md b/packages/typespec-client-generator-core/src/diagnostics/conflicting-multipart-model-usage.md new file mode 100644 index 0000000000..047c48570b --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/conflicting-multipart-model-usage.md @@ -0,0 +1,65 @@ +This diagnostic is issued when the same model is used as both multipart/form-data input and regular body input such as JSON or XML. Those request bodies have different wire shapes and cannot safely share one SDK model. + +## Impact + +- **Area:** Request body model generation. Blocks safe SDK input-model generation because one model would need incompatible multipart and regular-body serialization behavior. +- **Not affected:** The individual TypeSpec operations still describe their request body content types. + +## ❌ Incorrect Usage + +```typespec +@service(#{ title: "Test Service" }) +namespace TestService; + +model MultiPartRequest { + id: HttpPart; + profileImage: HttpPart; +} + +@put +op jsonUse(@body multipartBody: MultiPartRequest): NoContentResponse; + +@post +op multipartUse( + @header contentType: "multipart/form-data", + @multipartBody body: MultiPartRequest, // `MultiPartRequest` is also used as a regular body +): NoContentResponse; +``` + +## Diagnostic Message + +TCGC reports: + +```text +Model 'MultiPartRequest' cannot be used as both multipart/form-data input and regular body input. You can create a separate model with name 'model MultiPartRequestFormData' extends MultiPartRequest {} +``` + +## ✅ How to Fix + +Create a separate form-data model, such as `FormData`, and use each model only for its matching body kind. + +```typespec +using TypeSpec.Http; + +@service(#{ title: "Test Service" }) +namespace TestService; + +model JsonRequest { + id: string; + profileImage: bytes; +} + +model MultiPartRequestFormData { + id: HttpPart; + profileImage: HttpPart; +} + +@put +op jsonUse(@body body: JsonRequest): NoContentResponse; + +@post +op multipartUse( + @header contentType: "multipart/form-data", + @multipartBody body: MultiPartRequestFormData, +): NoContentResponse; +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/duplicate-client-name-warning.md b/packages/typespec-client-generator-core/src/diagnostics/duplicate-client-name-warning.md new file mode 100644 index 0000000000..80e403c0f3 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/duplicate-client-name-warning.md @@ -0,0 +1,66 @@ +This diagnostic is issued when two generated SDK declarations share the same client name. This is by design for languages such as C# that use the duplicated name to produce overloads. + +## Impact + +- **Area:** C# SDK naming and overload generation. Generation continues, but the duplicate generated name should be intentional for C# overload behavior. +- **Not affected:** Other language scopes are unaffected unless the same duplicate name applies to them. + +## Decorator-applied duplicate + +### ❌ Incorrect Usage + +```typespec +interface StorageTasks { + @route("/list") + list(): void; + + @clientName("list", "csharp") // duplicates the C# client name of `list` + @route("/listByParent") + listByParent(parent: string): void; +} +``` + +### Diagnostic Message + +TCGC reports: + +```text +Client name: "list" is duplicated in language scope: "csharp" +``` + +### ✅ How to Fix + +Give `listByParent` a distinct C# client name, or suppress the warning if the duplicate intentionally represents an overload. + +```typespec +interface StorageTasks { + @route("/list") + list(): void; + + @clientName("listByParent", "csharp") + @route("/listByParent") + listByParent(parent: string): void; +} +``` + +## Generated name conflict + +### Diagnostic Message + +TCGC reports: + +```text +Client name: "list" is defined somewhere causing naming conflicts in language scope: "csharp" +``` + +### ✅ How to Fix + +Rename one of the generated operations for the affected scope, or suppress the warning when the duplicate is an intentional overload. + +## Suppression + +Suppress this warning only when the duplicate generated name is intentional for the target emitter, such as a C# overload. + +```typespec +#suppress "@azure-tools/typespec-client-generator-core/duplicate-client-name-warning" "Intentional overload for C#" +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/duplicate-client-name.md b/packages/typespec-client-generator-core/src/diagnostics/duplicate-client-name.md new file mode 100644 index 0000000000..7129ec8b58 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/duplicate-client-name.md @@ -0,0 +1,50 @@ +This diagnostic is issued when two generated SDK declarations have the same client name in the same language scope and the duplicate is an error for that scope. + +## Impact + +- **Area:** SDK symbol naming. Blocks generation of two declarations with the same client name in one language scope because they would collide in the emitted API. +- **Not affected:** Wire names and service operation routes are unchanged. + +## Decorator-applied duplicate + +### ❌ Incorrect Usage + +```typespec +@clientName("Widget") // duplicates the generated client name of `model Widget` +model WidgetResponse {} + +model Widget {} +``` + +### Diagnostic Message + +TCGC reports: + +```text +Client name: "Widget" is duplicated in language scope: "AllScopes" +``` + +### ✅ How to Fix + +Rename one declaration with `@clientName`, change the applicable scope, or otherwise make generated names unique. + +```typespec +@clientName("WidgetResponse") +model WidgetResponse {} + +model Widget {} +``` + +## Generated name conflict + +### Diagnostic Message + +TCGC reports: + +```text +Client name: "Widget" is defined somewhere causing naming conflicts in language scope: "AllScopes" +``` + +### ✅ How to Fix + +Rename one of the declarations or adjust the language scope so the generated client names no longer collide. diff --git a/packages/typespec-client-generator-core/src/diagnostics/duplicate-example-file.md b/packages/typespec-client-generator-core/src/diagnostics/duplicate-example-file.md new file mode 100644 index 0000000000..4b6000560c --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/duplicate-example-file.md @@ -0,0 +1,18 @@ +This diagnostic is issued when two example files for the same `operationId` use the same `title`. + +## Impact + +- **Area:** Example file indexing. Blocks unambiguous loading of examples for a single operation because two files share the same operationId and title key. +- **Not affected:** The service definition and generated SDK type shapes are unchanged. + +## Diagnostic Message + +TCGC reports: + +```text +Example file get.json uses duplicate title 'get' for operationId 'get' +``` + +## ✅ How to Fix + +Give each example a unique `title` within the same `operationId`. diff --git a/packages/typespec-client-generator-core/src/diagnostics/empty-client-name.md b/packages/typespec-client-generator-core/src/diagnostics/empty-client-name.md new file mode 100644 index 0000000000..3c347740b9 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/empty-client-name.md @@ -0,0 +1,34 @@ +This diagnostic is issued when `@clientName` is given an empty or whitespace-only value. + +## Impact + +- **Area:** SDK naming customization. Generation continues, but the empty `@clientName` override is ignored and the default generated name is used. +- **Not affected:** The target declaration's TypeSpec name and wire representation are unchanged. + +## ❌ Incorrect Usage + +```typespec +@clientName(" ") // client name is empty/whitespace +model Widget {} +``` + +## Diagnostic Message + +TCGC reports: + +```text +Cannot pass an empty value to the @clientName decorator +``` + +## ✅ How to Fix + +Provide a non-empty name to `@clientName` or remove the decorator. + +```typespec +@clientName("WidgetClient") +model Widget {} +``` + +## Suppression + +This diagnostic should not be suppressed. Give `@clientName` a non-empty name, or remove the decorator. diff --git a/packages/typespec-client-generator-core/src/diagnostics/empty-client-namespace.md b/packages/typespec-client-generator-core/src/diagnostics/empty-client-namespace.md new file mode 100644 index 0000000000..2b9543c88c --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/empty-client-namespace.md @@ -0,0 +1,34 @@ +This diagnostic is issued when `@clientNamespace` is given an empty or whitespace-only value. + +## Impact + +- **Area:** SDK namespace customization. Generation continues, but the empty `@clientNamespace` override cannot place generated declarations in a custom namespace. +- **Not affected:** Model names, operation routes, and payload serialization are unchanged. + +## ❌ Incorrect Usage + +```typespec +@clientNamespace(" ") // client namespace is empty/whitespace +model Widget {} +``` + +## Diagnostic Message + +TCGC reports: + +```text +Cannot pass an empty value to the @clientNamespace decorator +``` + +## ✅ How to Fix + +Provide a non-empty namespace string or remove `@clientNamespace`. + +```typespec +@clientNamespace("Contoso.Widgets") +model Widget {} +``` + +## Suppression + +This diagnostic should not be suppressed. Give `@clientNamespace` a non-empty value, or remove the decorator. diff --git a/packages/typespec-client-generator-core/src/diagnostics/example-loading.md b/packages/typespec-client-generator-core/src/diagnostics/example-loading.md new file mode 100644 index 0000000000..bd77791e72 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/example-loading.md @@ -0,0 +1,52 @@ +This diagnostic is issued when TCGC skips loading examples because the examples directory cannot be read, an example file cannot be parsed, or required `operationId` or `title` metadata is missing. + +## Impact + +- **Area:** Example and sample loading. Generation continues without the affected example files, so generated samples or example-based tests may be incomplete. +- **Not affected:** SDK clients, models, and service protocol metadata are still generated from TypeSpec. + +## Invalid example file + +### Diagnostic Message + +TCGC reports: + +```text +Skipped loading invalid example file: get.json. Error: Unexpected token +``` + +### ✅ How to Fix + +Fix the JSON syntax or contents of the example file so it can be parsed. + +## Examples directory cannot be read + +### Diagnostic Message + +TCGC reports: + +```text +Skipping example loading from ./examples because there was an error reading the directory. +``` + +### ✅ How to Fix + +Create the configured examples directory, correct the `examples-dir` option, or omit the option when examples are not available. + +## Missing operationId or title + +### Diagnostic Message + +TCGC reports: + +```text +Skipping example file get.json because it does not contain an operationId and/or title. +``` + +### ✅ How to Fix + +Add both `operationId` and `title` to the example JSON file. + +## Suppression + +This diagnostic should not be suppressed. Fix the example directory/files so they can be loaded, including valid JSON with `operationId` and `title`. diff --git a/packages/typespec-client-generator-core/src/diagnostics/example-value-no-mapping.md b/packages/typespec-client-generator-core/src/diagnostics/example-value-no-mapping.md new file mode 100644 index 0000000000..4a7688d51c --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/example-value-no-mapping.md @@ -0,0 +1,54 @@ +This diagnostic is issued when a value from an example file cannot be matched to the TypeSpec definition. + +## Impact + +- **Area:** Example value conversion. Generation continues, but the mismatched example value is not mapped into SDK example data for the operation. +- **Not affected:** The operation signature and generated client method are unchanged. + +## ❌ Incorrect Usage + +```typespec +@service +namespace TestClient { + @route("/{b}") + op parametersDiagnostic( + // example file provides unmapped parameter `test` for this operation + @header a: string, + + @path b: string, + @query c: string, + @body d: string, + ): void; +} +``` + +## Diagnostic Message + +TCGC reports: + +```text +Value in example file 'parametersDiagnostic.json' does not follow its definition: +{"test":"a"} +``` + +## ✅ How to Fix + +Update the example value or the TypeSpec definition so parameters, request bodies, and responses follow the declared shapes. + +```typespec +@service +namespace TestClient { + @route("/{b}") + op parametersDiagnostic( + @header a: string, + @path b: string, + @query c: string, + @query test: string, + @body d: string, + ): void; +} +``` + +## Suppression + +This diagnostic should not be suppressed. Fix the example so its values map to the TypeSpec definition. diff --git a/packages/typespec-client-generator-core/src/diagnostics/external-library-version-mismatch.md b/packages/typespec-client-generator-core/src/diagnostics/external-library-version-mismatch.md new file mode 100644 index 0000000000..b2597541e7 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/external-library-version-mismatch.md @@ -0,0 +1,68 @@ +This diagnostic is issued when external `@alternateType` metadata refers to the same external package with more than one minimum version. + +## Impact + +- **Area:** Language-specific external type dependencies. Generation continues, but the targeted emitter may receive conflicting minimum versions for the same external package. +- **Not affected:** The TypeSpec source types and service payload schema are unchanged. + +## ❌ Incorrect Usage + +```typespec +@alternateType( + { + identity: "pystac.Item", + package: "pystac", + minVersion: "1.12.0", + }, + "python" +) +model Item {} + +@alternateType( + { + identity: "pystac.Collection", + package: "pystac", + minVersion: "1.13.0", // mismatches the earlier `pystac` minVersion `1.12.0` + }, + "python" +) +model Collection {} +``` + +## Diagnostic Message + +TCGC reports: + +```text +External library version mismatch. There are multiple versions of pystac: 1.12.0 and 1.13.0. Please unify the versions. +``` + +## ✅ How to Fix + +Use one package version for all external alternate type declarations that reference the same package. + +```typespec +@alternateType( + { + identity: "pystac.Item", + package: "pystac", + minVersion: "1.12.0", + }, + "python" +) +model Item {} + +@alternateType( + { + identity: "pystac.Collection", + package: "pystac", + minVersion: "1.12.0", + }, + "python" +) +model Collection {} +``` + +## Suppression + +This diagnostic should not be suppressed. Consolidate the dependency to a single version across the referenced packages. diff --git a/packages/typespec-client-generator-core/src/diagnostics/external-type-on-model-property.md b/packages/typespec-client-generator-core/src/diagnostics/external-type-on-model-property.md new file mode 100644 index 0000000000..05c5aa2e73 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/external-type-on-model-property.md @@ -0,0 +1,50 @@ +This diagnostic is issued when `@alternateType` with external type information is applied directly to a model property. + +## Impact + +- **Area:** External alternate-type mapping. Generation continues, but property-level external type information is not applied to SDK type substitution. +- **Not affected:** The model property remains in the service schema with its original TypeSpec type. + +## ❌ Incorrect Usage + +```typespec +model Widget { + @alternateType( // external alternate type is applied to a model property + { + identity: "external.WidgetName", + }, + "python" + ) + name: string; +} +``` + +## Diagnostic Message + +TCGC reports: + +```text +@alternateType with external type information cannot be applied to model properties. Please apply it to the type definition itself (Scalar, Model, Enum, or Union) instead. +``` + +## ✅ How to Fix + +Apply the external alternate type to the scalar, model, enum, or union definition instead of to a model property: + +```typespec +@alternateType( + { + identity: "external.WidgetName", + }, + "python" +) +scalar WidgetName extends string; + +model Widget { + name: WidgetName; +} +``` + +## Suppression + +This diagnostic should not be suppressed. Fix the external type usage so it is applied correctly. diff --git a/packages/typespec-client-generator-core/src/diagnostics/flatten-polymorphism.md b/packages/typespec-client-generator-core/src/diagnostics/flatten-polymorphism.md new file mode 100644 index 0000000000..0962fe28d0 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/flatten-polymorphism.md @@ -0,0 +1,49 @@ +This diagnostic is issued when `@flattenProperty` is applied to a model property whose type is polymorphic (a discriminated type). Flattening a polymorphic type is not supported, because most languages cannot represent it. + +## Impact + +- **Area:** SDK model flattening. Blocks applying `@flattenProperty` because flattening a discriminated model would break generated polymorphic model structure. +- **Not affected:** The discriminator and inheritance described by the service model remain unchanged. + +## ❌ Incorrect Usage + +```typespec +@discriminator("kind") +model Pet { + kind: string; +} +model Cat extends Pet { + kind: "cat"; +} + +model Owner { + @flattenProperty // `pet` has polymorphic type `Pet` + pet: Pet; +} +``` + +## Diagnostic Message + +TCGC reports: + +```text +Cannot flatten property of polymorphic type. +``` + +## ✅ How to Fix + +Remove `@flattenProperty` from the polymorphic property, or change the property to a non-polymorphic type. + +```typespec +@discriminator("kind") +model Pet { + kind: string; +} +model Cat extends Pet { + kind: "cat"; +} + +model Owner { + pet: Pet; +} +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/inconsistent-multiple-service-dependency.md b/packages/typespec-client-generator-core/src/diagnostics/inconsistent-multiple-service-dependency.md new file mode 100644 index 0000000000..d9223c1208 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/inconsistent-multiple-service-dependency.md @@ -0,0 +1,94 @@ +This diagnostic is issued when services merged into the same client resolve different versions of a shared dependency namespace. + +## Impact + +- **Area:** Multi-service dependency versioning. Blocks merging services into one client when their shared versioned dependencies would generate incompatible or duplicated SDK models. +- **Not affected:** Each service can still be generated separately with its own dependency resolution. + +## ❌ Incorrect Usage + +```typespec +@versioned(LibVersions) +namespace SharedLib { + enum LibVersions { + v1, + v2, + } +} + +@service +@versioned(VersionsA) +namespace ServiceA { + enum VersionsA { + @useDependency(SharedLib.LibVersions.v1) + av1, + } +} + +@service +@versioned(VersionsB) +namespace ServiceB { + enum VersionsB { + @useDependency(SharedLib.LibVersions.v2) // differs from ServiceA's dependency on `v1` + bv1, + } +} + +@client({ + name: "CombineClient", + service: [ServiceA, ServiceB], + autoMergeService: true, +}) +namespace CombineClient { + +} +``` + +## Diagnostic Message + +TCGC reports: + +```text +Services merged into client "CombineClient" depend on different versions of "SharedLib": "v1", "v2". +``` + +## ✅ How to Fix + +Align service versioning or `@useDependency` mappings so every merged service resolves the shared dependency to the same version. + +```typespec +@versioned(LibVersions) +namespace SharedLib { + enum LibVersions { + v1, + v2, + } +} + +@service +@versioned(VersionsA) +namespace ServiceA { + enum VersionsA { + @useDependency(SharedLib.LibVersions.v1) + av1, + } +} + +@service +@versioned(VersionsB) +namespace ServiceB { + enum VersionsB { + @useDependency(SharedLib.LibVersions.v1) + bv1, + } +} + +@client({ + name: "CombineClient", + service: [ServiceA, ServiceB], + autoMergeService: true, +}) +namespace CombineClient { + +} +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/inconsistent-multiple-service.md b/packages/typespec-client-generator-core/src/diagnostics/inconsistent-multiple-service.md new file mode 100644 index 0000000000..f0f16a715f --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/inconsistent-multiple-service.md @@ -0,0 +1,66 @@ +This diagnostic is issued when multiple services merged into one `@client` declaration do not have the same server and authentication definitions. + +## Impact + +- **Area:** Multi-service client merging. Blocks a combined client when merged services do not agree on endpoint or authentication shape. +- **Not affected:** The individual service definitions, servers, and auth settings remain unchanged. + +## ❌ Incorrect Usage + +```typespec +@service +@server("https://servicea.example.com") +namespace ServiceA { + +} + +@service +@server("https://serviceb.example.com") // differs from ServiceA server +namespace ServiceB { + +} + +@client({ + name: "CombineClient", + service: [ServiceA, ServiceB], + autoMergeService: true, +}) +namespace CombineClient { + +} +``` + +## Diagnostic Message + +TCGC reports: + +```text +All services must have the same server and auth definitions. +``` + +## ✅ How to Fix + +Make the merged services use matching server and auth definitions, or generate them as separate clients. + +```typespec +@service +@server("https://service.example.com") +namespace ServiceA { + +} + +@service +@server("https://service.example.com") +namespace ServiceB { + +} + +@client({ + name: "CombineClient", + service: [ServiceA, ServiceB], + autoMergeService: true, +}) +namespace CombineClient { + +} +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/invalid-access.md b/packages/typespec-client-generator-core/src/diagnostics/invalid-access.md new file mode 100644 index 0000000000..bf7559c82b --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/invalid-access.md @@ -0,0 +1,33 @@ +This diagnostic is issued when the value passed to the `@access` decorator is not one of the allowed access levels. + +## Impact + +- **Area:** SDK visibility overrides. Blocks the invalid `@access` value from changing generated public/internal visibility. +- **Not affected:** Model structure, operation signatures, and wire payloads are unchanged. + +## ❌ Incorrect Usage + +```typespec +enum CustomAccess { + private: "private", +} +@access(CustomAccess.private) // `"private"` is not a valid access value +model SecretModel {} +``` + +## Diagnostic Message + +TCGC reports: + +```text +Access value must be "public" or "internal". +``` + +## ✅ How to Fix + +Pass `Access.public` or `Access.internal` to `@access` and remove any other enum member or literal value: + +```typespec +@access(Access.internal) +model SecretModel {} +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/invalid-alternate-type.md b/packages/typespec-client-generator-core/src/diagnostics/invalid-alternate-type.md new file mode 100644 index 0000000000..e62db2d8bd --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/invalid-alternate-type.md @@ -0,0 +1,33 @@ +This diagnostic is issued when `@alternateType` is applied to a scalar source type with a non-scalar alternate type. + +## Impact + +- **Area:** SDK type substitution. Blocks an invalid `@alternateType` replacement that would change a scalar into a non-scalar SDK type. +- **Not affected:** The source scalar and its wire encoding remain unchanged. + +## ❌ Incorrect Usage + +```typespec +scalar storageDateTime extends utcDateTime; +model DateWrapper { + value: string; +} +@@alternateType(storageDateTime, DateWrapper); // scalar source cannot use a model alternate type +``` + +## Diagnostic Message + +TCGC reports: + +```text +Invalid alternate type. If the source type is Scalar, the alternate type must also be Scalar. Found alternate type kind: 'Model' +``` + +## ✅ How to Fix + +Use a scalar alternate type when the source is a scalar, or apply `@alternateType` to a non-scalar source when replacing it with a non-scalar shape: + +```typespec +scalar clientDateTime extends string; +@@alternateType(storageDateTime, clientDateTime); +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/invalid-client-doc-mode.md b/packages/typespec-client-generator-core/src/diagnostics/invalid-client-doc-mode.md new file mode 100644 index 0000000000..acf4d7b4d1 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/invalid-client-doc-mode.md @@ -0,0 +1,34 @@ +This diagnostic is issued when `@clientDoc` receives a mode whose value is not `"append"` or `"replace"`. + +## Impact + +- **Area:** Generated SDK documentation customization. Blocks the `@clientDoc` override because TCGC cannot decide whether to append or replace documentation. +- **Not affected:** SDK type names, operation signatures, and service behavior are unchanged. + +## ❌ Incorrect Usage + +```typespec +enum CustomDocMode { + prepend: "prepend", +} + +@clientDoc("Client-specific text.", CustomDocMode.prepend) // mode must be `append` or `replace` +model Widget {} +``` + +## Diagnostic Message + +TCGC reports: + +```text +Invalid mode 'prepend' for @clientDoc decorator. Valid values are "append" or "replace". +``` + +## ✅ How to Fix + +Pass `DocumentationMode.append` or `DocumentationMode.replace` to `@clientDoc`. + +```typespec +@clientDoc("Client-specific text.", DocumentationMode.append) +model Widget {} +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/invalid-client-service-multiple.md b/packages/typespec-client-generator-core/src/diagnostics/invalid-client-service-multiple.md new file mode 100644 index 0000000000..060e38f8b2 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/invalid-client-service-multiple.md @@ -0,0 +1,36 @@ +This diagnostic is issued when `@client` is applied to an interface with multiple services in the `service` option. + +## Impact + +- **Area:** Multi-service client declaration. Blocks using an interface as the root for a client that merges multiple services; only namespaces can represent that structure. +- **Not affected:** The referenced service namespaces remain valid. + +## ❌ Incorrect Usage + +```typespec +@service namespace ServiceA; +@service namespace ServiceB; + +@client({ service: [ServiceA, ServiceB] }) // multiple services are not allowed on an interface client +interface CombinedClient {} +``` + +## Diagnostic Message + +TCGC reports: + +```text +`@client` with multiple services is only allowed on `Namespace`. +``` + +## ✅ How to Fix + +Move the multi-service `@client` declaration to a namespace, or use a single service for an interface client. + +```typespec +@service namespace ServiceA; +@service namespace ServiceB; + +@client({ service: [ServiceA, ServiceB] }) +namespace CombinedClient {} +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/invalid-deserializeEmptyStringAsNull-target-type.md b/packages/typespec-client-generator-core/src/diagnostics/invalid-deserializeEmptyStringAsNull-target-type.md new file mode 100644 index 0000000000..61e4ac2d72 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/invalid-deserializeEmptyStringAsNull-target-type.md @@ -0,0 +1,34 @@ +This diagnostic is issued when `@deserializeEmptyStringAsNull` is applied to a property whose type is not `string` and is not a scalar derived from `string`. + +## Impact + +- **Area:** SDK deserialization customization. Blocks empty-string-to-null handling on non-string-like properties. +- **Not affected:** Other properties and the service payload schema are unchanged. + +## ❌ Incorrect Usage + +```typespec +model Widget { + @deserializeEmptyStringAsNull + count: int32; // property type is not `string` or string-derived +} +``` + +## Diagnostic Message + +TCGC reports: + +```text +@deserializeEmptyStringAsNull can only be applied to `ModelProperty` of type 'string' or a `Scalar` derived from 'string'. +``` + +## ✅ How to Fix + +Apply the decorator only to a `string` property or a property whose scalar type ultimately extends `string`. + +```typespec +model Widget { + @deserializeEmptyStringAsNull + name: string; +} +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/invalid-encode-for-collection-format.md b/packages/typespec-client-generator-core/src/diagnostics/invalid-encode-for-collection-format.md new file mode 100644 index 0000000000..566a43acd8 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/invalid-encode-for-collection-format.md @@ -0,0 +1,38 @@ +This diagnostic is issued when an array parameter uses `@encode` with an array encoding other than `ArrayEncoding.pipeDelimited` or `ArrayEncoding.spaceDelimited` for collection format. + +## Impact + +- **Area:** Collection parameter serialization metadata. Generation continues with the fallback collection format when an unsupported array encoding is used. +- **Not affected:** Non-array parameters and the declared parameter location are unchanged. + +## ❌ Incorrect Usage + +```typespec +@service +namespace My.Service; + +op myOp(@header @encode("tsv") header: string[]): void; // `tsv` is not a supported collection encoding +``` + +## Diagnostic Message + +TCGC reports: + +```text +Only encode of `ArrayEncoding.pipeDelimited` and `ArrayEncoding.spaceDelimited` is supported for collection format. +``` + +## ✅ How to Fix + +Use `ArrayEncoding.pipeDelimited`, use `ArrayEncoding.spaceDelimited`, rely on the default CSV format, or use exploded query serialization. + +```typespec +@service +namespace My.Service; + +op myOp(@header @encode(ArrayEncoding.pipeDelimited) header: string[]): void; +``` + +## Suppression + +This diagnostic should not be suppressed. Fix the `@encode` usage to a supported collection-format encoding. diff --git a/packages/typespec-client-generator-core/src/diagnostics/invalid-initialized-by.md b/packages/typespec-client-generator-core/src/diagnostics/invalid-initialized-by.md new file mode 100644 index 0000000000..ab84816e6d --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/invalid-initialized-by.md @@ -0,0 +1,38 @@ +This diagnostic is issued when the `initializedBy` option in `@clientInitialization` is not a valid `InitializedBy` value or is invalid for the client position. + +## Impact + +- **Area:** SDK client construction. Blocks invalid client initialization metadata that would produce an impossible root or sub-client creation pattern. +- **Not affected:** Service operations and request/response payload shapes are unchanged. + +## ❌ Incorrect Usage + +```typespec +@clientInitialization({ + initializedBy: InitializedBy.customizeCode | InitializedBy.parent, // `customizeCode` cannot be combined +}) +namespace BlobClient { + +} +``` + +## Diagnostic Message + +TCGC reports: + +```text +Invalid 'initializedBy' value. `InitializedBy.customizeCode` cannot be combined with other values. +``` + +## ✅ How to Fix + +Use `InitializedBy.individually` for root clients, `InitializedBy.parent` or `InitializedBy.individually | InitializedBy.parent` for sub clients, or `InitializedBy.customizeCode` by itself. + +```typespec +@clientInitialization({ + initializedBy: InitializedBy.customizeCode, +}) +namespace BlobClient { + +} +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/invalid-mark-as-lro-target.md b/packages/typespec-client-generator-core/src/diagnostics/invalid-mark-as-lro-target.md new file mode 100644 index 0000000000..a8ca78acfe --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/invalid-mark-as-lro-target.md @@ -0,0 +1,40 @@ +This diagnostic is issued when `@markAsLro` is applied to an operation that does not return a non-error model response. + +## Impact + +- **Area:** Legacy LRO SDK metadata. Generation continues with the operation treated as a regular method because no model response is available for forced LRO metadata. +- **Not affected:** The service operation's HTTP behavior and response type are unchanged. + +## ❌ Incorrect Usage + +```typespec +@markAsLro +@post +op start(): string; // LRO marker requires a model response +``` + +## Diagnostic Message + +TCGC reports: + +```text +@markAsLro decorator can only be applied to operations that return a model. We will ignore this decorator. +``` + +## ✅ How to Fix + +Apply `@markAsLro` only to operations that return a model, or remove the decorator. + +```typespec +model StartResponse { + id: string; +} + +@markAsLro +@post +op start(): StartResponse; +``` + +## Suppression + +This diagnostic should not be suppressed. Fix the target so it is a valid long-running operation, or remove `@markAsLro`. diff --git a/packages/typespec-client-generator-core/src/diagnostics/invalid-mark-as-pageable-target.md b/packages/typespec-client-generator-core/src/diagnostics/invalid-mark-as-pageable-target.md new file mode 100644 index 0000000000..1918262fe8 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/invalid-mark-as-pageable-target.md @@ -0,0 +1,44 @@ +This diagnostic is issued when `@markAsPageable` is applied to an operation that does not return a model with a property decorated with `@pageItems` or named `value`. + +## Impact + +- **Area:** Legacy pageable SDK metadata. Generation continues with the operation treated as a regular method because TCGC cannot identify page items. +- **Not affected:** The operation's service response schema is unchanged. + +## ❌ Incorrect Usage + +```typespec +@markAsPageable +@get +op listWidgets(): string; // pageable marker requires a page model response +``` + +## Diagnostic Message + +TCGC reports: + +```text +@markAsPageable decorator can only be applied to operations that return a model with a property decorated with @pageItems or a property named 'value'. We will ignore this decorator. +``` + +## ✅ How to Fix + +Apply `@markAsPageable` only to operations returning a suitable page model, or update the response model to include `@pageItems` or a `value` property. + +```typespec +model Widget { + name: string; +} + +model WidgetPage { + value: Widget[]; +} + +@markAsPageable +@get +op listWidgets(): WidgetPage; +``` + +## Suppression + +This diagnostic should not be suppressed. Fix the target so it is a valid pageable operation, or remove `@markAsPageable`. diff --git a/packages/typespec-client-generator-core/src/diagnostics/invalid-usage.md b/packages/typespec-client-generator-core/src/diagnostics/invalid-usage.md new file mode 100644 index 0000000000..36b15af16f --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/invalid-usage.md @@ -0,0 +1,33 @@ +This diagnostic is issued when the value passed to the `@usage` decorator is not one of the supported usage flags. + +## Impact + +- **Area:** SDK model usage overrides. Blocks unsupported explicit usage flags from being added to generated model, enum, or union metadata. +- **Not affected:** Usage inferred from actual operation inputs and outputs is still calculated separately. + +## ❌ Incorrect Usage + +```typespec +enum CustomUsage { + custom: 8, +} +@usage(CustomUsage.custom) // `8` is not a supported Usage flag +model Widget {} +``` + +## Diagnostic Message + +TCGC reports: + +```text +Usage value must be one of: 2 (input), 4 (output), 256 (json), or 512 (xml). +``` + +## ✅ How to Fix + +Pass one or more of `Usage.input`, `Usage.output`, `Usage.json`, or `Usage.xml` to `@usage`, and remove custom or empty usage values: + +```typespec +@usage(Usage.input | Usage.json) +model Widget {} +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/legacy-hierarchy-building-circular-reference.md b/packages/typespec-client-generator-core/src/diagnostics/legacy-hierarchy-building-circular-reference.md new file mode 100644 index 0000000000..19d69f2ac7 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/legacy-hierarchy-building-circular-reference.md @@ -0,0 +1,55 @@ +This diagnostic is issued when `@hierarchyBuilding` creates a recursive base-type chain. + +## Impact + +- **Area:** Legacy SDK inheritance rebasing. Blocks hierarchy rebasing because it would create a circular generated base-model chain. +- **Not affected:** The original TypeSpec inheritance declarations are unchanged. + +## ❌ Incorrect Usage + +```typespec +@usage(Usage.input) +namespace TestService; + +model A extends B { + propA: string; +} + +@hierarchyBuilding(A) // rebasing `B` onto `A` creates a circular base chain +model B extends C { + propB: string; +} + +model C { + propC: string; +} +``` + +## Diagnostic Message + +TCGC reports: + +```text +@hierarchyBuilding decorator causes recursive base type reference. +``` + +## ✅ How to Fix + +Remove or change the `@hierarchyBuilding` decorator so the rebased hierarchy is acyclic. + +```typespec +@usage(Usage.input) +namespace TestService; + +model A extends B { + propA: string; +} + +model B extends C { + propB: string; +} + +model C { + propC: string; +} +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/legacy-hierarchy-building-conflict.md b/packages/typespec-client-generator-core/src/diagnostics/legacy-hierarchy-building-conflict.md new file mode 100644 index 0000000000..38637197f3 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/legacy-hierarchy-building-conflict.md @@ -0,0 +1,60 @@ +This diagnostic is issued when `@hierarchyBuilding` rebases a model hierarchy and a same-named property from the old base chain has a type that does not match the property supplied by the new base chain. + +## Impact + +- **Area:** Legacy SDK inheritance rebasing. Generation continues, but a conflicting same-named property is dropped from the generated rebased hierarchy. +- **Not affected:** The service schema still contains the original property definitions. + +## ❌ Incorrect Usage + +```typespec +model C { + shared?: int32; +} + +model OldBase { + shared?: string; // conflicts with `C.shared` type `int32` after rebasing +} + +@hierarchyBuilding(C) +model A extends OldBase { + a?: string; +} + +@route("/test") +op test(): A; +``` + +## Diagnostic Message + +TCGC reports: + +```text +@hierarchyBuilding decorator: property 'shared' on model 'A' has type that does not match the same-named property supplied by the new base chain (rooted at 'C'). The property is dropped from 'A' to satisfy the rebase rule (own properties are filtered against the new base chain by name). Consider aligning the types or removing the property from 'A'. +``` + +## ✅ How to Fix + +Align the property types between the child and the new base chain, or remove the conflicting property from the child model. + +```typespec +model C { + shared?: int32; +} + +model OldBase { + shared?: int32; +} + +@hierarchyBuilding(C) +model A extends OldBase { + a?: string; +} + +@route("/test") +op test(): A; +``` + +## Suppression + +This diagnostic should not be suppressed. Fix the `@hierarchyBuilding` usage, or remove the decorator. diff --git a/packages/typespec-client-generator-core/src/diagnostics/mark-as-lro-ineffective.md b/packages/typespec-client-generator-core/src/diagnostics/mark-as-lro-ineffective.md new file mode 100644 index 0000000000..3b4a3f0e06 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/mark-as-lro-ineffective.md @@ -0,0 +1,18 @@ +This diagnostic is issued when `@markAsLro` is applied to an operation that already has real LRO metadata. + +## Impact + +- **Area:** LRO SDK metadata. Blocks or rejects a redundant legacy LRO marker when the operation already has real long-running-operation metadata. +- **Not affected:** Existing LRO polling metadata remains the source of generated behavior. + +## Diagnostic Message + +TCGC reports: + +```text +@markAsLro decorator is ineffective since this operation already returns real LRO metadata. Please remove the @markAsLro decorator. +``` + +## ✅ How to Fix + +Remove `@markAsLro` from operations that are already modeled as long-running operations. diff --git a/packages/typespec-client-generator-core/src/diagnostics/mark-as-pageable-ineffective.md b/packages/typespec-client-generator-core/src/diagnostics/mark-as-pageable-ineffective.md new file mode 100644 index 0000000000..08c8a478f5 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/mark-as-pageable-ineffective.md @@ -0,0 +1,59 @@ +This diagnostic is issued when `@markAsPageable` is applied to an operation that is already marked pageable with `@list`. + +## Impact + +- **Area:** Pageable SDK metadata. Generation continues using the existing `@list` paging metadata while the redundant legacy marker has no effect. +- **Not affected:** Page item and next-link metadata already discovered from the operation remain unchanged. + +## ❌ Incorrect Usage + +```typespec +model Employee is TrackedResource { + ...ResourceNameParameter; +} + +model EmployeeProperties { + salary: int32; +} + +@armResourceOperations +interface Employees { + get is ArmResourceRead; + + @markAsPageable("csharp") // operation is already pageable via `ArmResourceListByParent` + listEmployees is ArmResourceListByParent; +} +``` + +## Diagnostic Message + +TCGC reports: + +```text +@markAsPageable decorator is ineffective since this operation is already marked as pageable with @list decorator. Please remove the @markAsPageable decorator. +``` + +## ✅ How to Fix + +Remove `@markAsPageable` and keep the existing `@list` pageable metadata. + +```typespec +model Employee is TrackedResource { + ...ResourceNameParameter; +} + +model EmployeeProperties { + salary: int32; +} + +@armResourceOperations +interface Employees { + get is ArmResourceRead; + + listEmployees is ArmResourceListByParent; +} +``` + +## Suppression + +This diagnostic should not be suppressed. Remove the redundant `@markAsPageable`, or fix the operation so the marker is needed. diff --git a/packages/typespec-client-generator-core/src/diagnostics/missing-scope.md b/packages/typespec-client-generator-core/src/diagnostics/missing-scope.md new file mode 100644 index 0000000000..3f7cfee6aa --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/missing-scope.md @@ -0,0 +1,43 @@ +This diagnostic is issued when a decorator that is likely language-specific is used without a language scope; currently this is reported for external `@alternateType` metadata without a scope. + +## Impact + +- **Area:** Language-specific alternate-type customization. Blocks external type metadata that lacks a target emitter scope, preventing it from being applied globally by accident. +- **Not affected:** The TypeSpec type definition and service payload schema are unchanged. + +## ❌ Incorrect Usage + +```typespec +@alternateType({ + // external alternate type is missing a language scope + identity: "pystac.Collection", + + package: "pystac", + minVersion: "1.13.0", +}) +model ItemCollection {} +``` + +## Diagnostic Message + +TCGC reports: + +```text +@scope decorator should be applied with @alternateType since it is highly likely this is language-specific +``` + +## ✅ How to Fix + +Provide the appropriate language scope argument, such as `"python"`, `"csharp"`, or another emitter scope: + +```typespec +@alternateType( + { + identity: "pystac.Collection", + package: "pystac", + minVersion: "1.13.0", + }, + "python" +) +model ItemCollection {} +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/missing-service-versions.md b/packages/typespec-client-generator-core/src/diagnostics/missing-service-versions.md new file mode 100644 index 0000000000..73b53c83e0 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/missing-service-versions.md @@ -0,0 +1,57 @@ +This diagnostic is issued when the enum passed to `@clientApiVersions` omits one or more versions defined by the versioned service. + +## Impact + +- **Area:** Client API-version enum generation. Blocks a client API version list that would omit service versions required for compatibility. +- **Not affected:** The service's versioned namespace and declared service versions are unchanged. + +## ❌ Incorrect Usage + +```typespec +@service +@versioned(Versions) +namespace My.Service { + enum Versions { + v1, + v2, + v3, + } +} + +enum ClientApiVersions { // missing service version `v3` + v1, + v2, +} +@@clientApiVersions(My.Service, ClientApiVersions); +``` + +## Diagnostic Message + +TCGC reports: + +```text +The @clientApiVersions decorator is missing one or more versions defined in My.Service. Client API must support all service versions to ensure compatibility. Missing versions: v3. Please update the client API to support all required service versions. +``` + +## ✅ How to Fix + +Add the missing service versions to the client API versions enum. + +```typespec +@service +@versioned(Versions) +namespace My.Service { + enum Versions { + v1, + v2, + v3, + } +} + +enum ClientApiVersions { + v1, + v2, + v3, +} +@@clientApiVersions(My.Service, ClientApiVersions); +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/multiple-param-alias.md b/packages/typespec-client-generator-core/src/diagnostics/multiple-param-alias.md new file mode 100644 index 0000000000..4c73f1f192 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/multiple-param-alias.md @@ -0,0 +1,41 @@ +This diagnostic is issued when more than one `@paramAlias` is applied to the same model property in the same effective scope. + +## Impact + +- **Area:** Client initialization parameter naming. Generation continues using the first alias, while later aliases for the same scoped parameter are ignored. +- **Not affected:** Operation-level parameter names and wire names are unchanged. + +## ❌ Incorrect Usage + +```typespec +model ClientOptions { + account: string; +} + +@@paramAlias(ClientOptions.account, "accountName"); +@@paramAlias(ClientOptions.account, "storageAccountName"); // second alias for the same parameter scope +``` + +## Diagnostic Message + +TCGC reports: + +```text +Multiple param aliases applied to 'account'. Only the first one 'accountName' will be used. +``` + +## ✅ How to Fix + +Keep only one alias for the property in a given scope, or scope the aliases so only one applies to each emitter. + +```typespec +model ClientOptions { + account: string; +} + +@@paramAlias(ClientOptions.account, "accountName"); +``` + +## Suppression + +This diagnostic should not be suppressed. Fix the `@paramAlias` usage so each parameter has a single alias. diff --git a/packages/typespec-client-generator-core/src/diagnostics/multiple-response-types.md b/packages/typespec-client-generator-core/src/diagnostics/multiple-response-types.md new file mode 100644 index 0000000000..3712279fd4 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/multiple-response-types.md @@ -0,0 +1,26 @@ +This diagnostic is issued when one operation has multiple distinct response body types across its successful responses. Some emitters cannot expose all of those response shapes in a single method return type. + +## Impact + +- **Area:** SDK method response modeling. Generation continues, but some emitters may be unable to represent every distinct response body type in one method return shape. +- **Not affected:** The service can still declare multiple wire responses. + +## Diagnostic Message + +TCGC reports: + +```text +Multiple response types found in operation getWidget. Some emitters might not support returning all of these response types +``` + +## ✅ How to Fix + +Prefer a single response body model or redesign the responses so all successful response bodies share one SDK shape. + +## Suppression + +Suppress this warning only if the target emitter is known to handle the operation's multiple response body types correctly. + +```typespec +#suppress "@azure-tools/typespec-client-generator-core/multiple-response-types" "emitter supports multiple response bodies" +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/nested-client-service-not-subset.md b/packages/typespec-client-generator-core/src/diagnostics/nested-client-service-not-subset.md new file mode 100644 index 0000000000..d9e22ba9e1 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/nested-client-service-not-subset.md @@ -0,0 +1,52 @@ +This diagnostic is issued when a nested (sub) client declares services that are not a subset of its parent client's services. A sub client can only expose services that its parent client already exposes. + +## Impact + +- **Area:** Nested client service ownership. Blocks a sub-client from exposing services that its parent client does not own. +- **Not affected:** The parent client's declared services and the standalone service namespaces remain unchanged. + +## ❌ Incorrect Usage + +```typespec +@client({ + name: "ParentClient", + service: [ServiceOne, ServiceTwo], +}) +namespace ParentClient { + @client({ + name: "ChildClient", + service: ServiceThree, // not one of the parent's services (`ServiceOne`, `ServiceTwo`) + }) + namespace Child { + + } +} +``` + +## Diagnostic Message + +TCGC reports: + +```text +Nested client's services must be a subset of the parent client's services. If no service is needed, omit the `service` property to inherit from the parent. +``` + +## ✅ How to Fix + +Restrict the nested client's `service` to services the parent client already declares, or omit the `service` option to inherit the parent's services: + +```typespec +@client({ + name: "ParentClient", + service: [ServiceOne, ServiceTwo], +}) +namespace ParentClient { + @client({ + name: "ChildClient", + service: ServiceOne, + }) + namespace Child { + + } +} +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/no-corresponding-method-param.md b/packages/typespec-client-generator-core/src/diagnostics/no-corresponding-method-param.md new file mode 100644 index 0000000000..b9a90228e5 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/no-corresponding-method-param.md @@ -0,0 +1,18 @@ +This diagnostic is issued when a required HTTP operation parameter cannot be mapped to any generated method parameter. The generated client would have no reliable way to supply that required service parameter. + +## Impact + +- **Area:** Method-to-protocol parameter mapping. Blocks generation of a service method when a required HTTP parameter cannot be supplied by the generated SDK method signature. +- **Not affected:** The HTTP operation still declares the required parameter. + +## Diagnostic Message + +TCGC reports: + +```text +Missing HTTP operation parameter "apiVersion" in method "getWidget". Please check the method definition. +``` + +## ✅ How to Fix + +Check any client customization, such as `@override`, `@paramAlias`, parameter reordering, or parameter removal, that changes the method parameters, and fix it so every HTTP operation parameter still maps to a method parameter. diff --git a/packages/typespec-client-generator-core/src/diagnostics/no-emitter-name.md b/packages/typespec-client-generator-core/src/diagnostics/no-emitter-name.md new file mode 100644 index 0000000000..d1e1c80db5 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/no-emitter-name.md @@ -0,0 +1,22 @@ +This diagnostic is issued when TCGC cannot determine the emitter's language because no usable emitter name is available (for example, the emitter name is missing or does not match the expected `*-` pattern). + +## Impact + +- **Area:** TCGC host and tooling configuration. Generation falls back to an unknown emitter scope, which can make language-scoped decorators and names resolve incorrectly. +- **Not affected:** The TypeSpec service model is not changed by the missing option. + +## Diagnostic Message + +TCGC reports: + +```text +Can not find name for your emitter, please check your emitter name. +``` + +## ✅ How to Fix + +Make sure the emitter is invoked with a resolvable package name (such as `@azure-tools/typespec-csharp`) so TCGC can derive the target language. + +## Suppression + +This diagnostic should not be suppressed. Run TCGC with a proper emitter name and check the usage so language scope can be resolved. diff --git a/packages/typespec-client-generator-core/src/diagnostics/non-head-bool-response-decorator.md b/packages/typespec-client-generator-core/src/diagnostics/non-head-bool-response-decorator.md new file mode 100644 index 0000000000..9fa6e7aeee --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/non-head-bool-response-decorator.md @@ -0,0 +1,40 @@ +This diagnostic is issued when `@responseAsBool` is applied to an operation that is not decorated with `@head`. + +## Impact + +- **Area:** HEAD response convenience modeling. Generation continues, but `@responseAsBool` is ignored because the operation is not a HEAD request. +- **Not affected:** The operation's HTTP verb and response schema are unchanged. + +## ❌ Incorrect Usage + +```typespec +@responseAsBool +@get // `@responseAsBool` requires a HEAD operation +op exists(): void; +``` + +## Diagnostic Message + +TCGC reports: + +```text +@responseAsBool decorator can only be used on HEAD operations. Will ignore decorator on getOperation. +``` + +## ✅ How to Fix + +Add `@head` when the operation is a HEAD request, or remove `@responseAsBool`: + +```typespec +@responseAsBool +@head +op exists(): void; +``` + +## Suppression + +Suppress this warning only if `@responseAsBool` is intentionally ignored because the operation is not a HEAD operation for this emitter. + +```typespec +#suppress "@azure-tools/typespec-client-generator-core/non-head-bool-response-decorator" "non-HEAD boolean response is intentional" +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/override-parameters-mismatch.md b/packages/typespec-client-generator-core/src/diagnostics/override-parameters-mismatch.md new file mode 100644 index 0000000000..b7d4d2d849 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/override-parameters-mismatch.md @@ -0,0 +1,35 @@ +This diagnostic is issued when an `@override` operation does not preserve required parameters from the original operation, or a realized path parameter loses its `@path` role. + +## Impact + +- **Area:** Client method override signatures. Blocks an override that cannot be mapped back to the original service operation's required parameters. +- **Not affected:** The original operation remains available with its declared parameters. + +## ❌ Incorrect Usage + +```typespec +@service +namespace MyService { + op create(name: string, location: string): void; +} + +op createOverride(name: string): void; // missing required parameter `location` +@@override(MyService.create, createOverride); +``` + +## Diagnostic Message + +TCGC reports: + +```text +Method "create" has different parameters definition from the override operation. Please check the parameter defined in the override operation: "location". +``` + +## ✅ How to Fix + +Update the override operation so required parameters are present with compatible definitions and path parameters remain path parameters unless intentionally moved with `@clientLocation`: + +```typespec +op createOverride(name: string, location: string): void; +@@override(MyService.create, createOverride); +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/remove-parameter-not-found.md b/packages/typespec-client-generator-core/src/diagnostics/remove-parameter-not-found.md new file mode 100644 index 0000000000..2d9a13a8f6 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/remove-parameter-not-found.md @@ -0,0 +1,38 @@ +This diagnostic is issued when `removeParameter` is called with a selector that does not match any parameter on the operation. + +## Impact + +- **Area:** Client customization transformations. Blocks `removeParameter` from producing the intended customized signature because the selector does not match an operation parameter. +- **Not affected:** The original operation parameter list is unchanged. + +## ❌ Incorrect Usage + +```typespec +@service +namespace MyService { + op myOp(existingParam?: string): void; +} + +alias Modified = removeParameter(MyService.myOp, "missingParam"); // `missingParam` is not a parameter of `myOp` +``` + +## Diagnostic Message + +TCGC reports: + +```text +Parameter "missingParam" not found in operation "myOp". +``` + +## ✅ How to Fix + +Use the exact name or model property reference for an existing operation parameter. + +```typespec +@service +namespace MyService { + op myOp(existingParam?: string): void; +} + +alias Modified = removeParameter(MyService.myOp, "existingParam"); +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/reorder-parameter-duplicate.md b/packages/typespec-client-generator-core/src/diagnostics/reorder-parameter-duplicate.md new file mode 100644 index 0000000000..a2da40f89a --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/reorder-parameter-duplicate.md @@ -0,0 +1,38 @@ +This diagnostic is issued when `reorderParameters` lists the same parameter name more than once. + +## Impact + +- **Area:** Client customization transformations. Blocks `reorderParameters` because a duplicated name would make the generated method parameter order ambiguous. +- **Not affected:** The operation's actual parameters are unchanged. + +## ❌ Incorrect Usage + +```typespec +@service +namespace MyService { + op myOp(a: string, b: string): void; +} + +alias Modified = reorderParameters(MyService.myOp, #["a", "a"]); // `a` appears twice +``` + +## Diagnostic Message + +TCGC reports: + +```text +Parameter "a" appears more than once in the reorder list for operation "myOp". +``` + +## ✅ How to Fix + +Include each parameter exactly once in the reorder list. + +```typespec +@service +namespace MyService { + op myOp(a: string, b: string): void; +} + +alias Modified = reorderParameters(MyService.myOp, #["b", "a"]); +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/reorder-parameter-missing.md b/packages/typespec-client-generator-core/src/diagnostics/reorder-parameter-missing.md new file mode 100644 index 0000000000..319f82942e --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/reorder-parameter-missing.md @@ -0,0 +1,38 @@ +This diagnostic is issued when `reorderParameters` omits a parameter that exists on the operation. + +## Impact + +- **Area:** Client customization transformations. Blocks `reorderParameters` because the transformed signature would omit an existing operation parameter. +- **Not affected:** The original method parameter set remains intact. + +## ❌ Incorrect Usage + +```typespec +@service +namespace MyService { + op myOp(a: string, b: string, c: string): void; +} + +alias Modified = reorderParameters(MyService.myOp, #["c", "a"]); // missing parameter `b` +``` + +## Diagnostic Message + +TCGC reports: + +```text +Parameter "b" from operation "myOp" is missing in reorder list. +``` + +## ✅ How to Fix + +Include every operation parameter exactly once in the reorder list. + +```typespec +@service +namespace MyService { + op myOp(a: string, b: string, c: string): void; +} + +alias Modified = reorderParameters(MyService.myOp, #["c", "a", "b"]); +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/reorder-parameter-not-found.md b/packages/typespec-client-generator-core/src/diagnostics/reorder-parameter-not-found.md new file mode 100644 index 0000000000..a1a2f8d155 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/reorder-parameter-not-found.md @@ -0,0 +1,38 @@ +This diagnostic is issued when `reorderParameters` includes a parameter name that does not exist on the operation. + +## Impact + +- **Area:** Client customization transformations. Blocks `reorderParameters` because the order list names a parameter that the operation does not have. +- **Not affected:** Existing operation parameters and their wire metadata are unchanged. + +## ❌ Incorrect Usage + +```typespec +@service +namespace MyService { + op myOp(a: string, b: string): void; +} + +alias Modified = reorderParameters(MyService.myOp, #["a", "missing"]); // `missing` is not a parameter of `myOp` +``` + +## Diagnostic Message + +TCGC reports: + +```text +Parameter "missing" specified in reorder list not found in operation "myOp". +``` + +## ✅ How to Fix + +Remove the unknown name from the reorder list or add the parameter before reordering. + +```typespec +@service +namespace MyService { + op myOp(a: string, b: string): void; +} + +alias Modified = reorderParameters(MyService.myOp, #["b", "a"]); +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/replace-parameter-not-found.md b/packages/typespec-client-generator-core/src/diagnostics/replace-parameter-not-found.md new file mode 100644 index 0000000000..454c8aa6f3 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/replace-parameter-not-found.md @@ -0,0 +1,44 @@ +This diagnostic is issued when `replaceParameter` is called with a selector that does not match any parameter on the operation. + +## Impact + +- **Area:** Client customization transformations. Blocks `replaceParameter` from producing the intended customized signature because the selector does not match an operation parameter. +- **Not affected:** The original operation signature is unchanged. + +## ❌ Incorrect Usage + +```typespec +@service +namespace MyService { + op myOp(existingParam: string): void; +} + +model NewParams { + replacement: int32; +} +alias Modified = replaceParameter(MyService.myOp, "missingParam", NewParams.replacement); // `missingParam` is not a parameter of `myOp` +``` + +## Diagnostic Message + +TCGC reports: + +```text +Parameter "missingParam" not found in operation "myOp". +``` + +## ✅ How to Fix + +Use the exact name or model property reference for an existing operation parameter. + +```typespec +@service +namespace MyService { + op myOp(existingParam: string): void; +} + +model NewParams { + replacement: int32; +} +alias Modified = replaceParameter(MyService.myOp, "existingParam", NewParams.replacement); +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/require-versioned-service.md b/packages/typespec-client-generator-core/src/diagnostics/require-versioned-service.md new file mode 100644 index 0000000000..f50b1ece1f --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/require-versioned-service.md @@ -0,0 +1,52 @@ +This diagnostic is issued when `@clientApiVersions` is applied to a service namespace that is not versioned. + +## Impact + +- **Area:** API-version decorator validation. Generation continues, but `@clientApiVersions` has no service version list to extend on an unversioned namespace. +- **Not affected:** The unversioned service operations are otherwise generated normally. + +## ❌ Incorrect Usage + +```typespec +@service +@clientApiVersions(ApiVersions) // namespace is not decorated with `@versioned` +namespace My.Service { + enum ApiVersions { + v1, + v2, + } +} +``` + +## Diagnostic Message + +TCGC reports: + +```text +Service "My.Service" must be versioned if you want to apply the "@clientApiVersions" decorator +``` + +## ✅ How to Fix + +Add TypeSpec versioning to the service namespace or remove `@clientApiVersions`. + +```typespec +@service +@versioned(Versions) +@clientApiVersions(ApiVersions) +namespace My.Service { + enum Versions { + v1, + v2, + } + + enum ApiVersions { + v1, + v2, + } +} +``` + +## Suppression + +This diagnostic should not be suppressed. Add versioning to the service (with `@versioned`), or remove `@clientApiVersions` if the service is not versioned. diff --git a/packages/typespec-client-generator-core/src/diagnostics/required-parameter-scoped-out.md b/packages/typespec-client-generator-core/src/diagnostics/required-parameter-scoped-out.md new file mode 100644 index 0000000000..bdbf5d0714 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/required-parameter-scoped-out.md @@ -0,0 +1,43 @@ +This diagnostic is issued when a required HTTP parameter is scoped out of the current emitter with `@scope`, leaving the operation without a value it needs. This is usually not allowed. + +## Impact + +- **Area:** Target-emitter method signatures. Generation continues for that emitter without a required parameter, which can cause runtime failures unless the value is supplied another way. +- **Not affected:** Other emitter scopes and the HTTP parameter definition are unchanged. + +## ❌ Incorrect Usage + +```typespec +op getWidget( + @header("x-client-id") + @scope("!python") // scopes out required parameter `clientId` for Python + clientId: string, +): void; +``` + +## Diagnostic Message + +TCGC reports: + +```text +Required parameter "requiredHeader" is scoped out for emitter "python". This may cause runtime errors unless the parameter is provided through other means (e.g., custom headers). +``` + +## ✅ How to Fix + +Keep the required parameter in scope, or — when scoping it out is intentional and the value is supplied another way — confirm the intent and suppress this diagnostic. + +```typespec +op getWidget( + @header("x-client-id") + clientId: string, +): void; +``` + +## Suppression + +Suppress this warning only if the scoped-out required parameter is intentionally supplied another way, such as a custom policy for that emitter. + +```typespec +#suppress "@azure-tools/typespec-client-generator-core/required-parameter-scoped-out" "supplied by a custom Python policy" +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/root-client-missing-service.md b/packages/typespec-client-generator-core/src/diagnostics/root-client-missing-service.md new file mode 100644 index 0000000000..f28850e713 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/root-client-missing-service.md @@ -0,0 +1,39 @@ +This diagnostic is issued when a root namespace decorated with `@client` does not specify a service configuration. + +## Impact + +- **Area:** Root client construction. Blocks building an explicit root client because TCGC cannot determine which service metadata it represents. +- **Not affected:** The service namespace itself remains valid. + +## ❌ Incorrect Usage + +```typespec +@client // root client is missing `{ service: ... }` +namespace WidgetClient { + +} +``` + +## Diagnostic Message + +TCGC reports: + +```text +Root namespace decorated with @client must have service config. +``` + +## ✅ How to Fix + +Add the `service` option to the root `@client`, or make the client nested under a parent client whose services it should inherit: + +```typespec +@service +namespace WidgetService; + +@client({ + service: WidgetService, +}) +namespace WidgetClient { + +} +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/server-param-not-path.md b/packages/typespec-client-generator-core/src/diagnostics/server-param-not-path.md new file mode 100644 index 0000000000..f0842397e7 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/server-param-not-path.md @@ -0,0 +1,18 @@ +This diagnostic is issued when a server template argument resolves to something other than an HTTP path parameter. Server URL template variables must map to path parameters in the generated endpoint configuration. + +## Impact + +- **Area:** Endpoint parameter generation. Blocks server URL template parameter conversion when the template argument is not modeled as a path parameter. +- **Not affected:** Operation-level route parameters are unaffected unless they are part of the server template. + +## Diagnostic Message + +TCGC reports: + +```text +Template argument endpoint is not a path parameter, it is a query. It has to be a path. +``` + +## ✅ How to Fix + +Make the template argument a path parameter or update the server URL so it no longer requires that argument. diff --git a/packages/typespec-client-generator-core/src/diagnostics/union-circular.md b/packages/typespec-client-generator-core/src/diagnostics/union-circular.md new file mode 100644 index 0000000000..8b22647459 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/union-circular.md @@ -0,0 +1,48 @@ +This diagnostic is issued when TCGC converts a union that directly or indirectly contains itself. Recursive union shapes cannot be represented safely as generated SDK union types. + +## Impact + +- **Area:** SDK union type conversion. Generation continues with a fallback union shape, but the recursive union cannot become a usable SDK union. +- **Not affected:** The TypeSpec union declaration is still accepted by the compiler. + +## ❌ Incorrect Usage + +```typespec +@service +namespace Test { + union Test { + null, + Test, // union contains itself + } + + op test(test: Test): void; +} +``` + +## Diagnostic Message + +TCGC reports: + +```text +Cannot have a union containing self. +``` + +## ✅ How to Fix + +Break the circular union reference or model the recursive relationship through a model property instead. + +```typespec +@service +namespace Test { + union Test { + null, + string, + } + + op test(test: Test): void; +} +``` + +## Suppression + +This diagnostic should not be suppressed. Fix the spec to break the circular union reference, or model the recursion through a model property instead. diff --git a/packages/typespec-client-generator-core/src/diagnostics/union-null.md b/packages/typespec-client-generator-core/src/diagnostics/union-null.md new file mode 100644 index 0000000000..666d3ff51e --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/union-null.md @@ -0,0 +1,22 @@ +This diagnostic is issued when TCGC converts a union whose only possible value is `null`. A union with no non-null alternatives cannot produce a useful SDK type. + +## Impact + +- **Area:** SDK union type conversion. Generation continues with a fallback union shape, but a null-only union cannot become a useful SDK type. +- **Not affected:** Other nullable unions with non-null variants are unaffected. + +## Diagnostic Message + +TCGC reports: + +```text +Cannot have a union containing only null types. +``` + +## ✅ How to Fix + +Add at least one non-null variant to the union or remove the union from the generated client surface. + +## Suppression + +This diagnostic should not be suppressed. Fix the spec so the union has at least one non-null variant, or remove the union from the generated client surface. diff --git a/packages/typespec-client-generator-core/src/diagnostics/unsupported-generic-decorator-arg-type.md b/packages/typespec-client-generator-core/src/diagnostics/unsupported-generic-decorator-arg-type.md new file mode 100644 index 0000000000..ae2fa0fb9f --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/unsupported-generic-decorator-arg-type.md @@ -0,0 +1,26 @@ +This diagnostic is issued when TCGC tries to parse a generic decorator argument whose TypeSpec kind is unsupported. + +## Impact + +- **Area:** Decorator metadata emitted for SDK types. Generation continues, but the unsupported decorator argument is omitted from the metadata available to emitters. +- **Not affected:** The decorator's compile-time effect on the TypeSpec program has already occurred. + +## Diagnostic Message + +TCGC reports: + +```text +Can not parse the arg type for decorator "@service". +``` + +## ✅ How to Fix + +Use supported decorator argument values such as strings, numbers, booleans, values, or enum members. + +## Suppression + +Suppress this warning only if the decorator argument metadata is optional for the target emitter and may be safely omitted. + +```typespec +#suppress "@azure-tools/typespec-client-generator-core/unsupported-generic-decorator-arg-type" "decorator metadata not needed" +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/unsupported-kind.md b/packages/typespec-client-generator-core/src/diagnostics/unsupported-kind.md new file mode 100644 index 0000000000..99d1c0e3d8 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/unsupported-kind.md @@ -0,0 +1,26 @@ +This diagnostic is issued when TCGC encounters a TypeSpec type kind that it does not know how to convert into an SDK type. The value is treated as an unknown SDK type. + +## Impact + +- **Area:** SDK type conversion. Generation continues with an unknown SDK type placeholder for the unsupported TypeSpec kind. +- **Not affected:** Other supported TypeSpec types are converted normally. + +## Diagnostic Message + +TCGC reports: + +```text +Unsupported kind TemplateParameter +``` + +## ✅ How to Fix + +Replace the unsupported construct with a supported model, scalar, enum, union, operation, or model property shape. + +## Suppression + +Suppress this warning only if the unsupported TypeSpec kind is not part of the public generated SDK surface or an unknown SDK type is acceptable. + +```typespec +#suppress "@azure-tools/typespec-client-generator-core/unsupported-kind" "unsupported kind is not emitted" +``` diff --git a/packages/typespec-client-generator-core/src/diagnostics/unsupported-protocol.md b/packages/typespec-client-generator-core/src/diagnostics/unsupported-protocol.md new file mode 100644 index 0000000000..c0bba7d492 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/unsupported-protocol.md @@ -0,0 +1,18 @@ +This diagnostic is issued when TCGC cannot resolve an operation as an HTTP or HTTPS operation. + +## Impact + +- **Area:** Service method generation. Blocks creating a TCGC service operation for a non-HTTP/non-HTTPS protocol operation. +- **Not affected:** The TypeSpec operation can still exist outside TCGC HTTP client generation. + +## Diagnostic Message + +TCGC reports: + +```text +Currently we only support HTTP and HTTPS protocols +``` + +## ✅ How to Fix + +Add appropriate HTTP route and verb decorators to the operation, or remove it from the TCGC-generated client surface. diff --git a/packages/typespec-client-generator-core/src/diagnostics/wrong-client-decorator.md b/packages/typespec-client-generator-core/src/diagnostics/wrong-client-decorator.md new file mode 100644 index 0000000000..3e48aa6711 --- /dev/null +++ b/packages/typespec-client-generator-core/src/diagnostics/wrong-client-decorator.md @@ -0,0 +1,55 @@ +This diagnostic is issued when `@client` is applied through an augment decorator instead of directly on a namespace or interface in `client.tsp`. + +## Impact + +- **Area:** Explicit client declaration. Generation continues, but the augmented `@client` application is ignored because clients must be declared directly. +- **Not affected:** The namespace or interface itself remains in the TypeSpec program. + +## ❌ Incorrect Usage + +```typespec +@service +namespace ServiceNamespace; + +namespace ClientNamespace { + +} +@@client( // `@client` cannot be applied as an augment decorator + ClientNamespace, + { + service: ServiceNamespace, + } +); +``` + +## Diagnostic Message + +TCGC reports: + +```text +@client should decorate namespace or interface in client.tsp +``` + +## ✅ How to Fix + +Place `@client` directly on the namespace or interface declaration that represents the client: + +```typespec +@service +namespace ServiceNamespace; + +@client({ + service: ServiceNamespace, +}) +namespace ClientNamespace { + +} +``` + +## Suppression + +Suppress this warning only during a migration where an augmented `@client` is intentionally ignored and another valid client declaration is used instead. + +```typespec +#suppress "@azure-tools/typespec-client-generator-core/wrong-client-decorator" "augmented @client intentionally ignored" +``` diff --git a/packages/typespec-client-generator-core/src/http.ts b/packages/typespec-client-generator-core/src/http.ts index daf8729a0b..3c70a4ae83 100644 --- a/packages/typespec-client-generator-core/src/http.ts +++ b/packages/typespec-client-generator-core/src/http.ts @@ -307,20 +307,11 @@ function getSdkHttpParameters( const bodyParam = diagnostics.pipe( getSdkHttpParameter(context, tspBody.property, httpOperation.operation, undefined, "body"), ); - if (bodyParam.kind !== "body") { - diagnostics.add( - createDiagnostic({ - code: "unexpected-http-param-type", - target: tspBody.property, - format: { - paramName: tspBody.property.name, - expectedType: "body", - actualType: bodyParam.kind, - }, - }), - ); - return diagnostics.wrap(retval); - } + compilerAssert( + bodyParam.kind === "body", + `Expected parameter "${tspBody.property.name}" to be of type "body", but instead it is of type "${bodyParam.kind}"`, + tspBody.property, + ); retval.bodyParam = bodyParam; } else if (!isNeverOrVoidType(tspBody.type)) { const type = diagnostics.pipe( @@ -659,19 +650,11 @@ export function getSdkHttpParameter( explode: (httpParam as HttpOperationQueryParameter)?.explode, }); } - if (!(isHeader(context.program, param) || location === "header")) { - diagnostics.add( - createDiagnostic({ - code: "unexpected-http-param-type", - target: param, - format: { - paramName: param.name, - expectedType: "path, query, header, or body", - actualType: param.kind, - }, - }), - ); - } + compilerAssert( + isHeader(context.program, param) || location === "header", + `Expected parameter "${param.name}" to be of type "path, query, header, or body", but instead it is of type "${param.kind}"`, + param, + ); return diagnostics.wrap({ ...headerQueryBase, kind: "header", diff --git a/packages/typespec-client-generator-core/src/lib.ts b/packages/typespec-client-generator-core/src/lib.ts index f9a4a245d6..0e08376dae 100644 --- a/packages/typespec-client-generator-core/src/lib.ts +++ b/packages/typespec-client-generator-core/src/lib.ts @@ -1,4 +1,4 @@ -import { createTypeSpecLibrary, JSONSchemaType, paramMessage } from "@typespec/compiler"; +import { createTypeSpecLibrary, fileRef, JSONSchemaType, paramMessage } from "@typespec/compiler"; import { BrandedSdkEmitterOptionsInterface, TCGCEmitterOptions, @@ -139,124 +139,131 @@ const TCGCEmitterOptionsSchema: JSONSchemaType = { }, }; +const diagnosticDocsBaseUrl = + "https://azure.github.io/typespec-azure/docs/libraries/typespec-client-generator-core/reference/diagnostics"; + +/** + * Build the `docs` and `url` fields for a diagnostic from its code. `docs` points at the source + * markdown used to generate the reference page; `url` links to that published page so editors can + * surface it (for example as a clickable diagnostic code). + */ +function doc(code: string) { + return { + docs: fileRef.fromPackageRoot(`src/diagnostics/${code}.md`), + url: `${diagnosticDocsBaseUrl}/${code}`, + }; +} + export const $lib = createTypeSpecLibrary({ name: "@azure-tools/typespec-client-generator-core", diagnostics: { - "client-service": { - severity: "warning", - messages: { - default: paramMessage`Client "${"name"}" is not inside a service namespace. Use @client({service: MyServiceNS})`, - }, - }, "union-null": { + ...doc("union-null"), severity: "warning", messages: { default: "Cannot have a union containing only null types.", }, }, "union-circular": { + ...doc("union-circular"), severity: "warning", messages: { default: "Cannot have a union containing self.", }, }, "invalid-access": { + ...doc("invalid-access"), severity: "error", messages: { default: `Access value must be "public" or "internal".`, }, }, "invalid-usage": { + ...doc("invalid-usage"), severity: "error", messages: { default: `Usage value must be one of: 2 (input), 4 (output), 256 (json), or 512 (xml).`, }, }, "conflicting-multipart-model-usage": { + ...doc("conflicting-multipart-model-usage"), severity: "error", messages: { default: paramMessage`Model '${"modelName"}' cannot be used as both multipart/form-data input and regular body input. You can create a separate model with name 'model ${"modelName"}FormData' extends ${"modelName"} {}`, }, }, - "discriminator-not-constant": { - severity: "error", - messages: { - default: paramMessage`Discriminator ${"discriminator"} has to be constant`, - }, - }, - "discriminator-not-string": { - severity: "warning", - messages: { - default: paramMessage`Value of discriminator ${"discriminator"} has to be a string, not ${"discriminatorValue"}`, - }, - }, "wrong-client-decorator": { + ...doc("wrong-client-decorator"), severity: "warning", messages: { default: "@client should decorate namespace or interface in client.tsp", }, }, "unsupported-kind": { + ...doc("unsupported-kind"), severity: "warning", messages: { default: paramMessage`Unsupported kind ${"kind"}`, }, }, "server-param-not-path": { + ...doc("server-param-not-path"), severity: "error", messages: { default: paramMessage`Template argument ${"templateArgumentName"} is not a path parameter, it is a ${"templateArgumentType"}. It has to be a path.`, }, }, - "unexpected-http-param-type": { - severity: "error", - messages: { - default: paramMessage`Expected parameter "${"paramName"}" to be of type "${"expectedType"}", but instead it is of type "${"actualType"}"`, - }, - }, "multiple-response-types": { + ...doc("multiple-response-types"), severity: "warning", messages: { default: paramMessage`Multiple response types found in operation ${"operation"}. Some emitters might not support returning all of these response types`, }, }, "no-corresponding-method-param": { + ...doc("no-corresponding-method-param"), severity: "error", messages: { default: paramMessage`Missing HTTP operation parameter "${"paramName"}" in method "${"methodName"}". Please check the method definition.`, }, }, "unsupported-protocol": { + ...doc("unsupported-protocol"), severity: "error", messages: { default: "Currently we only support HTTP and HTTPS protocols", }, }, "no-emitter-name": { + ...doc("no-emitter-name"), severity: "warning", messages: { default: "Can not find name for your emitter, please check your emitter name.", }, }, "unsupported-generic-decorator-arg-type": { + ...doc("unsupported-generic-decorator-arg-type"), severity: "warning", messages: { default: paramMessage`Can not parse the arg type for decorator "${"decoratorName"}".`, }, }, "empty-client-name": { + ...doc("empty-client-name"), severity: "warning", messages: { default: `Cannot pass an empty value to the @clientName decorator`, }, }, "override-parameters-mismatch": { + ...doc("override-parameters-mismatch"), severity: "error", messages: { default: paramMessage`Method "${"methodName"}" has different parameters definition from the override operation. Please check the parameter defined in the override operation: "${"checkParameter"}".`, }, }, "duplicate-client-name": { + ...doc("duplicate-client-name"), severity: "error", messages: { default: paramMessage`Client name: "${"name"}" is duplicated in language scope: "${"scope"}"`, @@ -264,6 +271,7 @@ export const $lib = createTypeSpecLibrary({ }, }, "duplicate-client-name-warning": { + ...doc("duplicate-client-name-warning"), severity: "warning", messages: { default: paramMessage`Client name: "${"name"}" is duplicated in language scope: "${"scope"}"`, @@ -271,13 +279,14 @@ export const $lib = createTypeSpecLibrary({ }, }, "client-name-ineffective": { - severity: "warning", + ...doc("client-name-ineffective"), + severity: "error", messages: { - default: paramMessage`Application of @clientName decorator to ${"name"} is not effective`, - override: paramMessage`Application of @clientName decorator to ${"name"} is not effective because it is applied to the override method. Please apply it on the original method definition "${"originalMethodName"}" instead.`, + default: paramMessage`Application of @clientName decorator to ${"name"} is not effective because it is applied to the override method. Please apply it on the original method definition "${"originalMethodName"}" instead.`, }, }, "example-loading": { + ...doc("example-loading"), severity: "warning", messages: { default: paramMessage`Skipped loading invalid example file: ${"filename"}. Error: ${"error"}`, @@ -286,73 +295,64 @@ export const $lib = createTypeSpecLibrary({ }, }, "duplicate-example-file": { + ...doc("duplicate-example-file"), severity: "error", messages: { default: paramMessage`Example file ${"filename"} uses duplicate title '${"title"}' for operationId '${"operationId"}'`, }, }, "example-value-no-mapping": { + ...doc("example-value-no-mapping"), severity: "warning", messages: { default: paramMessage`Value in example file '${"relativePath"}' does not follow its definition:\n${"value"}`, }, }, "flatten-polymorphism": { + ...doc("flatten-polymorphism"), severity: "error", messages: { default: `Cannot flatten property of polymorphic type.`, }, }, "conflict-access-override": { + ...doc("conflict-access-override"), severity: "warning", messages: { default: `@access override conflicts with the access calculated from operation or other @access override.`, }, }, - "duplicate-decorator": { - severity: "warning", - messages: { - default: paramMessage`Decorator ${"decoratorName"} cannot be used twice on the same declaration with same scope.`, - }, - }, "empty-client-namespace": { + ...doc("empty-client-namespace"), severity: "warning", messages: { default: `Cannot pass an empty value to the @clientNamespace decorator`, }, }, - "unexpected-pageable-operation-return-type": { - severity: "error", - messages: { - default: `The response object for the pageable operation is either not a paging model, or is not correctly decorated with @nextLink and @pageItems.`, - }, - }, "invalid-alternate-type": { + ...doc("invalid-alternate-type"), severity: "error", messages: { default: paramMessage`Invalid alternate type. If the source type is Scalar, the alternate type must also be Scalar. Found alternate type kind: '${"kindName"}'`, }, }, "invalid-initialized-by": { + ...doc("invalid-initialized-by"), severity: "error", messages: { default: paramMessage`Invalid 'initializedBy' value. ${"message"}`, }, }, "invalid-deserializeEmptyStringAsNull-target-type": { + ...doc("invalid-deserializeEmptyStringAsNull-target-type"), severity: "error", messages: { default: "@deserializeEmptyStringAsNull can only be applied to `ModelProperty` of type 'string' or a `Scalar` derived from 'string'.", }, }, - "api-version-not-string": { - severity: "warning", - messages: { - default: `Api version must be a string or a string enum`, - }, - }, "invalid-encode-for-collection-format": { + ...doc("invalid-encode-for-collection-format"), severity: "warning", messages: { default: @@ -360,12 +360,14 @@ export const $lib = createTypeSpecLibrary({ }, }, "non-head-bool-response-decorator": { + ...doc("non-head-bool-response-decorator"), severity: "warning", messages: { default: paramMessage`@responseAsBool decorator can only be used on HEAD operations. Will ignore decorator on ${"operationName"}.`, }, }, "require-versioned-service": { + ...doc("require-versioned-service"), severity: "warning", description: "Require a versioned service to use this decorator", messages: { @@ -373,25 +375,29 @@ export const $lib = createTypeSpecLibrary({ }, }, "missing-service-versions": { - severity: "warning", + ...doc("missing-service-versions"), + severity: "error", description: "Missing service versions", messages: { default: paramMessage`The @clientApiVersions decorator is missing one or more versions defined in ${"serviceName"}. Client API must support all service versions to ensure compatibility. Missing versions: ${"missingVersions"}. Please update the client API to support all required service versions.`, }, }, "invalid-client-doc-mode": { + ...doc("invalid-client-doc-mode"), severity: "error", messages: { default: paramMessage`Invalid mode '${"mode"}' for @clientDoc decorator. Valid values are "append" or "replace".`, }, }, "multiple-param-alias": { + ...doc("multiple-param-alias"), severity: "warning", messages: { default: paramMessage`Multiple param aliases applied to '${"originalName"}'. Only the first one '${"firstParamAlias"}' will be used.`, }, }, "client-location-conflict": { + ...doc("client-location-conflict"), severity: "warning", messages: { default: @@ -405,6 +411,7 @@ export const $lib = createTypeSpecLibrary({ }, }, "client-location-wrong-type": { + ...doc("client-location-wrong-type"), severity: "warning", messages: { default: @@ -412,96 +419,112 @@ export const $lib = createTypeSpecLibrary({ }, }, "legacy-hierarchy-building-conflict": { + ...doc("legacy-hierarchy-building-conflict"), severity: "warning", messages: { "property-type-mismatch": paramMessage`@hierarchyBuilding decorator: property '${"propertyName"}' on model '${"childModel"}' has type that does not match the same-named property supplied by the new base chain (rooted at '${"parentModel"}'). The property is dropped from '${"childModel"}' to satisfy the rebase rule (own properties are filtered against the new base chain by name). Consider aligning the types or removing the property from '${"childModel"}'.`, }, }, "legacy-hierarchy-building-circular-reference": { + ...doc("legacy-hierarchy-building-circular-reference"), severity: "error", messages: { default: "@hierarchyBuilding decorator causes recursive base type reference.", }, }, "missing-scope": { - severity: "warning", + ...doc("missing-scope"), + severity: "error", messages: { default: paramMessage`@scope decorator should be applied with ${"decoratorName"} since it is highly likely this is language-specific`, }, }, "required-parameter-scoped-out": { + ...doc("required-parameter-scoped-out"), severity: "warning", messages: { default: paramMessage`Required parameter "${"paramName"}" is scoped out for emitter "${"scope"}". This may cause runtime errors unless the parameter is provided through other means (e.g., custom headers).`, }, }, "external-library-version-mismatch": { + ...doc("external-library-version-mismatch"), severity: "warning", messages: { default: paramMessage`External library version mismatch. There are multiple versions of ${"libraryName"}: ${"versionA"} and ${"versionB"}. Please unify the versions.`, }, }, "external-type-on-model-property": { + ...doc("external-type-on-model-property"), severity: "warning", messages: { default: `@alternateType with external type information cannot be applied to model properties. Please apply it to the type definition itself (Scalar, Model, Enum, or Union) instead.`, }, }, "invalid-mark-as-lro-target": { + ...doc("invalid-mark-as-lro-target"), severity: "warning", messages: { default: paramMessage`@markAsLro decorator can only be applied to operations that return a model. We will ignore this decorator.`, }, }, "mark-as-lro-ineffective": { - severity: "warning", + ...doc("mark-as-lro-ineffective"), + severity: "error", messages: { default: paramMessage`@markAsLro decorator is ineffective since this operation already returns real LRO metadata. Please remove the @markAsLro decorator.`, }, }, "invalid-mark-as-pageable-target": { + ...doc("invalid-mark-as-pageable-target"), severity: "warning", messages: { default: paramMessage`@markAsPageable decorator can only be applied to operations that return a model with a property decorated with @pageItems or a property named 'value'. We will ignore this decorator.`, }, }, "mark-as-pageable-ineffective": { + ...doc("mark-as-pageable-ineffective"), severity: "warning", messages: { default: paramMessage`@markAsPageable decorator is ineffective since this operation is already marked as pageable with @list decorator. Please remove the @markAsPageable decorator.`, }, }, "api-version-undefined": { + ...doc("api-version-undefined"), severity: "warning", messages: { default: paramMessage`The API version specified in the config: "${"version"}" is not defined in service versioning list. Fall back to the latest version.`, }, }, "root-client-missing-service": { + ...doc("root-client-missing-service"), severity: "error", messages: { default: "Root namespace decorated with @client must have service config.", }, }, "invalid-client-service-multiple": { + ...doc("invalid-client-service-multiple"), severity: "error", messages: { default: "`@client` with multiple services is only allowed on `Namespace`.", }, }, "inconsistent-multiple-service": { + ...doc("inconsistent-multiple-service"), severity: "error", messages: { default: "All services must have the same server and auth definitions.", }, }, "inconsistent-multiple-service-dependency": { - severity: "warning", + ...doc("inconsistent-multiple-service-dependency"), + severity: "error", messages: { default: paramMessage`Services merged into client "${"clientName"}" depend on different versions of "${"dependencyName"}": ${"versions"}.`, }, }, "client-option": { + ...doc("client-option"), severity: "warning", messages: { default: @@ -509,6 +532,7 @@ export const $lib = createTypeSpecLibrary({ }, }, "client-option-requires-scope": { + ...doc("client-option-requires-scope"), severity: "warning", messages: { default: @@ -516,42 +540,49 @@ export const $lib = createTypeSpecLibrary({ }, }, "replace-parameter-not-found": { + ...doc("replace-parameter-not-found"), severity: "error", messages: { default: paramMessage`Parameter "${"paramName"}" not found in operation "${"operationName"}".`, }, }, "reorder-parameter-not-found": { + ...doc("reorder-parameter-not-found"), severity: "error", messages: { default: paramMessage`Parameter "${"paramName"}" specified in reorder list not found in operation "${"operationName"}".`, }, }, "reorder-parameter-missing": { + ...doc("reorder-parameter-missing"), severity: "error", messages: { default: paramMessage`Parameter "${"paramName"}" from operation "${"operationName"}" is missing in reorder list.`, }, }, "add-parameter-duplicate": { + ...doc("add-parameter-duplicate"), severity: "error", messages: { default: paramMessage`Parameter "${"paramName"}" already exists in operation "${"operationName"}".`, }, }, "reorder-parameter-duplicate": { + ...doc("reorder-parameter-duplicate"), severity: "error", messages: { default: paramMessage`Parameter "${"paramName"}" appears more than once in the reorder list for operation "${"operationName"}".`, }, }, "remove-parameter-not-found": { + ...doc("remove-parameter-not-found"), severity: "error", messages: { default: paramMessage`Parameter "${"paramName"}" not found in operation "${"operationName"}".`, }, }, "nested-client-service-not-subset": { + ...doc("nested-client-service-not-subset"), severity: "error", messages: { default: @@ -559,6 +590,7 @@ export const $lib = createTypeSpecLibrary({ }, }, "auto-merge-service-conflict": { + ...doc("auto-merge-service-conflict"), severity: "error", messages: { default: "Auto-merging service client must be empty.", diff --git a/packages/typespec-client-generator-core/src/methods.ts b/packages/typespec-client-generator-core/src/methods.ts index 0341bfeb89..fe83c13e1f 100644 --- a/packages/typespec-client-generator-core/src/methods.ts +++ b/packages/typespec-client-generator-core/src/methods.ts @@ -182,23 +182,11 @@ function getSdkPagingServiceMethod { ]); }); -it("warning: inconsistent-multiple-service-dependency", async () => { +it("error: inconsistent-multiple-service-dependency", async () => { const [{ program }, diagnostics] = await SimpleBaseTester.compileAndDiagnose( createClientCustomizationInput( ` @@ -2029,7 +2029,7 @@ it("warning: inconsistent-multiple-service-dependency", async () => { expectDiagnostics(diagnostics, [ { code: "@azure-tools/typespec-client-generator-core/inconsistent-multiple-service-dependency", - severity: "warning", + severity: "error", message: 'Services merged into client "CombineClient" depend on different versions of "SharedLib": "v1", "v2".', }, diff --git a/packages/typespec-client-generator-core/test/validations/package.test.ts b/packages/typespec-client-generator-core/test/validations/package.test.ts index 9efef7a586..2c9a76ba66 100644 --- a/packages/typespec-client-generator-core/test/validations/package.test.ts +++ b/packages/typespec-client-generator-core/test/validations/package.test.ts @@ -78,7 +78,7 @@ it("missing-service-versions", async () => { expectDiagnostics(diagnostics, [ { code: "@azure-tools/typespec-client-generator-core/missing-service-versions", - severity: "warning", + severity: "error", message: `The @clientApiVersions decorator is missing one or more versions defined in My.Service. Client API must support all service versions to ensure compatibility. Missing versions: v1, v2, v3. Please update the client API to support all required service versions.`, }, ]);