Better redirecting constructor and initializers problems reporting.

Preparation for work on http://code.google.com/p/dart/issues/detail?id=351

R=zundel@google.com
BUG=
TEST=

Review URL: http://codereview.chromium.org//8527005

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@1511 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
scheglov@google.com 2011-11-14 16:18:08 +00:00
parent bd670664bb
commit 7cea876fef
9 changed files with 246 additions and 135 deletions

View file

@ -974,17 +974,18 @@ public class DartParser extends CompletionHooksParserBase {
done(null);
}
// Parse the argument definitions.
List<DartParameter> arguments = parseFormalParameterList();
// Parse the parameters definitions.
List<DartParameter> parameters = parseFormalParameterList();
if (arity != -1 && arguments.size() != arity) {
reportError(position(), ParserErrorCode.ILLEGAL_NUMBER_OF_ARGUMENTS);
if (arity != -1 && parameters.size() != arity) {
reportError(position(), ParserErrorCode.ILLEGAL_NUMBER_OF_PARAMETERS);
}
// Parse initializer expressions for constructors.
List<DartInitializer> initializers = new ArrayList<DartInitializer>();
if (match(Token.COLON) && !(isParsingInterface || modifiers.isFactory())) {
boolean isRedirectedConstructor = parseInitializers(initializers);
parseInitializers(initializers);
boolean isRedirectedConstructor = validateInitializers(parameters, initializers);
if (isRedirectedConstructor) {
modifiers = modifiers.makeRedirectedConstructor();
}
@ -1001,7 +1002,7 @@ public class DartParser extends CompletionHooksParserBase {
}
}
DartFunction function = doneWithoutConsuming(new DartFunction(arguments, body, returnType));
DartFunction function = doneWithoutConsuming(new DartFunction(parameters, body, returnType));
return DartMethodDefinition.create(name, function, modifiers, initializers, null);
}
@ -1057,7 +1058,7 @@ public class DartParser extends CompletionHooksParserBase {
* </pre>
* @return true if initializer is a redirected constructor, false otherwise.
*/
private boolean parseInitializers(List<DartInitializer> initializers) {
private void parseInitializers(List<DartInitializer> initializers) {
expect(Token.COLON);
do {
beginInitializer();
@ -1075,13 +1076,15 @@ public class DartParser extends CompletionHooksParserBase {
boolean hasThisPrefix = optional(Token.THIS);
if (hasThisPrefix) {
if (match(Token.LPAREN)) {
return parseRedirectedConstructorInvocation(null, initializers);
parseRedirectedConstructorInvocation(null, initializers);
continue;
}
expect(Token.PERIOD);
}
DartIdentifier name = parseIdentifier();
if (hasThisPrefix && match(Token.LPAREN)) {
return parseRedirectedConstructorInvocation(name, initializers);
parseRedirectedConstructorInvocation(name, initializers);
continue;
} else {
expect(Token.ASSIGN);
boolean save = setAllowFunctionExpression(false);
@ -1091,20 +1094,87 @@ public class DartParser extends CompletionHooksParserBase {
}
}
} while (optional(Token.COMMA));
return false;
}
private boolean parseRedirectedConstructorInvocation(DartIdentifier name,
private void parseRedirectedConstructorInvocation(DartIdentifier name,
List<DartInitializer> initializers) {
if (initializers.isEmpty()) {
DartRedirectConstructorInvocation redirConstructor =
new DartRedirectConstructorInvocation(name, parseArguments());
initializers.add(done(new DartInitializer(null, doneWithoutConsuming(redirConstructor))));
return true;
} else {
reportUnexpectedToken(position(), Token.ASSIGN, Token.LPAREN);
}
return false;
private boolean validateInitializers(List<DartParameter> parameters,
List<DartInitializer> initializers) {
// Try to find DartRedirectConstructorInvocation, check for multiple invocations.
// Check for DartSuperConstructorInvocation multiple invocations.
DartInitializer redirectInitializer = null;
boolean firstMultipleRedirectReported = false;
{
DartInitializer superInitializer = null;
boolean firstMultipleSuperReported = false;
for (DartInitializer initializer : initializers) {
if (initializer.isInvocation()) {
// DartSuperConstructorInvocation
DartExpression initializerInvocation = initializer.getValue();
if (initializerInvocation instanceof DartSuperConstructorInvocation) {
if (superInitializer != null) {
if (!firstMultipleSuperReported) {
reportError(superInitializer, ParserErrorCode.SUPER_CONSTRUCTOR_MULTIPLE);
firstMultipleSuperReported = true;
}
reportError(initializer, ParserErrorCode.SUPER_CONSTRUCTOR_MULTIPLE);
} else {
superInitializer = initializer;
}
}
// DartRedirectConstructorInvocation
if (initializerInvocation instanceof DartRedirectConstructorInvocation) {
if (redirectInitializer != null) {
if (!firstMultipleRedirectReported) {
reportError(redirectInitializer, ParserErrorCode.REDIRECTING_CONSTRUCTOR_MULTIPLE);
firstMultipleRedirectReported = true;
}
reportError(initializer, ParserErrorCode.REDIRECTING_CONSTRUCTOR_MULTIPLE);
} else {
redirectInitializer = initializer;
}
}
}
}
}
// If there is redirecting constructor, then there should be no other initializers.
if (redirectInitializer != null) {
boolean shouldRedirectInvocationReported = false;
// Implicit initializer in form of "this.id" parameter.
for (DartParameter parameter : parameters) {
if (parameter.getName() instanceof DartPropertyAccess) {
DartPropertyAccess propertyAccess = (DartPropertyAccess) parameter.getName();
if (propertyAccess.getQualifier() instanceof DartThisExpression) {
shouldRedirectInvocationReported = true;
reportError(
parameter,
ParserErrorCode.REDIRECTING_CONSTRUCTOR_PARAM);
}
}
}
// Iterate all initializers and mark all except of DartRedirectConstructorInvocation
for (DartInitializer initializer : initializers) {
if (!(initializer.getValue() instanceof DartRedirectConstructorInvocation)) {
shouldRedirectInvocationReported = true;
reportError(
initializer,
ParserErrorCode.REDIRECTING_CONSTRUCTOR_OTHER);
}
}
// Mark DartRedirectConstructorInvocation if needed.
if (shouldRedirectInvocationReported) {
reportError(
redirectInitializer,
ParserErrorCode.REDIRECTING_CONSTRUCTOR_ITSELF);
}
}
// Done.
return redirectInitializer != null;
}
/**

View file

@ -39,13 +39,18 @@ public enum ParserErrorCode implements ErrorCode {
FUNCTION_TYPED_PARAMETER_IS_FINAL("Formal parameter with a function type cannot be const"),
FUNCTION_TYPED_PARAMETER_IS_VAR("Formal parameter with a function type cannot be var"),
ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE("Illegal assignment to non-assignable expression"),
ILLEGAL_NUMBER_OF_ARGUMENTS("Illegal number of arguments"),
ILLEGAL_NUMBER_OF_PARAMETERS("Illegal number of parameters"),
INCOMPLETE_STRING_LITERAL("Incomplete string literal"),
INVALID_FIELD_DECLARATION("Wrong syntax for field declaration"),
INVALID_OPERATOR_CHAINING("Cannot chain '%s'"),
MISSING_FUNCTION_NAME("a function name is required for a declaration"),
NON_FINAL_STATIC_MEMBER_IN_INTERFACE("Non-final static members are not allowed in interfaces"),
OPERATOR_CANNOT_BE_STATIC("Operators cannot be static"),
REDIRECTING_CONSTRUCTOR_PARAM("Redirecting constructor can not have initializers"),
REDIRECTING_CONSTRUCTOR_ITSELF("Redirecting constructor can not have initializers"),
REDIRECTING_CONSTRUCTOR_MULTIPLE("Multiple redirecting constructor invocations"),
REDIRECTING_CONSTRUCTOR_OTHER("Redirecting constructor can not have initializers"),
SUPER_CONSTRUCTOR_MULTIPLE("'super' must be called only once in the initialization list"),
TOP_LEVEL_IS_STATIC("Top-level field or method may not be static"),
UNEXPECTED_TOKEN("Unexpected token '%s'"),
UNEXPECTED_TOKEN_IN_STRING_INTERPOLATION("Unexpected token in string interpolation: %s"),

View file

@ -1339,9 +1339,6 @@ public class Resolver {
DartInitializer initializer = initializers.next();
Element element = resolve(initializer);
if ((ElementKind.of(element) == ElementKind.CONSTRUCTOR) && initializer.isInvocation()) {
if (constructorElement != null) {
onError(initializer, ResolverErrorCode.SUPER_INVOCATION_NOT_UNIQUE);
}
constructorElement = (ConstructorElement) element;
}
}

View file

@ -98,7 +98,6 @@ public enum ResolverErrorCode implements ErrorCode {
STATIC_METHOD_ACCESS_SUPER("Cannot use 'super' in a static method"),
STATIC_METHOD_ACCESS_THIS("Cannot use 'this' in a static method"),
SUPER_OUTSIDE_OF_METHOD("Cannot use 'super' outside of a method"),
SUPER_INVOCATION_NOT_UNIQUE("'super' must be called only once in the initialization list"),
TOP_LEVEL_METHOD_ACCESS_SUPER("Cannot use 'super' in a top-level method"),
TOP_LEVEL_METHOD_ACCESS_THIS("Cannot use 'this' in a top-level method"),
TYPE_NOT_ASSIGNMENT_COMPATIBLE("%s is not assignable to %s"),

View file

@ -273,4 +273,57 @@ public abstract class CompilerTestCase extends TestCase {
DartCompilerListener listener) {
return new DartScannerParserContext(src, sourceCode, listener);
}
protected static class ErrorExpectation {
final ErrorCode errorCode;
final int line;
final int column;
final int length;
public ErrorExpectation(ErrorCode errorCode, int line, int column, int length) {
this.errorCode = errorCode;
this.line = line;
this.column = column;
this.length = length;
}
}
protected static ErrorExpectation errEx(ErrorCode errorCode, int line, int column, int length) {
return new ErrorExpectation(errorCode, line, column, length);
}
/**
* Asserts that given list of {@link DartCompilationError} is exactly same as expected.
*/
protected static void assertErrors(List<DartCompilationError> errors,
ErrorExpectation... expectedErrors) {
// count of errors
if (errors.size() != expectedErrors.length) {
fail(String.format(
"Expected %s errors, but got %s: %s",
expectedErrors.length,
errors.size(),
errors));
}
// content of errors
for (int i = 0; i < expectedErrors.length; i++) {
ErrorExpectation expectedError = expectedErrors[i];
DartCompilationError actualError = errors.get(i);
if (actualError.getErrorCode() != expectedError.errorCode
|| actualError.getLineNumber() != expectedError.line
|| actualError.getColumnNumber() != expectedError.column
|| actualError.getLength() != expectedError.length) {
fail(String.format(
"Expected %s:%d:%d/%d, but got %s:%d:%d/%d",
expectedError.errorCode,
expectedError.line,
expectedError.column,
expectedError.length,
actualError.getErrorCode(),
actualError.getLineNumber(),
actualError.getColumnNumber(),
actualError.getLength()));
}
}
}
}

View file

@ -1,49 +1,105 @@
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.google.dart.compiler.parser;
import com.google.dart.compiler.CompilerTestCase;
import com.google.dart.compiler.DartCompilationError;
import com.google.dart.compiler.ast.DartIdentifier;
import com.google.dart.compiler.ast.DartMethodDefinition;
import com.google.dart.compiler.ast.DartUnit;
import java.util.List;
/**
* Negative Parser/Syntax tests.
*/
public class NegativeParserTest extends CompilerTestCase {
private void parseExpectErrors(String code, int expectedErrorCount) {
assertEquals(expectedErrorCount, DartParserRunner.parse(getName(), code).getErrorCount());
private void parseExpectErrors(String code, ErrorExpectation... expectedErrors) {
List<DartCompilationError> errors = getParseErrors(code);
assertErrors(errors, expectedErrors);
}
private void parseExpectErrors(String code) {
assertTrue("expected errors.", DartParserRunner.parse(getName(), code).hasErrors());
private List<DartCompilationError> getParseErrors(String code) {
return DartParserRunner.parse(getName(), code, Integer.MAX_VALUE, false).getErrors();
}
public void testFieldInitializerInRedirectionConstructor1() {
parseExpectErrors("class A { A(x) { } A.foo() : this(5), y = 5; var y; }");
parseExpectErrors(
"class A { A(x) { } A.foo() : this(5), y = 5; var y; }",
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_OTHER, 1, 39, 5),
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_ITSELF, 1, 30, 7));
}
public void testFieldInitializerInRedirectionConstructor2() {
parseExpectErrors("class A { A(x) { } A.foo() : y = 5, this(5); var y; }");
parseExpectErrors(
"class A { A(x) { } A.foo() : y = 5, this(5); var y; }",
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_OTHER, 1, 30, 5),
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_ITSELF, 1, 37, 7));
}
public void testFieldInitializerInRedirectionConstructor3() {
parseExpectErrors("class A { A(x) { } A.foo(this.y) : this(5); var y; }", 1);
parseExpectErrors(
"class A { A.foo(x) { } A() : y = 5, this.foo(5); var y; }",
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_OTHER, 1, 30, 5),
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_ITSELF, 1, 37, 11));
}
public void testFieldInitializerInRedirectionConstructor4() {
parseExpectErrors(
"class A { A(x) { } A.foo(this.y, this.z) : this(5); var y; var z;}",
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_PARAM, 1, 26, 6),
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_PARAM, 1, 34, 6),
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_ITSELF, 1, 44, 7));
}
public void testFieldInitializerInRedirectionConstructor5() {
parseExpectErrors(
"class A { A(x) { } A.foo(this.y) : this(5), z = 7; var y; var z;}",
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_PARAM, 1, 26, 6),
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_OTHER, 1, 45, 5),
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_ITSELF, 1, 36, 7));
}
public void testSuperInRedirectionConstructor1() {
parseExpectErrors("class A { A(x) { } A.foo(this.y) : this(5), super(); var y; }");
parseExpectErrors(
"class A { A(x) { } A.foo() : this(5), super(); var y; }",
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_OTHER, 1, 39, 7),
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_ITSELF, 1, 30, 7));
}
public void testSuperInRedirectionConstructor2() {
parseExpectErrors("class A { A(x) { } A.foo(this.y) : super(), this(5); var y; }", 1);
parseExpectErrors(
"class A { A(x) { } A.foo() : super(), this(5); var y; }",
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_OTHER, 1, 30, 7),
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_ITSELF, 1, 39, 7));
}
public void testMultipleRedirectionConstructors() {
parseExpectErrors("class A { A(x) { } A.foo(this.y) : this(1), this(2); }", 1);
parseExpectErrors(
"class A { A(x) { } A.foo() : this(1), this(2); }",
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_MULTIPLE, 1, 30, 7),
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_MULTIPLE, 1, 39, 7));
}
public void testSuperMultipleInvocationsTest() {
String source =
makeCode(
"class A {",
" int a;",
" A(this.a);",
" A.foo(int x, int y);",
"}",
"",
"class B extends A {",
" int b1;",
" int b2;",
" B(int x) : this.b1 = x, super(x), this.b2 = x, super.foo(x, x);",
"}");
parseExpectErrors(
source,
errEx(ParserErrorCode.SUPER_CONSTRUCTOR_MULTIPLE, 10, 29, 8),
errEx(ParserErrorCode.SUPER_CONSTRUCTOR_MULTIPLE, 10, 52, 15));
}
public void testIncompleteClassDeclaration_noLBrace() {
@ -56,7 +112,9 @@ public class NegativeParserTest extends CompilerTestCase {
DartUnit unit =
parseSourceUnitErrors(
sourceCode,
ParserErrorCode.EXPECTED_CLASS_DECLARATION_LBRACE.getMessage(), 2, 1);
ParserErrorCode.EXPECTED_CLASS_DECLARATION_LBRACE.getMessage(),
2,
1);
// check structure of AST, top level Baz and Foo expected
assertEquals(2, unit.getTopLevelNodes().size());
assertEquals(
@ -84,7 +142,9 @@ public class NegativeParserTest extends CompilerTestCase {
DartUnit unit =
parseSourceUnitErrors(
"factory foo() {}",
ParserErrorCode.DISALLOWED_FACTORY_KEYWORD.getMessage(), 1, 1);
ParserErrorCode.DISALLOWED_FACTORY_KEYWORD.getMessage(),
1,
1);
DartMethodDefinition factory = (DartMethodDefinition) unit.getTopLevelNodes().get(0);
assertNotNull(factory);
// this factory has name, which is allowed for normal method
@ -103,7 +163,9 @@ public class NegativeParserTest extends CompilerTestCase {
DartUnit unit =
parseSourceUnitErrors(
"factory foo<T>() {}",
ParserErrorCode.DISALLOWED_FACTORY_KEYWORD.getMessage(), 1, 1);
ParserErrorCode.DISALLOWED_FACTORY_KEYWORD.getMessage(),
1,
1);
DartMethodDefinition factory = (DartMethodDefinition) unit.getTopLevelNodes().get(0);
assertNotNull(factory);
// normal method requires name, so we provide some name

View file

@ -22,7 +22,7 @@ public class ParserTests extends TestSetup {
suite.addTestSuite(CPParserTest.class);
suite.addTestSuite(ParserRoundTripTest.class);
suite.addTestSuite(LibraryParserTest.class);
//suite.addTestSuite(NegativeParserTest.class); TODO(scheglov) fix failures here and add again
suite.addTestSuite(NegativeParserTest.class);
suite.addTestSuite(ValidatingSyntaxTest.class);
suite.addTestSuite(CommentTest.class);
suite.addTestSuite(ErrorMessageLocationTest.class);

View file

@ -6,11 +6,9 @@ package com.google.dart.compiler.resolver;
import com.google.dart.compiler.CompilerTestCase;
import com.google.dart.compiler.DartCompilationError;
import com.google.dart.compiler.ErrorCode;
import com.google.dart.compiler.ast.DartUnit;
import com.google.dart.compiler.testing.TestCompilerContext;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
@ -19,20 +17,22 @@ public class NegativeResolverTest extends CompilerTestCase {
List<DartCompilationError> errors = new ArrayList<DartCompilationError>();
List<DartCompilationError> typeErrors = new ArrayList<DartCompilationError>();
private static class ErrorExpectation {
final ErrorCode errorCode;
final int line;
final int column;
public ErrorExpectation(ErrorCode errorCode, int line, int column) {
this.errorCode = errorCode;
this.line = line;
this.column = column;
}
/**
* Parses given Dart source, runs {@link Resolver} and checks that expected errors were generated.
*/
public void checkSourceErrors(String source, ErrorExpectation ...expectedErrors) {
DartUnit unit = parseUnit("Test.dart", source);
resolve(unit);
assertErrors(errors, expectedErrors);
}
private static ErrorExpectation errEx(ErrorCode errorCode, int line, int column) {
return new ErrorExpectation(errorCode, line, column);
/**
* Parses given Dart file, runs {@link Resolver} and checks that expected errors were generated.
*/
public void checkFileErrors(String source, ErrorExpectation ...expectedErrors) {
DartUnit unit = parseUnit(source);
resolve(unit);
assertErrors(errors, expectedErrors);
}
public void checkNumErrors(String fileName, int expectedErrorCount) {
@ -45,28 +45,6 @@ public class NegativeResolverTest extends CompilerTestCase {
}
}
public void checkNumErrors(String fileName, ErrorExpectation ...expectedErrors) {
DartUnit unit = parseUnit(fileName);
resolve(unit);
assertEquals(expectedErrors.length, errors.size());
for (int i = 0; i < expectedErrors.length; i++) {
ErrorExpectation expectedError = expectedErrors[i];
DartCompilationError actualError = errors.get(i);
if (actualError.getErrorCode() != expectedError.errorCode
|| actualError.getLineNumber() != expectedError.line
|| actualError.getColumnNumber() != expectedError.column) {
fail(String.format(
"Expected %s:%s:%s, but got %s:%s:%s",
expectedError.errorCode,
expectedError.line,
expectedError.column,
actualError.getErrorCode(),
actualError.getLineNumber(),
actualError.getColumnNumber()));
}
}
}
private void resolve(DartUnit unit) {
unit.addTopLevelNode(ResolverTestCase.makeClass("int", null));
unit.addTopLevelNode(ResolverTestCase.makeClass("Object", null));
@ -77,39 +55,6 @@ public class NegativeResolverTest extends CompilerTestCase {
ResolverTestCase.resolve(unit, getContext());
}
/**
* Parses given Dart source, runs {@link Resolver} and checks that expected errors were generated.
*/
public void checkErrors(String source, ErrorExpectation ...expectedErrors) {
DartUnit unit = parseUnit("Test.dart", source);
resolve(unit);
// count of errors
if (errors.size() != expectedErrors.length) {
fail(String.format(
"Expected %s errors, but got %s: %s",
expectedErrors.length,
errors.size(),
errors));
}
// content of errors
for (int i = 0; i < expectedErrors.length; i++) {
ErrorExpectation expectedError = expectedErrors[i];
DartCompilationError actualError = errors.get(i);
if (actualError.getErrorCode() != expectedError.errorCode
|| actualError.getLineNumber() != expectedError.line
|| actualError.getColumnNumber() != expectedError.column) {
fail(String.format(
"Expected %s:%s:%s, but got %s:%s:%s",
expectedError.errorCode,
expectedError.line,
expectedError.column,
actualError.getErrorCode(),
actualError.getLineNumber(),
actualError.getColumnNumber()));
}
}
}
public void testInitializer1() {
checkNumErrors("Initializer1NegativeTest.dart", 1);
}
@ -135,25 +80,25 @@ public class NegativeResolverTest extends CompilerTestCase {
}
public void testArrayLiteralNegativeTest() {
checkErrors(
checkSourceErrors(
makeCode(
"class A {",
" main() {",
" List<int, int> ints = [1];",
" }",
"}"),
errEx(TypeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, 3, 5));
errEx(TypeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, 3, 5, 14));
}
public void testMapLiteralNegativeTest() {
checkErrors(
checkSourceErrors(
makeCode(
"class A {",
" main() {",
" Map<String, int, int> map = {'foo':1};",
" }",
"}"),
errEx(TypeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, 3, 5));
errEx(TypeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, 3, 5, 21));
}
public void testCall1() {
@ -276,11 +221,6 @@ public class NegativeResolverTest extends CompilerTestCase {
checkNumErrors("RawTypesNegativeTest.dart", 4);
}
public void testSuperMultipleInvocationsTest() {
checkNumErrors("SuperMultipleInvocationsTest.dart",
errEx(ResolverErrorCode.SUPER_INVOCATION_NOT_UNIQUE, 14, 52));
}
private TestCompilerContext getContext() {
return new TestCompilerContext() {
@Override

View file

@ -1,15 +0,0 @@
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
class A {
int a;
A(this.a);
A.foo(int x, int y);
}
class B extends A {
int b1;
int b2;
B(int x) : this.b1 = x, super(x), this.b2 = x, super.foo(x, x);
}