| Index: pkg/analyzer/lib/src/generated/resolver.dart
|
| diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
|
| index 380a35d4a141288910059d09cb06dff5b4511bd6..f8ccff2fcd92337ab5de5f9e60ef266208275e65 100644
|
| --- a/pkg/analyzer/lib/src/generated/resolver.dart
|
| +++ b/pkg/analyzer/lib/src/generated/resolver.dart
|
| @@ -4669,6 +4669,77 @@ class ElementResolver extends SimpleAstVisitor<Object> {
|
| }
|
|
|
| /**
|
| + * Return a method representing the merge of the given elements. The type of the merged element is
|
| + * the component-wise union of the types of the given elements. If not all input elements have the
|
| + * same shape then [null] is returned.
|
| + *
|
| + * @param elements the `ExecutableElement`s to merge
|
| + * @return an `ExecutableElement` representing the merge of `elements`
|
| + */
|
| + static ExecutableElement _computeMergedExecutableElement(Set<ExecutableElement> elements) {
|
| + List<ExecutableElement> elementArrayToMerge = new List.from(elements);
|
| + if (elementArrayToMerge.length == 0) {
|
| + return null;
|
| + } else {
|
| + // Flatten methods structurally. Based on
|
| + // [InheritanceManager.computeMergedExecutableElement] and
|
| + // [InheritanceManager.createSyntheticExecutableElement].
|
| + //
|
| + // However, the approach we take here is much simpler, but expected to work
|
| + // well in the common case. It degrades gracefully in the uncommon case,
|
| + // by computing the type [dynamic] for the method, preventing any
|
| + // hints from being generated (TODO: not done yet).
|
| + //
|
| + // The approach is: we require that each [ExecutableElement] has the
|
| + // same shape: the same number of required, optional positional, and optional named
|
| + // parameters, in the same positions, and with the named parameters in the
|
| + // same order. We compute a type by unioning pointwise.
|
| + ExecutableElement e_0 = elementArrayToMerge[0];
|
| + List<ParameterElement> ps_0 = e_0.parameters;
|
| + List<ParameterElementImpl> ps_out = new List<ParameterElementImpl>(ps_0.length);
|
| + for (int j = 0; j < ps_out.length; j++) {
|
| + ps_out[j] = new ParameterElementImpl(ps_0[j].name, 0);
|
| + ps_out[j].synthetic = true;
|
| + ps_out[j].type = ps_0[j].type;
|
| + ps_out[j].parameterKind = ps_0[j].parameterKind;
|
| + }
|
| + DartType r_out = e_0.returnType;
|
| + for (int i = 1; i < elementArrayToMerge.length; i++) {
|
| + ExecutableElement e_i = elementArrayToMerge[i];
|
| + r_out = UnionTypeImpl.union([r_out, e_i.returnType]);
|
| + List<ParameterElement> ps_i = e_i.parameters;
|
| + // Each function must have the same number of params.
|
| + if (ps_0.length != ps_i.length) {
|
| + return null;
|
| + } else {
|
| + // Each function must have the same kind of params, with the same names,
|
| + // in the same order.
|
| + for (int j = 0; j < ps_i.length; j++) {
|
| + if (ps_0[j].parameterKind != ps_i[j].parameterKind || !identical(ps_0[j].name, ps_i[j].name)) {
|
| + return null;
|
| + } else {
|
| + // The output parameter type is the union of the input parameter types.
|
| + ps_out[j].type = UnionTypeImpl.union([ps_out[j].type, ps_i[j].type]);
|
| + }
|
| + }
|
| + }
|
| + }
|
| + // TODO (collinsn): this code should work for functions and methods,
|
| + // so we may want [FunctionElementImpl]
|
| + // instead here in some cases? And then there are constructors and property accessors.
|
| + // Maybe the answer is to create a new subclass of [ExecutableElementImpl] which
|
| + // is used for merged executable elements, in analogy with [MultiplyInheritedMethodElementImpl]
|
| + // and [MultiplyInheritedPropertyAcessorElementImpl].
|
| + ExecutableElementImpl e_out = new MethodElementImpl(e_0.name, 0);
|
| + e_out.synthetic = true;
|
| + e_out.returnType = r_out;
|
| + e_out.parameters = ps_out;
|
| + e_out.type = new FunctionTypeImpl.con1(e_out);
|
| + return e_out;
|
| + }
|
| + }
|
| +
|
| + /**
|
| * Return `true` if the given identifier is the return type of a constructor declaration.
|
| *
|
| * @return `true` if the given identifier is the return type of a constructor declaration.
|
| @@ -6237,6 +6308,19 @@ class ElementResolver extends SimpleAstVisitor<Object> {
|
| }
|
| return _lookUpMethodInInterfaces(interfaceType, false, methodName, new HashSet<ClassElement>());
|
| }
|
| + if (type is UnionType) {
|
| + Set<ExecutableElement> methods = new HashSet<ExecutableElement>();
|
| + for (DartType t in type.elements) {
|
| + MethodElement m = _lookUpMethod(target, t, methodName);
|
| + if (m != null) {
|
| + methods.add(m);
|
| + }
|
| + }
|
| + // TODO (collinsn): I want [computeMergedExecutableElement] to be general
|
| + // and work with functions, methods, constructors, and property accessors. However,
|
| + // I won't be able to assume it returns [MethodElement] here then.
|
| + return _computeMergedExecutableElement(methods) as MethodElement;
|
| + }
|
| return null;
|
| }
|
|
|
| @@ -6609,6 +6693,7 @@ class ElementResolver extends SimpleAstVisitor<Object> {
|
| List<ParameterElement> resolvedParameters = new List<ParameterElement>(argumentCount);
|
| int positionalArgumentCount = 0;
|
| HashSet<String> usedNames = new HashSet<String>();
|
| + bool noBlankArguments = true;
|
| for (int i = 0; i < argumentCount; i++) {
|
| Expression argument = arguments[i];
|
| if (argument is NamedExpression) {
|
| @@ -6626,16 +6711,19 @@ class ElementResolver extends SimpleAstVisitor<Object> {
|
| _resolver.reportErrorForNode(CompileTimeErrorCode.DUPLICATE_NAMED_ARGUMENT, nameNode, [name]);
|
| }
|
| } else {
|
| + if (argument is SimpleIdentifier && argument.name.isEmpty) {
|
| + noBlankArguments = false;
|
| + }
|
| positionalArgumentCount++;
|
| if (unnamedIndex < unnamedParameterCount) {
|
| resolvedParameters[i] = unnamedParameters[unnamedIndex++];
|
| }
|
| }
|
| }
|
| - if (positionalArgumentCount < requiredParameters.length) {
|
| + if (positionalArgumentCount < requiredParameters.length && noBlankArguments) {
|
| ErrorCode errorCode = (reportError ? CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS : StaticWarningCode.NOT_ENOUGH_REQUIRED_ARGUMENTS);
|
| _resolver.reportErrorForNode(errorCode, argumentList, [requiredParameters.length, positionalArgumentCount]);
|
| - } else if (positionalArgumentCount > unnamedParameterCount) {
|
| + } else if (positionalArgumentCount > unnamedParameterCount && noBlankArguments) {
|
| ErrorCode errorCode = (reportError ? CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS : StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS);
|
| _resolver.reportErrorForNode(errorCode, argumentList, [unnamedParameterCount, positionalArgumentCount]);
|
| }
|
| @@ -6751,14 +6839,14 @@ class ElementResolver extends SimpleAstVisitor<Object> {
|
| * @return the element being invoked
|
| */
|
| Element _resolveInvokedElementWithTarget(Expression target, DartType targetType, SimpleIdentifier methodName) {
|
| - if (targetType is InterfaceType) {
|
| - InterfaceType classType = targetType;
|
| - Element element = _lookUpMethod(target, classType, methodName.name);
|
| + if (targetType is InterfaceType || targetType is UnionType) {
|
| + Element element = _lookUpMethod(target, targetType, methodName.name);
|
| if (element == null) {
|
| //
|
| // If there's no method, then it's possible that 'm' is a getter that returns a function.
|
| //
|
| - element = _lookUpGetter(target, classType, methodName.name);
|
| + // TODO (collinsn): need to add union type support here too, in the style of [lookUpMethod].
|
| + element = _lookUpGetter(target, targetType, methodName.name);
|
| }
|
| return element;
|
| } else if (target is SimpleIdentifier) {
|
| @@ -14028,8 +14116,6 @@ class InheritanceManager {
|
| * <b>dynamic</b>, <i>h</i> positional parameters of type <b>dynamic</b>, named parameters
|
| * <i>s</i> of type <b>dynamic</b> and return type <b>dynamic</b>.
|
| *
|
| - * TODO (jwren) Associate a propagated type to the synthetic method element using least upper
|
| - * bounds instead of dynamic
|
| */
|
| static ExecutableElement _computeMergedExecutableElement(List<ExecutableElement> elementArrayToMerge) {
|
| int h = _getNumOfPositionalParameters(elementArrayToMerge[0]);
|
| @@ -15412,7 +15498,7 @@ class LibraryElementBuilder {
|
| //
|
| // Create and populate the library element.
|
| //
|
| - LibraryElementImpl libraryElement = new LibraryElementImpl.forNode(_analysisContext, libraryNameNode);
|
| + LibraryElementImpl libraryElement = new LibraryElementImpl.forNode(_analysisContext.getContextFor(librarySource), libraryNameNode);
|
| libraryElement.definingCompilationUnit = definingCompilationUnitElement;
|
| if (entryPoint != null) {
|
| libraryElement.entryPoint = entryPoint;
|
| @@ -15496,7 +15582,7 @@ class LibraryElementBuilder {
|
| //
|
| // Create and populate the library element.
|
| //
|
| - LibraryElementImpl libraryElement = new LibraryElementImpl.forNode(_analysisContext, libraryNameNode);
|
| + LibraryElementImpl libraryElement = new LibraryElementImpl.forNode(_analysisContext.getContextFor(librarySource), libraryNameNode);
|
| libraryElement.definingCompilationUnit = definingCompilationUnitElement;
|
| if (entryPoint != null) {
|
| libraryElement.entryPoint = entryPoint;
|
| @@ -18820,7 +18906,7 @@ class ResolverVisitor extends ScopedVisitor {
|
| try {
|
| super.visitFieldDeclaration(node);
|
| } finally {
|
| - HashMap<Element, DartType> overrides = _overrideManager.captureOverrides(node.fields);
|
| + Map<Element, DartType> overrides = _overrideManager.captureOverrides(node.fields);
|
| _overrideManager.exitScope();
|
| _overrideManager.applyOverrides(overrides);
|
| }
|
| @@ -18912,7 +18998,7 @@ class ResolverVisitor extends ScopedVisitor {
|
| Object visitIfStatement(IfStatement node) {
|
| Expression condition = node.condition;
|
| safelyVisit(condition);
|
| - HashMap<Element, DartType> thenOverrides = null;
|
| + Map<Element, DartType> thenOverrides = null;
|
| Statement thenStatement = node.thenStatement;
|
| if (thenStatement != null) {
|
| _overrideManager.enterScope();
|
| @@ -18934,7 +19020,7 @@ class ResolverVisitor extends ScopedVisitor {
|
| _overrideManager.exitScope();
|
| }
|
| }
|
| - HashMap<Element, DartType> elseOverrides = null;
|
| + Map<Element, DartType> elseOverrides = null;
|
| Statement elseStatement = node.elseStatement;
|
| if (elseStatement != null) {
|
| _overrideManager.enterScope();
|
| @@ -18960,10 +19046,16 @@ class ResolverVisitor extends ScopedVisitor {
|
| if (elseOverrides != null) {
|
| _overrideManager.applyOverrides(elseOverrides);
|
| }
|
| + } else if (!thenIsAbrupt && !elseIsAbrupt) {
|
| + // It would be more precise to ignore the existing override for any variable that
|
| + // is overridden in both branches.
|
| + if (thenOverrides != null) {
|
| + _overrideManager.mergeOverrides(thenOverrides);
|
| + }
|
| + if (elseOverrides != null) {
|
| + _overrideManager.mergeOverrides(elseOverrides);
|
| + }
|
| }
|
| - // TODO(collinsn): union the [thenOverrides] and [elseOverrides] if both branches
|
| - // are not abrupt. If both branches are abrupt, then we can mark the
|
| - // remaining code as dead.
|
| return null;
|
| }
|
|
|
| @@ -19086,7 +19178,7 @@ class ResolverVisitor extends ScopedVisitor {
|
| try {
|
| super.visitTopLevelVariableDeclaration(node);
|
| } finally {
|
| - HashMap<Element, DartType> overrides = _overrideManager.captureOverrides(node.variables);
|
| + Map<Element, DartType> overrides = _overrideManager.captureOverrides(node.variables);
|
| _overrideManager.exitScope();
|
| _overrideManager.applyOverrides(overrides);
|
| }
|
| @@ -19475,7 +19567,7 @@ class ResolverVisitor extends ScopedVisitor {
|
| //
|
| // In the presence of exceptions things become much more complicated, but while
|
| // we only use this to propagate at [if]-statement join points, checking for [return]
|
| - // is probably sound.
|
| + // may work well enough in the common case.
|
| if (statement is ReturnStatement) {
|
| return true;
|
| } else if (statement is ExpressionStatement) {
|
| @@ -19486,6 +19578,21 @@ class ResolverVisitor extends ScopedVisitor {
|
| if (size == 0) {
|
| return false;
|
| }
|
| + // This last-statement-is-return heuristic is unsound for adversarial code,
|
| + // but probably works well in the common case:
|
| + //
|
| + // var x = 123;
|
| + // var c = true;
|
| + // L: if (c) {
|
| + // x = "hello";
|
| + // c = false;
|
| + // break L;
|
| + // return;
|
| + // }
|
| + // print(x);
|
| + //
|
| + // Unsound to assume that [x = "hello";] never executed after the if-statement.
|
| + // Of course, a dead-code analysis could point out that [return] here is dead.
|
| return _isAbruptTerminationStatement(statements[size - 1]);
|
| }
|
| return false;
|
| @@ -22516,7 +22623,7 @@ class TypeOverrideManager {
|
| *
|
| * @param overrides the overrides to be applied
|
| */
|
| - void applyOverrides(HashMap<Element, DartType> overrides) {
|
| + void applyOverrides(Map<Element, DartType> overrides) {
|
| if (_currentScope == null) {
|
| throw new IllegalStateException("Cannot apply overrides without a scope");
|
| }
|
| @@ -22529,7 +22636,7 @@ class TypeOverrideManager {
|
| *
|
| * @return the overrides in the current scope
|
| */
|
| - HashMap<Element, DartType> captureLocalOverrides() {
|
| + Map<Element, DartType> captureLocalOverrides() {
|
| if (_currentScope == null) {
|
| throw new IllegalStateException("Cannot capture local overrides without a scope");
|
| }
|
| @@ -22543,7 +22650,7 @@ class TypeOverrideManager {
|
| * @param variableList the list of variables whose overriding types are to be captured
|
| * @return a table mapping elements to their overriding types
|
| */
|
| - HashMap<Element, DartType> captureOverrides(VariableDeclarationList variableList) {
|
| + Map<Element, DartType> captureOverrides(VariableDeclarationList variableList) {
|
| if (_currentScope == null) {
|
| throw new IllegalStateException("Cannot capture overrides without a scope");
|
| }
|
| @@ -22582,6 +22689,15 @@ class TypeOverrideManager {
|
| }
|
|
|
| /**
|
| + * Merge new overrides with existing overrides using union types.
|
| + *
|
| + * @param overrides the new overrides to merge in.
|
| + */
|
| + void mergeOverrides(Map<Element, DartType> overrides) {
|
| + _currentScope.mergeOverrides(overrides);
|
| + }
|
| +
|
| + /**
|
| * Set the overridden type of the given element to the given type
|
| *
|
| * @param element the element whose type might have been overridden
|
| @@ -22608,7 +22724,7 @@ class TypeOverrideManager_TypeOverrideScope {
|
| /**
|
| * A table mapping elements to the overridden type of that element.
|
| */
|
| - HashMap<Element, DartType> _overridenTypes = new HashMap<Element, DartType>();
|
| + Map<Element, DartType> _overridenTypes = new HashMap<Element, DartType>();
|
|
|
| /**
|
| * Initialize a newly created scope to be an empty child of the given scope.
|
| @@ -22622,7 +22738,7 @@ class TypeOverrideManager_TypeOverrideScope {
|
| *
|
| * @param overrides the overrides to be applied
|
| */
|
| - void applyOverrides(HashMap<Element, DartType> overrides) {
|
| + void applyOverrides(Map<Element, DartType> overrides) {
|
| for (MapEntry<Element, DartType> entry in getMapEntrySet(overrides)) {
|
| _overridenTypes[entry.getKey()] = entry.getValue();
|
| }
|
| @@ -22634,7 +22750,7 @@ class TypeOverrideManager_TypeOverrideScope {
|
| *
|
| * @return the overrides in the current scope
|
| */
|
| - HashMap<Element, DartType> captureLocalOverrides() => _overridenTypes;
|
| + Map<Element, DartType> captureLocalOverrides() => _overridenTypes;
|
|
|
| /**
|
| * Return a map from the elements for the variables in the given list that have their types
|
| @@ -22643,8 +22759,8 @@ class TypeOverrideManager_TypeOverrideScope {
|
| * @param variableList the list of variables whose overriding types are to be captured
|
| * @return a table mapping elements to their overriding types
|
| */
|
| - HashMap<Element, DartType> captureOverrides(VariableDeclarationList variableList) {
|
| - HashMap<Element, DartType> overrides = new HashMap<Element, DartType>();
|
| + Map<Element, DartType> captureOverrides(VariableDeclarationList variableList) {
|
| + Map<Element, DartType> overrides = new HashMap<Element, DartType>();
|
| if (variableList.isConst || variableList.isFinal) {
|
| for (VariableDeclaration variable in variableList.variables) {
|
| Element element = variable.element;
|
| @@ -22680,6 +22796,18 @@ class TypeOverrideManager_TypeOverrideScope {
|
| }
|
|
|
| /**
|
| + * Merge new overrides with existing overrides using union types.
|
| + *
|
| + * @param overrides the new overrides to merge in.
|
| + */
|
| + void mergeOverrides(Map<Element, DartType> overrides) {
|
| + for (MapEntry<Element, DartType> entry in getMapEntrySet(overrides)) {
|
| + Element key = entry.getKey();
|
| + _overridenTypes[key] = UnionTypeImpl.union([_overridenTypes[key], entry.getValue()]);
|
| + }
|
| + }
|
| +
|
| + /**
|
| * Set the overridden type of the given element to the given type
|
| *
|
| * @param element the element whose type might have been overridden
|
|
|