Fix numeric issues with exponential notation (#4420)

fix #4419
This commit is contained in:
Timothee Guerin 2024-09-12 12:43:02 -07:00 коммит произвёл GitHub
Родитель 4dcc475ca1
Коммит 5fa9d4c27c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
4 изменённых файлов: 51 добавлений и 3 удалений

Просмотреть файл

@ -0,0 +1,8 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: fix
packages:
- "@typespec/compiler"
---
Fix: Numeric values defined with e-notation incorrectly resolved

Просмотреть файл

@ -73,7 +73,7 @@ function parse(original: string): InternalData {
let sign: 1 | -1 = 1;
let n: bigint;
let exp: number | undefined;
let decimal: number;
let decimal: number | undefined = undefined;
if (stringValue[0] === "-") {
start = 1;
sign = -1;
@ -109,6 +109,7 @@ function parse(original: string): InternalData {
}
exp += Number(stringValue.slice(i + 1));
stringValue = stringValue.slice(start, i);
decimal = Math.max(stringValue.length - exp, 0);
} else if (exp === undefined) {
// Integer.
exp = stringValue.length - start;
@ -122,11 +123,24 @@ function parse(original: string): InternalData {
end--;
}
// Only if there is 0 before the decimal point, keeps checking how many 0 there is after it and update the exponent accordingly.
if (start === adjustedPointIndex + 1) {
let cur = adjustedPointIndex;
while (stringValue[cur] === "0" && cur < end) {
cur++;
exp--;
}
}
try {
stringValue = stringValue.slice(0, end);
stringValue = stringValue + "0".repeat(Math.max(exp - stringValue.length, 0)); // add remaining zeros for cases like 3e30
n = BigInt(stringValue);
decimal = n === 0n ? 0 : Math.max(stringValue.length - exp, 0);
if (n === 0n) {
decimal = 0;
} else if (decimal === undefined) {
decimal = Math.max(stringValue.length - Math.max(exp, 0), 0);
}
} catch {
throw new InvalidNumericError(`Invalid numeric value: ${original}`);
}
@ -140,7 +154,7 @@ function stringify(value: InternalData): string {
const n = value.n.toString();
const sign = value.s === -1 ? "-" : "";
const int = value.e === 0 ? "0" : n.slice(0, value.e);
const int = value.e <= 0 ? "0" : n.slice(0, value.e);
const decimal = value.e < n.length ? "." + n.slice(value.e).padStart(value.d, "0") : "";
return sign + int + decimal;
}

Просмотреть файл

@ -65,6 +65,14 @@ describe("parsing", () => {
expectNumericData("123.00456", 12300456n, 3);
});
it("0.1", () => {
expectNumericData("0.1", 1n, 0);
});
it("0.01", () => {
expectNumericData("0.01", 1n, -1);
});
it("large integer (> Number.MAX_SAFE_INTEGER)", () => {
expectNumericData("123456789123456789", 123456789123456789n, 18);
});
@ -201,6 +209,7 @@ describe("asNumber", () => {
["0.0", 0],
["0.1", 0.1],
["0.01", 0.01],
["1e-2", 0.01],
["123", 123],
["123.456", 123.456],
["123.00", 123],

Просмотреть файл

@ -259,6 +259,23 @@ describe("openapi3: models", () => {
});
});
describe("numeric defaults", () => {
it.each([
["0.01", 0.01],
["1e-2", 0.01],
])("%s => %s", async (value, expected) => {
const res = await openApiFor(
`
model Foo {
opt?: float = ${value};
};
`
);
expect(res.components.schemas.Foo.properties.opt.default).toEqual(expected);
});
});
it("emits models extended from models when parent is emitted", async () => {
const res = await openApiFor(
`