blob: a450e2f3d68f0faafdc713af95077bd6db13650f [file] [edit]
// Copyright (c) 2016, 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.
library kernel.checks;
import 'ast.dart';
void runSanityChecks(Program program) {
CheckParentPointers.check(program);
CheckReferences.check(program);
}
class CheckParentPointers extends Visitor {
static void check(TreeNode node) {
node.accept(new CheckParentPointers(node.parent));
}
TreeNode parent;
CheckParentPointers([this.parent]);
defaultTreeNode(TreeNode node) {
if (node.parent != parent) {
throw 'Parent pointer on ${node.runtimeType} '
'is ${node.parent.runtimeType} '
'but should be ${parent.runtimeType}';
}
var oldParent = parent;
parent = node;
node.visitChildren(this);
parent = oldParent;
}
}
/// Checks that references refer to something in scope.
///
/// Currently only checks member, class, and type parameter references.
class CheckReferences extends RecursiveVisitor {
final Set<Member> members = new Set<Member>();
final Set<Class> classes = new Set<Class>();
final Set<TypeParameter> typeParameters = new Set<TypeParameter>();
Member currentMember;
Class currentClass;
TreeNode get context => currentMember ?? currentClass;
static void check(Program program) {
program.accept(new CheckReferences());
}
visitProgram(Program program) {
for (var library in program.libraries) {
classes.addAll(library.classes);
members.addAll(library.members);
for (var class_ in library.classes) {
members.addAll(class_.members);
}
}
program.visitChildren(this);
}
defaultMember(Member node) {
currentMember = node;
node.visitChildren(this);
currentMember = null;
}
visitClass(Class node) {
currentClass = node;
typeParameters.addAll(node.typeParameters);
node.visitChildren(this);
typeParameters.removeAll(node.typeParameters);
currentClass = null;
}
visitFunctionNode(FunctionNode node) {
for (int i = 1; i < node.namedParameters.length; ++i) {
if (node.namedParameters[i - 1].compareTo(node.namedParameters[i]) >= 0) {
throw 'Named parameters are not sorted on function found in $context';
}
}
typeParameters.addAll(node.typeParameters);
node.visitChildren(this);
typeParameters.removeAll(node.typeParameters);
}
visitFunctionType(FunctionType node) {
for (int i = 1; i < node.namedParameters.length; ++i) {
if (node.namedParameters[i - 1].compareTo(node.namedParameters[i]) >= 0) {
throw 'Named parameters are not sorted on function type found in '
'$context';
}
}
node.visitChildren(this);
}
@override
defaultMemberReference(Member node) {
if (!members.contains(node)) {
throw 'Dangling reference to $node found in $context.\n'
'Parent pointer is set to ${node.parent}';
}
}
@override
visitClassReference(Class node) {
if (!classes.contains(node)) {
throw 'Dangling reference to $node found in $context.\n'
'Parent pointer is set to ${node.parent}';
}
}
@override
visitTypeParameterType(TypeParameterType node) {
if (!typeParameters.contains(node.parameter)) {
throw 'Type parameter ${node.parameter} referenced out of scope '
'in $context.\n'
'Parent pointer is set to ${node.parameter.parent}';
}
}
@override
visitInterfaceType(InterfaceType node) {
node.visitChildren(this);
if (node.typeArguments.length != node.classNode.typeParameters.length) {
throw 'Type $node provides ${node.typeArguments.length} type arguments '
'but the class declares ${node.classNode.typeParameters.length} '
'parameters. Found in $context.';
}
}
}
class SizeCounter extends RecursiveVisitor {
int size = 0;
int emptyArguments = 0;
void visit(TreeNode node) => node.accept(this);
visitArguments(Arguments node) {
super.visitArguments(node);
if (node.positional.isEmpty &&
node.positional.isEmpty &&
node.types.isEmpty) {
++emptyArguments;
}
}
defaultNode(Node node) {
++size;
node.visitChildren(this);
}
}