Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
3e7122b
Add ARM use-update-for-patch lint rule
msyyc Oct 10, 2026
5d131d4
Move PATCH SDK naming guidance to TCGC common names
msyyc Oct 10, 2026
abeb615
feat(tcgc): add opt-in DELETE SDK naming rule
msyyc Oct 12, 2026
ef1e589
Address promotion review feedback
msyyc Oct 12, 2026
6475b75
Use ARM templates in DELETE naming rule documentation
msyyc Oct 13, 2026
e3785aa
Merge branch 'main' into promote-lintdiff-delete-in-operation-name
msyyc Oct 13, 2026
2af91d5
refactor(tcgc): share PATCH and PUT SDK verb-prefix checks
msyyc Oct 14, 2026
6f0eb98
refactor(tcgc): share DELETE and PUT SDK verb-prefix checks
msyyc Oct 14, 2026
5a9015b
test(tcgc): remove PATCH version and suppression-only fixtures
msyyc Oct 14, 2026
bdb8b3f
test(tcgc): remove DELETE suppression-only fixture
msyyc Oct 14, 2026
fee3ceb
Merge branch 'main' into promote-lintdiff-patch-in-operation-name
msyyc Oct 14, 2026
30ae8a4
Merge branch 'main' into promote-lintdiff-delete-in-operation-name
msyyc Oct 14, 2026
6fcf466
Share standalone ARM fixtures and isolate DELETE dependency proof
msyyc Oct 15, 2026
90e1e7d
Move native SDK linter emitter isolation to package integration tests
msyyc Oct 15, 2026
ed35661
Merge branch 'main' into promote-lintdiff-patch-in-operation-name
msyyc Oct 15, 2026
862100d
Merge branch 'main' into promote-lintdiff-delete-in-operation-name
msyyc Oct 15, 2026
380a163
Merge branch 'main' into promote-lintdiff-delete-in-operation-name
msyyc Oct 16, 2026
6b9be23
test(tcgc): simplify naming suites and package isolation
msyyc Oct 19, 2026
50c4b74
test(tcgc): simplify DELETE groups and package isolation
msyyc Oct 19, 2026
5fc361c
Merge branch 'main' into promote-lintdiff-delete-in-operation-name
msyyc Oct 19, 2026
8710828
Merge main and preserve rule inventory assertions for #5665
msyyc Oct 19, 2026
d121756
Consolidate PUT, PATCH, and DELETE SDK naming rules
msyyc Oct 20, 2026
e0ee4cd
Merge remote-tracking branch 'origin/main' into promote-lintdiff-dele…
Copilot Oct 20, 2026
a93d695
test(tcgc): remove redundant package isolation guard
msyyc Oct 20, 2026
e564326
Merge branch 'main' into promote-lintdiff-delete-in-operation-name
msyyc Oct 20, 2026
937f5bc
fix(tcgc): simplify verb naming checks and service traversal
msyyc Oct 21, 2026
aaf8b5c
Update packages/typespec-client-generator-core/src/rules/use-correct-…
msyyc Oct 21, 2026
9a8f4f9
docs(tcgc): focus verb naming guidance on spec authors
msyyc Oct 21, 2026
91e0a0f
Merge branch 'main' into promote-lintdiff-delete-in-operation-name
msyyc Oct 22, 2026
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
7 changes: 7 additions & 0 deletions .chronus/changes/register-use-correct-verb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: internal
packages:
- "@azure-tools/typespec-azure-rulesets"
---

Replace the separate SDK verb naming registrations with `@azure-tools/typespec-client-generator-core/use-correct-verb`, disabled by default in the `client-sdk` ruleset.
7 changes: 7 additions & 0 deletions .chronus/changes/use-correct-verb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@azure-tools/typespec-client-generator-core"
---

