forked from Azure/typespec-azure
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
no-generic-numeric
rule to disable LintDiff `IntegerTypeMustHav…
…eFormat` (Azure#619) Closes Azure#669. **REST API Specs** Azure/azure-rest-api-specs#28731
- Loading branch information
1 parent
bd2d2a9
commit a5d144e
Showing
7 changed files
with
220 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking | ||
changeKind: fix | ||
packages: | ||
- "@azure-tools/typespec-azure-core" | ||
--- | ||
|
||
Add `no-generic-numeric` rule to disable LintDiff `IntegerTypeMustHaveFormat` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
--- | ||
title: "no-generic-numeric" | ||
--- | ||
|
||
```text title="Full name" | ||
@azure-tools/typespec-azure-core/no-generic-numeric | ||
``` | ||
|
||
Azure services should use numeric types that specify the bit-width instead of generic types. | ||
|
||
#### ❌ Incorrect | ||
|
||
```tsp | ||
model Widget { | ||
id: integer; | ||
cost: float; | ||
} | ||
``` | ||
|
||
#### ✅ Correct | ||
|
||
```tsp | ||
model Widget { | ||
id: safeint; | ||
cost: float32; | ||
} | ||
``` | ||
|
||
This includes extending generic numeric types. | ||
|
||
#### ❌ Incorrect | ||
|
||
```tsp | ||
model GenericInteger extends integer; | ||
model Widget { | ||
id: GenericInteger; | ||
} | ||
``` | ||
|
||
#### ✅ Correct | ||
|
||
```tsp | ||
model Widget { | ||
id: safeint; | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 64 additions & 0 deletions
64
packages/typespec-azure-core/src/rules/no-generic-numeric.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { Model, Scalar, 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 noGenericNumericRule = createRule({ | ||
name: "no-generic-numeric", | ||
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-numeric", | ||
messages: { | ||
default: paramMessage`Don't use generic type '${"name"}'. Use a more specific type that specifies the bit size, such as '${"alternative"}' instead.`, | ||
extend: paramMessage`Don't extend 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, | ||
format: { | ||
name: prop.type.name, | ||
alternative: alternatives.get(prop.type.name)!, | ||
}, | ||
}); | ||
} | ||
} | ||
} | ||
}, | ||
scalar: (scalar: Scalar) => { | ||
// if the scalar is the base scalar, then we don't need to check it as it will surface | ||
// in usage (for example: as a model property) | ||
if (disallowList.has(scalar.name)) { | ||
return; | ||
} | ||
let baseScalar: Scalar | undefined = undefined; | ||
while (scalar.baseScalar !== undefined) { | ||
baseScalar = scalar.baseScalar; | ||
break; | ||
} | ||
if (baseScalar === undefined) { | ||
return; | ||
} | ||
if (disallowList.has(baseScalar.name)) { | ||
context.reportDiagnostic({ | ||
target: scalar, | ||
messageId: "extend", | ||
format: { | ||
name: baseScalar.name, | ||
alternative: alternatives.get(baseScalar.name)!, | ||
}, | ||
}); | ||
} | ||
}, | ||
}; | ||
}, | ||
}); |
97 changes: 97 additions & 0 deletions
97
packages/typespec-azure-core/test/rules/no-generic-numeric.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { | ||
BasicTestRunner, | ||
LinterRuleTester, | ||
createLinterRuleTester, | ||
} from "@typespec/compiler/testing"; | ||
import { beforeEach, it } from "vitest"; | ||
import { noGenericNumericRule } from "../../src/rules/no-generic-numeric.js"; | ||
import { createAzureCoreTestRunner } from "../test-host.js"; | ||
|
||
let runner: BasicTestRunner; | ||
let tester: LinterRuleTester; | ||
|
||
beforeEach(async () => { | ||
runner = await createAzureCoreTestRunner({ omitServiceNamespace: true }); | ||
tester = createLinterRuleTester(runner, noGenericNumericRule, "@azure-tools/typespec-azure-core"); | ||
}); | ||
|
||
it("emits a warning for generic numeric types", async () => { | ||
await tester | ||
.expect( | ||
` | ||
namespace Azure.Widget; | ||
model Widget { | ||
prop1: integer; | ||
prop2: numeric; | ||
prop3: float; | ||
prop4: decimal; | ||
} | ||
` | ||
) | ||
.toEmitDiagnostics([ | ||
{ | ||
code: "@azure-tools/typespec-azure-core/no-generic-numeric", | ||
message: | ||
"Don't use generic type 'integer'. Use a more specific type that specifies the bit size, such as 'int32' instead.", | ||
}, | ||
{ | ||
code: "@azure-tools/typespec-azure-core/no-generic-numeric", | ||
message: | ||
"Don't use generic type 'numeric'. Use a more specific type that specifies the bit size, such as 'int32' instead.", | ||
}, | ||
{ | ||
code: "@azure-tools/typespec-azure-core/no-generic-numeric", | ||
message: | ||
"Don't use generic type 'float'. Use a more specific type that specifies the bit size, such as 'float32' instead.", | ||
}, | ||
{ | ||
code: "@azure-tools/typespec-azure-core/no-generic-numeric", | ||
message: | ||
"Don't use generic type 'decimal'. Use a more specific type that specifies the bit size, such as 'float32' instead.", | ||
}, | ||
]); | ||
}); | ||
|
||
it("emits a warning when extending generic numeric types", async () => { | ||
await tester | ||
.expect( | ||
` | ||
namespace Azure.Widget; | ||
scalar GenericInteger extends integer; | ||
scalar GenericNumeric extends numeric; | ||
scalar GenericFloat extends float; | ||
scalar GenericDecimal extends decimal; | ||
model Widget { | ||
prop1: GenericInteger; | ||
prop2: GenericNumeric; | ||
prop3: GenericFloat; | ||
prop4: GenericDecimal; | ||
} | ||
` | ||
) | ||
.toEmitDiagnostics([ | ||
{ | ||
code: "@azure-tools/typespec-azure-core/no-generic-numeric", | ||
message: | ||
"Don't extend generic type 'integer'. Use a more specific type that specifies the bit size, such as 'int32' instead.", | ||
}, | ||
{ | ||
code: "@azure-tools/typespec-azure-core/no-generic-numeric", | ||
message: | ||
"Don't extend generic type 'numeric'. Use a more specific type that specifies the bit size, such as 'int32' instead.", | ||
}, | ||
{ | ||
code: "@azure-tools/typespec-azure-core/no-generic-numeric", | ||
message: | ||
"Don't extend generic type 'float'. Use a more specific type that specifies the bit size, such as 'float32' instead.", | ||
}, | ||
{ | ||
code: "@azure-tools/typespec-azure-core/no-generic-numeric", | ||
message: | ||
"Don't extend generic type 'decimal'. Use a more specific type that specifies the bit size, such as 'float32' instead.", | ||
}, | ||
]); | ||
}); |