Skip to content

Commit 91f8d0b

Browse files
author
iscai-msft
committed
add reserved words test
1 parent 5775163 commit 91f8d0b

8 files changed

Lines changed: 908 additions & 12 deletions

File tree

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,46 @@
11
import { defineLinter } from "@typespec/compiler";
22
import { noUnnamedTypesRule } from "./rules/no-unnamed-types.rule.js";
33
import { propertyNameConflictRule } from "./rules/property-name-conflict.rule.js";
4+
import { csharpReservedWordsRule } from "./rules/reserved-words/csharp.rule.js";
5+
import { javaReservedWordsRule } from "./rules/reserved-words/java.rule.js";
6+
import { javascriptReservedWordsRule } from "./rules/reserved-words/javascript.rule.js";
7+
import { pythonReservedWordsRule } from "./rules/reserved-words/python.rule.js";
48
import { requireClientSuffixRule } from "./rules/require-client-suffix.rule.js";
59

6-
const rules = [requireClientSuffixRule, propertyNameConflictRule, noUnnamedTypesRule];
10+
const rules = [
11+
requireClientSuffixRule,
12+
propertyNameConflictRule,
13+
noUnnamedTypesRule,
14+
csharpReservedWordsRule,
15+
pythonReservedWordsRule,
16+
javaReservedWordsRule,
17+
javascriptReservedWordsRule,
18+
];
719

8-
const csharpRules = [propertyNameConflictRule];
20+
const csharpRules = [propertyNameConflictRule, csharpReservedWordsRule];
21+
const pythonRules = [pythonReservedWordsRule];
22+
const javaRules = [javaReservedWordsRule];
23+
const javascriptRules = [javascriptReservedWordsRule];
24+
25+
function createRuleSet(langRules: typeof rules) {
26+
return {
27+
enable: {
28+
...Object.fromEntries(
29+
langRules.map((rule) => [
30+
`@azure-tools/typespec-client-generator-core/${rule.name}`,
31+
true,
32+
]),
33+
),
34+
},
35+
};
36+
}
937

1038
export const $linter = defineLinter({
1139
rules,
1240
ruleSets: {
13-
"best-practices:csharp": {
14-
enable: {
15-
...Object.fromEntries(
16-
csharpRules.map((rule) => [
17-
`@azure-tools/typespec-client-generator-core/${rule.name}`,
18-
true,
19-
]),
20-
),
21-
},
22-
},
41+
"best-practices:csharp": createRuleSet(csharpRules),
42+
"best-practices:python": createRuleSet(pythonRules),
43+
"best-practices:java": createRuleSet(javaRules),
44+
"best-practices:javascript": createRuleSet(javascriptRules),
2345
},
2446
});
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import {
2+
Enum,
3+
EnumMember,
4+
Model,
5+
ModelProperty,
6+
Operation,
7+
Union,
8+
UnionVariant,
9+
createRule,
10+
paramMessage,
11+
} from "@typespec/compiler";
12+
import { createTCGCContext } from "../../context.js";
13+
import { getLibraryName } from "../../public-utils.js";
14+
import { LanguageReservedWords } from "./words.js";
15+
16+
/**
17+
* Creates a linter rule that warns when identifiers conflict with reserved
18+
* words in a target language. The rule is context-aware: different reserved
19+
* word sets can be specified for models, properties, operations, enum types,
20+
* and enum members.
21+
*
22+
* @param language - The scope string for the language (e.g. "python", "csharp").
23+
* Used with `getLibraryName` to resolve `@clientName` overrides.
24+
* @param displayName - Human-readable language name for diagnostic messages (e.g. "Python", "C#").
25+
* @param reservedWords - Context-specific reserved word sets.
26+
*/
27+
export function createReservedWordRule(
28+
language: string,
29+
displayName: string,
30+
reservedWords: LanguageReservedWords,
31+
) {
32+
return createRule({
33+
name: `reserved-words-${language}`,
34+
description: `Warns when identifiers conflict with ${displayName} reserved words.`,
35+
severity: "warning",
36+
url: `https://azure.github.io/typespec-azure/docs/libraries/typespec-client-generator-core/rules/reserved-words-${language}`,
37+
messages: {
38+
default: paramMessage`'${"name"}' cannot be used as a ${"context"} name since it is a reserved word in ${"language"}. Consider using the @clientName decorator to rename it for ${"language"} code generation.`,
39+
},
40+
create(context) {
41+
const tcgcContext = createTCGCContext(
42+
context.program,
43+
"@azure-tools/typespec-client-generator-core",
44+
{
45+
mutateNamespace: false,
46+
},
47+
);
48+
49+
function check(
50+
target: Model | ModelProperty | Operation | Enum | EnumMember | Union | UnionVariant,
51+
wordSet: ReadonlySet<string>,
52+
contextName: string,
53+
) {
54+
if (typeof target.name !== "string") return;
55+
const effectiveName = getLibraryName(tcgcContext, target, language);
56+
if (wordSet.has(effectiveName)) {
57+
context.reportDiagnostic({
58+
format: { name: effectiveName, language: displayName, context: contextName },
59+
target,
60+
});
61+
}
62+
}
63+
64+
return {
65+
model: (node: Model) => check(node, reservedWords.model, "model"),
66+
modelProperty: (node: ModelProperty) => check(node, reservedWords.property, "property"),
67+
operation: (node: Operation) => check(node, reservedWords.operation, "operation"),
68+
enum: (node: Enum) => {
69+
check(node, reservedWords.enumType, "enum");
70+
for (const member of node.members.values()) {
71+
check(member, reservedWords.enumMember, "enum member");
72+
}
73+
},
74+
union: (node: Union) => check(node, reservedWords.enumType, "union"),
75+
unionVariant: (node: UnionVariant) => check(node, reservedWords.enumMember, "union variant"),
76+
};
77+
},
78+
});
79+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { createReservedWordRule } from "./create-reserved-word-rule.js";
2+
import { csharpReservedWords } from "./words.js";
3+
4+
export const csharpReservedWordsRule = createReservedWordRule(
5+
"csharp",
6+
"C#",
7+
csharpReservedWords,
8+
);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { createReservedWordRule } from "./create-reserved-word-rule.js";
2+
import { javaReservedWords } from "./words.js";
3+
4+
export const javaReservedWordsRule = createReservedWordRule("java", "Java", javaReservedWords);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { createReservedWordRule } from "./create-reserved-word-rule.js";
2+
import { javascriptReservedWords } from "./words.js";
3+
4+
export const javascriptReservedWordsRule = createReservedWordRule(
5+
"javascript",
6+
"JavaScript",
7+
javascriptReservedWords,
8+
);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { createReservedWordRule } from "./create-reserved-word-rule.js";
2+
import { pythonReservedWords } from "./words.js";
3+
4+
export const pythonReservedWordsRule = createReservedWordRule(
5+
"python",
6+
"Python",
7+
pythonReservedWords,
8+
);

0 commit comments

Comments
 (0)