Remove the checker and corresponding dead code
This is all logic now in the analyzer. R=jmesserly@google.com Review URL: https://codereview.chromium.org/1406983003 .
This commit is contained in:
@@ -23,7 +23,6 @@ import 'package:source_span/source_span.dart';
|
||||
import 'package:dev_compiler/src/analysis_context.dart';
|
||||
import 'package:dev_compiler/src/options.dart';
|
||||
import 'package:dev_compiler/src/summary.dart';
|
||||
import 'package:dev_compiler/strong_mode.dart';
|
||||
|
||||
final ArgParser argParser = new ArgParser()
|
||||
..addOption('level', help: 'Minimum error level', defaultsTo: "info")
|
||||
@@ -154,8 +153,7 @@ void main(List<String> argv) {
|
||||
? new RegExp(args['include-pattern'])
|
||||
: null;
|
||||
|
||||
var context =
|
||||
createAnalysisContextWithSources(new StrongModeOptions(), options);
|
||||
var context = createAnalysisContextWithSources(options);
|
||||
var visitor = new EditFileSummaryVisitor(
|
||||
context,
|
||||
args['level'],
|
||||
|
||||
@@ -9,7 +9,6 @@ export 'src/analysis_context.dart'
|
||||
show createAnalysisContext, createAnalysisContextWithSources;
|
||||
export 'src/compiler.dart' show BatchCompiler, setupLogger, createErrorReporter;
|
||||
export 'src/server/server.dart' show DevServer;
|
||||
export 'strong_mode.dart' show StrongModeOptions;
|
||||
|
||||
// When updating this version, also update the version in the pubspec.
|
||||
const devCompilerVersion = '0.1.9';
|
||||
|
||||
@@ -10,9 +10,6 @@ import 'package:analyzer/src/generated/sdk_io.dart' show DirectoryBasedDartSdk;
|
||||
import 'package:analyzer/src/generated/source.dart' show DartUriResolver;
|
||||
import 'package:analyzer/src/generated/source_io.dart';
|
||||
|
||||
import '../strong_mode.dart' show StrongModeOptions;
|
||||
|
||||
import 'checker/resolver.dart';
|
||||
import 'dart_sdk.dart';
|
||||
import 'multi_package_resolver.dart';
|
||||
import 'options.dart';
|
||||
@@ -20,29 +17,22 @@ import 'options.dart';
|
||||
/// Creates an [AnalysisContext] with dev_compiler type rules and inference,
|
||||
/// using [createSourceFactory] to set up its [SourceFactory].
|
||||
AnalysisContext createAnalysisContextWithSources(
|
||||
StrongModeOptions strongOptions, SourceResolverOptions srcOptions,
|
||||
{DartUriResolver sdkResolver, List<UriResolver> fileResolvers}) {
|
||||
SourceResolverOptions srcOptions,
|
||||
{DartUriResolver sdkResolver,
|
||||
List<UriResolver> fileResolvers}) {
|
||||
AnalysisEngine.instance.useTaskModel = true;
|
||||
var srcFactory = createSourceFactory(srcOptions,
|
||||
sdkResolver: sdkResolver, fileResolvers: fileResolvers);
|
||||
return createAnalysisContext(strongOptions)..sourceFactory = srcFactory;
|
||||
return createAnalysisContext()..sourceFactory = srcFactory;
|
||||
}
|
||||
|
||||
/// Creates an analysis context that contains our restricted typing rules.
|
||||
AnalysisContext createAnalysisContext(StrongModeOptions options) {
|
||||
AnalysisContext createAnalysisContext() {
|
||||
var res = AnalysisEngine.instance.createAnalysisContext();
|
||||
res.analysisOptions.strongMode = true;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Enables dev_compiler inference rules.
|
||||
// TODO(jmesserly): is there a cleaner way to plug this in?
|
||||
void enableDevCompilerInference(
|
||||
AnalysisContextImpl context, StrongModeOptions options) {
|
||||
context.libraryResolverFactory = (c) =>
|
||||
new LibraryResolverWithInference(c, options);
|
||||
}
|
||||
|
||||
/// Creates a SourceFactory configured by the [options].
|
||||
///
|
||||
/// Use [options.useMockSdk] to specify the SDK mode, or use [sdkResolver]
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
// Copyright (c) 2015, 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 dev_compiler.src.checker.checker;
|
||||
|
||||
export 'package:analyzer/src/task/strong/checker.dart';
|
||||
@@ -1,757 +0,0 @@
|
||||
// Copyright (c) 2015, 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.
|
||||
|
||||
/// Encapsulates how to invoke the analyzer resolver and overrides how it
|
||||
/// computes types on expressions to use our restricted set of types.
|
||||
library dev_compiler.src.checker.resolver;
|
||||
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:analyzer/src/generated/ast.dart';
|
||||
import 'package:analyzer/src/generated/element.dart';
|
||||
import 'package:analyzer/src/generated/resolver.dart';
|
||||
import 'package:analyzer/src/generated/source.dart' show Source;
|
||||
import 'package:analyzer/src/generated/source_io.dart';
|
||||
import 'package:analyzer/src/generated/static_type_analyzer.dart';
|
||||
import 'package:analyzer/src/generated/utilities_collection.dart'
|
||||
show DirectedGraph;
|
||||
import 'package:logging/logging.dart' as logger;
|
||||
|
||||
import '../../strong_mode.dart' show StrongModeOptions;
|
||||
import '../utils.dart';
|
||||
import 'rules.dart';
|
||||
|
||||
final _log = new logger.Logger('dev_compiler.src.resolver');
|
||||
|
||||
/// A [LibraryResolver] that performs inference on top-levels and fields based
|
||||
/// on the value of the initializer, and on fields and methods based on
|
||||
/// overridden members in super classes.
|
||||
class LibraryResolverWithInference extends LibraryResolver {
|
||||
final StrongModeOptions _options;
|
||||
|
||||
LibraryResolverWithInference(context, this._options) : super(context);
|
||||
|
||||
@override
|
||||
void resolveReferencesAndTypes() {
|
||||
_resolveVariableReferences();
|
||||
|
||||
// Run resolution in two stages, skipping method bodies first, so we can run
|
||||
// type-inference before we fully analyze methods.
|
||||
var visitors = _createVisitors();
|
||||
_resolveEverything(visitors);
|
||||
_runInference(visitors);
|
||||
|
||||
visitors.values.forEach((v) => v.skipMethodBodies = false);
|
||||
_resolveEverything(visitors);
|
||||
}
|
||||
|
||||
// Note: this was split from _resolveReferencesAndTypesInLibrary so we do it
|
||||
// only once.
|
||||
void _resolveVariableReferences() {
|
||||
for (Library library in resolvedLibraries) {
|
||||
for (Source source in library.compilationUnitSources) {
|
||||
library.getAST(source).accept(new VariableResolverVisitor(
|
||||
library.libraryElement, source, typeProvider, library.errorListener,
|
||||
nameScope: library.libraryScope));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: this was split from _resolveReferencesAndTypesInLibrary so we can do
|
||||
// resolution in pieces.
|
||||
Map<Source, RestrictedResolverVisitor> _createVisitors() {
|
||||
var visitors = <Source, RestrictedResolverVisitor>{};
|
||||
for (Library library in resolvedLibraries) {
|
||||
for (Source source in library.compilationUnitSources) {
|
||||
var visitor = new RestrictedResolverVisitor(
|
||||
library, source, typeProvider, _options);
|
||||
visitors[source] = visitor;
|
||||
}
|
||||
}
|
||||
return visitors;
|
||||
}
|
||||
|
||||
/// Runs the resolver on the entire library cycle.
|
||||
void _resolveEverything(Map<Source, RestrictedResolverVisitor> visitors) {
|
||||
for (Library library in resolvedLibraries) {
|
||||
for (Source source in library.compilationUnitSources) {
|
||||
library.getAST(source).accept(visitors[source]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_runInference(Map<Source, RestrictedResolverVisitor> visitors) {
|
||||
var globalsAndStatics = <VariableDeclaration>[];
|
||||
var classes = <ClassDeclaration>[];
|
||||
|
||||
// Extract top-level members that are const, statics, or classes.
|
||||
for (Library library in resolvedLibraries) {
|
||||
for (Source source in library.compilationUnitSources) {
|
||||
CompilationUnit ast = library.getAST(source);
|
||||
for (var declaration in ast.declarations) {
|
||||
if (declaration is TopLevelVariableDeclaration) {
|
||||
globalsAndStatics.addAll(declaration.variables.variables);
|
||||
} else if (declaration is ClassDeclaration) {
|
||||
classes.add(declaration);
|
||||
for (var member in declaration.members) {
|
||||
if (member is FieldDeclaration &&
|
||||
(member.fields.isConst || member.isStatic)) {
|
||||
globalsAndStatics.addAll(member.fields.variables);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_inferGlobalsAndStatics(globalsAndStatics, visitors);
|
||||
_inferInstanceFields(classes, visitors);
|
||||
}
|
||||
|
||||
_inferGlobalsAndStatics(List<VariableDeclaration> globalsAndStatics,
|
||||
Map<Source, RestrictedResolverVisitor> visitors) {
|
||||
var elementToDeclaration = {};
|
||||
for (var c in globalsAndStatics) {
|
||||
elementToDeclaration[c.element] = c;
|
||||
}
|
||||
var constGraph = new DirectedGraph<VariableDeclaration>();
|
||||
globalsAndStatics.forEach(constGraph.addNode);
|
||||
for (var c in globalsAndStatics) {
|
||||
for (var e in _VarExtractor.extract(c.initializer)) {
|
||||
// Note: declaration is null for variables that come from other strongly
|
||||
// connected components.
|
||||
var declaration = elementToDeclaration[e];
|
||||
if (declaration != null) constGraph.addEdge(c, declaration);
|
||||
}
|
||||
}
|
||||
|
||||
for (var component in constGraph.computeTopologicalSort()) {
|
||||
component.forEach((v) => _reanalyzeVar(visitors, v));
|
||||
_inferVariableFromInitializer(component);
|
||||
}
|
||||
}
|
||||
|
||||
_inferInstanceFields(List<ClassDeclaration> classes,
|
||||
Map<Source, RestrictedResolverVisitor> visitors) {
|
||||
// First propagate what was inferred from globals to all instance fields.
|
||||
|
||||
// TODO(sigmund): also do a fine-grain propagation between fields. We want
|
||||
// infer-by-override to take precedence, so we would have to include
|
||||
// classes in the dependency graph and ensure that fields depend on their
|
||||
// class, and classes depend on superclasses.
|
||||
classes
|
||||
.expand((c) => c.members.where(_isInstanceField))
|
||||
.expand((f) => f.fields.variables)
|
||||
.forEach((v) => _reanalyzeVar(visitors, v));
|
||||
|
||||
// Track types in this strongly connected component, ensure we visit
|
||||
// supertypes before subtypes.
|
||||
var typeToDeclaration = <InterfaceType, ClassDeclaration>{};
|
||||
classes.forEach((c) => typeToDeclaration[c.element.type] = c);
|
||||
var seen = new Set<InterfaceType>();
|
||||
visit(ClassDeclaration cls) {
|
||||
var element = cls.element;
|
||||
var type = element.type;
|
||||
if (seen.contains(type)) return;
|
||||
seen.add(type);
|
||||
for (var supertype in element.allSupertypes) {
|
||||
var supertypeClass = typeToDeclaration[supertype];
|
||||
if (supertypeClass != null) visit(supertypeClass);
|
||||
}
|
||||
|
||||
// Infer field types from overrides first, otherwise from initializers.
|
||||
var pending = new Set<VariableDeclaration>();
|
||||
cls.members
|
||||
.where(_isInstanceField)
|
||||
.forEach((f) => _inferFieldTypeFromOverride(f, pending));
|
||||
if (pending.isNotEmpty) _inferVariableFromInitializer(pending);
|
||||
|
||||
// Infer return-types and param-types from overrides
|
||||
cls.members
|
||||
.where((m) => m is MethodDeclaration && !m.isStatic)
|
||||
.forEach(_inferMethodTypesFromOverride);
|
||||
}
|
||||
classes.forEach(visit);
|
||||
}
|
||||
|
||||
void _reanalyzeVar(Map<Source, RestrictedResolverVisitor> visitors,
|
||||
VariableDeclaration variable) {
|
||||
if (variable.initializer == null) return;
|
||||
var visitor = visitors[(variable.root as CompilationUnit).element.source];
|
||||
visitor.reanalyzeInitializer(variable);
|
||||
}
|
||||
|
||||
static bool _isInstanceField(f) =>
|
||||
f is FieldDeclaration && !f.isStatic && !f.fields.isConst;
|
||||
|
||||
/// Attempts to infer the type on [field] from overridden fields or getters if
|
||||
/// a type was not specified. If no type could be inferred, but it contains an
|
||||
/// initializer, we add it to [pending] so we can try to infer it using the
|
||||
/// initializer type instead.
|
||||
void _inferFieldTypeFromOverride(
|
||||
FieldDeclaration field, Set<VariableDeclaration> pending) {
|
||||
var variables = field.fields;
|
||||
for (var variable in variables.variables) {
|
||||
var varElement = variable.element as FieldElement;
|
||||
if (!varElement.type.isDynamic || variables.type != null) continue;
|
||||
var getter = varElement.getter;
|
||||
// Note: type will be null only when there are no overrides. When some
|
||||
// override's type was not specified and couldn't be inferred, the type
|
||||
// here will be dynamic.
|
||||
var enclosingElement = varElement.enclosingElement;
|
||||
var type = searchTypeFor(enclosingElement.type, getter);
|
||||
|
||||
// Infer from the RHS when there are no overrides.
|
||||
if (type == null) {
|
||||
if (variable.initializer != null) pending.add(variable);
|
||||
continue;
|
||||
}
|
||||
|
||||
// When field is final and overridden getter is dynamic, we can infer from
|
||||
// the RHS without breaking subtyping rules (return type is covariant).
|
||||
if (type.returnType.isDynamic) {
|
||||
if (variables.isFinal && variable.initializer != null) {
|
||||
pending.add(variable);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use type from the override.
|
||||
var newType = type.returnType;
|
||||
varElement.type = newType;
|
||||
varElement.getter.returnType = newType;
|
||||
if (!varElement.isFinal) varElement.setter.parameters[0].type = newType;
|
||||
}
|
||||
}
|
||||
|
||||
void _inferMethodTypesFromOverride(MethodDeclaration method) {
|
||||
var methodElement = method.element;
|
||||
if (methodElement is! MethodElement &&
|
||||
methodElement is! PropertyAccessorElement) return;
|
||||
|
||||
var enclosingElement = methodElement.enclosingElement as ClassElement;
|
||||
FunctionType type = null;
|
||||
|
||||
// Infer the return type if omitted
|
||||
if (methodElement.returnType.isDynamic && method.returnType == null) {
|
||||
type = searchTypeFor(enclosingElement.type, methodElement);
|
||||
if (type == null) return;
|
||||
if (!type.returnType.isDynamic) {
|
||||
methodElement.returnType = type.returnType;
|
||||
}
|
||||
}
|
||||
|
||||
// Infer parameter types if omitted
|
||||
if (method.parameters == null) return;
|
||||
var parameters = method.parameters.parameters;
|
||||
var length = parameters.length;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
var parameter = parameters[i];
|
||||
if (parameter is DefaultFormalParameter) parameter = parameter.parameter;
|
||||
if (parameter is SimpleFormalParameter && parameter.type == null) {
|
||||
type = type ?? searchTypeFor(enclosingElement.type, methodElement);
|
||||
if (type == null) return;
|
||||
if (type.parameters.length > i && !type.parameters[i].type.isDynamic) {
|
||||
parameter.element.type = type.parameters[i].type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _inferVariableFromInitializer(Iterable<VariableDeclaration> variables) {
|
||||
for (var variable in variables) {
|
||||
var declaration = variable.parent as VariableDeclarationList;
|
||||
// Only infer on variables that don't have any declared type.
|
||||
if (declaration.type != null) continue;
|
||||
var initializer = variable.initializer;
|
||||
if (initializer == null) continue;
|
||||
var type = initializer.staticType;
|
||||
if (type == null || type.isDynamic || type.isBottom) continue;
|
||||
var element = variable.element as PropertyInducingElement;
|
||||
// Note: it's ok to update the type here, since initializer.staticType
|
||||
// is already computed for all declarations in the library cycle. The
|
||||
// new types will only be propagated on a second run of the
|
||||
// ResolverVisitor.
|
||||
element.type = type;
|
||||
element.getter.returnType = type;
|
||||
if (!element.isFinal && !element.isConst) {
|
||||
element.setter.parameters[0].type = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts the [VariableElement]s used in an initializer expression.
|
||||
class _VarExtractor extends RecursiveAstVisitor {
|
||||
final elements = <VariableElement>[];
|
||||
visitSimpleIdentifier(SimpleIdentifier node) {
|
||||
var e = node.staticElement;
|
||||
if (e is PropertyAccessorElement) elements.add(e.variable);
|
||||
}
|
||||
|
||||
static List<VariableElement> extract(Expression initializer) {
|
||||
if (initializer == null) return const [];
|
||||
var extractor = new _VarExtractor();
|
||||
initializer.accept(extractor);
|
||||
return extractor.elements;
|
||||
}
|
||||
}
|
||||
|
||||
/// Overrides the default [ResolverVisitor] to support type inference in
|
||||
/// [LibraryResolverWithInference] above.
|
||||
///
|
||||
/// Before inference, this visitor is used to resolve top-levels, classes, and
|
||||
/// fields, but nothing within method bodies. After inference, this visitor is
|
||||
/// used again to step into method bodies and complete resolution as a second
|
||||
/// phase.
|
||||
class RestrictedResolverVisitor extends ResolverVisitor {
|
||||
final TypeProvider _typeProvider;
|
||||
|
||||
/// Whether to skip resolution within method bodies.
|
||||
bool skipMethodBodies = true;
|
||||
|
||||
/// State of the resolver at the point a field or variable was declared.
|
||||
final _stateAtDeclaration = <AstNode, _ResolverState>{};
|
||||
|
||||
/// Internal tracking of whether a node was skipped while visiting, for
|
||||
/// example, if it contained a function expression with a function body.
|
||||
bool _nodeWasSkipped = false;
|
||||
|
||||
/// Internal state, whether we are revisiting an initializer, so we minimize
|
||||
/// the work being done elsewhere.
|
||||
bool _revisiting = false;
|
||||
|
||||
/// Initializers that have been visited, reanalyzed, and for which no node was
|
||||
/// internally skipped. These initializers are fully resolved and don't need
|
||||
/// to be re-resolved on a sunsequent pass.
|
||||
final _visitedInitializers = new Set<VariableDeclaration>();
|
||||
|
||||
RestrictedResolverVisitor(Library library, Source source,
|
||||
TypeProvider typeProvider, StrongModeOptions options)
|
||||
: _typeProvider = typeProvider,
|
||||
super(
|
||||
library.libraryElement, source, typeProvider, library.errorListener,
|
||||
nameScope: library.libraryScope,
|
||||
inheritanceManager: library.inheritanceManager,
|
||||
typeAnalyzerFactory: RestrictedStaticTypeAnalyzer.constructor);
|
||||
|
||||
reanalyzeInitializer(VariableDeclaration variable) {
|
||||
try {
|
||||
_revisiting = true;
|
||||
_nodeWasSkipped = false;
|
||||
var node = variable.parent.parent;
|
||||
var oldState;
|
||||
var state = _stateAtDeclaration[node];
|
||||
if (state != null) {
|
||||
oldState = new _ResolverState(this);
|
||||
state.restore(this);
|
||||
if (node is FieldDeclaration) {
|
||||
var cls = node.parent as ClassDeclaration;
|
||||
enclosingClass = cls.element;
|
||||
}
|
||||
}
|
||||
visitNode(variable.initializer);
|
||||
if (!_nodeWasSkipped) _visitedInitializers.add(variable);
|
||||
if (oldState != null) oldState.restore(this);
|
||||
} finally {
|
||||
_revisiting = false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
|
||||
_stateAtDeclaration[node] = new _ResolverState(this);
|
||||
return super.visitTopLevelVariableDeclaration(node);
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitFieldDeclaration(FieldDeclaration node) {
|
||||
_stateAtDeclaration[node] = new _ResolverState(this);
|
||||
return super.visitFieldDeclaration(node);
|
||||
}
|
||||
|
||||
Object visitVariableDeclaration(VariableDeclaration node) {
|
||||
var state = new _ResolverState(this);
|
||||
try {
|
||||
if (_revisiting) {
|
||||
_stateAtDeclaration[node].restore(this);
|
||||
} else {
|
||||
_stateAtDeclaration[node] = state;
|
||||
}
|
||||
return super.visitVariableDeclaration(node);
|
||||
} finally {
|
||||
state.restore(this);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitNode(AstNode node) {
|
||||
if (skipMethodBodies && node is FunctionBody) {
|
||||
_nodeWasSkipped = true;
|
||||
return null;
|
||||
}
|
||||
if (_visitedInitializers.contains(node)) return null;
|
||||
assert(node is! Statement || !skipMethodBodies);
|
||||
return super.visitNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitMethodDeclaration(MethodDeclaration node) {
|
||||
if (skipMethodBodies) {
|
||||
node.accept(elementResolver);
|
||||
node.accept(typeAnalyzer);
|
||||
return null;
|
||||
} else {
|
||||
return super.visitMethodDeclaration(node);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitFunctionDeclaration(FunctionDeclaration node) {
|
||||
if (skipMethodBodies) {
|
||||
node.accept(elementResolver);
|
||||
node.accept(typeAnalyzer);
|
||||
return null;
|
||||
} else {
|
||||
return super.visitFunctionDeclaration(node);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitConstructorDeclaration(ConstructorDeclaration node) {
|
||||
if (skipMethodBodies) {
|
||||
node.accept(elementResolver);
|
||||
node.accept(typeAnalyzer);
|
||||
return null;
|
||||
} else {
|
||||
return super.visitConstructorDeclaration(node);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
visitFieldFormalParameter(FieldFormalParameter node) {
|
||||
// Ensure the field formal parameter's type is updated after inference.
|
||||
// Normally this happens during TypeResolver, but that's before we've done
|
||||
// inference on the field type.
|
||||
var element = node.element;
|
||||
if (element is FieldFormalParameterElement) {
|
||||
if (element.type.isDynamic) {
|
||||
// In malformed code, there may be no actual field.
|
||||
if (element.field != null) {
|
||||
element.type = element.field.type;
|
||||
}
|
||||
}
|
||||
}
|
||||
super.visitFieldFormalParameter(node);
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal state of the resolver, stored so we can reanalyze portions of the
|
||||
/// AST quickly, without recomputing everything from the top.
|
||||
class _ResolverState {
|
||||
final TypePromotionManager_TypePromoteScope promotionScope;
|
||||
final TypeOverrideManager_TypeOverrideScope overrideScope;
|
||||
final Scope nameScope;
|
||||
|
||||
_ResolverState(ResolverVisitor visitor)
|
||||
: promotionScope = visitor.promoteManager.currentScope,
|
||||
overrideScope = visitor.overrideManager.currentScope,
|
||||
nameScope = visitor.nameScope;
|
||||
|
||||
void restore(ResolverVisitor visitor) {
|
||||
visitor.promoteManager.currentScope = promotionScope;
|
||||
visitor.overrideManager.currentScope = overrideScope;
|
||||
visitor.nameScope = nameScope;
|
||||
}
|
||||
}
|
||||
|
||||
/// Overrides the default [StaticTypeAnalyzer] to adjust rules that are stricter
|
||||
/// in the restricted type system and to infer types for untyped local
|
||||
/// variables.
|
||||
class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer {
|
||||
final TypeProvider _typeProvider;
|
||||
Map<String, DartType> _objectMembers;
|
||||
|
||||
RestrictedStaticTypeAnalyzer(ResolverVisitor r)
|
||||
: _typeProvider = r.typeProvider,
|
||||
super(r) {
|
||||
_objectMembers = getObjectMemberMap(_typeProvider);
|
||||
}
|
||||
|
||||
static constructor(ResolverVisitor r) => new RestrictedStaticTypeAnalyzer(r);
|
||||
|
||||
@override // to infer type from initializers
|
||||
visitVariableDeclaration(VariableDeclaration node) {
|
||||
_inferType(node);
|
||||
return super.visitVariableDeclaration(node);
|
||||
}
|
||||
|
||||
/// Infer the type of a variable based on the initializer's type.
|
||||
void _inferType(VariableDeclaration node) {
|
||||
var initializer = node.initializer;
|
||||
if (initializer == null) return;
|
||||
|
||||
var declaredType = (node.parent as VariableDeclarationList).type;
|
||||
if (declaredType != null) return;
|
||||
var element = node.element;
|
||||
if (element is! LocalVariableElement) return;
|
||||
if (element.type != _typeProvider.dynamicType) return;
|
||||
|
||||
var type = initializer.staticType;
|
||||
if (type == null || type == _typeProvider.bottomType) return;
|
||||
element.type = type;
|
||||
if (element is PropertyInducingElement) {
|
||||
element.getter.returnType = type;
|
||||
if (!element.isFinal && !element.isConst) {
|
||||
element.setter.parameters[0].type = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(vsm): Use leafp's matchType here?
|
||||
DartType _findIteratedType(InterfaceType type) {
|
||||
if (type.element == _typeProvider.iterableType.element) {
|
||||
var typeArguments = type.typeArguments;
|
||||
assert(typeArguments.length == 1);
|
||||
return typeArguments[0];
|
||||
}
|
||||
|
||||
if (type == _typeProvider.objectType) return null;
|
||||
|
||||
var result = _findIteratedType(type.superclass);
|
||||
if (result != null) return result;
|
||||
|
||||
for (final parent in type.interfaces) {
|
||||
result = _findIteratedType(parent);
|
||||
if (result != null) return result;
|
||||
}
|
||||
|
||||
for (final parent in type.mixins) {
|
||||
result = _findIteratedType(parent);
|
||||
if (result != null) return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
visitDeclaredIdentifier(DeclaredIdentifier node) {
|
||||
super.visitDeclaredIdentifier(node);
|
||||
if (node.type != null) return;
|
||||
|
||||
var parent = node.parent as ForEachStatement;
|
||||
var expr = parent.iterable;
|
||||
var element = node.element as LocalVariableElementImpl;
|
||||
var exprType = expr.staticType;
|
||||
if (exprType is InterfaceType) {
|
||||
var iteratedType = _findIteratedType(exprType);
|
||||
if (iteratedType != null) {
|
||||
element.type = iteratedType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _isSealed(DartType t) {
|
||||
return _typeProvider.nonSubtypableTypes.contains(t);
|
||||
}
|
||||
|
||||
List<List> _genericList = null;
|
||||
|
||||
DartType _matchGeneric(MethodInvocation node, Element element) {
|
||||
var e = node.methodName.staticElement;
|
||||
|
||||
if (_genericList == null) {
|
||||
var minmax = (DartType tx, DartType ty) => (tx == ty &&
|
||||
(tx == _typeProvider.intType || tx == _typeProvider.doubleType))
|
||||
? tx
|
||||
: null;
|
||||
|
||||
var map = (DartType tx) => (tx is FunctionType)
|
||||
? _typeProvider.iterableType.substitute4([tx.returnType])
|
||||
: null;
|
||||
|
||||
// TODO(vsm): LUB?
|
||||
var fold = (DartType tx, DartType ty) =>
|
||||
(ty is FunctionType && tx == ty.returnType) ? tx : null;
|
||||
|
||||
// TODO(vsm): Flatten?
|
||||
var then = (DartType tx) => (tx is FunctionType)
|
||||
? _typeProvider.futureType.substitute4([tx.returnType])
|
||||
: null;
|
||||
|
||||
var wait = (DartType tx) {
|
||||
// Iterable<Future<T>> -> Future<List<T>>
|
||||
var futureType = _findIteratedType(tx);
|
||||
if (futureType.element.type != _typeProvider.futureType) return null;
|
||||
var typeArguments = futureType.typeArguments;
|
||||
if (typeArguments.length != 1) return null;
|
||||
var baseType = typeArguments[0];
|
||||
if (baseType.isDynamic) return null;
|
||||
return _typeProvider.futureType.substitute4([
|
||||
_typeProvider.listType.substitute4([baseType])
|
||||
]);
|
||||
};
|
||||
|
||||
_genericList = [
|
||||
// Top-level methods
|
||||
['dart:math', 'max', 2, minmax],
|
||||
['dart:math', 'min', 2, minmax],
|
||||
// Static methods
|
||||
[_typeProvider.futureType, 'wait', 1, wait],
|
||||
// Instance methods
|
||||
[_typeProvider.iterableDynamicType, 'map', 1, map],
|
||||
[_typeProvider.iterableDynamicType, 'fold', 2, fold],
|
||||
[_typeProvider.futureDynamicType, 'then', 1, then],
|
||||
];
|
||||
}
|
||||
|
||||
var targetType = node.target?.staticType;
|
||||
var arguments = node.argumentList.arguments;
|
||||
|
||||
for (var generic in _genericList) {
|
||||
if (e?.name == generic[1]) {
|
||||
if ((generic[0] is String &&
|
||||
element?.library.source.uri.toString() == generic[0]) ||
|
||||
(generic[0] is DartType &&
|
||||
targetType != null &&
|
||||
targetType.isSubtypeOf(generic[0]))) {
|
||||
if (arguments.length == generic[2]) {
|
||||
return Function.apply(
|
||||
generic[3], arguments.map((arg) => arg.staticType).toList());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@override // to propagate types to identifiers
|
||||
visitMethodInvocation(MethodInvocation node) {
|
||||
// TODO(jmesserly): we rely on having a staticType propagated to the
|
||||
// methodName identifier. This shouldn't be necessary for method calls, so
|
||||
// analyzer doesn't do it by default. Conceptually what we're doing here
|
||||
// is asking for a tear off. We need this until we can fix #132, and rely
|
||||
// on `node.staticElement == null` instead of `rules.isDynamicCall(node)`.
|
||||
visitSimpleIdentifier(node.methodName);
|
||||
|
||||
super.visitMethodInvocation(node);
|
||||
|
||||
// Search for Object methods.
|
||||
var name = node.methodName.name;
|
||||
if (node.staticType.isDynamic &&
|
||||
_objectMembers.containsKey(name) &&
|
||||
isDynamicTarget(node.target)) {
|
||||
var type = _objectMembers[name];
|
||||
if (type is FunctionType &&
|
||||
type.parameters.isEmpty &&
|
||||
node.argumentList.arguments.isEmpty) {
|
||||
node.methodName.staticType = type;
|
||||
// Only infer the type of the overall expression if we have an exact
|
||||
// type - e.g., a sealed type. Otherwise, it may be too strict.
|
||||
if (_isSealed(type.returnType)) {
|
||||
node.staticType = type.returnType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var e = node.methodName.staticElement;
|
||||
if (isInlineJS(e)) {
|
||||
// Fix types for JS builtin calls.
|
||||
//
|
||||
// This code was taken from analyzer. It's not super sophisticated:
|
||||
// only looks for the type name in dart:core, so we just copy it here.
|
||||
//
|
||||
// TODO(jmesserly): we'll likely need something that can handle a wider
|
||||
// variety of types, especially when we get to JS interop.
|
||||
var args = node.argumentList.arguments;
|
||||
var first = args.isNotEmpty ? args.first : null;
|
||||
if (first is SimpleStringLiteral) {
|
||||
var typeStr = first.stringValue;
|
||||
if (typeStr == '-dynamic') {
|
||||
node.staticType = _typeProvider.bottomType;
|
||||
} else {
|
||||
var coreLib = _typeProvider.objectType.element.library;
|
||||
var classElem = coreLib.getType(typeStr);
|
||||
if (classElem != null) {
|
||||
var type = fillDynamicTypeArgs(classElem.type, _typeProvider);
|
||||
node.staticType = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pretend dart:math's min and max are generic:
|
||||
//
|
||||
// T min<T extends num>(T x, T y);
|
||||
//
|
||||
// and infer T. In practice, this just means if the type of x and y are
|
||||
// both double or both int, we treat that as the return type.
|
||||
//
|
||||
// The Dart spec has similar treatment for binary operations on numbers.
|
||||
//
|
||||
// TODO(jmesserly): remove this when we have a fix for
|
||||
// https://github.com/dart-lang/dev_compiler/issues/28
|
||||
var inferred = _matchGeneric(node, e);
|
||||
// TODO(vsm): If the inferred type is not a subtype, should we use a GLB instead?
|
||||
if (inferred != null && inferred.isSubtypeOf(node.staticType)) {
|
||||
node.staticType = inferred;
|
||||
}
|
||||
}
|
||||
|
||||
void _inferObjectAccess(
|
||||
Expression node, Expression target, SimpleIdentifier id) {
|
||||
// Search for Object accesses.
|
||||
var name = id.name;
|
||||
if (node.staticType.isDynamic &&
|
||||
_objectMembers.containsKey(name) &&
|
||||
isDynamicTarget(target)) {
|
||||
var type = _objectMembers[name];
|
||||
id.staticType = type;
|
||||
// Only infer the type of the overall expression if we have an exact
|
||||
// type - e.g., a sealed type. Otherwise, it may be too strict.
|
||||
if (_isSealed(type)) {
|
||||
node.staticType = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
visitPropertyAccess(PropertyAccess node) {
|
||||
super.visitPropertyAccess(node);
|
||||
|
||||
_inferObjectAccess(node, node.target, node.propertyName);
|
||||
}
|
||||
|
||||
@override
|
||||
visitPrefixedIdentifier(PrefixedIdentifier node) {
|
||||
super.visitPrefixedIdentifier(node);
|
||||
|
||||
_inferObjectAccess(node, node.prefix, node.identifier);
|
||||
}
|
||||
|
||||
@override
|
||||
visitConditionalExpression(ConditionalExpression node) {
|
||||
// TODO(vsm): The static type of a conditional should be the LUB of the
|
||||
// then and else expressions. The analyzer appears to compute dynamic when
|
||||
// one or the other is the null literal. Remove this fix once the
|
||||
// corresponding analyzer bug is fixed:
|
||||
// https://code.google.com/p/dart/issues/detail?id=22854
|
||||
super.visitConditionalExpression(node);
|
||||
if (node.staticType.isDynamic) {
|
||||
var thenExpr = node.thenExpression;
|
||||
var elseExpr = node.elseExpression;
|
||||
if (thenExpr.staticType.isBottom) {
|
||||
node.staticType = elseExpr.staticType;
|
||||
} else if (elseExpr.staticType.isBottom) {
|
||||
node.staticType = thenExpr.staticType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Review note: no longer need to override visitFunctionExpression, this is
|
||||
// handled by the analyzer internally.
|
||||
// TODO(vsm): in visitbinaryExpression: check computeStaticReturnType result?
|
||||
// TODO(vsm): in visitFunctionDeclaration: Should we ever use the expression
|
||||
// type in a (...) => expr or just the written type?
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
// Copyright (c) 2015, 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 dev_compiler.src.checker.rules;
|
||||
|
||||
export 'package:analyzer/src/task/strong/rules.dart';
|
||||
@@ -7,12 +7,12 @@ library dev_compiler.src.codegen.code_generator;
|
||||
import 'package:analyzer/src/generated/element.dart'
|
||||
show CompilationUnitElement, LibraryElement;
|
||||
import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
|
||||
import 'package:analyzer/src/task/strong/rules.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import '../compiler.dart' show AbstractCompiler;
|
||||
import '../info.dart';
|
||||
import '../utils.dart' show canonicalLibraryName;
|
||||
import '../checker/rules.dart';
|
||||
import '../options.dart' show CodegenOptions;
|
||||
|
||||
abstract class CodeGenerator {
|
||||
|
||||
@@ -14,6 +14,7 @@ import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
|
||||
import 'package:analyzer/src/generated/scanner.dart'
|
||||
show StringToken, Token, TokenType;
|
||||
import 'package:analyzer/src/task/dart.dart' show PublicNamespaceBuilder;
|
||||
import 'package:analyzer/src/task/strong/rules.dart';
|
||||
|
||||
import 'ast_builder.dart' show AstBuilder;
|
||||
import 'reify_coercions.dart' show CoercionReifier, Tuple2;
|
||||
@@ -24,7 +25,6 @@ import '../js/js_ast.dart' show js;
|
||||
|
||||
import '../closure/closure_annotator.dart' show ClosureAnnotator;
|
||||
import '../compiler.dart' show AbstractCompiler;
|
||||
import '../checker/rules.dart';
|
||||
import '../info.dart';
|
||||
import '../options.dart' show CodegenOptions;
|
||||
import '../utils.dart';
|
||||
|
||||
@@ -7,9 +7,9 @@ library dev_compiler.src.codegen.reify_coercions;
|
||||
import 'package:analyzer/analyzer.dart' as analyzer;
|
||||
import 'package:analyzer/src/generated/ast.dart';
|
||||
import 'package:analyzer/src/generated/element.dart';
|
||||
import 'package:analyzer/src/task/strong/rules.dart';
|
||||
import 'package:logging/logging.dart' as logger;
|
||||
|
||||
import '../checker/rules.dart';
|
||||
import '../info.dart';
|
||||
|
||||
import 'ast_builder.dart';
|
||||
|
||||
@@ -63,8 +63,7 @@ CompilerOptions validateOptions(List<String> args, {bool forceOutDir: false}) {
|
||||
bool compile(CompilerOptions options) {
|
||||
assert(!options.serverMode);
|
||||
|
||||
var context = createAnalysisContextWithSources(
|
||||
options.strongOptions, options.sourceOptions);
|
||||
var context = createAnalysisContextWithSources(options.sourceOptions);
|
||||
var reporter = createErrorReporter(context, options);
|
||||
bool status = new BatchCompiler(context, options, reporter: reporter).run();
|
||||
|
||||
|
||||
@@ -13,8 +13,6 @@ import 'package:logging/logging.dart' show Level;
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
import '../strong_mode.dart' show StrongModeOptions;
|
||||
|
||||
const bool _CLOSURE_DEFAULT = false;
|
||||
|
||||
/// Options used to set up Source URI resolution in the analysis context.
|
||||
@@ -94,7 +92,6 @@ class RunnerOptions {
|
||||
|
||||
/// General options used by the dev compiler and server.
|
||||
class CompilerOptions {
|
||||
final StrongModeOptions strongOptions;
|
||||
final SourceResolverOptions sourceOptions;
|
||||
final CodegenOptions codegenOptions;
|
||||
final RunnerOptions runnerOptions;
|
||||
@@ -149,8 +146,7 @@ class CompilerOptions {
|
||||
final String inputBaseDir;
|
||||
|
||||
CompilerOptions(
|
||||
{this.strongOptions: const StrongModeOptions(),
|
||||
this.sourceOptions: const SourceResolverOptions(),
|
||||
{this.sourceOptions: const SourceResolverOptions(),
|
||||
this.codegenOptions: const CodegenOptions(),
|
||||
this.runnerOptions: const RunnerOptions(),
|
||||
this.checkSdk: false,
|
||||
@@ -238,7 +234,6 @@ CompilerOptions parseOptions(List<String> argv, {bool forceOutDir: false}) {
|
||||
packagePaths: args['package-paths'].split(','),
|
||||
resources:
|
||||
args['resources'].split(',').where((s) => s.isNotEmpty).toList()),
|
||||
strongOptions: new StrongModeOptions.fromArguments(args),
|
||||
runnerOptions: new RunnerOptions(v8Binary: v8Binary),
|
||||
checkSdk: args['sdk-check'],
|
||||
dumpInfo: dumpInfo,
|
||||
@@ -256,7 +251,7 @@ CompilerOptions parseOptions(List<String> argv, {bool forceOutDir: false}) {
|
||||
inputs: args.rest);
|
||||
}
|
||||
|
||||
final ArgParser argParser = StrongModeOptions.addArguments(new ArgParser()
|
||||
final ArgParser argParser = new ArgParser()
|
||||
..addFlag('sdk-check',
|
||||
abbr: 's', help: 'Typecheck sdk libs', defaultsTo: false)
|
||||
..addFlag('mock-sdk',
|
||||
@@ -321,7 +316,7 @@ final ArgParser argParser = StrongModeOptions.addArguments(new ArgParser()
|
||||
..addOption('dump-info-file',
|
||||
abbr: 'f',
|
||||
help: 'Dump info json file (requires dump-info)',
|
||||
defaultsTo: null));
|
||||
defaultsTo: null);
|
||||
|
||||
// TODO: Switch over to the `pub_cache` package (or the Resource API)?
|
||||
|
||||
|
||||
@@ -210,8 +210,7 @@ class DevServer {
|
||||
fileResolvers.insert(0, _createImplicitEntryResolver(options.inputs[0]));
|
||||
}
|
||||
|
||||
var context = createAnalysisContextWithSources(
|
||||
options.strongOptions, options.sourceOptions,
|
||||
var context = createAnalysisContextWithSources(options.sourceOptions,
|
||||
fileResolvers: fileResolvers);
|
||||
|
||||
var entryPath = path.basename(options.inputs[0]);
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
// Copyright (c) 2015, 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.
|
||||
|
||||
/// Types needed to implement "strong" checking in the Dart analyzer. This is
|
||||
/// intended to be used by `analyzer_cli` and `analysis_server` packages.
|
||||
library dev_compiler.strong_mode;
|
||||
|
||||
import 'package:analyzer/src/generated/engine.dart'
|
||||
show
|
||||
AnalysisContext,
|
||||
AnalysisContextImpl,
|
||||
AnalysisEngine,
|
||||
AnalysisErrorInfo,
|
||||
AnalysisErrorInfoImpl;
|
||||
import 'package:analyzer/src/generated/error.dart'
|
||||
show
|
||||
AnalysisError,
|
||||
AnalysisErrorListener,
|
||||
CompileTimeErrorCode,
|
||||
ErrorCode,
|
||||
ErrorSeverity,
|
||||
HintCode,
|
||||
StaticTypeWarningCode;
|
||||
import 'package:analyzer/src/generated/source.dart' show Source;
|
||||
import 'package:args/args.dart';
|
||||
|
||||
import 'src/analysis_context.dart' show enableDevCompilerInference;
|
||||
import 'src/checker/checker.dart' show CodeChecker;
|
||||
import 'src/checker/rules.dart' show TypeRules;
|
||||
|
||||
/// A type checker for Dart code that operates under stronger rules, and has
|
||||
/// the ability to do local type inference in some situations.
|
||||
// TODO(jmesserly): remove this class.
|
||||
class StrongChecker {
|
||||
final AnalysisContext _context;
|
||||
final CodeChecker _checker;
|
||||
final _ErrorCollector _reporter;
|
||||
|
||||
StrongChecker._(this._context, this._checker, this._reporter);
|
||||
|
||||
factory StrongChecker(AnalysisContext context, StrongModeOptions options) {
|
||||
// TODO(vsm): Remove this once analyzer_cli is completely switched to the
|
||||
// task model.
|
||||
if (!AnalysisEngine.instance.useTaskModel) {
|
||||
enableDevCompilerInference(context, options);
|
||||
var rules = new TypeRules(context.typeProvider);
|
||||
var reporter = new _ErrorCollector(options.hints);
|
||||
var checker = new CodeChecker(rules, reporter);
|
||||
return new StrongChecker._(context, checker, reporter);
|
||||
}
|
||||
return new StrongChecker._(context, null, null);
|
||||
}
|
||||
|
||||
/// Computes and returns DDC errors for the [source].
|
||||
AnalysisErrorInfo computeErrors(Source source) {
|
||||
var errors = new List<AnalysisError>();
|
||||
if (_checker != null) {
|
||||
_reporter.errors = errors;
|
||||
|
||||
for (Source librarySource in _context.getLibrariesContaining(source)) {
|
||||
var resolved = _context.resolveCompilationUnit2(source, librarySource);
|
||||
_checker.visitCompilationUnit(resolved);
|
||||
}
|
||||
_reporter.errors = null;
|
||||
}
|
||||
return new AnalysisErrorInfoImpl(errors, _context.getLineInfo(source));
|
||||
}
|
||||
}
|
||||
|
||||
class _ErrorCollector implements AnalysisErrorListener {
|
||||
List<AnalysisError> errors;
|
||||
final bool hints;
|
||||
_ErrorCollector(this.hints);
|
||||
|
||||
void onError(AnalysisError error) {
|
||||
// Unless DDC hints are requested, filter them out.
|
||||
var HINT = ErrorSeverity.INFO.ordinal;
|
||||
if (hints || error.errorCode.errorSeverity.ordinal > HINT) {
|
||||
errors.add(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(jmesserly): this type is dead now. It's preserved because analyzer_cli
|
||||
// passes the `hints` option.
|
||||
class StrongModeOptions {
|
||||
/// Whether to include hints about dynamic invokes and runtime checks.
|
||||
// TODO(jmesserly): this option is not used yet by DDC server mode or batch
|
||||
// compile to JS.
|
||||
final bool hints;
|
||||
|
||||
const StrongModeOptions({this.hints: false});
|
||||
|
||||
StrongModeOptions.fromArguments(ArgResults args, {String prefix: ''})
|
||||
: hints = args[prefix + 'hints'];
|
||||
|
||||
static ArgParser addArguments(ArgParser parser,
|
||||
{String prefix: '', bool hide: false}) {
|
||||
return parser
|
||||
..addFlag(prefix + 'hints',
|
||||
help: 'Display hints about dynamic casts and dispatch operations',
|
||||
defaultsTo: false,
|
||||
hide: hide);
|
||||
}
|
||||
|
||||
bool operator ==(Object other) {
|
||||
if (other is! StrongModeOptions) return false;
|
||||
StrongModeOptions s = other;
|
||||
return hints == s.hints;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ library dev_compiler.test.all_tests;
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'checker/self_host_test.dart' as self_host;
|
||||
import 'closure/closure_annotation_test.dart' as closure_annotation_test;
|
||||
import 'closure/closure_type_test.dart' as closure_type_test;
|
||||
import 'codegen_test.dart' as codegen_test;
|
||||
@@ -20,7 +19,6 @@ void main() {
|
||||
group('report', report_test.main);
|
||||
group('dependency_graph', dependency_graph_test.main);
|
||||
group('codegen', () => codegen_test.main([]));
|
||||
group('self_host', self_host.main);
|
||||
group('closure', () {
|
||||
closure_annotation_test.main();
|
||||
closure_type_test.main();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +0,0 @@
|
||||
// Copyright (c) 2015, 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.
|
||||
|
||||
/// This is a test for --url-mapping option.
|
||||
/// Analyzer's CustomUriResolver only supports on-disk resources, so we make
|
||||
/// this file accessible.
|
||||
library dart.foobar;
|
||||
|
||||
class Baz {
|
||||
static String quux = "hello world";
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +0,0 @@
|
||||
// Copyright (c) 2015, 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.
|
||||
|
||||
/// Tests that run the checker end-to-end using the file system.
|
||||
library dev_compiler.test.checker.self_host_test;
|
||||
|
||||
import 'package:dev_compiler/devc.dart' show BatchCompiler;
|
||||
import 'package:dev_compiler/src/options.dart';
|
||||
import 'package:test/test.dart';
|
||||
import '../testing.dart' show testDirectory, realSdkContext;
|
||||
|
||||
void main() {
|
||||
test('checker can run on itself ', () {
|
||||
new BatchCompiler(realSdkContext, new CompilerOptions())
|
||||
.compileFromUriString('$testDirectory/all_tests.dart');
|
||||
}, skip: 'test is very slow');
|
||||
}
|
||||
@@ -17,7 +17,6 @@ import 'package:path/path.dart' as path;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:dev_compiler/devc.dart';
|
||||
import 'package:dev_compiler/strong_mode.dart';
|
||||
import 'package:dev_compiler/src/compiler.dart' show defaultRuntimeFiles;
|
||||
import 'package:dev_compiler/src/options.dart';
|
||||
|
||||
@@ -184,7 +183,6 @@ $compilerMessages''';
|
||||
|
||||
test('devc dart:core', () {
|
||||
var testSdkContext = createAnalysisContextWithSources(
|
||||
new StrongModeOptions(),
|
||||
new SourceResolverOptions(
|
||||
dartSdkPath:
|
||||
path.join(testDirectory, '..', 'tool', 'generated_sdk')));
|
||||
|
||||
@@ -65,8 +65,7 @@ void main() {
|
||||
/// tests (since some tests modify the state of the files).
|
||||
testResourceProvider = createTestResourceProvider(testFiles);
|
||||
testUriResolver = new ResourceUriResolver(testResourceProvider);
|
||||
context = createAnalysisContextWithSources(
|
||||
options.strongOptions, options.sourceOptions,
|
||||
context = createAnalysisContextWithSources(options.sourceOptions,
|
||||
fileResolvers: [testUriResolver]);
|
||||
graph = new SourceGraph(context, new LogReporter(context), options);
|
||||
});
|
||||
@@ -685,8 +684,7 @@ void main() {
|
||||
runtimeDir: '/dev_compiler_runtime/',
|
||||
sourceOptions: new SourceResolverOptions(useMockSdk: true),
|
||||
serverMode: true);
|
||||
context = createAnalysisContextWithSources(
|
||||
opts.strongOptions, opts.sourceOptions,
|
||||
context = createAnalysisContextWithSources(opts.sourceOptions,
|
||||
fileResolvers: [testUriResolver]);
|
||||
graph = new SourceGraph(context, new LogReporter(context), opts);
|
||||
});
|
||||
@@ -1165,8 +1163,7 @@ void main() {
|
||||
|
||||
group('null for non-existing files', () {
|
||||
setUp(() {
|
||||
context = createAnalysisContextWithSources(
|
||||
options.strongOptions, options.sourceOptions,
|
||||
context = createAnalysisContextWithSources(options.sourceOptions,
|
||||
fileResolvers: [testUriResolver]);
|
||||
graph = new SourceGraph(context, new LogReporter(context), options);
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ import 'testing.dart' show realSdkContext, testDirectory;
|
||||
|
||||
main() {
|
||||
var mockSdkContext = createAnalysisContextWithSources(
|
||||
new StrongModeOptions(), new SourceResolverOptions(useMockSdk: true));
|
||||
new SourceResolverOptions(useMockSdk: true));
|
||||
var compiler = new BatchCompiler(mockSdkContext, new CompilerOptions());
|
||||
_check(file) => compiler.compileFromUriString('$testDirectory/$file.dart');
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ library dev_compiler.test.report_test;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:dev_compiler/devc.dart';
|
||||
import 'package:dev_compiler/strong_mode.dart' show StrongModeOptions;
|
||||
|
||||
import 'package:dev_compiler/src/analysis_context.dart';
|
||||
import 'package:dev_compiler/src/options.dart';
|
||||
@@ -38,9 +37,8 @@ void main() {
|
||||
var provider = createTestResourceProvider(files);
|
||||
var uriResolver = new TestUriResolver(provider);
|
||||
var srcOpts = new SourceResolverOptions(useMockSdk: true);
|
||||
var context = createAnalysisContextWithSources(
|
||||
new StrongModeOptions(), srcOpts,
|
||||
fileResolvers: [uriResolver]);
|
||||
var context =
|
||||
createAnalysisContextWithSources(srcOpts, fileResolvers: [uriResolver]);
|
||||
var reporter = new SummaryReporter(context);
|
||||
new BatchCompiler(context, new CompilerOptions(sourceOptions: srcOpts),
|
||||
reporter: reporter).compileFromUriString('/main.dart');
|
||||
|
||||
@@ -7,39 +7,29 @@ library dev_compiler.src.testing;
|
||||
import 'dart:mirrors';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/file_system/memory_file_system.dart';
|
||||
import 'package:analyzer/src/generated/ast.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart'
|
||||
show AnalysisContext, AnalysisEngine, AnalysisOptionsImpl;
|
||||
import 'package:analyzer/src/generated/error.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:cli_util/cli_util.dart' show getSdkDir;
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:source_span/source_span.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:dev_compiler/strong_mode.dart';
|
||||
import 'package:dev_compiler/src/analysis_context.dart';
|
||||
|
||||
import 'package:dev_compiler/src/server/dependency_graph.dart'
|
||||
show runtimeFilesForServerMode;
|
||||
import 'package:dev_compiler/src/info.dart';
|
||||
import 'package:dev_compiler/src/options.dart';
|
||||
import 'package:dev_compiler/src/utils.dart';
|
||||
|
||||
/// Shared analysis context used for compilation.
|
||||
final AnalysisContext realSdkContext = () {
|
||||
var context = createAnalysisContextWithSources(
|
||||
new StrongModeOptions(),
|
||||
new SourceResolverOptions(
|
||||
dartSdkPath: getSdkDir().path,
|
||||
customUrlMappings: {
|
||||
'package:expect/expect.dart': _testCodegenPath('expect.dart'),
|
||||
'package:async_helper/async_helper.dart':
|
||||
_testCodegenPath('async_helper.dart'),
|
||||
'package:unittest/unittest.dart': _testCodegenPath('unittest.dart'),
|
||||
'package:dom/dom.dart': _testCodegenPath('sunflower', 'dom.dart')
|
||||
}));
|
||||
var context = createAnalysisContextWithSources(new SourceResolverOptions(
|
||||
dartSdkPath: getSdkDir().path,
|
||||
customUrlMappings: {
|
||||
'package:expect/expect.dart': _testCodegenPath('expect.dart'),
|
||||
'package:async_helper/async_helper.dart':
|
||||
_testCodegenPath('async_helper.dart'),
|
||||
'package:unittest/unittest.dart': _testCodegenPath('unittest.dart'),
|
||||
'package:dom/dom.dart': _testCodegenPath('sunflower', 'dom.dart')
|
||||
}));
|
||||
(context.analysisOptions as AnalysisOptionsImpl).cacheSize = 512;
|
||||
return context;
|
||||
}();
|
||||
@@ -52,71 +42,6 @@ final String testDirectory =
|
||||
|
||||
class _TestUtils {}
|
||||
|
||||
/// Run the checker on a program with files contents as indicated in
|
||||
/// [testFiles].
|
||||
///
|
||||
/// This function makes several assumptions to make it easier to describe error
|
||||
/// expectations:
|
||||
///
|
||||
/// * a file named `/main.dart` exists in [testFiles].
|
||||
/// * all expected failures are listed in the source code using comments
|
||||
/// immediately in front of the AST node that should contain the error.
|
||||
/// * errors are formatted as a token `level:Type`, where `level` is the
|
||||
/// logging level were the error would be reported at, and `Type` is the
|
||||
/// concrete subclass of [StaticInfo] that denotes the error.
|
||||
///
|
||||
/// For example, to check that an assignment produces a warning about a boxing
|
||||
/// conversion, you can describe the test as follows:
|
||||
///
|
||||
/// testChecker({
|
||||
/// '/main.dart': '''
|
||||
/// testMethod() {
|
||||
/// dynamic x = /*warning:Box*/3;
|
||||
/// }
|
||||
/// '''
|
||||
/// });
|
||||
///
|
||||
void testChecker(String name, Map<String, String> testFiles,
|
||||
{String sdkDir, customUrlMappings: const {}}) {
|
||||
test(name, () {
|
||||
expect(testFiles.containsKey('/main.dart'), isTrue,
|
||||
reason: '`/main.dart` is missing in testFiles');
|
||||
|
||||
var provider = createTestResourceProvider(testFiles);
|
||||
var uriResolver = new TestUriResolver(provider);
|
||||
// Enable task model strong mode
|
||||
AnalysisEngine.instance.useTaskModel = true;
|
||||
var context = AnalysisEngine.instance.createAnalysisContext();
|
||||
context.analysisOptions.strongMode = true;
|
||||
context.sourceFactory = createSourceFactory(
|
||||
new SourceResolverOptions(
|
||||
customUrlMappings: customUrlMappings,
|
||||
useMockSdk: sdkDir == null,
|
||||
dartSdkPath: sdkDir),
|
||||
fileResolvers: [uriResolver]);
|
||||
|
||||
var checker =
|
||||
new StrongChecker(context, new StrongModeOptions(hints: true));
|
||||
|
||||
// Run the checker on /main.dart.
|
||||
var mainSource = uriResolver.resolveAbsolute(new Uri.file('/main.dart'));
|
||||
var initialLibrary =
|
||||
context.resolveCompilationUnit2(mainSource, mainSource);
|
||||
|
||||
// Extract expectations from the comments in the test files, and
|
||||
// check that all errors we emit are included in the expected map.
|
||||
var allLibraries = reachableLibraries(initialLibrary.element.library);
|
||||
for (var lib in allLibraries) {
|
||||
for (var unit in lib.units) {
|
||||
if (unit.source.uri.scheme == 'dart') continue;
|
||||
|
||||
var errorInfo = checker.computeErrors(unit.source);
|
||||
new _ExpectedErrorVisitor(errorInfo.errors).validate(unit.unit);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Creates a [MemoryResourceProvider] with test data
|
||||
MemoryResourceProvider createTestResourceProvider(
|
||||
Map<String, String> testFiles) {
|
||||
@@ -150,131 +75,3 @@ class TestUriResolver extends ResourceUriResolver {
|
||||
return super.resolveAbsolute(uri, actualUri);
|
||||
}
|
||||
}
|
||||
|
||||
class _ExpectedErrorVisitor extends UnifyingAstVisitor {
|
||||
final Set<AnalysisError> _actualErrors;
|
||||
CompilationUnit _unit;
|
||||
String _unitSourceCode;
|
||||
|
||||
_ExpectedErrorVisitor(List<AnalysisError> actualErrors)
|
||||
: _actualErrors = new Set.from(actualErrors);
|
||||
|
||||
validate(CompilationUnit unit) {
|
||||
_unit = unit;
|
||||
// This reads the file. Only safe because tests use MemoryFileSystem.
|
||||
_unitSourceCode = unit.element.source.contents.data;
|
||||
|
||||
// Visit the compilation unit.
|
||||
unit.accept(this);
|
||||
|
||||
if (_actualErrors.isNotEmpty) {
|
||||
var actualMsgs = _actualErrors.map(_formatActualError).join('\n');
|
||||
fail('Unexpected errors reported by checker:\n\n$actualMsgs');
|
||||
}
|
||||
}
|
||||
|
||||
visitNode(AstNode node) {
|
||||
var token = node.beginToken;
|
||||
var comment = token.precedingComments;
|
||||
// Use error marker found in an immediately preceding comment,
|
||||
// and attach it to the outermost expression that starts at that token.
|
||||
if (comment != null) {
|
||||
while (comment.next != null) {
|
||||
comment = comment.next;
|
||||
}
|
||||
if (comment.end == token.offset && node.parent.beginToken != token) {
|
||||
var commentText = '$comment';
|
||||
var start = commentText.lastIndexOf('/*');
|
||||
var end = commentText.lastIndexOf('*/');
|
||||
if (start != -1 && end != -1) {
|
||||
expect(start, lessThan(end));
|
||||
var errors = commentText.substring(start + 2, end).split(',');
|
||||
var expectations =
|
||||
errors.map(_ErrorExpectation.parse).where((x) => x != null);
|
||||
|
||||
for (var e in expectations) _expectError(node, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visitNode(node);
|
||||
}
|
||||
|
||||
void _expectError(AstNode node, _ErrorExpectation expected) {
|
||||
// See if we can find the expected error in our actual errors
|
||||
for (var actual in _actualErrors) {
|
||||
if (actual.offset == node.offset && actual.length == node.length) {
|
||||
var actualMsg = _formatActualError(actual);
|
||||
expect(_actualErrorLevel(actual), expected.level,
|
||||
reason: 'expected different error code at:\n\n$actualMsg');
|
||||
expect(errorCodeName(actual.errorCode), expected.typeName,
|
||||
reason: 'expected different error type at:\n\n$actualMsg');
|
||||
|
||||
// We found it. Stop the search.
|
||||
_actualErrors.remove(actual);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var span = _createSpan(node.offset, node.length);
|
||||
var levelName = expected.level.name.toLowerCase();
|
||||
var msg = span.message(expected.typeName, color: colorOf(levelName));
|
||||
fail('expected error was not reported at:\n\n$levelName: $msg');
|
||||
}
|
||||
|
||||
Level _actualErrorLevel(AnalysisError actual) {
|
||||
return const <ErrorSeverity, Level>{
|
||||
ErrorSeverity.ERROR: Level.SEVERE,
|
||||
ErrorSeverity.WARNING: Level.WARNING,
|
||||
ErrorSeverity.INFO: Level.INFO
|
||||
}[actual.errorCode.errorSeverity];
|
||||
}
|
||||
|
||||
String _formatActualError(AnalysisError actual) {
|
||||
var span = _createSpan(actual.offset, actual.length);
|
||||
var levelName = _actualErrorLevel(actual).name.toLowerCase();
|
||||
var msg = span.message(actual.message, color: colorOf(levelName));
|
||||
return '$levelName: [${errorCodeName(actual.errorCode)}] $msg';
|
||||
}
|
||||
|
||||
SourceSpan _createSpan(int offset, int len) {
|
||||
return createSpanHelper(_unit.lineInfo, offset, offset + len,
|
||||
_unit.element.source, _unitSourceCode);
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes an expected message that should be produced by the checker.
|
||||
class _ErrorExpectation {
|
||||
final Level level;
|
||||
final String typeName;
|
||||
_ErrorExpectation(this.level, this.typeName);
|
||||
|
||||
static _ErrorExpectation _parse(String descriptor) {
|
||||
var tokens = descriptor.split(':');
|
||||
expect(tokens.length, 2, reason: 'invalid error descriptor');
|
||||
var name = tokens[0].toUpperCase();
|
||||
var typeName = tokens[1];
|
||||
|
||||
var level =
|
||||
Level.LEVELS.firstWhere((l) => l.name == name, orElse: () => null);
|
||||
expect(level, isNotNull,
|
||||
reason: 'invalid level in error descriptor: `${tokens[0]}`');
|
||||
expect(typeName, isNotNull,
|
||||
reason: 'invalid type in error descriptor: ${tokens[1]}');
|
||||
return new _ErrorExpectation(level, typeName);
|
||||
}
|
||||
|
||||
static _ErrorExpectation parse(String descriptor) {
|
||||
descriptor = descriptor.trim();
|
||||
var tokens = descriptor.split(' ');
|
||||
if (tokens.length == 1) return _parse(tokens[0]);
|
||||
expect(tokens.length, 4, reason: 'invalid error descriptor');
|
||||
expect(tokens[1], "should", reason: 'invalid error descriptor');
|
||||
expect(tokens[2], "be", reason: 'invalid error descriptor');
|
||||
if (tokens[0] == "pass") return null;
|
||||
// TODO(leafp) For now, we just use whatever the current expectation is,
|
||||
// eventually we could do more automated reporting here.
|
||||
return _parse(tokens[0]);
|
||||
}
|
||||
|
||||
String toString() => '$level $typeName';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user