Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.
8 changes: 5 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -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);
Comment thread
tadelesh marked this conversation as resolved.
```

## 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.
Comment thread
tadelesh marked this conversation as resolved.

```typespec
@service
namespace MyService {
op myOp(name: string): void;
}

model ExtraParams {
description: string;
}
alias Modified = addParameter(MyService.myOp, ExtraParams.description);
```
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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 {

}
}
```
Original file line number Diff line number Diff line change
@@ -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

Comment thread
tadelesh marked this conversation as resolved.
### ❌ 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<T extends FeatureOptInKeys> = {
@clientLocation(Default) // templated parameter moves different concrete types to the same client
@header("x-preview-features")
previewFeatures: T;
};

op getInsights(...WithPreviewHeader<FeatureOptInKeys.insights>): void;
op getSchedules(...WithPreviewHeader<FeatureOptInKeys.schedules>): void;
Comment thread
tadelesh marked this conversation as resolved.
```
Comment thread
tadelesh marked this conversation as resolved.

### 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.
Loading
Loading