Update noDuplicateCase to use tryStaticEvaluation
MODIFY
packages/@romejs/compiler/lint/rules/js/noDuplicateCase.ts
packages/@romejs/compiler/lint/rules/js/noDuplicateCase.ts
import {Path} from "@romejs/compiler";
import {AnyNode} from "@romejs/ast";
import {descriptions} from "@romejs/diagnostics";
-
-function getRawValue(node: AnyNode): string | undefined {
- switch (node.type) {
- case "JSStringLiteral":
- return `'${node.value}'`;
- case "JSBigIntLiteral":
- return `${node.value}n`;
- case "JSNumericLiteral":
- return String(node.value);
- case "JSReferenceIdentifier":
- return String(node.name);
- case "JSNullLiteral":
- return "null";
- }
- return undefined;
-}
+import {DiagnosticsDuplicateHelper} from "@romejs/compiler/lib/DiagnosticsDuplicateHelper";
+import {resolveIndirection, tryStaticEvaluation} from "@romejs/js-ast-utils";
+import prettyFormat from "@romejs/pretty-format";
export default {
name: "noDuplicateCase",
@@ -31,24 +18,34 @@ export default {
const {node, context} = path;
if (node.type === "JSSwitchStatement") {
- const uniqueSwitchCases = new Set();
+ const duplicates = new DiagnosticsDuplicateHelper(
+ context,
+ descriptions.LINT.JS_NO_DUPLICATE_CASE,
+ );
for (const param of node.cases) {
- const rawValue = param.test && getRawValue(param.test);
+ if (param.test === undefined) {
+ continue;
+ }
- if (rawValue) {
- const {test} = param;
+ const test = resolveIndirection(param.test, path.scope);
+ const res = tryStaticEvaluation(test.node, test.scope);
- if (uniqueSwitchCases.has(rawValue)) {
- context.addNodeDiagnostic(
- test,
- descriptions.LINT.JS_NO_DUPLICATE_CASE(rawValue),
- );
+ if (res.bailed && test.node.type === "JSReferenceIdentifier") {
+ // We weren't able to resolve this variable further
+ duplicates.addLocation(test.node.name, param.test.loc);
+ continue;
}
- uniqueSwitchCases.add(rawValue);
+ // No idea what this could be
+ if (res.bailed) {
+ continue;
}
+
+ duplicates.addLocation(prettyFormat(res.value), param.test.loc);
}
+
+ duplicates.process();
}
return node;

MODIFY
packages/@romejs/compiler/lint/rules/testHelpers.ts
packages/@romejs/compiler/lint/rules/testHelpers.ts
@@ -11,7 +11,7 @@ import {parseJS} from "@romejs/js-parser";
import {createUnknownFilePath} from "@romejs/path";
import {DEFAULT_PROJECT_CONFIG} from "@romejs/project";
import {ConstProgramSyntax, ConstSourceType} from "@romejs/ast";
-import {DiagnosticCategory} from "@romejs/diagnostics";
+import {DiagnosticCategory, DiagnosticsProcessor} from "@romejs/diagnostics";
import {printDiagnosticsToString} from "@romejs/cli-diagnostics";
type TestLintOptions = {
@@ -108,21 +108,19 @@ async function testLintExpect(
},
});
- const diagnostics = res.diagnostics.filter((diag) => {
- return diag.description.category === category;
- }).map((diag) => {
- return {
- ...diag,
- location: {
- ...diag.location,
- sourceText: input,
- },
- };
+ const processor = new DiagnosticsProcessor();
+ processor.normalizer.setInlineSourceText("unknown", input);
+ processor.addFilter({
+ test: (diag) => diag.description.category === category,
});
+ processor.addDiagnostics(res.diagnostics);
+
+ const diagnostics = processor.getDiagnostics();
if (expect === "INVALID") {
t.true(diagnostics.length > 0, "Expected test to have diagnostics.");
}
+
if (expect === "VALID") {
t.is(diagnostics.length, 0, "Expected test not to have diagnostics.");
}

MODIFY
packages/@romejs/diagnostics/DiagnosticsNormalizer.ts
packages/@romejs/diagnostics/DiagnosticsNormalizer.ts
@@ -20,12 +20,18 @@ export default class DiagnosticsNormalizer {
) {
this.sourceMaps = sourceMaps;
this.markupOptions = markupOptions || {};
+ this.inlineSourceText = new Map();
this.hasMarkupOptions = markupOptions !== undefined;
}
sourceMaps: undefined | SourceMapConsumerCollection;
markupOptions: MarkupFormatNormalizeOptions;
hasMarkupOptions: boolean;
+ inlineSourceText: Map<string, string>;
+
+ setInlineSourceText(filename: string, sourceText: string) {
+ this.inlineSourceText.set(filename, sourceText);
+ }
normalizeFilename(filename: undefined | string): undefined | string {
const {markupOptions} = this;
@@ -91,9 +97,20 @@ export default class DiagnosticsNormalizer {
}
}
+ const normalizedFilename = this.normalizeFilename(filename);
+
+ let {sourceText} = location;
+ if (sourceText === undefined && filename !== undefined) {
+ sourceText = this.inlineSourceText.get(filename);
+ }
+ if (sourceText === undefined && normalizedFilename !== undefined) {
+ sourceText = this.inlineSourceText.get(normalizedFilename);
+ }
+
return {
...location,
- filename: this.normalizeFilename(filename),
+ sourceText,
+ filename: normalizedFilename,
marker: this.maybeNormalizeMarkup(marker),
start: this.normalizePositionValue(start),
end: this.normalizePositionValue(end),
@@ -193,7 +210,8 @@ export default class DiagnosticsNormalizer {
// Fast path for a common case
if (
!this.hasMarkupOptions &&
- (sourceMaps === undefined || !sourceMaps.hasAny())
+ (sourceMaps === undefined || !sourceMaps.hasAny()) &&
+ this.inlineSourceText.size === 0
) {
return diag;
}

MODIFY
packages/@romejs/js-ast-utils/resolveIndirection.ts
packages/@romejs/js-ast-utils/resolveIndirection.ts
import {AnyNode} from "@romejs/ast";
import {ConstBinding, Scope} from "@romejs/compiler";
-export default function resolveIndirection(node: AnyNode, scope: Scope): AnyNode {
+export default function resolveIndirection(
+ node: AnyNode,
+ scope: Scope,
+): {
+ node: AnyNode;
+ scope: Scope;
+} {
switch (node.type) {
case "JSReferenceIdentifier": {
const binding = scope.getBinding(node.name);
@@ -23,5 +29,5 @@ export default function resolveIndirection(node: AnyNode, scope: Scope): AnyNode
return resolveIndirection(node.expression, scope);
}
- return node;
+ return {node, scope};
}

MODIFY
packages/@romejs/js-ast-utils/tryStaticEvaluation.ts
packages/@romejs/js-ast-utils/tryStaticEvaluation.ts
@@ -160,7 +160,7 @@ export default function tryStaticEvaluation(
let res: EvalResult = BAILED;
- node = resolveIndirection(node, scope);
+ ({node, scope} = resolveIndirection(node, scope));
switch (node.type) {
case "JSUnaryExpression": {

MODIFY
packages/@romejs/pretty-format/index.ts
packages/@romejs/pretty-format/index.ts
@@ -138,7 +138,7 @@ function formatString(val: string): string {
// This function is used by rome-json so make sure it can parse whatever you return here
export function formatNumber(val: bigint | number): string {
if (typeof val === "bigint") {
- return humanizeNumber(val, "_");
+ return `${humanizeNumber(val, "_")}n`;
} else if (isNaN(val)) {
return "NaN";
} else if (Object.is(val, -0)) {

Update noDuplicateCase to use tryStaticEvaluation (#571)