typir
Version:
General purpose type checking library
163 lines • 7.21 kB
JavaScript
/******************************************************************************
* Copyright 2024 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { isIndexedTypeConflict, isValueConflict } from '../utils/utils-type-comparison.js';
import { toArray } from '../utils/utils.js';
import { isAssignabilityProblem } from './assignability.js';
import { isTypeEqualityProblem } from './equality.js';
import { isInferenceProblem } from './inference.js';
import { isSubTypeProblem } from './subtype.js';
import { isValidationProblem } from './validation.js';
export class DefaultTypeConflictPrinter {
constructor() {
}
printValueConflict(problem, level = 0) {
let result = `At ${problem.location}, `;
const left = problem.firstValue;
const right = problem.secondValue;
if (left !== undefined && right !== undefined) {
result += `${left} and ${right} do not match.`;
}
else if (left !== undefined && right === undefined) {
result += `${left} on the left has no opposite value on the right to match.`;
}
else if (left === undefined && right !== undefined) {
result += `there is no value on the left to match with ${right} on the right.`;
}
else {
throw new Error();
}
result = this.printIndentation(result, level);
result = this.printSubProblems(result, problem.subProblems, level);
return result;
}
printIndexedTypeConflict(problem, level = 0) {
const left = problem.expected;
const right = problem.actual;
let result = '';
if (problem.propertyName) {
if (problem.propertyIndex) {
result += `For property '${problem.propertyName} at index ${problem.propertyIndex}', `;
}
else {
result += `For property '${problem.propertyName}', `;
}
}
else if (problem.propertyIndex) {
result += `At index ${problem.propertyIndex}, `;
}
else {
result += 'At an unknown location, ';
}
if (left !== undefined && right !== undefined) {
result += `the types '${this.printTypeName(left)}' and '${this.printTypeName(right)}' do not match.`;
}
else if (left !== undefined && right === undefined) {
result += `the type '${this.printTypeName(left)}' on the left has no opposite type on the right to match with.`;
}
else if (left === undefined && right !== undefined) {
result += `there is no type on the left to match with the type '${this.printTypeName(right)}' on the right.`;
}
else {
result += 'both types are unclear.';
}
result = this.printIndentation(result, level);
result = this.printSubProblems(result, problem.subProblems, level);
return result;
}
printAssignabilityProblem(problem, level = 0) {
let result = `The type '${this.printTypeName(problem.source)}' is not assignable to the type '${this.printTypeName(problem.target)}'.`;
result = this.printIndentation(result, level);
result = this.printSubProblems(result, problem.subProblems, level);
return result;
}
printSubTypeProblem(problem, level = 0) {
let result = `The type '${this.printTypeName(problem.superType)}' is no super-type of '${this.printTypeName(problem.subType)}'.`;
result = this.printIndentation(result, level);
result = this.printSubProblems(result, problem.subProblems, level);
return result;
}
printTypeEqualityProblem(problem, level = 0) {
let result = `The types '${this.printTypeName(problem.type1)}' and '${this.printTypeName(problem.type2)}' are not equal.`;
result = this.printIndentation(result, level);
result = this.printSubProblems(result, problem.subProblems, level);
return result;
}
printInferenceProblem(problem, level = 0) {
let result = `While inferring the type for ${this.printLanguageNode(problem.languageNode)}, at ${problem.location}`;
if (problem.inferenceCandidate) {
result += ` of the type '${this.printTypeName(problem.inferenceCandidate)}' as candidate to infer`;
}
result += ', some problems occurred.';
// Since Rules have no name, it is not possible to print problem.rule here.
result = this.printIndentation(result, level);
result = this.printSubProblems(result, problem.subProblems, level);
return result;
}
printValidationProblem(problem, level = 0) {
let result = `While validating ${this.printLanguageNode(problem.languageNode)}, this ${problem.severity} is found: ${problem.message}`.trim();
result = this.printIndentation(result, level);
result = this.printSubProblems(result, problem.subProblems, level);
return result;
}
printTypirProblem(problem, level = 0) {
if (isValueConflict(problem)) {
return this.printValueConflict(problem, level);
}
else if (isIndexedTypeConflict(problem)) {
return this.printIndexedTypeConflict(problem, level);
}
else if (isAssignabilityProblem(problem)) {
return this.printAssignabilityProblem(problem, level);
}
else if (isSubTypeProblem(problem)) {
return this.printSubTypeProblem(problem, level);
}
else if (isTypeEqualityProblem(problem)) {
return this.printTypeEqualityProblem(problem, level);
}
else if (isInferenceProblem(problem)) {
return this.printInferenceProblem(problem, level);
}
else if (isValidationProblem(problem)) {
return this.printValidationProblem(problem, level);
}
else {
throw new Error(`Unhandled typir problem ${problem.$problem}`);
}
}
printTypirProblems(problems, level = 0) {
return problems.map(p => this.printTypirProblem(p, level)).join('\n');
}
printLanguageNode(languageNode, sentenceBegin = false) {
return `${sentenceBegin ? 'T' : 't'}he language node '${languageNode}'`;
}
printTypeName(type) {
return type.getName();
}
printTypeUserRepresentation(type) {
return type.getUserRepresentation();
}
printSubProblems(result, subProblems, level = 0) {
const problems = toArray(subProblems);
if (problems.length >= 1) {
return result + '\n' + this.printTypirProblems(problems, level + 1);
}
else {
return result;
}
}
printIndentation(result, level) {
// Note, that VSCode skips long whitespace in the "Problems" view
if (level >= 1) {
result = `-> ${result}`;
}
for (let i = 2; i <= level; i++) {
result = `-${result}`;
}
return result;
}
}
//# sourceMappingURL=printing.js.map