diff --git a/docs/libraries/azure-core/rules/no-generic-types.md b/docs/libraries/azure-core/rules/no-generic-types.md new file mode 100644 index 0000000000..11f0a8bff6 --- /dev/null +++ b/docs/libraries/azure-core/rules/no-generic-types.md @@ -0,0 +1,25 @@ +--- +title: "no-generic-types" +--- + +```text title="Full name" +@azure-tools/typespec-azure-core/no-generic-types +``` + +Azure services should use types which specify the bit-width instead of generic types. + +#### ❌ Incorrect + +```tsp +model Widget { + id: integer; +} +``` + +#### ✅ Correct + +```tsp +model Widget { + id: int32; +} +``` diff --git a/docs/libraries/azure-core/rules/use-standard-integer.md b/docs/libraries/azure-core/rules/use-standard-integer.md deleted file mode 100644 index 6428c6cb6b..0000000000 --- a/docs/libraries/azure-core/rules/use-standard-integer.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: "use-standard-integer" ---- - -```text title="Full name" -@azure-tools/typespec-azure-core/use-standard-integer -``` - -Azure services favor the standard types int32 and int64 (or safeint) when generating code using Autorest. - -#### ❌ Incorrect - -```tsp -model Widget { - id: int8; -} -``` - -#### ✅ Correct - -```tsp -model Widget { - id: int32; -} -``` diff --git a/packages/typespec-autorest/test/formats.test.ts b/packages/typespec-autorest/test/formats.test.ts index 311d3eb124..a60635b72b 100644 --- a/packages/typespec-autorest/test/formats.test.ts +++ b/packages/typespec-autorest/test/formats.test.ts @@ -1,7 +1,7 @@ import { expectDiagnosticEmpty, expectDiagnostics } from "@typespec/compiler/testing"; import { deepStrictEqual } from "assert"; import { describe, it } from "vitest"; -import { diagnoseOpenApiFor, emitOpenApiWithDiagnostics, openApiFor } from "./test-host.js"; +import { diagnoseOpenApiFor, openApiFor } from "./test-host.js"; describe("typespec-autorest: format", () => { it("allows supported formats", async () => { @@ -71,48 +71,4 @@ describe("typespec-autorest: format", () => { ); expectDiagnosticEmpty(diagnostics); }); - - it("ensures certain scalars emit int32 and int64 formats", async () => { - const [res, _] = await emitOpenApiWithDiagnostics( - ` - @service - namespace Test; - - model Widget { - intA: int32; - intB: int64; - intC: safeint; - intD: numeric; - intE: integer; - } - ` - ); - const model = res.definitions!["Widget"]!; - deepStrictEqual(model, { - properties: { - intA: { - format: "int32", - type: "integer", - }, - intB: { - format: "int64", - type: "integer", - }, - intC: { - format: "int64", - type: "integer", - }, - intD: { - format: "int64", - type: "integer", - }, - intE: { - format: "int64", - type: "integer", - }, - }, - required: ["intA", "intB", "intC", "intD", "intE"], - type: "object", - }); - }); }); diff --git a/packages/typespec-azure-core/src/linter.ts b/packages/typespec-azure-core/src/linter.ts index 05d946649d..b07e84844f 100644 --- a/packages/typespec-azure-core/src/linter.ts +++ b/packages/typespec-azure-core/src/linter.ts @@ -13,6 +13,7 @@ import { noEnumRule } from "./rules/no-enum.js"; import { noErrorStatusCodesRule } from "./rules/no-error-status-codes.js"; import { noExplicitRoutesResourceOps } from "./rules/no-explicit-routes-resource-ops.js"; import { noFixedEnumDiscriminatorRule } from "./rules/no-fixed-enum-discriminator.js"; +import { noGenericTypesRule } from "./rules/no-generic-types.js"; import { noNullableRule } from "./rules/no-nullable.js"; import { noOffsetDateTimeRule } from "./rules/no-offsetdatetime.js"; import { operationIdRule } from "./rules/no-operation-id.js"; @@ -32,7 +33,6 @@ import { requireKeyVisibility } from "./rules/require-key-visibility.js"; import { responseSchemaMultiStatusCodeRule } from "./rules/response-schema-multi-status-code.js"; import { rpcOperationRequestBodyRule } from "./rules/rpc-operation-request-body.js"; import { spreadDiscriminatedModelRule } from "./rules/spread-discriminated-model.js"; -import { useStandardInteger } from "./rules/use-standard-integer.js"; import { useStandardNames } from "./rules/use-standard-names.js"; import { useStandardOperations } from "./rules/use-standard-ops.js"; @@ -52,6 +52,7 @@ const rules = [ noExplicitRoutesResourceOps, noFixedEnumDiscriminatorRule, nonBreakingVersioningRule, + noGenericTypesRule, noNullableRule, noOffsetDateTimeRule, noResponseBodyRule, @@ -69,7 +70,6 @@ const rules = [ responseSchemaMultiStatusCodeRule, rpcOperationRequestBodyRule, spreadDiscriminatedModelRule, - useStandardInteger, useStandardNames, useStandardOperations, ]; diff --git a/packages/typespec-azure-core/src/rules/no-generic-types.ts b/packages/typespec-azure-core/src/rules/no-generic-types.ts new file mode 100644 index 0000000000..c88ff7330d --- /dev/null +++ b/packages/typespec-azure-core/src/rules/no-generic-types.ts @@ -0,0 +1,38 @@ +import { Model, createRule, paramMessage } from "@typespec/compiler"; + +const disallowList = new Set(["integer", "numeric", "float", "decimal"]); +const alternatives = new Map([ + ["integer", "int32"], + ["numeric", "int32"], + ["float", "float32"], + ["decimal", "float32"], +]); + +export const noGenericTypesRule = createRule({ + name: "no-generic-types", + description: "Don't use generic types. Use more specific types instead.", + severity: "warning", + url: "https://azure.github.io/typespec-azure/docs/libraries/azure-core/rules/no-generic-types", + messages: { + default: paramMessage`Don't use generic type '${"name"}'. Use a more specific type that specifies the bit size, such as '${"alternative"}' instead.`, + }, + create(context) { + return { + model: (model: Model) => { + for (const [_, prop] of model.properties) { + if (prop.type.kind === "Scalar") { + if (disallowList.has(prop.type.name)) { + context.reportDiagnostic({ + target: prop.type, + format: { + name: prop.type.name, + alternative: alternatives.get(prop.type.name)!, + }, + }); + } + } + } + }, + }; + }, +}); diff --git a/packages/typespec-azure-core/src/rules/use-standard-integer.ts b/packages/typespec-azure-core/src/rules/use-standard-integer.ts deleted file mode 100644 index 00e7724a37..0000000000 --- a/packages/typespec-azure-core/src/rules/use-standard-integer.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Model, createRule } from "@typespec/compiler"; - -export const useStandardInteger = createRule({ - name: "use-standard-integer", - description: "Use only types that map to int32 or int64.", - severity: "warning", - url: "https://azure.github.io/typespec-azure/docs/libraries/azure-core/rules/use-standard-integer", - messages: { - default: "Recommended integer types are 'int32', 'int64' or 'safeint'.", - }, - create(context) { - return { - model: (model: Model) => { - const nonRecommendedInts = [ - "int64", - "int8", - "int16", - "uint8", - "uint16", - "uint32", - "uint64", - ]; - for (const [_, prop] of model.properties) { - if (prop.type.kind === "Scalar") { - if (nonRecommendedInts.includes(prop.type.name)) { - context.reportDiagnostic({ - target: prop.type, - }); - } - } - } - }, - }; - }, -}); diff --git a/packages/typespec-azure-core/test/rules/use-standard-integer.test.ts b/packages/typespec-azure-core/test/rules/no-generic-types.test.ts similarity index 91% rename from packages/typespec-azure-core/test/rules/use-standard-integer.test.ts rename to packages/typespec-azure-core/test/rules/no-generic-types.test.ts index 5b74156606..5e3213f0fa 100644 --- a/packages/typespec-azure-core/test/rules/use-standard-integer.test.ts +++ b/packages/typespec-azure-core/test/rules/no-generic-types.test.ts @@ -4,7 +4,7 @@ import { createLinterRuleTester, } from "@typespec/compiler/testing"; import { beforeEach, it } from "vitest"; -import { useStandardInteger } from "../../src/rules/use-standard-integer.js"; +import { noGenericTypesRule } from "../../src/rules/no-generic-types.js"; import { createAzureCoreTestRunner } from "../test-host.js"; let runner: BasicTestRunner; @@ -12,7 +12,7 @@ let tester: LinterRuleTester; beforeEach(async () => { runner = await createAzureCoreTestRunner({ omitServiceNamespace: true }); - tester = createLinterRuleTester(runner, useStandardInteger, "@azure-tools/typespec-azure-core"); + tester = createLinterRuleTester(runner, noGenericTypesRule, "@azure-tools/typespec-azure-core"); }); it("emits a warning diagnostic for non-standard integer types", async () => {