Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/expression/definitions/assertion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
NumberType,
BooleanType,
checkSubtype,
typeToString,

Check warning on line 9 in src/expression/definitions/assertion.ts

View workflow job for this annotation

GitHub Actions / Code Hygiene

'typeToString' is defined but never used
array
array,
checkEnum
} from '../types';
import {RuntimeError} from '../runtime_error';
import {typeOf} from '../values';
Expand Down Expand Up @@ -85,11 +86,11 @@
evaluate(ctx: EvaluationContext) {
for (let i = 0; i < this.args.length; i++) {
const value = this.args[i].evaluate(ctx);
const error = checkSubtype(this.type, typeOf(value));
const error = checkSubtype(this.type, typeOf(value)) || checkEnum(this.type, value);
if (!error) {
return value;
} else if (i === this.args.length - 1) {
throw new RuntimeError(`Expected value to be of type ${typeToString(this.type)}, but found ${typeToString(typeOf(value))} instead.`);
throw new RuntimeError(error);
}
}

Expand Down
7 changes: 2 additions & 5 deletions src/expression/definitions/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
StringType,
ColorType,
ResolvedImageType,
enumType,
} from '../types';
import {Formatted, FormattedSection, VERTICAL_ALIGN_OPTIONS, VerticalAlign} from '../types/formatted';

Check warning on line 11 in src/expression/definitions/format.ts

View workflow job for this annotation

GitHub Actions / Code Hygiene

'VerticalAlign' is defined but never used
import {valueToString, typeOf} from '../values';

import type {Expression} from '../expression';
Expand Down Expand Up @@ -72,11 +73,7 @@

let verticalAlign = null;
if (arg['vertical-align']) {
if (typeof arg['vertical-align'] === 'string' && !VERTICAL_ALIGN_OPTIONS.includes(arg['vertical-align'] as VerticalAlign)) {
return context.error(`'vertical-align' must be one of: 'bottom', 'center', 'top' but found '${arg['vertical-align']}' instead.`) as null;
}

verticalAlign = context.parse(arg['vertical-align'], 1, StringType);
verticalAlign = context.parse(arg['vertical-align'], 1, enumType(VERTICAL_ALIGN_OPTIONS));
if (!verticalAlign) return null;
}

