OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library servicec.errors; | |
6 | |
7 import 'package:compiler/src/tokens/token.dart' show | |
8 Token; | |
9 | |
10 import 'package:compiler/src/util/characters.dart' show | |
11 $LF; | |
12 | |
13 import 'dart:io' show | |
14 File; | |
15 | |
16 import 'node.dart' show | |
17 FunctionNode, | |
18 FormalNode, | |
19 IdentifierNode, | |
20 ListType, | |
21 FieldNode, | |
22 MemberNode, | |
23 Node, | |
24 NodeVisitor, | |
25 ServiceNode, | |
26 StructNode, | |
27 TopLevelNode, | |
28 TypeNode, | |
29 UnionNode; | |
30 | |
31 enum ErrorTag { | |
32 badField, | |
33 badFieldType, | |
34 badFormal, | |
35 badFunction, | |
36 badListType, | |
37 badPointerType, | |
38 badReturnType, | |
39 badServiceDefinition, | |
40 badSingleFormal, | |
41 badStructDefinition, | |
42 badTopLevel, | |
43 badTypeParameter, | |
44 badUnion, | |
45 cyclicStruct, | |
46 expectedPrimitiveFormal, | |
47 multipleDefinitions, | |
48 multipleUnions, | |
49 serviceStructNameClash, | |
50 undefinedService | |
51 } | |
52 | |
53 // A reverse map from error names to errors. | |
54 final Map<String, ErrorTag> compilerErrorTypes = | |
55 new Map<String, ErrorTag>.fromIterables( | |
56 ErrorTag.values.map((value) => value.toString()), | |
57 ErrorTag.values | |
58 ); | |
59 | |
60 // Error nodes. | |
61 class ServiceErrorNode extends ServiceNode with ErrorNode { | |
62 ServiceErrorNode(IdentifierNode identifier, | |
63 List<FunctionNode> functions, | |
64 Token begin) | |
65 : super(identifier, functions) { | |
66 this.begin = begin; | |
67 tag = ErrorTag.badServiceDefinition; | |
68 } | |
69 } | |
70 | |
71 class StructErrorNode extends StructNode with ErrorNode { | |
72 StructErrorNode(IdentifierNode identifier, | |
73 List<MemberNode> members, | |
74 Token begin) | |
75 : super(identifier, members) { | |
76 this.begin = begin; | |
77 tag = ErrorTag.badStructDefinition; | |
78 } | |
79 } | |
80 | |
81 class TopLevelErrorNode extends TopLevelNode with ErrorNode { | |
82 TopLevelErrorNode(Token begin) | |
83 : super(null) { | |
84 this.begin = begin; | |
85 tag = ErrorTag.badTopLevel; | |
86 } | |
87 | |
88 void accept(NodeVisitor visitor) { | |
89 visitor.visitError(this); | |
90 } | |
91 } | |
92 | |
93 class FunctionErrorNode extends FunctionNode | |
94 with ErrorNode { | |
95 FunctionErrorNode(TypeNode type, | |
96 IdentifierNode identifier, | |
97 List<FormalNode> formals, | |
98 Token begin) | |
99 : super(type, identifier, formals) { | |
100 this.begin = begin; | |
101 tag = ErrorTag.badFunction; | |
102 } | |
103 } | |
104 | |
105 class UnionErrorNode extends UnionNode with ErrorNode { | |
106 UnionErrorNode(List<FieldNode> fields, Token begin) | |
107 : super(fields) { | |
108 this.begin = begin; | |
109 tag = ErrorTag.badUnion; | |
110 } | |
111 } | |
112 | |
113 class FieldErrorNode extends FieldNode with ErrorNode { | |
114 FieldErrorNode(TypeNode type, IdentifierNode identifier, Token begin) | |
115 : super(type, identifier) { | |
116 this.begin = begin; | |
117 tag = ErrorTag.badField; | |
118 } | |
119 } | |
120 | |
121 class FormalErrorNode extends FormalNode with ErrorNode { | |
122 FormalErrorNode(TypeNode type, IdentifierNode identifier, Token begin) | |
123 : super(type, identifier) { | |
124 this.begin = begin; | |
125 tag = ErrorTag.badFormal; | |
126 } | |
127 } | |
128 | |
129 class ListTypeError extends ListType with ErrorNode { | |
130 ListTypeError(IdentifierNode identifier, TypeNode typeParameter, Token begin) | |
131 : super(identifier, typeParameter) { | |
132 this.begin = begin; | |
133 tag = ErrorTag.badListType; | |
134 } | |
135 } | |
136 | |
137 class ErrorNode { | |
138 Token begin; | |
139 ErrorTag tag; | |
140 } | |
141 | |
142 class InternalCompilerError extends Error { | |
143 String message; | |
144 InternalCompilerError(this.message); | |
145 | |
146 String toString() => "InternalCompilerError: $message"; | |
147 } | |
148 | |
149 // Error reporter. | |
150 class ErrorReporter { | |
151 String absolutePath; | |
152 String relativePath; | |
153 String fileContents; | |
154 | |
155 List<int> lineStarts; | |
156 | |
157 ErrorReporter(this.absolutePath, this.relativePath) { | |
158 fileContents = new File(absolutePath).readAsStringSync(); | |
159 | |
160 lineStarts = <int>[-1]; | |
161 | |
162 for (int i = 0; i < fileContents.length; ++i) { | |
163 if ($LF == fileContents.codeUnitAt(i)) { | |
164 lineStarts.add(i); | |
165 } | |
166 } | |
167 } | |
168 | |
169 void report(List<CompilationError> errors) { | |
170 print("Number of errors: ${errors.length}"); | |
171 for (CompilationError error in errors) { | |
172 error.report(this); | |
173 } | |
174 } | |
175 | |
176 void _reportMessage(String message, Token token, String type) { | |
177 if (null != token) { | |
178 int lineNumber = getLineNumber(token); | |
179 int lineOffset = getLineOffset(token, lineNumber); | |
180 print("$relativePath:$lineNumber:$lineOffset: $type: $message"); | |
181 int end = lineNumber < lineStarts.length ? lineStarts[lineNumber] | |
182 : fileContents.length; | |
183 print(fileContents.substring(lineStarts[lineNumber - 1] + 1, end)); | |
184 print(" " * (lineOffset - 1) + "^"); | |
185 } else { | |
186 print("$relativePath: $type: $message"); | |
187 } | |
188 } | |
189 | |
190 void reportError(String message, [Token token]) { | |
191 _reportMessage(message, token, "error"); | |
192 } | |
193 | |
194 void reportWarning(String message, [Token token]) { | |
195 _reportMessage(message, token, "warning"); | |
196 } | |
197 | |
198 void reportInfo(String message, [Token token]) { | |
199 _reportMessage(message, token, "info"); | |
200 } | |
201 | |
202 int getLineNumber(Token token) { | |
203 for (int i = 1; i < lineStarts.length; ++i) { | |
204 if (lineStarts[i] >= token.charOffset) { | |
205 return i; | |
206 } | |
207 } | |
208 return lineStarts.length; | |
209 } | |
210 | |
211 int getLineOffset(Token token, int currentLine) { | |
212 return token.charOffset - lineStarts[currentLine - 1]; | |
213 } | |
214 } | |
215 | |
216 // Compilation errors. | |
217 abstract class CompilationError { | |
218 ErrorTag get tag; | |
219 void report(ErrorReporter reporter); | |
220 } | |
221 | |
222 class UndefinedServiceError extends CompilationError { | |
223 ErrorTag get tag => ErrorTag.undefinedService; | |
224 void report(ErrorReporter reporter) { | |
225 reporter.reportError("There should be at least one service per " + | |
226 "compilation unit."); | |
227 } | |
228 } | |
229 | |
230 class SyntaxError extends CompilationError { | |
231 ErrorNode node; | |
232 ErrorTag get tag => node.tag; | |
233 | |
234 SyntaxError(this.node); | |
235 | |
236 Map<ErrorTag, String> errorMessages = { | |
237 ErrorTag.badField: "Unfinished field declaration.", | |
238 ErrorTag.badFormal: "Unfinished formal argument declaration.", | |
239 ErrorTag.badFunction: "Unfinished function declaration.", | |
240 ErrorTag.badListType: "Unexpected token while parsing type parameter.", | |
241 ErrorTag.badServiceDefinition: "Unfinished service definition.", | |
242 ErrorTag.badStructDefinition: "Unfinished struct definition.", | |
243 ErrorTag.badTopLevel: "Unexpected token while parsing top-level " + | |
244 "definition." | |
245 }; | |
246 | |
247 Map<ErrorTag, String> infoMessages = { | |
248 ErrorTag.badField: null, | |
249 ErrorTag.badFormal: null, | |
250 ErrorTag.badFunction: null, | |
251 ErrorTag.badListType: "Expected a primitive type, a string, or a " + | |
252 "structure as the List type parameter", | |
253 ErrorTag.badServiceDefinition: null, | |
254 ErrorTag.badStructDefinition: null, | |
255 ErrorTag.badTopLevel: "Top-level defintions start with `service` or " + | |
256 "`struct`." | |
257 }; | |
258 | |
259 void report(ErrorReporter reporter) { | |
260 reporter.reportError(errorMessages[node.tag], node.begin); | |
261 if (null != infoMessages[node.tag]) { | |
262 reporter.reportInfo(infoMessages[node.tag], node.begin); | |
263 } | |
264 } | |
265 | |
266 } | |
267 | |
268 class CyclicStructError extends CompilationError { | |
269 Iterable<StructNode> chain; | |
270 ErrorTag get tag => ErrorTag.cyclicStruct; | |
271 | |
272 CyclicStructError(this.chain); | |
273 | |
274 void report(ErrorReporter reporter) { | |
275 String message; | |
276 StructNode struct = chain.first; | |
277 if (chain.length == 1) { | |
278 message = "Struct ${struct.identifier.value} references itself."; | |
279 } else { | |
280 message = "Struct ${struct.identifier.value} has a cyclic reference;"; | |
281 } | |
282 reporter.reportError(message, struct.identifier.token); | |
283 for (StructNode struct in chain) { | |
284 if (struct == chain.first) continue; | |
285 message = "references ${struct.identifier.value}"; | |
286 if (struct == chain.last) { | |
287 message += "."; | |
288 } else { | |
289 message += " which in turn"; | |
290 } | |
291 reporter.reportInfo(message, struct.identifier.token); | |
292 } | |
293 } | |
294 } | |
295 | |
296 class MultipleUnionsError extends CompilationError { | |
297 StructNode struct; | |
298 ErrorTag get tag => ErrorTag.multipleUnions; | |
299 | |
300 MultipleUnionsError(this.struct); | |
301 | |
302 void report(ErrorReporter reporter) { | |
303 reporter.reportError( | |
304 "Struct ${struct.identifier.value} contains multiple unions.", | |
305 struct.identifier.token); | |
306 } | |
307 } | |
308 | |
309 class NotPrimitiveFormalError extends CompilationError { | |
310 FormalNode formal; | |
311 ErrorTag get tag => ErrorTag.expectedPrimitiveFormal; | |
312 | |
313 NotPrimitiveFormalError(this.formal); | |
314 | |
315 void report(ErrorReporter reporter) { | |
316 reporter.reportError( | |
317 "Unexpected type of formal argument '${formal.identifier.value}'.", | |
318 formal.type.identifier.token); | |
319 reporter.reportInfo( | |
320 "All formal arguments should have primitive types when the function " + | |
321 "has more than one formal argument.", | |
322 formal.type.identifier.token); | |
323 } | |
324 } | |
325 | |
326 class MultipleDefinitionsError extends CompilationError { | |
327 IdentifierNode original; | |
328 IdentifierNode redefined; | |
329 ErrorTag get tag => ErrorTag.multipleDefinitions; | |
330 | |
331 MultipleDefinitionsError(this.original, this.redefined); | |
332 | |
333 void report(ErrorReporter reporter) { | |
334 reporter.reportError("Redefined symbol ${redefined.value};", | |
335 redefined.token); | |
336 reporter.reportInfo("Original definition found here.", original.token); | |
337 } | |
338 } | |
339 | |
340 class ServiceStructNameClashError extends CompilationError { | |
341 IdentifierNode original; | |
342 IdentifierNode redefined; | |
343 ErrorTag get tag => ErrorTag.serviceStructNameClash; | |
344 | |
345 ServiceStructNameClashError(this.original, this.redefined); | |
346 | |
347 void report(ErrorReporter reporter) { | |
348 reporter.reportError("Identifier ${redefined.value} used both as a " + | |
349 "service name and as a struct name;", | |
350 redefined.token); | |
351 reporter.reportInfo("Original definition found here.", original.token); | |
352 } | |
353 } | |
354 | |
355 abstract class BadTypeError extends CompilationError { | |
356 TypeNode type; | |
357 String get errorMessage; | |
358 String get infoMessage; | |
359 | |
360 BadTypeError(this.type); | |
361 | |
362 void report(ErrorReporter reporter) { | |
363 reporter.reportError(errorMessage, type.identifier.token); | |
364 reporter.reportInfo(infoMessage, type.identifier.token); | |
365 } | |
366 } | |
367 | |
368 class BadReturnTypeError extends BadTypeError { | |
369 ErrorTag get tag => ErrorTag.badReturnType; | |
370 String get errorMessage => "Unexpected return type."; | |
371 String get infoMessage => "Expected a pointer type or a primitive type as " + | |
372 "the return type of a function."; | |
373 | |
374 BadReturnTypeError(TypeNode type) | |
375 : super(type); | |
376 } | |
377 | |
378 class BadSingleFormalError extends BadTypeError { | |
379 ErrorTag get tag => ErrorTag.badSingleFormal; | |
380 String get errorMessage => "Unexpected type of formal argument."; | |
381 String get infoMessage => "Expected a primitive type or a pointer type for " + | |
382 "a function with just one formal argument."; | |
383 | |
384 BadSingleFormalError(TypeNode type) | |
385 : super(type); | |
386 } | |
387 | |
388 class BadFieldTypeError extends BadTypeError { | |
389 ErrorTag get tag => ErrorTag.badFieldType; | |
390 String get errorMessage => "Unexpected field type."; | |
391 String get infoMessage => | |
392 "A field type should be one of the following:\n" + | |
393 " * a primitive type, e.g. int32;\n" + | |
394 " * a String;\n" + | |
395 " * a struct type, e.g. Foo.\n" + | |
396 " * a pointer to a struct, e.g. Foo*;\n" + | |
397 " * a list of structs, e.g. List<Foo>."; | |
398 | |
399 BadFieldTypeError(TypeNode type) | |
400 : super(type); | |
401 } | |
402 | |
403 class BadPointerTypeError extends BadTypeError { | |
404 ErrorTag get tag => ErrorTag.badPointerType; | |
405 String get errorMessage => "Undefined struct '${type.identifier.value}'."; | |
406 String get infoMessage => "Expected a pointer to a known struct type."; | |
407 | |
408 BadPointerTypeError(TypeNode type) | |
409 : super(type); | |
410 } | |
411 | |
412 class BadListTypeError extends BadTypeError { | |
413 ErrorTag get tag => ErrorTag.badListType; | |
414 String get errorMessage => | |
415 "Unexpected generic type '${type.identifier.value}'."; | |
416 String get infoMessage => "'List' is the only supported generic type."; | |
417 | |
418 BadListTypeError(TypeNode type) | |
419 : super(type); | |
420 } | |
421 | |
422 class BadTypeParameterError extends BadTypeError { | |
423 ErrorTag get tag => ErrorTag.badTypeParameter; | |
424 String get errorMessage => "Unexpected type parameter."; | |
425 String get infoMessage => "Expected a primitive type or a structure as " + | |
426 "the List type parameter."; | |
427 | |
428 BadTypeParameterError(TypeNode type) | |
429 : super(type); | |
430 } | |
OLD | NEW |