Consolidate `use-create-for-put`, PATCH naming guidance, and DELETE naming guidance into the opt-in `use-correct-verb` lint rule. Common SDK method names must start with `create` for PUT, `update` for PATCH, and `delete` for DELETE, case-insensitively. Replace `use-create-for-put` in linter configuration and suppressions with `use-correct-verb`; enabling it also checks PATCH and DELETE. Renaming an already-shipped SDK method may be a breaking change.
2 changes: 1 addition & 1 deletion eng/scripts/doc-updater/knowledge/tcgc.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ namespace (@clientNamespace), naming (@clientName), overload, structure (@client
## SDK Method Naming Rules

- `get-operation-name` checks the common TCGC SDK name of concrete GET operations and requires a `get` or `list` prefix. It honors unscoped `@clientName`, ignores emitter-scoped overrides and OpenAPI operation IDs, and skips template declarations/artifacts and non-GET operations.
- `use-create-for-put` checks concrete PUT endpoints and requires the common TCGC SDK name to start with `create`, case-insensitively. It uses the same common-name resolution, applies without requiring ARM provider metadata, and is disabled by default in the `client-sdk` ruleset.
- `use-correct-verb` checks concrete PUT, PATCH, and DELETE endpoints and requires the common TCGC SDK name to start with `create`, `update`, and `delete`, respectively, case-insensitively. It replaces `use-create-for-put`, uses the same common-name resolution, applies without requiring ARM provider metadata, and is disabled by default in the `client-sdk` ruleset.
- Linter-only naming rules do not alter the generated client graph or wire behavior, so their unit tests and generated rule reference pages are the appropriate coverage; do not add Spector carrier scenarios for them.

## isExactName Property (May 2026)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ export default {
"@azure-tools/typespec-client-generator-core/csharp-model-suffix": true,
"@azure-tools/typespec-client-generator-core/use-union-hierarchy": true,
"@azure-tools/typespec-client-generator-core/get-operation-name": false,
"@azure-tools/typespec-client-generator-core/use-correct-verb": false,
},
disable: {
"@azure-tools/typespec-client-generator-core/use-create-for-put":
"Opt-in ARM naming guidance; data-plane PUT methods may use other verbs.",
"@azure-tools/typespec-client-generator-core/csharp-use-standard-acronyms":
"Disabled until the C# emitter handles standard acronym casing.",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,33 @@ describe("expect all rules to be defined", () => {
ruleset.enable?.["@azure-tools/typespec-client-generator-core/get-operation-name"],
false,
);
strictEqual(
ruleset.enable?.["@azure-tools/typespec-client-generator-core/use-correct-verb"],
false,
);
ok(
ruleset.disable?.["@azure-tools/typespec-client-generator-core/csharp-use-standard-acronyms"],
);
});