Expand Down
4 changes: 3 additions & 1 deletion src/expression/parsing_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export class ParsingContext {
// * The "coalesce" operator, which needs to omit type annotations.
// * String-valued properties (e.g. `text-field`), where coercion is more convenient than assertion.
//
if ((expected.kind === 'string' || expected.kind === 'number' || expected.kind === 'boolean' || expected.kind === 'object' || expected.kind === 'array') && actual.kind === 'value') {
if ((expected.kind === 'string' || expected.kind === 'number' || expected.kind === 'boolean' || expected.kind === 'object' || expected.kind === 'array' || expected.kind === 'enum') && actual.kind === 'value') {
parsed = annotate(parsed, expected, options.typeAnnotation || 'assert');
} else if ((expected.kind === 'projectionDefinition') && (actual.kind === 'string' || actual.kind === 'array')) {
parsed = annotate(parsed, expected, options.typeAnnotation || 'coerce');
Expand All @@ -128,6 +128,8 @@ export class ParsingContext {
parsed = annotate(parsed, expected, options.typeAnnotation || 'coerce');
} else if (expected.kind === 'variableAnchorOffsetCollection' && (actual.kind === 'value' || actual.kind === 'array')) {
parsed = annotate(parsed, expected, options.typeAnnotation || 'coerce');
} else if (expected.kind === 'enum' && actual.kind === 'string') {
parsed = annotate(parsed, expected, options.typeAnnotation || 'assert');
} else if (this.checkSubtype(expected, actual)) {
return null;
}
Expand Down
27 changes: 25 additions & 2 deletions src/expression/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,19 @@ export type VariableAnchorOffsetCollectionTypeT = {
export type EvaluationKind = 'constant' | 'source' | 'camera' | 'composite';

export type Type = NullTypeT | NumberTypeT | StringTypeT | BooleanTypeT | ColorTypeT | ProjectionDefinitionTypeT | ObjectTypeT | ValueTypeT |
ArrayType | ErrorTypeT | CollatorTypeT | FormattedTypeT | PaddingTypeT | ResolvedImageTypeT | VariableAnchorOffsetCollectionTypeT;
ArrayType | ErrorTypeT | CollatorTypeT | FormattedTypeT | PaddingTypeT | ResolvedImageTypeT | VariableAnchorOffsetCollectionTypeT | EnumType;

export interface ArrayType<T extends Type = Type> {
kind: 'array';
itemType: T;
N: number;
}

export interface EnumType {
kind: 'enum';
values: readonly string[];
}

export type NativeType = 'number' | 'string' | 'boolean' | 'null' | 'array' | 'object';

export const NullType = {kind: 'null'} as NullTypeT;
Expand All @@ -77,12 +82,21 @@ export function array<T extends Type>(itemType: T, N?: number | null): ArrayType
};
}

export function enumType(values: readonly string[]): EnumType {
return {
kind: 'enum',
values
};
}

export function typeToString(type: Type): string {
if (type.kind === 'array') {
const itemType = typeToString(type.itemType);
return typeof type.N === 'number' ?
`array<${itemType}, ${type.N}>` :
type.itemType.kind === 'value' ? 'array' : `array<${itemType}>`;
} else if (type.kind === 'enum') {
return `one of: ${type.values.map(v => `"${v}"`).join(', ')};`;
} else {
return type.kind;
}
Expand Down Expand Up @@ -118,6 +132,8 @@ export function checkSubtype(expected: Type, t: Type): string {
(typeof expected.N !== 'number' || expected.N === t.N)) {
return null;
}
} else if (expected.kind === 'enum' && t.kind === 'string') {
return null;
} else if (expected.kind === t.kind) {
return null;
} else if (expected.kind === 'value') {
Expand All @@ -128,7 +144,14 @@ export function checkSubtype(expected: Type, t: Type): string {
}
}

return `Expected ${typeToString(expected)} but found ${typeToString(t)} instead.`;
return `Expected value to be of type ${typeToString(expected)}, but found ${typeToString(t)} instead.`;
}

export function checkEnum(expected: Type, value: unknown): string {
if (expected.kind === 'enum' && typeof value === 'string' && !expected.values.includes(value)) {
return `Expected ${typeToString(expected)} but found "${value}" instead.`;
}
return null;
}

export function isValidType(provided: Type, allowedTypes: Array<Type>): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"errors": [
{
"key": "[2]",
"error": "Expected array<string> but found array<number, 3> instead."
"error": "Expected value to be of type array<string>, but found array<number, 3> instead."
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"errors": [
{
"key": "[2]",
"error": "Expected string but found number instead."
"error": "Expected value to be of type string, but found number instead."
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"errors": [
{
"key": "[3]",
"error": "Expected array<string> but found null instead."
"error": "Expected value to be of type array<string>, but found null instead."
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"errors": [
{
"key": "[1]",
"error": "Expected array<string> but found array instead."
"error": "Expected value to be of type array<string>, but found array instead."
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"errors": [
{
"key": "",
"error": "Expected formatted but found number instead."
"error": "Expected value to be of type formatted, but found number instead."
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"result": "error",
"errors": [
{
"key": "",
"error": "'vertical-align' must be one of: 'bottom', 'center', 'top' but found 'unknown' instead."
"key": "[1]",
"error": "Expected one of: \"bottom\", \"center\", \"top\"; but found \"unknown\" instead."
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"errors": [
{
"key": "",
"error": "Expected resolvedImage but found number instead."
"error": "Expected value to be of type resolvedImage, but found number instead."
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"errors": [
{
"key": "[1]",
"error": "Expected number but found string instead."
"error": "Expected value to be of type number, but found string instead."
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"errors": [
{
"key": "[4]",
"error": "Expected string but found number instead."
"error": "Expected value to be of type string, but found number instead."
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"errors": [
{
"key": "[2]",
"error": "Expected string but found number instead."
"error": "Expected value to be of type string, but found number instead."
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"errors": [
{
"key": "[4]",
"error": "Expected string but found boolean instead."
"error": "Expected value to be of type string, but found boolean instead."
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"errors": [
{
"key": "[4]",
"error": "Expected string but found number instead."
"error": "Expected value to be of type string, but found number instead."
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"errors": [
{
"key": "",
"error": "Expected array<string, 2> but found array<number, 2> instead."
"error": "Expected value to be of type array<string, 2>, but found array<number, 2> instead."
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"errors": [
{
"key": "",
"error": "Expected array<number, 3> but found array<number> instead."
"error": "Expected value to be of type array<number, 3>, but found array<number> instead."
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"errors": [
{
"key": "",
"error": "Expected array<number, 3> but found array<number, 2> instead."
"error": "Expected value to be of type array<number, 3>, but found array<number, 2> instead."
}
]
}
Expand Down
2 changes: 1 addition & 1 deletion test/integration/style-spec/tests/filters.output.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"line": 103
},
{
"message": "layers[12].filter[2]: Expected object but found string instead.",
"message": "layers[12].filter[2]: Expected value to be of type object, but found string instead.",
"line": 131
},
{
Expand Down
4 changes: 2 additions & 2 deletions test/integration/style-spec/tests/functions.output.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,11 @@
"line": 857
},
{
"message": "layers[47].paint.fill-color: Expected color but found number instead.",
"message": "layers[47].paint.fill-color: Expected value to be of type color, but found number instead.",
"line": 898
},
{
"message": "layers[48].paint.fill-color[2]: Expected number but found string instead.",
"message": "layers[48].paint.fill-color[2]: Expected value to be of type number, but found string instead.",
"line": 907
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
"line": 53
},
{
"message": "layers[6].layout.text-field[1]: Expected number but found string instead.",
"message": "layers[6].layout.text-field[1]: Expected value to be of type number, but found string instead.",
"line": 99
},
{
"message": "layers[7].layout.text-field[1][0]: Unknown expression \"Helvetica\". If you wanted a literal array, use [\"literal\", [...]].",
"line": 111
},
{
"message": "layers[10].layout.text-field[1]: Expected array<string> but found number instead.",
"message": "layers[10].layout.text-field[1]: Expected value to be of type array<string>, but found number instead.",
"line": 147
}
]
Loading