Files
sdk/pkg/kernel/lib/analyzer/loader.dart
T
Asger Feldthaus b02cdaccf7 [kernel] Report more errors.
Include up to 100 errors in the output, and print the number
of errors that were truncated.

Also report errors from the SDK and package files.

BUG=
R=kmillikin@google.com

Review URL: https://chromereviews.googleplex.com/506077014 .
2016-09-13 13:16:05 +02:00

522 lines
19 KiB
Dart

// 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.analyzer.loader;
import '../ast.dart' as ast;
import '../repository.dart';
import '../target/targets.dart' show Target;
import '../type_algebra.dart';
import 'analyzer.dart';
import 'ast_from_analyzer.dart';
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/java_io.dart';
import 'package:analyzer/src/generated/parser.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source_io.dart';
import 'package:analyzer/src/dart/sdk/sdk.dart';
import 'package:analyzer/src/dart/scanner/scanner.dart';
abstract class ReferenceLevelLoader {
ast.Library getLibraryReference(LibraryElement element);
ast.Class getClassReference(ClassElement element);
ast.Member getMemberReference(Element element);
ast.Class getRootClassReference();
ast.Constructor getRootClassConstructorReference();
ast.Class getCoreClassReference(String className);
ast.Constructor getCoreClassConstructorReference(String className,
{String constructorName, String library});
ast.TypeParameter tryGetClassTypeParameter(TypeParameterElement element);
ast.Class getMixinApplicationClass(
ast.Library library, ast.Class supertype, ast.Class mixin);
bool get strongMode;
}
class AnalyzerLoader implements ReferenceLevelLoader {
final Repository repository;
final LoadMap<ClassElement, ast.Class> _classes =
new LoadMap<ClassElement, ast.Class>();
final LoadMap<Element, ast.Member> _members =
new LoadMap<Element, ast.Member>();
final Map<TypeParameterElement, ast.TypeParameter> _classTypeParameters =
<TypeParameterElement, ast.TypeParameter>{};
final Map<ast.Library, Map<String, ast.Class>> _mixinApplications =
<ast.Library, Map<String, ast.Class>>{};
final AnalysisContext context;
LibraryElement _dartCoreLibrary;
final List errors = [];
bool get strongMode => context.analysisOptions.strongMode;
AnalyzerLoader(Repository repository,
{AnalysisContext context, bool strongMode: false})
: this.repository = repository,
this.context = context ??
createContext(repository.sdk, repository.packageRoot, strongMode);
LibraryElement getLibraryElement(ast.Library node) {
return context
.getLibraryElement(context.sourceFactory.forUri2(node.importUri));
}
ast.Library getLibraryReference(LibraryElement element) {
return repository.getLibraryReference(element.source.uri);
}
ast.Library getLibraryBody(LibraryElement element) {
var library = getLibraryReference(element);
if (!library.isLoaded) {
_buildLibraryBody(element, library);
}
return library;
}
void _buildLibraryBody(LibraryElement element, ast.Library library) {
library.name = element.name.isEmpty ? null : element.name;
_mixinApplications[library] = <String, ast.Class>{};
for (var unit in element.units) {
for (var type in unit.types) {
library.addClass(getClassReference(type));
}
for (var type in unit.enums) {
library.addClass(getClassReference(type));
}
for (var accessor in unit.accessors) {
if (!accessor.isSynthetic) {
library.addMember(getMemberReference(accessor));
}
}
for (var function in unit.functions) {
library.addMember(getMemberReference(function));
}
for (var field in unit.topLevelVariables) {
if (!field.isSynthetic) {
library.addMember(getMemberReference(field));
}
}
}
library.isLoaded = true;
}
LibraryElement getDartCoreLibrary() {
return _dartCoreLibrary ??= _findLibraryElement('dart:core');
}
LibraryElement _findLibraryElement(String uri) {
var source = context.sourceFactory.forUri(uri);
if (source == null) return null;
return context.computeLibraryElement(source);
}
ast.Class getRootClassReference() {
return getCoreClassReference('Object');
}
ast.Constructor getRootClassConstructorReference() {
var element = getDartCoreLibrary().getType('Object').constructors[0];
return getMemberReference(element);
}
ast.Class getCoreClassReference(String className) {
return getClassReference(getDartCoreLibrary().getType(className));
}
ast.Constructor getCoreClassConstructorReference(String className,
{String constructorName, String library}) {
LibraryElement libraryElement =
library != null ? _findLibraryElement(library) : getDartCoreLibrary();
ClassElement element = libraryElement.getType(className);
if (element == null) {
throw 'Missing core class $className from ${libraryElement.name}';
}
var constructor = element.constructors.firstWhere((constructor) {
return (constructorName == null)
? (constructor.nameLength == 0)
: (constructor.name == constructorName);
});
return getMemberReference(constructor);
}
ClassElement getClassElement(ast.Class node) {
return _classes.inverse[node];
}
ast.Class getClassReference(ClassElement element) {
return _classes.getReference(element, _buildClassReference);
}
ast.Class _buildClassReference(ClassElement element) {
var classNode =
new ast.Class(name: element.name, isAbstract: element.isAbstract);
for (TypeParameterElement parameter in element.typeParameters) {
var parameterNode = new ast.TypeParameter(parameter.name);
_classTypeParameters[parameter] = parameterNode;
classNode.typeParameters.add(parameterNode);
parameterNode.parent = classNode;
}
return classNode;
}
ast.Class getClassBody(ClassElement element) {
var classNode = getClassReference(element);
if (_classes.level[classNode] == LoadingLevel.Reference) {
_buildClassBody(element, classNode);
}
return classNode;
}
void _buildClassBody(ClassElement element, ast.Class classNode) {
// Ensure that the enclosing library is loaded first.
var library = getLibraryBody(element.library);
assert(classNode.enclosingLibrary == library);
new ClassBodyBuilder(this, classNode, element).build(element.computeNode());
_classes.level[classNode] = LoadingLevel.Body;
}
ast.TypeParameter tryGetClassTypeParameter(TypeParameterElement element) {
return _classTypeParameters[element];
}
Element getMemberElement(ast.Member node) {
return _members.inverse[node];
}
ast.Member getMemberReference(Element element) {
return _members.getReference(element, _buildMemberReference);
}
ast.Member _buildMemberReference(Element element) {
assert(element is! Member); // Use the "base element".
switch (element.kind) {
case ElementKind.CONSTRUCTOR:
ConstructorElement constructor = element;
if (constructor.isFactory) {
return new ast.Procedure(
_nameOfMember(constructor), ast.ProcedureKind.Factory, null,
isAbstract: false,
isStatic: true,
isExternal: constructor.isExternal,
isConst: constructor.isConst);
}
return new ast.Constructor(null,
name: _nameOfMember(element),
isConst: constructor.isConst,
isExternal: constructor.isExternal);
case ElementKind.FIELD:
case ElementKind.TOP_LEVEL_VARIABLE:
VariableElement variable = element;
return new ast.Field(_nameOfMember(variable),
isStatic: variable.isStatic,
isFinal: variable.isFinal,
isConst: variable.isConst);
case ElementKind.METHOD:
case ElementKind.GETTER:
case ElementKind.SETTER:
case ElementKind.FUNCTION:
if (element is FunctionElement &&
element.enclosingElement is! CompilationUnitElement) {
throw 'Function $element is nested in ${element.enclosingElement} '
'and hence is not a member';
}
ExecutableElement executable = element;
return new ast.Procedure(
_nameOfMember(element), _procedureKindOf(executable), null,
isAbstract: executable.isAbstract,
isStatic: executable.isStatic,
isExternal: executable.isExternal);
default:
throw 'Unexpected member kind: $element';
}
}
ast.Member getMemberBody(Element element) {
var member = getMemberReference(element);
if (_members.level[member] == LoadingLevel.Reference) {
_buildMemberBody(element, member);
}
return member;
}
void _buildMemberBody(Element element, ast.Member member) {
// Ensure the enclosing library and class are loaded.
var libraryNode = getLibraryBody(element.library);
var enclosing = element.enclosingElement;
if (enclosing is ClassElement) {
var classNode = getClassBody(enclosing);
assert(classNode.parent == libraryNode);
assert(member.parent == classNode);
} else {
assert(member.parent == libraryNode);
}
new MemberBodyBuilder(this, member, element).build(element.computeNode());
_members.level[member] = LoadingLevel.Body;
}
ast.ProcedureKind _procedureKindOf(ExecutableElement element) {
if (element is PropertyAccessorElement) {
return element.isGetter
? ast.ProcedureKind.Getter
: ast.ProcedureKind.Setter;
}
if (element is MethodElement) {
if (element.isOperator) return ast.ProcedureKind.Operator;
return ast.ProcedureKind.Method;
}
if (element is FunctionElement) {
return ast.ProcedureKind.Method;
}
if (element is ConstructorElement) {
assert(element.isFactory);
return ast.ProcedureKind.Factory;
}
throw 'Unexpected procedure: $element';
}
ast.Name _nameOfMember(Element element) {
// Use 'displayName' to avoid a trailing '=' for setters and 'name' to
// ensure unary minus is called 'unary-'.
String name =
element is PropertyAccessorElement ? element.displayName : element.name;
return new ast.Name(name, getLibraryReference(element.library));
}
/// Returns the canonical mixin application of [superclass] and [mixedInClass]
/// in the given [library].
ast.Class getMixinApplicationClass(
ast.Library library, ast.Class superclass, ast.Class mixedInClass) {
// TODO(asgerf): Avoid potential name clash due to associativity.
// As it is, these mixins get the same name:
// (A with B) with C
// A with (B with C)
String name = '${superclass.name}&${mixedInClass.name}';
return _mixinApplications[library].putIfAbsent(name, () {
List<ast.TypeParameter> typeParameters = <ast.TypeParameter>[];
ast.InterfaceType makeSuper(ast.Class class_) {
if (class_.typeParameters.isEmpty) return class_.rawType;
// We need to copy over type parameters from the given super type,
// including its bounds. We handle two cases separately:
//
// 1. The super class is derived from a ClassElement.
// At this point, the IR node can only be assumed to be loaded as
// a reference, meaning its type parameter bound are not yet
// initialized.
// Build the type parameters based on the element model.
//
// 2. The super class is a mixin application previously created here.
// In this case, there does not exist a corresponding ClassElement.
// However, we know the class has its type parameter bounds
// already initialized since it was created here.
// Copy the type parameters based on the IR of the super class.
//
ClassElement element = getClassElement(class_);
if (element != null) {
var scope = new TypeScope(this);
// Build type parameter objects and put them in our local scope.
for (var parameter in element.typeParameters) {
var parameterNode = new ast.TypeParameter(parameter.name);
scope.localTypeParameters[parameter] = parameterNode;
typeParameters.add(parameterNode);
}
// Build the bounds, with all the type parameters in scope.
for (var parameter in element.typeParameters) {
if (parameter.bound != null) {
var parameterNode = scope.getTypeParameterReference(parameter);
parameterNode.bound = scope.buildType(parameter.bound);
}
}
return scope.buildType(element.type);
} else {
// Build copies of the existing type parameters.
var parameters = getFreshTypeParameters(class_.typeParameters);
typeParameters.addAll(parameters.freshTypeParameters);
return parameters.substitute(class_.thisType);
}
}
var supertype = makeSuper(superclass);
var mixedInType = makeSuper(mixedInClass);
var result = new ast.Class(
name: name,
typeParameters: typeParameters,
supertype: supertype,
mixedInType: mixedInType);
library.addClass(result);
return result;
});
}
String formatErrorMessage(
AnalysisError error, String filename, LineInfo lines) {
var location = lines.getLocation(error.offset);
return '[error] ${error.message} ($filename, '
'line ${location.lineNumber}, '
'col ${location.columnNumber})';
}
void ensureLibraryIsLoaded(ast.Library node) {
if (node.isLoaded) return;
var source = context.sourceFactory.forUri2(node.importUri);
assert(source != null);
var element = context.computeLibraryElement(source);
context.resolveCompilationUnit(source, element);
_buildLibraryBody(element, node);
for (var unit in element.units) {
LineInfo lines;
for (var error in context.computeErrors(unit.source)) {
if (error.errorCode is CompileTimeErrorCode ||
error.errorCode is ParserErrorCode ||
error.errorCode is ScannerErrorCode ||
error.errorCode is StrongModeCode) {
if (error.errorCode == ParserErrorCode.CONST_FACTORY &&
node.importUri.scheme == 'dart') {
// Ignore warnings about 'const' factories in the patched SDK.
continue;
}
lines ??= context.computeLineInfo(source);
errors.add(formatErrorMessage(error, source.shortName, lines));
}
}
}
}
void loadEverything({Target target}) {
ensureLibraryIsLoaded(getLibraryReference(getDartCoreLibrary()));
if (target != null) {
for (var uri in target.extraRequiredLibraries) {
var library = _findLibraryElement(uri);
if (library == null) {
errors.add('Could not find required library $uri');
continue;
}
ensureLibraryIsLoaded(getLibraryReference(library));
}
}
int libraryIndex = 0;
bool changed = true;
while (changed) {
changed = false;
while (libraryIndex < repository.libraries.length) {
ensureLibraryIsLoaded(repository.libraries[libraryIndex]);
++libraryIndex;
}
while (_classes.referencedNodes.isNotEmpty) {
getClassBody(_classes.referencedNodes.removeLast());
changed = true;
}
while (_members.referencedNodes.isNotEmpty) {
getMemberBody(_members.referencedNodes.removeLast());
changed = true;
}
}
}
/// Builds a list of sources that have been loaded.
///
/// This operation may be expensive and should only be used for diagnostics.
List<String> getLoadedFileNames() {
var list = <String>[];
for (var library in repository.libraries) {
LibraryElement element = context.computeLibraryElement(
context.sourceFactory.forUri2(library.importUri));
for (var unit in element.units) {
list.add(unit.source.fullName);
}
}
return list;
}
ast.Program loadProgram(String mainLibrary, {Target target}) {
ast.Library library = repository.getLibrary(mainLibrary);
ensureLibraryIsLoaded(library);
loadEverything(target: target);
var program = new ast.Program(repository.libraries);
program.mainMethod = library.procedures.firstWhere(
(member) => member.name?.name == 'main',
orElse: () => null);
return program;
}
ast.Library loadLibrary(String mainLibrary) {
ast.Library library = repository.getLibrary(mainLibrary);
ensureLibraryIsLoaded(library);
return library;
}
}
enum LoadingLevel {
/// A library, member, or class whose object has been created so it can
/// be referenced, but its contents have not been completely initialized.
///
/// Classes contain their name and type parameter arity.
///
/// Members contain their name and modifiers (abstract, static, etc).
///
/// At this level, a class may be used in an [ast.InterfaceType].
Reference,
/// Everything is loaded.
Body,
}
class LoadMap<K, V> {
final Map<K, V> nodeMap = <K, V>{};
final Map<V, K> inverse = <V, K>{};
final Map<V, LoadingLevel> level = <V, LoadingLevel>{};
final List<K> referencedNodes = <K>[];
V getReference(K key, V build(K key)) {
return nodeMap.putIfAbsent(key, () {
var result = build(key);
referencedNodes.add(key);
level[result] = LoadingLevel.Reference;
inverse[result] = key;
return result;
});
}
}
AnalysisOptions createAnalysisOptions(bool strongMode) {
return new AnalysisOptionsImpl()
..strongMode = strongMode
..enableGenericMethods = strongMode
..generateImplicitErrors = true
..generateSdkErrors = true
..preserveComments = false
..hint = false
..enableSuperMixins = true;
}
DartSdk createDartSdk(String path, bool strongMode) {
var resources = PhysicalResourceProvider.INSTANCE;
return new FolderBasedDartSdk(resources, resources.getFolder(path))
..context
.analysisOptions
.setCrossContextOptionsFrom(createAnalysisOptions(strongMode));
}
AnalysisContext createContext(String sdk, String packageRoot, bool strongMode,
{DartSdk dartSdk}) {
dartSdk ??= createDartSdk(sdk, strongMode);
List<UriResolver> resolvers = [
new DartUriResolver(dartSdk),
new ResourceUriResolver(PhysicalResourceProvider.INSTANCE)
];
if (packageRoot != null) {
var packageDirectory = new JavaFile(packageRoot);
resolvers.add(new PackageUriResolver([packageDirectory]));
}
AnalysisContext context = AnalysisEngine.instance.createAnalysisContext()
..sourceFactory = new SourceFactory(resolvers)
..analysisOptions = createAnalysisOptions(strongMode);
return context;
}