blob: aabe838ea85f84af87645c6d5163c280a976d8a5 [file] [log] [blame]
// Copyright (c) 2012, 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 dart2js.resolution;
import 'dart:collection' show Queue;
import '../common.dart';
import '../common/names.dart' show Identifiers;
import '../common/resolution.dart'
show ParsingContext, Resolution, ResolutionImpact, Target;
import '../common/tasks.dart' show CompilerTask, Measurer;
import '../compile_time_constants.dart' show ConstantCompiler;
import '../constants/expressions.dart'
show
ConstantExpression,
ConstantExpressionKind,
ConstructedConstantExpression,
ErroneousConstantExpression;
import '../constants/values.dart' show ConstantValue;
import '../common_elements.dart' show CommonElements;
import '../elements/resolution_types.dart';
import '../elements/elements.dart';
import '../elements/entities.dart' show AsyncMarker;
import '../elements/modelx.dart'
show
BaseClassElementX,
BaseFunctionElementX,
ConstructorElementX,
FieldElementX,
FunctionElementX,
GetterElementX,
MetadataAnnotationX,
MixinApplicationElementX,
ParameterMetadataAnnotation,
SetterElementX,
TypedefElementX;
import '../enqueue.dart';
import '../options.dart';
import 'package:front_end/src/fasta/scanner.dart'
show
isBinaryOperator,
isMinusOperator,
isTernaryOperator,
isUnaryOperator,
isUserDefinableOperator;
import '../tree/tree.dart';
import '../universe/call_structure.dart' show CallStructure;
import '../universe/feature.dart' show Feature;
import '../universe/use.dart' show StaticUse;
import '../universe/world_impact.dart' show WorldImpact;
import '../util/util.dart' show Link, Setlet;
import '../world.dart';
import 'class_hierarchy.dart';
import 'class_members.dart' show MembersCreator;
import 'constructors.dart';
import 'members.dart';
import 'registry.dart';
import 'resolution_result.dart';
import 'signatures.dart';
import 'tree_elements.dart';
import 'typedefs.dart';
import 'type_resolver.dart' show FunctionTypeParameterScope;
class ResolverTask extends CompilerTask {
final ConstantCompiler constantCompiler;
final Resolution resolution;
ResolverTask(this.resolution, this.constantCompiler, Measurer measurer)
: super(measurer);
String get name => 'Resolver';
DiagnosticReporter get reporter => resolution.reporter;
Target get target => resolution.target;
CommonElements get commonElements => resolution.commonElements;
ParsingContext get parsingContext => resolution.parsingContext;
CompilerOptions get options => resolution.options;
ResolutionEnqueuer get enqueuer => resolution.enqueuer;
OpenWorld get world => enqueuer.worldBuilder;
ResolutionImpact resolve(Element element) {
return measure(() {
if (Elements.isMalformed(element)) {
// TODO(johnniwinther): Add a predicate for this.
assert(
element is! ErroneousElement,
failedAt(
element, "Element $element expected to have parse errors."));
_ensureTreeElements(element);
return const ResolutionImpact();
}
WorldImpact processMetadata([WorldImpact result]) {
for (MetadataAnnotation metadata in element.implementation.metadata) {
metadata.ensureResolved(resolution);
}
return result;
}
if (element.isConstructor ||
element.isFunction ||
element.isGetter ||
element.isSetter) {
return processMetadata(resolveMethodElement(element));
}
if (element.isField) {
return processMetadata(resolveField(element));
}
if (element.isClass) {
ClassElement cls = element;
cls.ensureResolved(resolution);
return processMetadata(const ResolutionImpact());
} else if (element.isTypedef) {
TypedefElement typdef = element;
return processMetadata(resolveTypedef(typdef));
}
reporter.internalError(element, "resolve($element) not implemented.");
});
}
void resolveRedirectingConstructor(InitializerResolver resolver, Node node,
FunctionElement constructor, FunctionElement redirection) {
assert(
constructor.isImplementation,
failedAt(
node,
'Redirecting constructors must be resolved on implementation '
'elements.'));
Setlet<FunctionElement> seen = new Setlet<FunctionElement>();
seen.add(constructor);
while (redirection != null) {
// Ensure that we follow redirections through implementation elements.
redirection = redirection.implementation;
if (redirection.isError) {
break;
}
if (seen.contains(redirection)) {
reporter.reportErrorMessage(
node, MessageKind.REDIRECTING_CONSTRUCTOR_CYCLE);
return;
}
seen.add(redirection);
redirection = resolver.visitor.resolveConstructorRedirection(redirection);
}
}
static void processAsyncMarker(Resolution resolution,
BaseFunctionElementX element, ResolutionRegistry registry) {
DiagnosticReporter reporter = resolution.reporter;
CommonElements commonElements = resolution.commonElements;
FunctionExpression functionExpression = element.node;
AsyncModifier asyncModifier = functionExpression.asyncModifier;
if (asyncModifier != null) {
if (!resolution.target.supportsAsyncAwait) {
reporter.reportErrorMessage(functionExpression.asyncModifier,
MessageKind.ASYNC_AWAIT_NOT_SUPPORTED);
} else {
if (asyncModifier.isAsynchronous) {
element.asyncMarker = asyncModifier.isYielding
? AsyncMarker.ASYNC_STAR
: AsyncMarker.ASYNC;
} else {
element.asyncMarker = AsyncMarker.SYNC_STAR;
}
if (element.isAbstract) {
reporter.reportErrorMessage(
asyncModifier,
MessageKind.ASYNC_MODIFIER_ON_ABSTRACT_METHOD,
{'modifier': element.asyncMarker});
} else if (element.isConstructor) {
reporter.reportErrorMessage(
asyncModifier,
MessageKind.ASYNC_MODIFIER_ON_CONSTRUCTOR,
{'modifier': element.asyncMarker});
} else {
if (element.isSetter) {
reporter.reportErrorMessage(
asyncModifier,
MessageKind.ASYNC_MODIFIER_ON_SETTER,
{'modifier': element.asyncMarker});
}
if (functionExpression.body.asReturn() != null &&
element.asyncMarker.isYielding) {
reporter.reportErrorMessage(
asyncModifier,
MessageKind.YIELDING_MODIFIER_ON_ARROW_BODY,
{'modifier': element.asyncMarker});
}
}
ClassElement cls;
switch (element.asyncMarker) {
case AsyncMarker.ASYNC:
registry.registerFeature(Feature.ASYNC);
cls = commonElements.futureClass;
break;
case AsyncMarker.ASYNC_STAR:
registry.registerFeature(Feature.ASYNC_STAR);
cls = commonElements.streamClass;
break;
case AsyncMarker.SYNC_STAR:
registry.registerFeature(Feature.SYNC_STAR);
cls = commonElements.iterableClass;
break;
}
cls?.ensureResolved(resolution);
}
}
}
bool _isNativeClassOrExtendsNativeClass(ClassElement classElement) {
assert(classElement != null);
while (classElement != null) {
if (target.isNativeClass(classElement)) return true;
classElement = classElement.superclass;
}
return false;
}
WorldImpact resolveMethodElementImplementation(
FunctionElementX element, FunctionExpression tree) {
return reporter.withCurrentElement(element, () {
if (element.isExternal && tree.hasBody) {
reporter.reportErrorMessage(element, MessageKind.EXTERNAL_WITH_BODY,
{'functionName': element.name});
}
if (element.isConstructor) {
if (tree.returnType != null) {
reporter.reportErrorMessage(
tree, MessageKind.CONSTRUCTOR_WITH_RETURN_TYPE);
}
if (tree.hasBody && element.isConst) {
if (element.isGenerativeConstructor) {
reporter.reportErrorMessage(
tree, MessageKind.CONST_CONSTRUCTOR_WITH_BODY);
} else if (!tree.isRedirectingFactory) {
reporter.reportErrorMessage(tree, MessageKind.CONST_FACTORY);
}
}
}
ResolverVisitor visitor = visitorFor(element);
ResolutionRegistry registry = visitor.registry;
registry.defineFunction(tree, element);
visitor.setupFunction(tree, element); // Modifies the scope.
processAsyncMarker(resolution, element, registry);
if (element.isGenerativeConstructor) {
// Even if there is no initializer list we still have to do the
// resolution in case there is an implicit super constructor call.
InitializerResolver resolver =
new InitializerResolver(visitor, element, tree);
FunctionElement redirection = resolver.resolveInitializers();
if (redirection != null) {
resolveRedirectingConstructor(resolver, tree, element, redirection);
}
} else if (tree.initializers != null) {
reporter.reportErrorMessage(
tree, MessageKind.FUNCTION_WITH_INITIALIZER);
}
if (!options.analyzeSignaturesOnly || tree.isRedirectingFactory) {
// We need to analyze the redirecting factory bodies to ensure that
// we can analyze compile-time constants.
visitor.visit(tree.body);
}
// Get the resolution tree and check that the resolved
// function doesn't use 'super' if it is mixed into another
// class. This is the part of the 'super' mixin check that
// happens when a function is resolved after the mixin
// application has been performed.
TreeElements resolutionTree = registry.mapping;
ClassElement enclosingClass = element.enclosingClass;
if (enclosingClass != null) {
// TODO(johnniwinther): Find another way to obtain mixin uses.
ClassElement mixin = enclosingClass;
for (MixinApplicationElement mixinApplication
in world.allMixinUsesOf(enclosingClass)) {
checkMixinSuperUses(resolutionTree, mixinApplication, mixin);
}
}
// TODO(9631): support noSuchMethod on native classes.
if (element.isFunction &&
element.isInstanceMember &&
element.name == Identifiers.noSuchMethod_ &&
_isNativeClassOrExtendsNativeClass(enclosingClass)) {
reporter.reportErrorMessage(tree, MessageKind.NO_SUCH_METHOD_IN_NATIVE);
}
resolution.target.resolveNativeMember(element, registry.impactBuilder);
return registry.impactBuilder;
});
}
/// Returns `true` if [element] has been processed by the resolution enqueuer.
bool _hasBeenProcessed(MemberElement element) {
assert(element == element.analyzableElement.declaration,
failedAt(element, "Unexpected element $element"));
return enqueuer.processedEntities.contains(element);
}
WorldImpact resolveMethodElement(FunctionElementX element) {
assert(element.isDeclaration, failedAt(element));
return reporter.withCurrentElement(element, () {
if (_hasBeenProcessed(element)) {
// TODO(karlklose): Remove the check for [isConstructor]. [elememts]
// should never be non-null, not even for constructors.
assert(
element.isConstructor,
failedAt(element,
'Non-constructor element $element has already been analyzed.'));
return const ResolutionImpact();
}
if (element.isSynthesized) {
if (element.isGenerativeConstructor) {
ResolutionRegistry registry =
new ResolutionRegistry(this.target, _ensureTreeElements(element));
ConstructorElement constructor = element.asFunctionElement();
ConstructorElement target = constructor.definingConstructor;
// Ensure the signature of the synthesized element is
// resolved. This is the only place where the resolver is
// seeing this element.
ResolutionFunctionType type = element.computeType(resolution);
if (!target.isMalformed) {
registry.registerStaticUse(new StaticUse.superConstructorInvoke(
// TODO(johnniwinther): Provide the right call structure for
// forwarding constructors.
target,
CallStructure.NO_ARGS));
}
// TODO(johnniwinther): Remove this substitution when synthesized
// constructors handle type variables correctly.
type = type.substByContext(
constructor.enclosingClass.asInstanceOf(target.enclosingClass));
type.parameterTypes.forEach(registry.registerCheckedModeCheck);
type.optionalParameterTypes
.forEach(registry.registerCheckedModeCheck);
type.namedParameterTypes.forEach(registry.registerCheckedModeCheck);
return registry.impactBuilder;
} else {
assert(element.isDeferredLoaderGetter || element.isMalformed);
_ensureTreeElements(element);
return const ResolutionImpact();
}
} else {
element.parseNode(resolution.parsingContext);
element.computeType(resolution);
FunctionElementX implementation = element;
if (element.isExternal) {
implementation = target.resolveExternalFunction(element);
}
return resolveMethodElementImplementation(
implementation, implementation.node);
}
});
}
/// Creates a [ResolverVisitor] for resolving an AST in context of [element].
/// If [useEnclosingScope] is `true` then the initial scope of the visitor
/// does not include inner scope of [element].
///
/// This method should only be used by this library (or tests of
/// this library).
ResolverVisitor visitorFor(Element element, {bool useEnclosingScope: false}) {
return new ResolverVisitor(resolution, element,
new ResolutionRegistry(target, _ensureTreeElements(element)),
useEnclosingScope: useEnclosingScope);
}
WorldImpact resolveField(FieldElementX element) {
return reporter.withCurrentElement(element, () {
VariableDefinitions tree = element.parseNode(parsingContext);
if (element.modifiers.isStatic && element.isTopLevel) {
reporter.reportErrorMessage(element.modifiers.getStatic(),
MessageKind.TOP_LEVEL_VARIABLE_DECLARED_STATIC);
}
ResolverVisitor visitor = visitorFor(element);
ResolutionRegistry registry = visitor.registry;
// TODO(johnniwinther): Maybe remove this when placeholderCollector
// migrates to the backend ast.
registry.defineElement(element.definition, element);
// TODO(johnniwinther): Share the resolved type between all variables
// declared in the same declaration.
if (tree.type != null) {
ResolutionDartType type = visitor.resolveTypeAnnotation(tree.type);
assert(
element.variables.type == null ||
// Crude check but we have no equivalence relation that
// equates malformed types, like matching creations of type
// `Foo<Unresolved>`.
element.variables.type.toString() == type.toString(),
failedAt(
element,
"Unexpected type computed for $element. "
"Was ${element.variables.type}, computed $type."));
element.variables.type = type;
} else if (element.variables.type == null) {
// Only assign the dynamic type if the element has no known type. This
// happens for enum fields where the type is known but is not in the
// synthesized AST.
element.variables.type = const ResolutionDynamicType();
} else {
registry.registerCheckedModeCheck(element.variables.type);
}
Expression initializer = element.initializer;
Modifiers modifiers = element.modifiers;
if (initializer != null) {
// TODO(johnniwinther): Avoid analyzing initializers if
// [Compiler.analyzeSignaturesOnly] is set.
ResolutionResult result = visitor.visit(initializer);
if (result.isConstant) {
element.constant = result.constant;
}
} else if (modifiers.isConst) {
reporter.reportErrorMessage(
element, MessageKind.CONST_WITHOUT_INITIALIZER);
} else if (modifiers.isFinal && !element.isInstanceMember) {
reporter.reportErrorMessage(
element, MessageKind.FINAL_WITHOUT_INITIALIZER);
} else {
registry.registerFeature(Feature.FIELD_WITHOUT_INITIALIZER);
}
if (Elements.isStaticOrTopLevelField(element)) {
visitor.addDeferredAction(element, () {
if (element.modifiers.isConst) {
element.constant = constantCompiler.compileConstant(element);
} else {
element.constant = constantCompiler.compileVariable(element);
}
});
if (initializer != null) {
if (!element.modifiers.isConst &&
initializer.asLiteralNull() == null) {
// TODO(johnniwinther): Determine the const-ness eagerly to avoid
// unnecessary registrations.
registry.registerFeature(Feature.LAZY_FIELD);
}
}
}
// Perform various checks as side effect of "computing" the type.
element.computeType(resolution);
resolution.target.resolveNativeMember(element, registry.impactBuilder);
return registry.impactBuilder;
});
}
ResolutionDartType resolveTypeAnnotation(
Element element, TypeAnnotation annotation) {
ResolutionDartType type = _resolveReturnType(element, annotation);
if (type.isVoid) {
reporter.reportErrorMessage(annotation, MessageKind.VOID_NOT_ALLOWED);
}
return type;
}
ResolutionDartType _resolveReturnType(
Element element, TypeAnnotation annotation) {
if (annotation == null) return const ResolutionDynamicType();
ResolutionDartType result =
visitorFor(element).resolveTypeAnnotation(annotation);
assert(result != null,
failedAt(annotation, "No type computed for $annotation."));
if (result == null) {
// TODO(karklose): warning.
return const ResolutionDynamicType();
}
return result;
}
void resolveRedirectionChain(ConstructorElement constructor, Spannable node) {
ConstructorElement target = constructor;
ResolutionDartType targetType;
List<ConstructorElement> seen = new List<ConstructorElement>();
bool isMalformed = false;
// Follow the chain of redirections and check for cycles.
while (target.isRedirectingFactory) {
if (target.hasEffectiveTarget) {
// We found a constructor that already has been processed.
// TODO(johnniwinther): Should `effectiveTargetType` be part of the
// interface?
targetType =
target.computeEffectiveTargetType(target.enclosingClass.thisType);
assert(
targetType != null,
failedAt(target,
'Redirection target type has not been computed for $target'));
target = target.effectiveTarget;
break;
}
Element nextTarget = target.immediateRedirectionTarget;
if (seen.contains(nextTarget)) {
reporter.reportErrorMessage(
node, MessageKind.CYCLIC_REDIRECTING_FACTORY);
targetType = target.enclosingClass.thisType;
isMalformed = true;
break;
}
seen.add(target);
target = nextTarget;
}
if (target.isGenerativeConstructor && target.enclosingClass.isAbstract) {
isMalformed = true;
}
if (target.isMalformed) {
isMalformed = true;
}
if (targetType == null) {
assert(!target.isRedirectingFactory);
targetType = target.enclosingClass.thisType;
}
// [target] is now the actual target of the redirections. Run through
// the constructors again and set their [redirectionTarget], so that we
// do not have to run the loop for these constructors again. Furthermore,
// compute [redirectionTargetType] for each factory by computing the
// substitution of the target type with respect to the factory type.
while (!seen.isEmpty) {
ConstructorElementX factory = seen.removeLast();
ResolvedAst resolvedAst = factory.resolvedAst;
assert(
resolvedAst != null, failedAt(node, 'No ResolvedAst for $factory.'));
RedirectingFactoryBody redirectionNode = resolvedAst.body;
ResolutionDartType factoryType =
resolvedAst.elements.getType(redirectionNode);
if (!factoryType.isDynamic) {
targetType = targetType.substByContext(factoryType);
}
factory.setEffectiveTarget(target, targetType, isMalformed: isMalformed);
}
}
/**
* Load and resolve the supertypes of [cls].
*
* Warning: do not call this method directly. It should only be
* called by [resolveClass] and [ClassSupertypeResolver].
*/
void loadSupertypes(BaseClassElementX cls, Spannable from) {
measure(() {
if (cls.supertypeLoadState == STATE_DONE) return;
if (cls.supertypeLoadState == STATE_STARTED) {
reporter.reportErrorMessage(
from, MessageKind.CYCLIC_CLASS_HIERARCHY, {'className': cls.name});
cls.supertypeLoadState = STATE_DONE;
cls.hasIncompleteHierarchy = true;
ClassElement objectClass = commonElements.objectClass;
cls.allSupertypesAndSelf = objectClass.allSupertypesAndSelf
.extendClass(cls.computeType(resolution));
cls.supertype = cls.allSupertypes.head;
assert(cls.supertype != null,
failedAt(from, 'Missing supertype on cyclic class $cls.'));
cls.interfaces = const Link<ResolutionDartType>();
return;
}
cls.supertypeLoadState = STATE_STARTED;
reporter.withCurrentElement(cls, () {
// TODO(ahe): Cache the node in cls.
cls
.parseNode(parsingContext)
.accept(new ClassSupertypeResolver(resolution, cls));
if (cls.supertypeLoadState != STATE_DONE) {
cls.supertypeLoadState = STATE_DONE;
}
});
});
}
// TODO(johnniwinther): Remove this queue when resolution has been split into
// syntax and semantic resolution.
TypeDeclarationElement currentlyResolvedTypeDeclaration;
Queue<ClassElement> pendingClassesToBeResolved = new Queue<ClassElement>();
Queue<ClassElement> pendingClassesToBePostProcessed =
new Queue<ClassElement>();
/// Resolve [element] using [resolveTypeDeclaration].
///
/// This methods ensure that class declarations encountered through type
/// annotations during the resolution of [element] are resolved after
/// [element] has been resolved.
// TODO(johnniwinther): Encapsulate this functionality in a
// 'TypeDeclarationResolver'.
_resolveTypeDeclaration(
TypeDeclarationElement element, resolveTypeDeclaration()) {
return reporter.withCurrentElement(element, () {
return measure(() {
TypeDeclarationElement previousResolvedTypeDeclaration =
currentlyResolvedTypeDeclaration;
currentlyResolvedTypeDeclaration = element;
var result = resolveTypeDeclaration();
if (previousResolvedTypeDeclaration == null) {
do {
while (!pendingClassesToBeResolved.isEmpty) {
pendingClassesToBeResolved
.removeFirst()
.ensureResolved(resolution);
}
while (!pendingClassesToBePostProcessed.isEmpty) {
_postProcessClassElement(
pendingClassesToBePostProcessed.removeFirst());
}
} while (!pendingClassesToBeResolved.isEmpty);
assert(pendingClassesToBeResolved.isEmpty);
assert(pendingClassesToBePostProcessed.isEmpty);
}
currentlyResolvedTypeDeclaration = previousResolvedTypeDeclaration;
return result;
});
});
}
/**
* Resolve the class [element].
*
* Before calling this method, [element] was constructed by the
* scanner and most fields are null or empty. This method fills in
* these fields and also ensure that the supertypes of [element] are
* resolved.
*
* Warning: Do not call this method directly. Instead use
* [:element.ensureResolved(resolution):].
*/
TreeElements resolveClass(BaseClassElementX element) {
return _resolveTypeDeclaration(element, () {
// TODO(johnniwinther): Store the mapping in the resolution enqueuer.
ResolutionRegistry registry = new ResolutionRegistry(
resolution.target, _ensureTreeElements(element));
resolveClassInternal(element, registry);
return element.treeElements;
});
}
void ensureClassWillBeResolvedInternal(ClassElement element) {
if (currentlyResolvedTypeDeclaration == null) {
element.ensureResolved(resolution);
} else {
pendingClassesToBeResolved.add(element);
}
}
void resolveClassInternal(
BaseClassElementX element, ResolutionRegistry registry) {
if (!element.isPatch) {
reporter.withCurrentElement(
element,
() => measure(() {
assert(element.resolutionState == STATE_NOT_STARTED);
element.resolutionState = STATE_STARTED;
Node tree = element.parseNode(parsingContext);
loadSupertypes(element, tree);
ClassResolverVisitor visitor =
new ClassResolverVisitor(resolution, element, registry);
visitor.visit(tree);
element.resolutionState = STATE_DONE;
pendingClassesToBePostProcessed.add(element);
}));
if (element.isPatched) {
// Ensure handling patch after origin.
element.patch.ensureResolved(resolution);
}
} else {
// Handle patch classes:
element.resolutionState = STATE_STARTED;
// Ensure handling origin before patch.
element.origin.ensureResolved(resolution);
// Ensure that the type is computed.
element.computeType(resolution);
// Copy class hierarchy from origin.
element.supertype = element.origin.supertype;
element.interfaces = element.origin.interfaces;
element.allSupertypesAndSelf = element.origin.allSupertypesAndSelf;
// Stepwise assignment to ensure invariant.
element.supertypeLoadState = STATE_STARTED;
element.supertypeLoadState = STATE_DONE;
element.resolutionState = STATE_DONE;
// TODO(johnniwinther): Check matching type variables and
// empty extends/implements clauses.
}
}
void _postProcessClassElement(BaseClassElementX element) {
for (MetadataAnnotation metadata in element.implementation.metadata) {
metadata.ensureResolved(resolution);
ConstantValue value =
resolution.constants.getConstantValue(metadata.constant);
if (!element.isProxy && resolution.isProxyConstant(value)) {
element.isProxy = true;
}
}
// Force resolution of metadata on non-instance members since they may be
// inspected by the backend while emitting. Metadata on instance members is
// handled as a result of processing instantiated class members in the
// enqueuer.
// TODO(ahe): Avoid this eager resolution.
element.forEachMember((_, Element member) {
if (!member.isInstanceMember) {
reporter.withCurrentElement(member, () {
for (MetadataAnnotation metadata in member.implementation.metadata) {
metadata.ensureResolved(resolution);
}
});
}
});
computeClassMember(element, Identifiers.call);
}
void computeClassMembers(ClassElement element) {
MembersCreator.computeAllClassMembers(resolution, element);
}
void computeClassMember(ClassElement element, String name) {
MembersCreator.computeClassMembersByName(resolution, element, name);
}
void checkClass(ClassElement element) {
computeClassMembers(element);
if (element.isMixinApplication) {
checkMixinApplication(element);
} else {
checkClassMembers(element);
}
}
void checkMixinApplication(MixinApplicationElementX mixinApplication) {
Modifiers modifiers = mixinApplication.modifiers;
int illegalFlags = modifiers.flags & ~Modifiers.FLAG_ABSTRACT;
if (illegalFlags != 0) {
Modifiers illegalModifiers = new Modifiers.withFlags(null, illegalFlags);
reporter.reportErrorMessage(
modifiers,
MessageKind.ILLEGAL_MIXIN_APPLICATION_MODIFIERS,
{'modifiers': illegalModifiers});
}
// In case of cyclic mixin applications, the mixin chain will have
// been cut. If so, we have already reported the error to the
// user so we just return from here.
ClassElement mixin = mixinApplication.mixin;
if (mixin == null) return;
// Check that we're not trying to use Object as a mixin.
if (mixin.superclass == null) {
reporter.reportErrorMessage(
mixinApplication, MessageKind.ILLEGAL_MIXIN_OBJECT);
// Avoid reporting additional errors for the Object class.
return;
}
if (mixin.isEnumClass) {
// Mixing in an enum has already caused a compile-time error.
return;
}
// Check that the mixed in class has Object as its superclass.
if (!mixin.superclass.isObject) {
reporter.reportErrorMessage(mixin, MessageKind.ILLEGAL_MIXIN_SUPERCLASS);
}
// Check that the mixed in class doesn't have any constructors and
// make sure we aren't mixing in methods that use 'super'.
mixin.forEachLocalMember((_member) {
AstElement member = _member;
if (member.isGenerativeConstructor && !member.isSynthesized) {
reporter.reportErrorMessage(
member, MessageKind.ILLEGAL_MIXIN_CONSTRUCTOR);
} else {
// Get the resolution tree and check that the resolved member
// doesn't use 'super'. This is the part of the 'super' mixin
// check that happens when a function is resolved before the
// mixin application has been performed.
// TODO(johnniwinther): Obtain the [TreeElements] for [member]
// differently.
if (_hasBeenProcessed(member)) {
if (member.resolvedAst.kind == ResolvedAstKind.PARSED) {
checkMixinSuperUses(
member.resolvedAst.elements, mixinApplication, mixin);
}
}
}
});
}
void checkMixinSuperUses(TreeElements elements,
MixinApplicationElement mixinApplication, ClassElement mixin) {
// TODO(johnniwinther): Avoid the use of [TreeElements] here.
Iterable<SourceSpan> superUses = elements.superUses;
if (superUses.isEmpty) return;
DiagnosticMessage error = reporter.createMessage(mixinApplication,
MessageKind.ILLEGAL_MIXIN_WITH_SUPER, {'className': mixin.name});
// Show the user the problematic uses of 'super' in the mixin.
List<DiagnosticMessage> infos = <DiagnosticMessage>[];
for (SourceSpan use in superUses) {
infos.add(
reporter.createMessage(use, MessageKind.ILLEGAL_MIXIN_SUPER_USE));
}
reporter.reportError(error, infos);
}
void checkClassMembers(ClassElement cls) {
assert(cls.isDeclaration, failedAt(cls));
if (cls.isObject) return;
// TODO(johnniwinther): Should this be done on the implementation element as
// well?
List<Element> constConstructors = <Element>[];
List<Element> nonFinalInstanceFields = <Element>[];
cls.forEachMember((holder, dynamic member) {
reporter.withCurrentElement(member, () {
// Perform various checks as side effect of "computing" the type.
member.computeType(resolution);
// Check modifiers.
// ignore: UNDEFINED_GETTER
if (member.isFunction && member.modifiers.isFinal) {
reporter.reportErrorMessage(
member, MessageKind.ILLEGAL_FINAL_METHOD_MODIFIER);
}
if (member.isConstructor) {
// ignore: UNDEFINED_GETTER
final mismatchedFlagsBits = member.modifiers.flags &
(Modifiers.FLAG_STATIC | Modifiers.FLAG_ABSTRACT);
if (mismatchedFlagsBits != 0) {
final mismatchedFlags =
new Modifiers.withFlags(null, mismatchedFlagsBits);
reporter.reportErrorMessage(
member,
MessageKind.ILLEGAL_CONSTRUCTOR_MODIFIERS,
{'modifiers': mismatchedFlags});
}
// ignore: UNDEFINED_GETTER
if (member.modifiers.isConst) {
constConstructors.add(member);
}
}
if (member.isField) {
// ignore: UNDEFINED_GETTER
if (member.modifiers.isConst && !member.modifiers.isStatic) {
reporter.reportErrorMessage(
member, MessageKind.ILLEGAL_CONST_FIELD_MODIFIER);
}
// ignore: UNDEFINED_GETTER
if (!member.modifiers.isStatic && !member.modifiers.isFinal) {
nonFinalInstanceFields.add(member);
}
}
checkAbstractField(member);
checkUserDefinableOperator(member);
});
});
if (!constConstructors.isEmpty && !nonFinalInstanceFields.isEmpty) {
Spannable span =
constConstructors.length > 1 ? cls : constConstructors[0];
DiagnosticMessage error = reporter.createMessage(
span,
MessageKind.CONST_CONSTRUCTOR_WITH_NONFINAL_FIELDS,
{'className': cls.name});
List<DiagnosticMessage> infos = <DiagnosticMessage>[];
if (constConstructors.length > 1) {
for (Element constructor in constConstructors) {
infos.add(reporter.createMessage(constructor,
MessageKind.CONST_CONSTRUCTOR_WITH_NONFINAL_FIELDS_CONSTRUCTOR));
}
}
for (Element field in nonFinalInstanceFields) {
infos.add(reporter.createMessage(
field, MessageKind.CONST_CONSTRUCTOR_WITH_NONFINAL_FIELDS_FIELD));
}
reporter.reportError(error, infos);
}
}
void checkAbstractField(Element member) {
// Only check for getters. The test can only fail if there is both a setter
// and a getter with the same name, and we only need to check each abstract
// field once, so we just ignore setters.
if (!member.isGetter) return;
// Find the associated abstract field.
ClassElement classElement = member.enclosingClass;
Element lookupElement = classElement.lookupLocalMember(member.name);
if (lookupElement == null) {
reporter.internalError(member, "No abstract field for accessor");
} else if (!lookupElement.isAbstractField) {
if (lookupElement.isMalformed || lookupElement.isAmbiguous) return;
reporter.internalError(
member, "Inaccessible abstract field for accessor");
}
AbstractFieldElement field = lookupElement;
GetterElementX getter = field.getter;
if (getter == null) return;
SetterElementX setter = field.setter;
if (setter == null) return;
int getterFlags = getter.modifiers.flags | Modifiers.FLAG_ABSTRACT;
int setterFlags = setter.modifiers.flags | Modifiers.FLAG_ABSTRACT;
if (getterFlags != setterFlags) {
final mismatchedFlags =
new Modifiers.withFlags(null, getterFlags ^ setterFlags);
reporter.reportWarningMessage(field.getter, MessageKind.GETTER_MISMATCH,
{'modifiers': mismatchedFlags});
reporter.reportWarningMessage(field.setter, MessageKind.SETTER_MISMATCH,
{'modifiers': mismatchedFlags});
}
}
void checkUserDefinableOperator(Element member) {
FunctionElement function = member.asFunctionElement();
if (function == null) return;
String value = member.name;
if (value == null) return;
if (!(isUserDefinableOperator(value) || identical(value, 'unary-'))) return;
bool isMinus = false;
int requiredParameterCount;
MessageKind messageKind;
if (identical(value, 'unary-')) {
isMinus = true;
messageKind = MessageKind.MINUS_OPERATOR_BAD_ARITY;
requiredParameterCount = 0;
} else if (isMinusOperator(value)) {
isMinus = true;
messageKind = MessageKind.MINUS_OPERATOR_BAD_ARITY;
requiredParameterCount = 1;
} else if (isUnaryOperator(value)) {
messageKind = MessageKind.UNARY_OPERATOR_BAD_ARITY;
requiredParameterCount = 0;
} else if (isBinaryOperator(value)) {
messageKind = MessageKind.BINARY_OPERATOR_BAD_ARITY;
requiredParameterCount = 1;
if (identical(value, '==')) checkOverrideHashCode(member);
} else if (isTernaryOperator(value)) {
messageKind = MessageKind.TERNARY_OPERATOR_BAD_ARITY;
requiredParameterCount = 2;
} else {
reporter.internalError(
function, 'Unexpected user defined operator $value');
}
checkArity(function, requiredParameterCount, messageKind, isMinus);
}
void checkOverrideHashCode(FunctionElement operatorEquals) {
if (operatorEquals.isAbstract) return;
ClassElement cls = operatorEquals.enclosingClass;
Element hashCodeImplementation = cls.lookupLocalMember('hashCode');
if (hashCodeImplementation != null) return;
reporter.reportHintMessage(operatorEquals,
MessageKind.OVERRIDE_EQUALS_NOT_HASH_CODE, {'class': cls.name});
}
void checkArity(FunctionElement function, int requiredParameterCount,
MessageKind messageKind, bool isMinus) {
FunctionExpression node = function.node;
FunctionSignature signature = function.functionSignature;
if (signature.requiredParameterCount != requiredParameterCount) {
Node errorNode = node;
if (node.parameters != null) {
if (isMinus ||
signature.requiredParameterCount < requiredParameterCount) {
// If there are too few parameters, point to the whole parameter list.
// For instance
//
// int operator +() {}
// ^^
//
// int operator []=(value) {}
// ^^^^^^^
//
// For operator -, always point the whole parameter list, like
//
// int operator -(a, b) {}
// ^^^^^^
//
// instead of
//
// int operator -(a, b) {}
// ^
//
// since the correction might not be to remove 'b' but instead to
// remove 'a, b'.
errorNode = node.parameters;
} else {
errorNode = node.parameters.nodes.skip(requiredParameterCount).head;
}
}
reporter.reportErrorMessage(
errorNode, messageKind, {'operatorName': function.name});
}
if (signature.optionalParameterCount != 0) {
Node errorNode =
node.parameters.nodes.skip(signature.requiredParameterCount).head;
if (signature.optionalParametersAreNamed) {
reporter.reportErrorMessage(
errorNode,
MessageKind.OPERATOR_NAMED_PARAMETERS,
{'operatorName': function.name});
} else {
reporter.reportErrorMessage(
errorNode,
MessageKind.OPERATOR_OPTIONAL_PARAMETERS,
{'operatorName': function.name});
}
}
}
reportErrorWithContext(Element errorneousElement, MessageKind errorMessage,
Element contextElement, MessageKind contextMessage) {
reporter.reportError(
reporter.createMessage(errorneousElement, errorMessage, {
'memberName': contextElement.name,
'className': contextElement.enclosingClass.name
}),
<DiagnosticMessage>[
reporter.createMessage(contextElement, contextMessage),
]);
}
FunctionSignature resolveSignature(FunctionElementX element) {
MessageKind defaultValuesError = null;
if (element.isFactoryConstructor) {
FunctionExpression body = element.parseNode(parsingContext);
if (body.isRedirectingFactory) {
defaultValuesError = MessageKind.REDIRECTING_FACTORY_WITH_DEFAULT;
}
}
return reporter.withCurrentElement(element, () {
FunctionExpression node = element.parseNode(parsingContext);
return measure(() => SignatureResolver.analyze(
resolution,
element.enclosingElement.buildScope(),
const FunctionTypeParameterScope(),
node.typeVariables,
node.parameters,
node.returnType,
element,
new ResolutionRegistry(
resolution.target, _ensureTreeElements(element)),
defaultValuesError: defaultValuesError,
createRealParameters: true));
});
}
WorldImpact resolveTypedef(TypedefElementX element) {
if (element.isResolved) return const ResolutionImpact();
world.registerTypedef(element);
return _resolveTypeDeclaration(element, () {
ResolutionRegistry registry = new ResolutionRegistry(
resolution.target, _ensureTreeElements(element));
return reporter.withCurrentElement(element, () {
return measure(() {
assert(element.resolutionState == STATE_NOT_STARTED);
element.resolutionState = STATE_STARTED;
Typedef node = element.parseNode(parsingContext);
TypedefResolverVisitor visitor =
new TypedefResolverVisitor(resolution, element, registry);
visitor.visit(node);
element.resolutionState = STATE_DONE;
return registry.impactBuilder;
});
});
});
}
void resolveMetadataAnnotation(MetadataAnnotationX annotation) {
reporter.withCurrentElement(
annotation.annotatedElement,
() => measure(() {
assert(annotation.resolutionState == STATE_NOT_STARTED);
annotation.resolutionState = STATE_STARTED;
Node node = annotation.parseNode(parsingContext);
Element annotatedElement = annotation.annotatedElement;
AnalyzableElement context = annotatedElement.analyzableElement;
ClassElement classElement = annotatedElement.enclosingClass;
if (classElement != null) {
// The annotation is resolved in the scope of [classElement].
classElement.ensureResolved(resolution);
}
assert(
context != null,
failedAt(
node,
"No context found for metadata annotation "
"on $annotatedElement."));
ResolverVisitor visitor =
visitorFor(context, useEnclosingScope: true);
ResolutionRegistry registry = visitor.registry;
node.accept(visitor);
// TODO(johnniwinther): Avoid passing the [TreeElements] to
// [compileMetadata].
ConstantExpression constant = constantCompiler.compileMetadata(
annotation, node, registry.mapping);
switch (constant.kind) {
case ConstantExpressionKind.CONSTRUCTED:
ConstructedConstantExpression constructedConstant = constant;
ResolutionInterfaceType type = constructedConstant.type;
if (type.isGeneric && !type.isRaw) {
// Const constructor calls cannot have type arguments.
// TODO(24312): Remove this.
reporter.reportErrorMessage(
node, MessageKind.INVALID_METADATA_GENERIC);
constant = new ErroneousConstantExpression();
}
break;
case ConstantExpressionKind.FIELD:
case ConstantExpressionKind.ERRONEOUS:
break;
default:
reporter.reportErrorMessage(
node, MessageKind.INVALID_METADATA);
constant = new ErroneousConstantExpression();
break;
}
annotation.constant = constant;
constantCompiler.evaluate(annotation.constant);
// TODO(johnniwinther): Register the relation between the
// annotation and the annotated element instead. This will allow
// the backend to retrieve the backend constant and only register
// metadata on the elements for which it is needed. (Issue 17732).
annotation.resolutionState = STATE_DONE;
}));
}
List<MetadataAnnotation> resolveMetadata(
Element element, VariableDefinitions node) {
List<MetadataAnnotation> metadata = <MetadataAnnotation>[];
for (Metadata annotation in node.metadata.nodes) {
ParameterMetadataAnnotation metadataAnnotation =
new ParameterMetadataAnnotation(annotation);
metadataAnnotation.annotatedElement = element;
metadata.add(metadataAnnotation.ensureResolved(resolution));
}
return metadata;
}
}
TreeElements _ensureTreeElements(AnalyzableElementX element) {
if (element._treeElements == null) {
element._treeElements = new TreeElementMapping(element);
}
return element._treeElements;
}
abstract class AnalyzableElementX implements AnalyzableElement {
TreeElements _treeElements;
bool get hasTreeElements => _treeElements != null;
TreeElements get treeElements {
assert(_treeElements != null,
failedAt(this, "TreeElements have not been computed for $this."));
return _treeElements;
}
void reuseElement() {
_treeElements = null;
}
}