diff --git a/dialect/agentscript/src/tests/list-type-parameters.test.ts b/dialect/agentscript/src/tests/list-type-parameters.test.ts new file mode 100644 index 00000000..475525d9 --- /dev/null +++ b/dialect/agentscript/src/tests/list-type-parameters.test.ts @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2026, Salesforce, Inc. + * All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * For full license text, see the LICENSE file in the repo root or https://www.apache.org/licenses/LICENSE-2.0 + */ + +import { expect, test } from 'vitest'; +import { + AGENTSCRIPT_PRIMITIVE_TYPES, + SubscriptExpression, +} from '@agentscript/language'; +import { DiagnosticSeverity } from '@agentscript/types'; +import { AgentScriptSchema } from '../schema.js'; +import { parseWithDiagnostics } from './test-utils.js'; + +function parseListType(parameter: string) { + const result = parseWithDiagnostics( + `variables: + items: mutable list[${parameter}] +`, + AgentScriptSchema + ); + expect(result.value.variables?.get('items')?.type).toBeInstanceOf( + SubscriptExpression + ); + expect(result.diagnostics.filter(d => d.code === 'syntax-error')).toEqual([]); + return result.diagnostics; +} + +test.each(['1', 'True', '"string"', '@variables.other', 'string + number'])( + 'rejects a non-type list parameter: %s', + parameter => { + const errors = parseListType(parameter).filter( + d => d.severity === DiagnosticSeverity.Error + ); + expect(errors).toHaveLength(1); + expect(errors[0].code).toBe('invalid-type-parameter'); + expect(errors[0].message).toContain('Expected a primitive type'); + expect(errors[0].range).toEqual({ + start: { line: 1, character: 24 }, + end: { line: 1, character: 24 + parameter.length }, + }); + } +); + +test.each(AGENTSCRIPT_PRIMITIVE_TYPES.map(type => type.keyword))( + 'accepts a supported list element type: %s', + parameter => { + expect( + parseListType(parameter).filter( + d => d.severity === DiagnosticSeverity.Error + ) + ).toEqual([]); + } +); + +test('still rejects an unknown list element type', () => { + const errors = parseListType('missing').filter( + d => d.severity === DiagnosticSeverity.Error + ); + expect(errors).toHaveLength(1); + expect(errors[0].code).toBe('unknown-type'); +}); + +test('still rejects a nested list element type', () => { + const errors = parseListType('list[string]').filter( + d => d.severity === DiagnosticSeverity.Error + ); + expect(errors).toHaveLength(1); + expect(errors[0].code).toBe('nested-list-type'); +}); diff --git a/packages/language/src/core/typed-map-parser.ts b/packages/language/src/core/typed-map-parser.ts index b692fe0e..6de405c7 100644 --- a/packages/language/src/core/typed-map-parser.ts +++ b/packages/language/src/core/typed-map-parser.ts @@ -775,6 +775,15 @@ export class TypedMapParser { } else { this.emitDeprecatedTypeDiagnostic(elemType, idx as Parsed); } + } else { + this.dc.add( + createDiagnostic( + idx as Parsed, + `Invalid list element type for ${this.blockLabel} ${element.name}. Expected a primitive type (e.g., list[string]).`, + DiagnosticSeverity.Error, + 'invalid-type-parameter' + ) + ); } } }