blob: b88d5e8dbabec93df7ef6f65314e74f9f1a361df [file] [log] [blame]
// Copyright (c) 2017, 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.
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:test/test.dart';
import '../../../generated/parser_fasta_test.dart';
/**
* The base class for tests that test how well the parser recovers from various
* syntactic errors.
*/
abstract class AbstractRecoveryTest extends FastaParserTestCase {
void testRecovery(
String invalidCode, List<ErrorCode> errorCodes, String validCode) {
CompilationUnit invalidUnit = parseCompilationUnit(invalidCode, errorCodes);
CompilationUnit validUnit = parseCompilationUnit(validCode);
ResultComparator.compare(invalidUnit, validUnit);
}
}
/**
* An object used to compare to AST structures and cause the test to fail if
* they differ in any important ways.
*/
class ResultComparator extends AstComparator {
bool failDifferentLength(List first, List second) {
StringBuffer buffer = new StringBuffer();
buffer.write('Expected a list of length ');
buffer.write(second.length);
buffer.write('; found a list of length ');
buffer.writeln(first.length);
if (first is NodeList) {
_safelyWriteNodePath(buffer, first.owner);
}
fail(buffer.toString());
return false;
}
@override
bool failIfNotNull(Object first, Object second) {
if (second != null) {
StringBuffer buffer = new StringBuffer();
buffer.write('Expected null; found a ');
buffer.writeln(second.runtimeType);
if (second is AstNode) {
_safelyWriteNodePath(buffer, second);
}
fail(buffer.toString());
}
return true;
}
@override
bool failIsNull(Object first, Object second) {
StringBuffer buffer = new StringBuffer();
buffer.write('Expected a ');
buffer.write(first.runtimeType);
buffer.writeln('; found null');
if (first is AstNode) {
_safelyWriteNodePath(buffer, first);
}
fail(buffer.toString());
return false;
}
@override
bool failRuntimeType(Object first, Object second) {
StringBuffer buffer = new StringBuffer();
buffer.write('Expected a ');
buffer.writeln(second.runtimeType);
buffer.write('; found ');
buffer.writeln(first.runtimeType);
if (first is AstNode) {
_safelyWriteNodePath(buffer, first);
}
fail(buffer.toString());
return false;
}
/**
* Overridden to allow the valid code to contain an explicit identifier where
* a synthetic identifier is expected to be inserted by recovery.
*/
@override
bool isEqualNodes(AstNode first, AstNode second) {
if (first is SimpleIdentifier && second is SimpleIdentifier) {
if (first.isSynthetic && second.name == '_s_') {
return true;
}
}
return super.isEqualNodes(first, second);
}
/**
* Overridden to ignore the offsets of tokens because these can legitimately
* be different.
*/
@override
bool isEqualTokensNotNull(Token first, Token second) =>
first.length == second.length && first.lexeme == second.lexeme;
void _safelyWriteNodePath(StringBuffer buffer, AstNode node) {
buffer.write(' path: ');
if (node == null) {
buffer.write(' null');
} else {
_writeNodePath(buffer, node);
}
}
void _writeNodePath(StringBuffer buffer, AstNode node) {
AstNode parent = node.parent;
if (parent != null) {
_writeNodePath(buffer, parent);
buffer.write(', ');
}
buffer.write(node.runtimeType);
}
/**
* Compare the [first] and [second] nodes, failing the test if they are
* different.
*/
static void compare(AstNode first, AstNode second) {
ResultComparator comparator = new ResultComparator();
comparator.isEqualNodes(first, second);
}
}