UNPKG

5.74 kBPlain TextView Raw
1// tslint:disable ban-types
2import {
3 DiagnosticCategory,
4 Node,
5 Symbol,
6 Type,
7 TypeFlags,
8 TypeGuards,
9 ts,
10 Identifier,
11} from 'ts-simple-ast';
12
13import { CompilerDiagnostic } from './CompilerDiagnostic';
14import { DiagnosticCode } from './DiagnosticCode';
15import { Globals, Libs, LibAliases } from './symbols';
16
17import * as typeUtils from './typeUtils';
18
19export class Context {
20 public readonly diagnostics: ts.Diagnostic[] = [];
21
22 constructor(
23 private readonly globals: Globals,
24 private readonly libs: Libs,
25 private readonly libAliases: LibAliases,
26 ) {}
27
28 public reportError(node: Node, message: string, code: DiagnosticCode): void {
29 this.diagnostics.push(
30 new CompilerDiagnostic(node, message, code, DiagnosticCategory.Error),
31 );
32 }
33
34 public reportWarning(
35 node: Node,
36 message: string,
37 code: DiagnosticCode,
38 ): void {
39 this.diagnostics.push(
40 new CompilerDiagnostic(node, message, code, DiagnosticCategory.Warning),
41 );
42 }
43
44 public reportUnsupported(node: Node): void {
45 this.reportError(
46 node,
47 'Unsupported syntax',
48 DiagnosticCode.UNSUPPORTED_SYNTAX,
49 );
50 }
51
52 public reportTypeError(node: Node): void {
53 this.reportError(
54 node,
55 'Could not infer type. Please add an explicit type annotation.',
56 DiagnosticCode.UNKNOWN_TYPE,
57 );
58 }
59
60 public reportTypeWarning(node: Node): void {
61 this.reportWarning(
62 node,
63 'Could not infer type. Deoptimized implementation will be used. Add an explicit type annotation ' +
64 'to optimize the output.',
65 DiagnosticCode.UNKNOWN_TYPE,
66 );
67 }
68
69 public getType(node: Node, required: boolean = false): Type | undefined {
70 let type = this.getNotAnyType(node.getType());
71
72 if (type == null && TypeGuards.isExpression(node)) {
73 type = this.getNotAnyType(node.getContextualType());
74 }
75
76 if (type == null) {
77 if (required) {
78 this.reportTypeError(node);
79 } else {
80 this.reportTypeWarning(node);
81 }
82 }
83
84 return type;
85 }
86
87 public getTypeOfSymbol(
88 symbol: Symbol | undefined,
89 node: Node,
90 required: boolean = false,
91 ): Type | undefined {
92 if (symbol == null) {
93 return undefined;
94 }
95
96 const type = this.getNotAnyType(symbol.getTypeAtLocation(node));
97 if (type == null) {
98 if (required) {
99 this.reportTypeError(node);
100 } else {
101 this.reportTypeWarning(node);
102 }
103 }
104
105 return type;
106 }
107
108 public getSymbol(node: Node, required: boolean = false): Symbol | undefined {
109 const symbol = node.getSymbol();
110 if (symbol == null) {
111 const message = 'Could not determine source symbol.';
112 if (required) {
113 this.reportError(node, message, DiagnosticCode.UNKNOWN_SYMBOL);
114 } else {
115 this.reportWarning(node, message, DiagnosticCode.UNKNOWN_SYMBOL);
116 }
117
118 return undefined;
119 }
120
121 const aliased = symbol.getAliasedSymbol();
122 if (aliased != null) {
123 return aliased;
124 }
125
126 return symbol;
127 }
128
129 public getSymbolForType(
130 node: Node,
131 type: Type | undefined,
132 required: boolean = false,
133 ): Symbol | undefined {
134 if (type == null) {
135 return undefined;
136 }
137
138 const symbol = type.getSymbol();
139 if (symbol == null) {
140 const message = `Could not determine source symbol for type: ${type.getText()}.`;
141 if (required) {
142 this.reportError(node, message, DiagnosticCode.UNKNOWN_SYMBOL);
143 } else {
144 this.reportWarning(node, message, DiagnosticCode.UNKNOWN_SYMBOL);
145 }
146
147 return undefined;
148 }
149
150 const aliased = symbol.getAliasedSymbol();
151 if (aliased != null) {
152 return aliased;
153 }
154
155 return symbol;
156 }
157
158 public assertUnreachable(value: never): never {
159 throw new Error('Should not be reached.');
160 }
161
162 public assertNotNull<T>(value: T | undefined | null): T {
163 if (value == null) {
164 throw new Error('Something went wrong. Unexpected null.');
165 }
166
167 return value;
168 }
169
170 public isOnlyGlobal(
171 node: Node,
172 type: Type | undefined,
173 name: keyof Globals,
174 ): boolean {
175 return (
176 this.isSymbolic(type) &&
177 this.isGlobalSymbol(node, this.getSymbolForType(node, type), name)
178 );
179 }
180
181 public isGlobal(
182 node: Node,
183 type: Type | undefined,
184 name: keyof Globals,
185 ): boolean {
186 return (
187 this.isSymbolic(type) &&
188 this.isGlobalSymbol(node, this.getSymbolForType(node, type), name)
189 );
190 }
191
192 public isGlobalSymbol(
193 node: Node,
194 symbol: Symbol | undefined,
195 name: keyof Globals,
196 ): boolean {
197 return symbol === this.globals[name];
198 }
199
200 public isOnlyLib(
201 node: Node,
202 type: Type | undefined,
203 name: keyof Libs,
204 ): boolean {
205 return (
206 this.isSymbolic(type) &&
207 this.isLibSymbol(node, this.getSymbolForType(node, type), name)
208 );
209 }
210
211 public isLibSymbol(
212 node: Node,
213 symbol: Symbol | undefined,
214 name: keyof Libs,
215 ): boolean {
216 return symbol === this.libs[name];
217 }
218
219 public isLibAlias(
220 identifier: Identifier | undefined,
221 name: keyof LibAliases,
222 ): boolean {
223 if (identifier == null) {
224 return false;
225 }
226
227 return this.libAliases[name].has(identifier);
228 }
229
230 private isSymbolic(type: Type | undefined): boolean {
231 return (
232 type == null ||
233 (!typeUtils.isLiteral(type) &&
234 !typeUtils.isPrimitive(type) &&
235 !typeUtils.isTuple(type) &&
236 !typeUtils.isUnion(type) &&
237 !typeUtils.isIntersection(type))
238 );
239 }
240
241 private getNotAnyType(type: Type | undefined): Type | undefined {
242 // tslint:disable-next-line no-bitwise
243 if (type == null || (type.getFlags() & TypeFlags.Any) !== 0) {
244 return undefined;
245 }
246
247 return type;
248 }
249}