From b992e623c83e67e44d71d3bcd6894da8578c8dba Mon Sep 17 00:00:00 2001 From: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com> Date: Tue, 15 Sep 2026 00:26:40 -0700 Subject: [PATCH] fix(psl): describe sort-argument misuse in index field lists Parsing @@index([created_at(sort: Desc)]) failed with the cryptic 'Expected a field name' because fieldRef only accepts bare identifiers while a sort annotation parses as a FunctionCallAst. Detect that shape and explain that field lists take bare field names without arguments. Fixes prisma/orm#30270 --- .../attribute-spec/combinators/field-ref.ts | 21 ++++++++++++++- .../test/attribute-spec-combinators.test.ts | 14 ++++++++++ .../contract-psl/test/interpreter.test.ts | 26 +++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/combinators/field-ref.ts b/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/combinators/field-ref.ts index 952c247e1d24..ed447f2c39df 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/combinators/field-ref.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/combinators/field-ref.ts @@ -2,6 +2,7 @@ import type { PslDiagnostic } from '@internal/framework-components/psl-ast'; import { notOk, ok, type Result } from '@internal/utils/result'; import type { ModelSymbol } from '../../symbol-table'; import type { ExpressionAst } from '../../syntax/ast/expressions'; +import { FunctionCallAst } from '../../syntax/ast/expressions'; import { IdentifierAst } from '../../syntax/ast/identifier'; import type { AttributeCtx, @@ -12,6 +13,24 @@ import type { } from '../types'; import { leafDiagnostic } from './diagnostic'; +/** + * A function call where a field name belongs is almost always a Prisma-style + * sort annotation (`field(sort: Desc)`). Name the real problem — arguments + * are not accepted in field lists — instead of the cryptic "Expected a field + * name". + */ +function fieldNameProblem(arg: ExpressionAst): string { + const call = FunctionCallAst.cast(arg.syntax); + const callee = call?.path() ?? []; + if (callee.length === 1 && callee[0] !== undefined) { + return ( + `Expected a bare field name, but found "${callee[0]}(...)" — ` + + 'field lists do not accept arguments, so (sort: Asc/Desc) is not supported here.' + ); + } + return 'Expected a field name'; +} + function parseFieldName( arg: ExpressionAst, ctx: AttributeCtx, @@ -19,7 +38,7 @@ function parseFieldName( ): Result { const identifier = IdentifierAst.cast(arg.syntax); if (identifier === undefined) { - return notOk([leafDiagnostic(ctx, arg, 'Expected a field name')]); + return notOk([leafDiagnostic(ctx, arg, fieldNameProblem(arg))]); } const name = identifier.name(); if (name === undefined) { diff --git a/packages/1-framework/2-authoring/psl-parser/test/attribute-spec-combinators.test.ts b/packages/1-framework/2-authoring/psl-parser/test/attribute-spec-combinators.test.ts index 0459c3c82bc2..55dbde309ccf 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/attribute-spec-combinators.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/attribute-spec-combinators.test.ts @@ -596,6 +596,20 @@ describe('fieldRef', () => { expect(result.ok).toBe(false); if (!result.ok) expect(result.failure[0]?.code).toBe('PSL_INVALID_ATTRIBUTE_SYNTAX'); }); + + it('names the sort-argument problem instead of "Expected a field name"', () => { + const { expr, ctx } = argOf('id(sort: Desc)'); + + const result = fieldRef().parse(expr, ctx); + + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.failure).toHaveLength(1); + expect(result.failure[0]?.code).toBe('PSL_INVALID_ATTRIBUTE_SYNTAX'); + expect(result.failure[0]?.message).toMatch(/sort/i); + expect(result.failure[0]?.message).not.toBe('Expected a field name'); + } + }); }); describe('entityRef', () => { diff --git a/packages/2-sql/2-authoring/contract-psl/test/interpreter.test.ts b/packages/2-sql/2-authoring/contract-psl/test/interpreter.test.ts index a5da4251deb6..0d9320d8b020 100644 --- a/packages/2-sql/2-authoring/contract-psl/test/interpreter.test.ts +++ b/packages/2-sql/2-authoring/contract-psl/test/interpreter.test.ts @@ -813,6 +813,32 @@ model OrderItem { indexes: [{ columns: ['body'] }], }); }); + + it('explains sort annotations in @@index field lists instead of "Expected a field name"', () => { + const document = symbolTableInputFromParseArgs({ + schema: `model Item { + id Int @id + createdAt DateTime + @@index([createdAt(sort: Desc)]) +}`, + sourceId: 'schema.prisma', + }); + + const result = interpretPslDocumentToSqlContract({ + ...document, + controlMutationDefaults: builtinControlMutationDefaults, + }); + expect(result.ok).toBe(false); + if (result.ok) return; + expect( + result.failure.diagnostics.some( + (d) => + d.code === 'PSL_INVALID_ATTRIBUTE_SYNTAX' && + /sort/i.test(d.message) && + d.message !== 'Expected a field name', + ), + ).toBe(true); + }); }); describe('per-target namespace resolution', () => {