it("keeps PUT SDK naming guidance opt-in", () => {
const ruleName = "@azure-tools/typespec-client-generator-core/use-create-for-put";
ok($linter.ruleSets?.["client-sdk"].disable?.[ruleName]);
for (const rulesetName of ["data-plane", "resource-manager", "client-sdk"]) {
ok(!$linter.ruleSets?.[rulesetName].enable?.[ruleName]);
it("keeps combined SDK naming guidance opt-in", () => {
const ruleName = "@azure-tools/typespec-client-generator-core/use-correct-verb";
strictEqual($linter.ruleSets?.["client-sdk"].enable?.[ruleName], false);
for (const rulesetName of ["data-plane", "resource-manager"]) {
strictEqual($linter.ruleSets?.[rulesetName].enable?.[ruleName], undefined);
strictEqual($linter.ruleSets?.[rulesetName].disable?.[ruleName], undefined);
}
for (const oldName of ["use-create-for-put", "use-update-for-patch", "use-delete-for-delete"]) {
for (const ruleset of Object.values($linter.ruleSets ?? {})) {
strictEqual(
ruleset.enable?.[`@azure-tools/typespec-client-generator-core/${oldName}`],
undefined,
);
strictEqual(
ruleset.disable?.[`@azure-tools/typespec-client-generator-core/${oldName}`],
undefined,
);
}
}
});

Expand Down
2 changes: 1 addition & 1 deletion packages/typespec-client-generator-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ Available ruleSets:

| Name | Description |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`@azure-tools/typespec-client-generator-core/use-create-for-put`](https://azure.github.io/typespec-azure/docs/libraries/typespec-client-generator-core/rules/use-create-for-put) | ARM PUT SDK method names should use 'create' as the verb prefix. |
| [`@azure-tools/typespec-client-generator-core/use-correct-verb`](https://azure.github.io/typespec-azure/docs/libraries/typespec-client-generator-core/rules/use-correct-verb) | PUT, PATCH, and DELETE SDK method names should start with 'create', 'update', and 'delete', respectively. |
| [`@azure-tools/typespec-client-generator-core/require-client-suffix`](https://azure.github.io/typespec-azure/docs/libraries/typespec-client-generator-core/rules/require-client-suffix) | Client names should end with 'Client'. |
| [`@azure-tools/typespec-client-generator-core/property-name-conflict`](https://azure.github.io/typespec-azure/docs/libraries/typespec-client-generator-core/rules/property-name-conflict) | Avoid naming conflicts between a property and a model of the same name. |
| [`@azure-tools/typespec-client-generator-core/csharp-no-url-suffix`](https://azure.github.io/typespec-azure/docs/libraries/typespec-client-generator-core/rules/csharp-no-url-suffix) | Properties ending with 'Url' should use 'Uri' suffix instead to follow .NET naming conventions. |
Expand Down
4 changes: 2 additions & 2 deletions packages/typespec-client-generator-core/src/linter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { csharpUseStandardAcronymsRule } from "./rules/csharp-use-standard-acron
import { getOperationNameRule } from "./rules/get-operation-name.rule.js";
import { propertyNameConflictRule } from "./rules/property-name-conflict.rule.js";
import { requireClientSuffixRule } from "./rules/require-client-suffix.rule.js";
import { useCreateForPutRule } from "./rules/use-create-for-put.js";
import { useCorrectVerbRule } from "./rules/use-correct-verb.js";
import { useUnionHierarchyRule } from "./rules/use-union-hierarchy.js";

const rules = [
useCreateForPutRule,
useCorrectVerbRule,
requireClientSuffixRule,
propertyNameConflictRule,
csharpNoUrlSuffixRule,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
Use common SDK method names for resource lifecycle operations. Operation names should begin with `create` for PUT, `update` for PATCH,
and `delete` for DELETE. Prefix matching is case-insensitive. GET, POST, HEAD,
and other HTTP verbs are outside this rule.

This guidance applies to both ARM and data-plane services. To fix a warning,
rename the operation or use `@clientName` without a language scope, as shown below.
Changing only a language-specific name or an OpenAPI operation ID does not fix
the common SDK name.

The rule is disabled by default in the `client-sdk` ruleset.

These examples assume `Widget` and `WidgetProperties` are defined ARM resource types.

#### ❌ Incorrect

```typespec
@armResourceOperations
interface Widgets {
replace is ArmResourceCreateOrReplaceAsync<Widget>;
modify is ArmResourcePatchSync<Widget, Properties = WidgetProperties>;
remove is ArmResourceDeleteSync<Widget>;
}
```

#### ✅ Correct

```typespec
@armResourceOperations
interface Widgets {
createOrUpdate is ArmResourceCreateOrReplaceAsync<Widget>;
update is ArmResourcePatchSync<Widget, Properties = WidgetProperties>;
delete is ArmResourceDeleteSync<Widget>;
}
```

## Impact

- **Area:** SDK

Renaming an already-shipped SDK method may introduce a breaking change.
Review compatibility before changing names. An unscoped `@clientName` changes
the common SDK name without renaming the TypeSpec operation:

```typespec
@armResourceOperations
interface Widgets {
@clientName("deleteWidget")
remove is ArmResourceDeleteSync<Widget>;
}
```

## Suppression

For new methods, fix the name or use an unscoped `@clientName`. When preserving
an already-shipped name requires an API-reviewer-approved exception, suppress
the warning on that operation with a justification:

```typespec
@armResourceOperations
interface Widgets {
#suppress "@azure-tools/typespec-client-generator-core/use-correct-verb" "Preserve the already-shipped SDK method name."
remove is ArmResourceDeleteSync<Widget>;
}
```

## Migration
Comment thread
msyyc marked this conversation as resolved.

If you use `use-create-for-put`, replace it with `use-correct-verb` in linter
configuration and suppressions. Enabling the replacement checks PATCH and DELETE
names as well as PUT names.

## LintDiff Equivalent
Comment thread
msyyc marked this conversation as resolved.

| Swagger validator | Native SDK naming requirement |
| -------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| [PutInOperationName](https://github.com/Azure/azure-openapi-validator/blob/main/docs/put-in-operation-name.md) | PUT names start with `create`. |
| [PatchInOperationName](https://github.com/Azure/azure-openapi-validator/blob/main/docs/patch-in-operation-name.md) | PATCH names start with `update`. |
| [DeleteInOperationName](https://github.com/Azure/azure-openapi-validator/blob/main/docs/delete-in-operation-name.md) | DELETE names start with `delete`. |
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import {
createRule,
fileRef,
isTemplateDeclaration,
isTemplateDeclarationOrInstance,
listServices,
paramMessage,
type Namespace,
} from "@typespec/compiler";
import { getHttpOperation } from "@typespec/http";
import { createTCGCContext } from "../context.js";
import { AllScopes } from "../internal-utils.js";
import { getLibraryName } from "../public-utils.js";

const verbPrefixes = { put: "create", patch: "update", delete: "delete" };

export const useCorrectVerbRule = createRule({
name: "use-correct-verb",
description:
"PUT, PATCH, and DELETE SDK method names should start with 'create', 'update', and 'delete', respectively.",
severity: "warning",
url: "https://azure.github.io/typespec-azure/docs/libraries/typespec-client-generator-core/rules/use-correct-verb",
docs: fileRef.fromPackageRoot("src/rules/use-correct-verb.md"),
messages: {
default: paramMessage`${"verb"} SDK method name '${"operationName"}' should start with '${"prefix"}'. Note: If you have already shipped an SDK on top of this spec, fixing this warning may introduce a breaking change.`,
},
create(context) {
const tcgcContext = createTCGCContext(
context.program,
"@azure-tools/typespec-client-generator-core",
{ mutateNamespace: false },
);
const serviceNamespaces = new Set(listServices(context.program).map((service) => service.type));
const globalNamespace = context.program.getGlobalNamespaceType();

return {
operation: (operation) => {
if (isTemplateDeclarationOrInstance(operation)) return;
const container = operation.interface ?? operation.namespace;
const namespace = operation.interface?.namespace ?? operation.namespace;
if (!container || !namespace || container.operations.get(operation.name) !== operation) {
return;
}
if (
operation.interface &&
(isTemplateDeclaration(operation.interface) ||
namespace.interfaces.get(operation.interface.name) !== operation.interface)
) {
return;
}

// HTTP's implicit global service is nonrecursive, including only direct interfaces.
let eligible =
namespace === globalNamespace &&
(serviceNamespaces.size === 0 || serviceNamespaces.has(globalNamespace));
for (
let ancestor: Namespace | undefined = namespace;
ancestor && ancestor !== globalNamespace;
ancestor = ancestor.namespace
) {
if (serviceNamespaces.has(ancestor)) {
eligible = true;
break;
}
}
if (!eligible) return;

const [{ verb }] = getHttpOperation(context.program, operation);
if (verb !== "put" && verb !== "patch" && verb !== "delete") return;
const prefix = verbPrefixes[verb];
const operationName = getLibraryName(tcgcContext, operation, AllScopes);
if (!operationName.toLowerCase().startsWith(prefix)) {
context.reportDiagnostic({
target: operation,
format: { verb: verb.toUpperCase(), operationName, prefix },
});
}
},
};
},
});

This file was deleted.

This file was deleted.

Loading
Loading