Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(451)

Side by Side Diff: pkg/front_end/lib/src/fasta/kernel/body_builder.dart

Issue 2800083002: Complain about incorrect this/super constructor initializers. (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library fasta.body_builder; 5 library fasta.body_builder;
6 6
7 import '../fasta_codes.dart' 7 import '../fasta_codes.dart'
8 show FastaMessage, codeExpectedButGot, codeExpectedFunctionBody; 8 show FastaMessage, codeExpectedButGot, codeExpectedFunctionBody;
9 9
10 import '../parser/parser.dart' show FormalParameterType, optional; 10 import '../parser/parser.dart' show FormalParameterType, optional;
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
72 final Map<String, FieldInitializer> fieldInitializers = 72 final Map<String, FieldInitializer> fieldInitializers =
73 <String, FieldInitializer>{}; 73 <String, FieldInitializer>{};
74 74
75 final Scope enclosingScope; 75 final Scope enclosingScope;
76 76
77 final bool isDartLibrary; 77 final bool isDartLibrary;
78 78
79 @override 79 @override
80 final Uri uri; 80 final Uri uri;
81 81
82 /// Only used when [member] is a constructor. It tracks if an implicit super
83 /// initializer is needed.
84 ///
85 /// An implicit super initializer isn't needed
86 ///
87 /// 1. if the current class is Object,
88 /// 2. if there is an explicit super initializer,
89 /// 3. if there is a redirecting (this) initializer, or
90 /// 4. if an initializer will always throw.
Paul Berry 2017/04/07 15:56:38 It sounds like you're talking about a case like th
ahe 2017/04/07 18:19:18 Will do.
91 bool needsImplicitSuperInitializer;
92
82 Scope formalParameterScope; 93 Scope formalParameterScope;
83 94
84 bool inInitializer = false; 95 bool inInitializer = false;
85 96
86 bool inCatchClause = false; 97 bool inCatchClause = false;
87 98
88 bool inCatchBlock = false; 99 bool inCatchBlock = false;
89 100
90 int functionNestingLevel = 0; 101 int functionNestingLevel = 0;
91 102
(...skipping 19 matching lines...) Expand all
111 Scope scope, 122 Scope scope,
112 this.formalParameterScope, 123 this.formalParameterScope,
113 this.hierarchy, 124 this.hierarchy,
114 this.coreTypes, 125 this.coreTypes,
115 this.classBuilder, 126 this.classBuilder,
116 this.isInstanceMember, 127 this.isInstanceMember,
117 this.uri) 128 this.uri)
118 : enclosingScope = scope, 129 : enclosingScope = scope,
119 library = library, 130 library = library,
120 isDartLibrary = library.uri.scheme == "dart", 131 isDartLibrary = library.uri.scheme == "dart",
132 needsImplicitSuperInitializer =
133 coreTypes.objectClass != classBuilder?.cls,
121 super(scope); 134 super(scope);
122 135
123 bool get hasParserError => recoverableErrors.isNotEmpty; 136 bool get hasParserError => recoverableErrors.isNotEmpty;
124 137
125 bool get inConstructor { 138 bool get inConstructor {
126 return functionNestingLevel == 0 && member is KernelConstructorBuilder; 139 return functionNestingLevel == 0 && member is KernelConstructorBuilder;
127 } 140 }
128 141
129 bool get isInstanceContext { 142 bool get isInstanceContext {
130 return isInstanceMember || member is KernelConstructorBuilder; 143 return isInstanceMember || member is KernelConstructorBuilder;
(...skipping 247 matching lines...) Expand 10 before | Expand all | Expand 10 after
378 debugEvent("endInitializer"); 391 debugEvent("endInitializer");
379 assert(!inInitializer); 392 assert(!inInitializer);
380 final member = this.member; 393 final member = this.member;
381 var node = pop(); 394 var node = pop();
382 Initializer initializer; 395 Initializer initializer;
383 if (node is Initializer) { 396 if (node is Initializer) {
384 initializer = node; 397 initializer = node;
385 } else if (node is FastaAccessor) { 398 } else if (node is FastaAccessor) {
386 initializer = node.buildFieldInitializer(fieldInitializers); 399 initializer = node.buildFieldInitializer(fieldInitializers);
387 } else if (node is ConstructorInvocation) { 400 } else if (node is ConstructorInvocation) {
388 initializer = new SuperInitializer(node.target, node.arguments); 401 initializer =
402 buildSuperInitializer(node.target, node.arguments, token.charOffset);
389 } else { 403 } else {
390 if (node is! Throw) { 404 if (node is! Throw) {
405 // TODO(ahe): This is probably an internal error.
Paul Berry 2017/04/07 15:56:38 Why is this a TODO?
ahe 2017/04/07 18:19:18 Because I want to try inserting a call to internal
406 needsImplicitSuperInitializer = false;
391 node = wrapInvalid(node); 407 node = wrapInvalid(node);
392 } 408 }
393 initializer = 409 initializer = buildInvalidIntializer(node, token.charOffset);
394 new LocalInitializer(new VariableDeclaration.forValue(node));
395 } 410 }
396 if (member is KernelConstructorBuilder) { 411 if (member is KernelConstructorBuilder) {
397 member.addInitializer(initializer); 412 member.addInitializer(initializer);
398 } else { 413 } else {
399 addCompileTimeError( 414 addCompileTimeError(
400 token.charOffset, "Can't have initializers: ${member.name}"); 415 token.charOffset, "Can't have initializers: ${member.name}");
401 } 416 }
402 } 417 }
403 418
404 @override 419 @override
405 void handleNoInitializers() { 420 void handleNoInitializers() {
406 debugEvent("NoInitializers"); 421 debugEvent("NoInitializers");
407 } 422 }
408 423
409 @override 424 @override
410 void endInitializers(int count, Token beginToken, Token endToken) { 425 void endInitializers(int count, Token beginToken, Token endToken) {
411 debugEvent("Initializers"); 426 debugEvent("Initializers");
412 } 427 }
413 428
414 @override 429 @override
415 void finishFunction( 430 void finishFunction(
416 FormalParameters formals, AsyncMarker asyncModifier, Statement body) { 431 FormalParameters formals, AsyncMarker asyncModifier, Statement body) {
417 debugEvent("finishFunction"); 432 debugEvent("finishFunction");
418 KernelFunctionBuilder builder = member; 433 KernelFunctionBuilder builder = member;
419 if (builder is KernelConstructorBuilder) {
420 if (asyncModifier != AsyncMarker.Sync) {
421 // TODO(ahe): Change this to a null check.
422 addCompileTimeError(body?.fileOffset,
423 "Can't be marked as ${asyncModifier}: ${builder.name}");
424 }
425 } else if (builder is KernelProcedureBuilder) {
426 builder.asyncModifier = asyncModifier;
427 } else {
428 internalError("Unhandled: ${builder.runtimeType}");
429 }
430 builder.body = body; 434 builder.body = body;
431 if (formals?.optional != null) { 435 if (formals?.optional != null) {
432 Iterator<FormalParameterBuilder> formalBuilders = 436 Iterator<FormalParameterBuilder> formalBuilders =
433 builder.formals.skip(formals.required.length).iterator; 437 builder.formals.skip(formals.required.length).iterator;
434 for (VariableDeclaration parameter in formals.optional.formals) { 438 for (VariableDeclaration parameter in formals.optional.formals) {
435 bool hasMore = formalBuilders.moveNext(); 439 bool hasMore = formalBuilders.moveNext();
436 assert(hasMore); 440 assert(hasMore);
437 VariableDeclaration realParameter = formalBuilders.current.target; 441 VariableDeclaration realParameter = formalBuilders.current.target;
438 Expression initializer = parameter.initializer ?? new NullLiteral(); 442 Expression initializer = parameter.initializer ?? new NullLiteral();
439 realParameter.initializer = initializer..parent = realParameter; 443 realParameter.initializer = initializer..parent = realParameter;
440 } 444 }
441 } 445 }
446 if (builder is KernelConstructorBuilder) {
447 finishConstructor(builder, asyncModifier);
448 } else if (builder is KernelProcedureBuilder) {
449 builder.asyncModifier = asyncModifier;
450 } else {
451 internalError("Unhandled: ${builder.runtimeType}");
452 }
453 }
454
455 void finishConstructor(
456 KernelConstructorBuilder builder, AsyncMarker asyncModifier) {
457 /// Quotes below are from [Dart Programming Language Specification, 4th
458 /// Edition](
459 /// https://ecma-international.org/publications/files/ECMA-ST/ECMA-408.pdf).
460 assert(builder == member);
461 Constructor constructor = builder.constructor;
462 if (asyncModifier != AsyncMarker.Sync) {
463 // TODO(ahe): Change this to a null check.
464 int offset = builder.body?.fileOffset ?? builder.charOffset;
465 constructor.initializers.add(buildInvalidIntializer(
466 buildCompileTimeError(
467 "A constructor can't be '${asyncModifier}'.", offset),
468 offset));
469 }
470 if (needsImplicitSuperInitializer) {
471 /// >If no superinitializer is provided, an implicit superinitializer
472 /// >of the form super() is added at the end of k’s initializer list,
473 /// >unless the enclosing class is class Object.
474 Constructor superTarget = lookupConstructor(emptyName, isSuper: true);
475 Initializer initializer;
476 Arguments arguments = new Arguments.empty();
477 if (superTarget == null ||
478 !checkArguments(
479 superTarget.function, arguments, const <TypeParameter>[])) {
480 String superclass = classBuilder.supertype.fullNameForErrors;
481 initializer = buildInvalidIntializer(
482 buildCompileTimeError(
483 "'$superclass' has no constructor that takes no arguments.",
Paul Berry 2017/04/07 15:56:38 Given the following code: class C { C.foo(); }
ahe 2017/04/07 18:19:18 Will do. Those message are much better.
484 builder.charOffset),
485 builder.charOffset);
486 } else {
487 initializer =
488 buildSuperInitializer(superTarget, arguments, builder.charOffset);
489 }
490 constructor.initializers.add(initializer);
491 }
492 setParents(constructor.initializers, constructor);
493 if (constructor.function.body == null) {
494 /// >If a generative constructor c is not a redirecting constructor
495 /// >and no body is provided, then c implicitly has an empty body {}.
496 /// We use an empty statement instead.
497 constructor.function.body = new EmptyStatement();
498 constructor.function.body.parent = constructor.function;
499 }
442 } 500 }
443 501
444 @override 502 @override
445 void endExpressionStatement(Token token) { 503 void endExpressionStatement(Token token) {
446 debugEvent("ExpressionStatement"); 504 debugEvent("ExpressionStatement");
447 push(new ExpressionStatement(popForEffect())); 505 push(new ExpressionStatement(popForEffect()));
448 } 506 }
449 507
450 @override 508 @override
451 void endArguments(int count, Token beginToken, Token endToken) { 509 void endArguments(int count, Token beginToken, Token endToken) {
(...skipping 1272 matching lines...) Expand 10 before | Expand all | Expand 10 after
1724 return new ConstructorInvocation(target, arguments) 1782 return new ConstructorInvocation(target, arguments)
1725 ..isConst = isConst 1783 ..isConst = isConst
1726 ..fileOffset = charOffset; 1784 ..fileOffset = charOffset;
1727 } else { 1785 } else {
1728 return new StaticInvocation(target, arguments) 1786 return new StaticInvocation(target, arguments)
1729 ..isConst = isConst 1787 ..isConst = isConst
1730 ..fileOffset = charOffset; 1788 ..fileOffset = charOffset;
1731 } 1789 }
1732 } 1790 }
1733 1791
1792 @override
1734 bool checkArguments(FunctionNode function, Arguments arguments, 1793 bool checkArguments(FunctionNode function, Arguments arguments,
1735 List<TypeParameter> typeParameters) { 1794 List<TypeParameter> typeParameters) {
1736 if (arguments.positional.length < function.requiredParameterCount || 1795 if (arguments.positional.length < function.requiredParameterCount ||
1737 arguments.positional.length > function.positionalParameters.length) { 1796 arguments.positional.length > function.positionalParameters.length) {
1738 return false; 1797 return false;
1739 } 1798 }
1740 Map<String, VariableDeclaration> names; 1799 Map<String, VariableDeclaration> names;
1741 if (function.namedParameters.isNotEmpty) { 1800 if (function.namedParameters.isNotEmpty) {
1742 names = <String, VariableDeclaration>{}; 1801 names = <String, VariableDeclaration>{};
1743 for (VariableDeclaration parameter in function.namedParameters) { 1802 for (VariableDeclaration parameter in function.namedParameters) {
(...skipping 678 matching lines...) Expand 10 before | Expand all | Expand 10 after
2422 Builder constructor = library.loader.getAbstractClassInstantiationError(); 2481 Builder constructor = library.loader.getAbstractClassInstantiationError();
2423 return new Throw(buildStaticInvocation(constructor.target, 2482 return new Throw(buildStaticInvocation(constructor.target,
2424 new Arguments(<Expression>[new StringLiteral(className)]))); 2483 new Arguments(<Expression>[new StringLiteral(className)])));
2425 } 2484 }
2426 2485
2427 Statement buildCompileTimeErrorStatement(error, [int charOffset = -1]) { 2486 Statement buildCompileTimeErrorStatement(error, [int charOffset = -1]) {
2428 return new ExpressionStatement(buildCompileTimeError(error, charOffset)); 2487 return new ExpressionStatement(buildCompileTimeError(error, charOffset));
2429 } 2488 }
2430 2489
2431 @override 2490 @override
2432 Initializer buildCompileTimeErrorIntializer(error, [int charOffset = -1]) { 2491 Initializer buildInvalidIntializer(Expression expression,
2433 return new LocalInitializer(new VariableDeclaration.forValue( 2492 [int charOffset = -1]) {
2434 buildCompileTimeError(error, charOffset))); 2493 needsImplicitSuperInitializer = false;
2494 return new LocalInitializer(new VariableDeclaration.forValue(expression))
2495 ..fileOffset = charOffset;
2435 } 2496 }
2436 2497
2437 @override 2498 @override
2499 Initializer buildSuperInitializer(
2500 Constructor constructor, Arguments arguments,
2501 [int charOffset = -1]) {
2502 needsImplicitSuperInitializer = false;
2503 return new SuperInitializer(constructor, arguments)
2504 ..fileOffset = charOffset;
2505 }
2506
2507 @override
2508 Initializer buildRedirectingInitializer(
2509 Constructor constructor, Arguments arguments,
2510 [int charOffset = -1]) {
2511 needsImplicitSuperInitializer = false;
2512 return new RedirectingInitializer(constructor, arguments)
2513 ..fileOffset = charOffset;
2514 }
2515
2516 @override
2438 Expression buildProblemExpression(ProblemBuilder builder, int charOffset) { 2517 Expression buildProblemExpression(ProblemBuilder builder, int charOffset) {
2439 return buildCompileTimeError(builder.message, charOffset); 2518 return buildCompileTimeError(builder.message, charOffset);
2440 } 2519 }
2441 2520
2442 @override 2521 @override
2443 void handleOperator(Token token) { 2522 void handleOperator(Token token) {
2444 debugEvent("Operator"); 2523 debugEvent("Operator");
2445 push(new Operator(token.stringValue)..fileOffset = token.charOffset); 2524 push(new Operator(token.stringValue)..fileOffset = token.charOffset);
2446 } 2525 }
2447 2526
(...skipping 513 matching lines...) Expand 10 before | Expand all | Expand 10 after
2961 } else if (node is PrefixBuilder) { 3040 } else if (node is PrefixBuilder) {
2962 return node.name; 3041 return node.name;
2963 } else if (node is ThisAccessor) { 3042 } else if (node is ThisAccessor) {
2964 return node.isSuper ? "super" : "this"; 3043 return node.isSuper ? "super" : "this";
2965 } else if (node is FastaAccessor) { 3044 } else if (node is FastaAccessor) {
2966 return node.plainNameForRead; 3045 return node.plainNameForRead;
2967 } else { 3046 } else {
2968 return internalError("Unhandled: ${node.runtimeType}"); 3047 return internalError("Unhandled: ${node.runtimeType}");
2969 } 3048 }
2970 } 3049 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698