| 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 8209d22097dbc5ee554a398c45573301b15b99e7..f8b90e975fecff7fdaa6bf9b8b1700a35bdb3f41 100644
|
| --- a/pkg/analyzer/lib/src/generated/resolver.dart
|
| +++ b/pkg/analyzer/lib/src/generated/resolver.dart
|
| @@ -70,33 +70,42 @@ class AngularCompilationUnitBuilder {
|
| static String _NG_TWO_WAY = "NgTwoWay";
|
|
|
| static Element getElement(ASTNode node, int offset) {
|
| + // maybe no node
|
| if (node == null) {
|
| return null;
|
| }
|
| + // prepare enclosing ClassDeclaration
|
| ClassDeclaration classDeclaration = node.getAncestor(ClassDeclaration);
|
| if (classDeclaration == null) {
|
| return null;
|
| }
|
| + // prepare ClassElement
|
| ClassElement classElement = classDeclaration.element;
|
| if (classElement == null) {
|
| return null;
|
| }
|
| + // check toolkit objects
|
| for (ToolkitObjectElement toolkitObject in classElement.toolkitObjects) {
|
| List<AngularPropertyElement> properties = AngularPropertyElement.EMPTY_ARRAY;
|
| + // try properties of AngularComponentElement
|
| if (toolkitObject is AngularComponentElement) {
|
| AngularComponentElement component = toolkitObject;
|
| properties = component.properties;
|
| }
|
| + // try properties of AngularDirectiveElement
|
| if (toolkitObject is AngularDirectiveElement) {
|
| AngularDirectiveElement directive = toolkitObject;
|
| properties = directive.properties;
|
| }
|
| + // check properties
|
| for (AngularPropertyElement property in properties) {
|
| + // property name (use complete node range)
|
| int propertyOffset = property.nameOffset;
|
| int propertyEnd = propertyOffset + property.name.length;
|
| if (node.offset <= propertyOffset && propertyEnd < node.end) {
|
| return property;
|
| }
|
| + // field name (use complete node range, including @, => and <=>)
|
| FieldElement field = property.field;
|
| if (field != null) {
|
| int fieldOffset = property.fieldNameOffset;
|
| @@ -107,6 +116,7 @@ class AngularCompilationUnitBuilder {
|
| }
|
| }
|
| }
|
| + // no Element
|
| return null;
|
| }
|
|
|
| @@ -118,16 +128,21 @@ class AngularCompilationUnitBuilder {
|
| return false;
|
| }
|
| InterfaceType interfaceType = type as InterfaceType;
|
| + // check hierarchy
|
| Set<Type2> seenTypes = new Set();
|
| while (interfaceType != null) {
|
| + // check for recursion
|
| if (!seenTypes.add(interfaceType)) {
|
| return false;
|
| }
|
| + // check for "Module"
|
| if (interfaceType.element.name == "Module") {
|
| return true;
|
| }
|
| + // try supertype
|
| interfaceType = interfaceType.superclass;
|
| }
|
| + // no
|
| return false;
|
| }
|
|
|
| @@ -139,6 +154,7 @@ class AngularCompilationUnitBuilder {
|
| if (text.startsWith("[") && text.endsWith("]")) {
|
| int nameOffset = offset + "[".length;
|
| String attributeName = text.substring(1, text.length - 1);
|
| + // TODO(scheglov) report warning if there are spaces between [ and identifier
|
| return new HasAttributeSelectorElementImpl(attributeName, nameOffset);
|
| }
|
| if (StringUtilities.isTagName(text)) {
|
| @@ -238,38 +254,46 @@ class AngularCompilationUnitBuilder {
|
| * @param unit the compilation unit with built Dart element models
|
| */
|
| void build(CompilationUnit unit) {
|
| + // process classes
|
| for (CompilationUnitMember unitMember in unit.declarations) {
|
| if (unitMember is ClassDeclaration) {
|
| this._classDeclaration = unitMember;
|
| this._classElement = _classDeclaration.element as ClassElementImpl;
|
| this._classToolkitObjects.clear();
|
| parseModuleClass();
|
| + // process annotations
|
| NodeList<Annotation> annotations = _classDeclaration.metadata;
|
| for (Annotation annotation in annotations) {
|
| this._annotation = annotation;
|
| + // @NgFilter
|
| if (isAngularAnnotation2(_NG_FILTER)) {
|
| parseNgFilter();
|
| continue;
|
| }
|
| + // @NgComponent
|
| if (isAngularAnnotation2(_NG_COMPONENT)) {
|
| parseNgComponent();
|
| continue;
|
| }
|
| + // @NgController
|
| if (isAngularAnnotation2(_NG_CONTROLLER)) {
|
| parseNgController();
|
| continue;
|
| }
|
| + // @NgDirective
|
| if (isAngularAnnotation2(_NG_DIRECTIVE)) {
|
| parseNgDirective();
|
| continue;
|
| }
|
| }
|
| + // set toolkit objects
|
| if (!_classToolkitObjects.isEmpty) {
|
| List<ToolkitObjectElement> objects = _classToolkitObjects;
|
| _classElement.toolkitObjects = new List.from(objects);
|
| }
|
| }
|
| }
|
| + // process modules in variables
|
| parseModuleVariables(unit);
|
| }
|
|
|
| @@ -363,9 +387,11 @@ class AngularCompilationUnitBuilder {
|
| if (!isModule4) {
|
| return;
|
| }
|
| + // check install(), type() and value() invocations
|
| List<AngularModuleElement> childModules = [];
|
| List<ClassElement> keyTypes = [];
|
| _classDeclaration.accept(new RecursiveASTVisitor_AngularCompilationUnitBuilder_parseModuleClass(this, childModules, keyTypes));
|
| + // set module element
|
| AngularModuleElementImpl module = createModuleElement(childModules, keyTypes);
|
| _classToolkitObjects.add(module);
|
| }
|
| @@ -377,6 +403,7 @@ class AngularCompilationUnitBuilder {
|
| void parseModuleInvocation(MethodInvocation node, List<AngularModuleElement> childModules, List<ClassElement> keyTypes) {
|
| String methodName = node.methodName.name;
|
| NodeList<Expression> arguments = node.argumentList.arguments;
|
| + // install()
|
| if (arguments.length == 1 && methodName == "install") {
|
| Type2 argType = arguments[0].bestType;
|
| if (argType is InterfaceType) {
|
| @@ -390,6 +417,7 @@ class AngularCompilationUnitBuilder {
|
| }
|
| return;
|
| }
|
| + // type() and value()
|
| if (arguments.length >= 1 && (methodName == "type" || methodName == "value")) {
|
| Expression arg = arguments[0];
|
| if (arg is Identifier) {
|
| @@ -412,10 +440,12 @@ class AngularCompilationUnitBuilder {
|
|
|
| void parseNgComponent() {
|
| bool isValid = true;
|
| + // publishAs
|
| if (!hasStringArgument(_PUBLISH_AS)) {
|
| reportErrorForAnnotation(AngularCode.MISSING_PUBLISH_AS, []);
|
| isValid = false;
|
| }
|
| + // selector
|
| AngularSelectorElement selector = null;
|
| if (!hasStringArgument(_SELECTOR)) {
|
| reportErrorForAnnotation(AngularCode.MISSING_SELECTOR, []);
|
| @@ -428,14 +458,17 @@ class AngularCompilationUnitBuilder {
|
| isValid = false;
|
| }
|
| }
|
| + // templateUrl
|
| if (!hasStringArgument(_TEMPLATE_URL)) {
|
| reportErrorForAnnotation(AngularCode.MISSING_TEMPLATE_URL, []);
|
| isValid = false;
|
| }
|
| + // cssUrl
|
| if (!hasStringArgument(_CSS_URL)) {
|
| reportErrorForAnnotation(AngularCode.MISSING_CSS_URL, []);
|
| isValid = false;
|
| }
|
| + // create
|
| if (isValid) {
|
| String name = getStringArgument(_PUBLISH_AS);
|
| int nameOffset = getStringArgumentOffset(_PUBLISH_AS);
|
| @@ -475,6 +508,7 @@ class AngularCompilationUnitBuilder {
|
| if (member is FieldDeclaration) {
|
| FieldDeclaration fieldDeclaration = member;
|
| for (Annotation annotation in fieldDeclaration.metadata) {
|
| + // prepare property kind (if property annotation at all)
|
| AngularPropertyKind kind = null;
|
| if (isAngularAnnotation(annotation, _NG_ATTR)) {
|
| kind = AngularPropertyKind.ATTR;
|
| @@ -487,6 +521,7 @@ class AngularCompilationUnitBuilder {
|
| } else if (isAngularAnnotation(annotation, _NG_TWO_WAY)) {
|
| kind = AngularPropertyKind.TWO_WAY;
|
| }
|
| + // add property
|
| if (kind != null) {
|
| SimpleStringLiteral nameLiteral = getOnlySimpleStringLiteralArgument(annotation);
|
| FieldElement field = getOnlyFieldElement(fieldDeclaration);
|
| @@ -507,15 +542,19 @@ class AngularCompilationUnitBuilder {
|
| */
|
| void parseNgComponentProperties_fromMap(List<AngularPropertyElement> properties) {
|
| Expression mapExpression = getArgument("map");
|
| + // may be not properties
|
| if (mapExpression == null) {
|
| return;
|
| }
|
| + // prepare map literal
|
| if (mapExpression is! MapLiteral) {
|
| reportError(mapExpression, AngularCode.INVALID_PROPERTY_MAP, []);
|
| return;
|
| }
|
| MapLiteral mapLiteral = mapExpression as MapLiteral;
|
| + // analyze map entries
|
| for (MapLiteralEntry entry in mapLiteral.entries) {
|
| + // prepare property name
|
| Expression nameExpression = entry.key;
|
| if (nameExpression is! SimpleStringLiteral) {
|
| reportError(nameExpression, AngularCode.INVALID_PROPERTY_NAME, []);
|
| @@ -524,6 +563,7 @@ class AngularCompilationUnitBuilder {
|
| SimpleStringLiteral nameLiteral = nameExpression as SimpleStringLiteral;
|
| String name = nameLiteral.value;
|
| int nameOffset = nameLiteral.valueOffset;
|
| + // prepare field specification
|
| Expression specExpression = entry.value;
|
| if (specExpression is! SimpleStringLiteral) {
|
| reportError(specExpression, AngularCode.INVALID_PROPERTY_SPEC, []);
|
| @@ -531,6 +571,7 @@ class AngularCompilationUnitBuilder {
|
| }
|
| SimpleStringLiteral specLiteral = specExpression as SimpleStringLiteral;
|
| String spec = specLiteral.value;
|
| + // parse binding kind and field name
|
| AngularPropertyKind kind;
|
| int fieldNameOffset;
|
| if (spec.startsWith(_PREFIX_ATTR)) {
|
| @@ -554,11 +595,13 @@ class AngularCompilationUnitBuilder {
|
| }
|
| String fieldName = spec.substring(fieldNameOffset);
|
| fieldNameOffset += specLiteral.valueOffset;
|
| + // prepare field
|
| FieldElement field = _classElement.getField(fieldName);
|
| if (field == null) {
|
| reportError2(fieldNameOffset, fieldName.length, AngularCode.INVALID_PROPERTY_FIELD, [fieldName]);
|
| continue;
|
| }
|
| + // add property
|
| AngularPropertyElementImpl property = new AngularPropertyElementImpl(name, nameOffset);
|
| property.field = field;
|
| property.propertyKind = kind;
|
| @@ -569,10 +612,12 @@ class AngularCompilationUnitBuilder {
|
|
|
| void parseNgController() {
|
| bool isValid = true;
|
| + // publishAs
|
| if (!hasStringArgument(_PUBLISH_AS)) {
|
| reportErrorForAnnotation(AngularCode.MISSING_PUBLISH_AS, []);
|
| isValid = false;
|
| }
|
| + // selector
|
| AngularSelectorElement selector = null;
|
| if (!hasStringArgument(_SELECTOR)) {
|
| reportErrorForAnnotation(AngularCode.MISSING_SELECTOR, []);
|
| @@ -585,6 +630,7 @@ class AngularCompilationUnitBuilder {
|
| isValid = false;
|
| }
|
| }
|
| + // create
|
| if (isValid) {
|
| String name = getStringArgument(_PUBLISH_AS);
|
| int nameOffset = getStringArgumentOffset(_PUBLISH_AS);
|
| @@ -596,6 +642,7 @@ class AngularCompilationUnitBuilder {
|
|
|
| void parseNgDirective() {
|
| bool isValid = true;
|
| + // selector
|
| AngularSelectorElement selector = null;
|
| if (!hasStringArgument(_SELECTOR)) {
|
| reportErrorForAnnotation(AngularCode.MISSING_SELECTOR, []);
|
| @@ -608,6 +655,7 @@ class AngularCompilationUnitBuilder {
|
| isValid = false;
|
| }
|
| }
|
| + // create
|
| if (isValid) {
|
| int offset = _annotation.offset;
|
| AngularDirectiveElementImpl element = new AngularDirectiveElementImpl(offset);
|
| @@ -619,10 +667,12 @@ class AngularCompilationUnitBuilder {
|
|
|
| void parseNgFilter() {
|
| bool isValid = true;
|
| + // name
|
| if (!hasStringArgument(_NAME)) {
|
| reportErrorForAnnotation(AngularCode.MISSING_NAME, []);
|
| isValid = false;
|
| }
|
| + // create
|
| if (isValid) {
|
| String name = getStringArgument(_NAME);
|
| int nameOffset = getStringArgumentOffset(_NAME);
|
| @@ -680,6 +730,8 @@ class RecursiveASTVisitor_AngularCompilationUnitBuilder_parseModuleVariables ext
|
|
|
| List<ClassElement> _keyTypes = [];
|
|
|
| + Object visitClassDeclaration(ClassDeclaration node) => null;
|
| +
|
| Object visitFunctionDeclaration(FunctionDeclaration node) {
|
| _childModules.clear();
|
| _keyTypes.clear();
|
| @@ -711,13 +763,17 @@ class RecursiveASTVisitor_AngularCompilationUnitBuilder_parseModuleVariables ext
|
|
|
| bool isVariableInvocation(MethodInvocation node) {
|
| Expression target = node.realTarget;
|
| + // var module = new Module()..type(t1)..type(t2);
|
| if (_variableInit is CascadeExpression && target != null && identical(target.parent, _variableInit)) {
|
| return true;
|
| }
|
| + // var module = new Module();
|
| + // module.type(t);
|
| if (target is Identifier) {
|
| Element targetElement = target.staticElement;
|
| return identical(targetElement, _variable);
|
| }
|
| + // no
|
| return false;
|
| }
|
| }
|
| @@ -845,6 +901,9 @@ class ElementBuilder extends RecursiveASTVisitor<Object> {
|
| element.type = interfaceType;
|
| List<ConstructorElement> constructors = holder.constructors;
|
| if (constructors.length == 0) {
|
| + //
|
| + // Create the default constructor.
|
| + //
|
| constructors = createDefaultConstructors(interfaceType);
|
| }
|
| element.abstract = node.abstractKeyword != null;
|
| @@ -879,6 +938,7 @@ class ElementBuilder extends RecursiveASTVisitor<Object> {
|
| InterfaceTypeImpl interfaceType = new InterfaceTypeImpl.con1(element);
|
| interfaceType.typeArguments = typeArguments;
|
| element.type = interfaceType;
|
| + // set default constructor
|
| element.constructors = createDefaultConstructors(interfaceType);
|
| for (FunctionTypeImpl functionType in _functionTypesToFix) {
|
| functionType.typeArguments = typeArguments;
|
| @@ -951,6 +1011,7 @@ class ElementBuilder extends RecursiveASTVisitor<Object> {
|
| parameter.const3 = node.isConst;
|
| parameter.final2 = node.isFinal;
|
| parameter.parameterKind = node.kind;
|
| + // set initializer, default value range
|
| Expression defaultValue = node.defaultValue;
|
| if (defaultValue != null) {
|
| visit(holder, defaultValue);
|
| @@ -963,6 +1024,7 @@ class ElementBuilder extends RecursiveASTVisitor<Object> {
|
| parameter.initializer = initializer;
|
| parameter.setDefaultValueRange(defaultValue.offset, defaultValue.length);
|
| }
|
| + // visible range
|
| setParameterVisibleRange(node, parameter);
|
| _currentHolder.addParameter(parameter);
|
| parameterName.staticElement = parameter;
|
| @@ -992,6 +1054,9 @@ class ElementBuilder extends RecursiveASTVisitor<Object> {
|
| _currentHolder.addParameter(parameter);
|
| parameterName.staticElement = parameter;
|
| }
|
| + //
|
| + // The children of this parameter include any parameters defined on the type of this parameter.
|
| + //
|
| ElementHolder holder = new ElementHolder();
|
| visitChildren(holder, node);
|
| (node.element as ParameterElementImpl).parameters = holder.parameters;
|
| @@ -1032,6 +1097,7 @@ class ElementBuilder extends RecursiveASTVisitor<Object> {
|
| } else {
|
| SimpleIdentifier propertyNameNode = node.name;
|
| if (propertyNameNode == null) {
|
| + // TODO(brianwilkerson) Report this internal error.
|
| return null;
|
| }
|
| String propertyName = propertyNameNode.name;
|
| @@ -1135,6 +1201,9 @@ class ElementBuilder extends RecursiveASTVisitor<Object> {
|
| _currentHolder.addParameter(parameter);
|
| parameterName.staticElement = parameter;
|
| }
|
| + //
|
| + // The children of this parameter include any parameters defined on the type of this parameter.
|
| + //
|
| ElementHolder holder = new ElementHolder();
|
| visitChildren(holder, node);
|
| (node.element as ParameterElementImpl).parameters = holder.parameters;
|
| @@ -1300,6 +1369,7 @@ class ElementBuilder extends RecursiveASTVisitor<Object> {
|
| Block enclosingBlock = node.getAncestor(Block);
|
| int functionEnd = node.offset + node.length;
|
| int blockEnd = enclosingBlock.offset + enclosingBlock.length;
|
| + // TODO(brianwilkerson) This isn't right for variables declared in a for loop.
|
| variable.setVisibleRange(functionEnd, blockEnd - functionEnd - 1);
|
| _currentHolder.addLocalVariable(variable);
|
| variableName.staticElement = element;
|
| @@ -1927,6 +1997,7 @@ class HtmlUnitBuilder implements ht.XmlVisitor<Object> {
|
| _resolvedLibraries.addAll(resolver.resolvedLibraries);
|
| _errorListener.addAll(resolver.errorListener);
|
| } on AnalysisException catch (exception) {
|
| + //TODO (danrubel): Handle or forward the exception
|
| AnalysisEngine.instance.logger.logError3(exception);
|
| }
|
| node.scriptElement = script;
|
| @@ -1936,6 +2007,8 @@ class HtmlUnitBuilder implements ht.XmlVisitor<Object> {
|
| if (scriptSourcePath != null) {
|
| try {
|
| scriptSourcePath = Uri.encodeFull(scriptSourcePath);
|
| + // Force an exception to be thrown if the URI is invalid so that we can report the
|
| + // problem.
|
| parseUriWithException(scriptSourcePath);
|
| Source scriptSource = _context.sourceFactory.resolveUri(htmlSource, scriptSourcePath);
|
| script.scriptSource = scriptSource;
|
| @@ -2007,6 +2080,11 @@ class HtmlUnitBuilder implements ht.XmlVisitor<Object> {
|
| }
|
|
|
| Object reportCircularity(ht.XmlTagNode node) {
|
| + //
|
| + // This should not be possible, but we have an error report that suggests that it happened at
|
| + // least once. This code will guard against infinite recursion and might help us identify the
|
| + // cause of the issue.
|
| + //
|
| JavaStringBuilder builder = new JavaStringBuilder();
|
| builder.append("Found circularity in XML nodes: ");
|
| bool first = true;
|
| @@ -2141,6 +2219,8 @@ class BestPracticesVerifier extends RecursiveASTVisitor<Object> {
|
| ClassElement outerClass = _enclosingClass;
|
| try {
|
| _enclosingClass = node.element;
|
| + // Commented out until we decide that we want this hint in the analyzer
|
| + // checkForOverrideEqualsButNotHashCode(node);
|
| return super.visitClassDeclaration(node);
|
| } finally {
|
| _enclosingClass = outerClass;
|
| @@ -2178,7 +2258,8 @@ class BestPracticesVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
|
|
| Object visitMethodDeclaration(MethodDeclaration node) {
|
| - checkForOverridingPrivateMember(node);
|
| + // This was determined to not be a good hint, see: dartbug.com/16029
|
| + //checkForOverridingPrivateMember(node);
|
| checkForMissingReturn(node.returnType, node.body);
|
| return super.visitMethodDeclaration(node);
|
| }
|
| @@ -2228,10 +2309,13 @@ class BestPracticesVerifier extends RecursiveASTVisitor<Object> {
|
| return false;
|
| }
|
| String rhsNameStr = typeName.name.name;
|
| + // if x is dynamic
|
| if (rhsType.isDynamic && rhsNameStr == sc.Keyword.DYNAMIC.syntax) {
|
| if (node.notOperator == null) {
|
| + // the is case
|
| _errorReporter.reportError3(HintCode.UNNECESSARY_TYPE_CHECK_TRUE, node, []);
|
| } else {
|
| + // the is not case
|
| _errorReporter.reportError3(HintCode.UNNECESSARY_TYPE_CHECK_FALSE, node, []);
|
| }
|
| return true;
|
| @@ -2239,17 +2323,22 @@ class BestPracticesVerifier extends RecursiveASTVisitor<Object> {
|
| Element rhsElement = rhsType.element;
|
| LibraryElement libraryElement = rhsElement != null ? rhsElement.library : null;
|
| if (libraryElement != null && libraryElement.isDartCore) {
|
| + // if x is Object or null is Null
|
| if (rhsType.isObject || (expression is NullLiteral && rhsNameStr == _NULL_TYPE_NAME)) {
|
| if (node.notOperator == null) {
|
| + // the is case
|
| _errorReporter.reportError3(HintCode.UNNECESSARY_TYPE_CHECK_TRUE, node, []);
|
| } else {
|
| + // the is not case
|
| _errorReporter.reportError3(HintCode.UNNECESSARY_TYPE_CHECK_FALSE, node, []);
|
| }
|
| return true;
|
| } else if (rhsNameStr == _NULL_TYPE_NAME) {
|
| if (node.notOperator == null) {
|
| + // the is case
|
| _errorReporter.reportError3(HintCode.TYPE_CHECK_IS_NULL, node, []);
|
| } else {
|
| + // the is not case
|
| _errorReporter.reportError3(HintCode.TYPE_CHECK_IS_NOT_NULL, node, []);
|
| }
|
| return true;
|
| @@ -2271,6 +2360,8 @@ class BestPracticesVerifier extends RecursiveASTVisitor<Object> {
|
| if (element != null && element.isDeprecated) {
|
| String displayName = element.displayName;
|
| if (element is ConstructorElement) {
|
| + // TODO(jwren) We should modify ConstructorElement.getDisplayName(), or have the logic
|
| + // centralized elsewhere, instead of doing this logic here.
|
| ConstructorElement constructorElement = element;
|
| displayName = constructorElement.enclosingElement.displayName;
|
| if (!constructorElement.displayName.isEmpty) {
|
| @@ -2316,9 +2407,11 @@ class BestPracticesVerifier extends RecursiveASTVisitor<Object> {
|
| * @see HintCode#DIVISION_OPTIMIZATION
|
| */
|
| bool checkForDivisionOptimizationHint(BinaryExpression node) {
|
| + // Return if the operator is not '/'
|
| if (node.operator.type != sc.TokenType.SLASH) {
|
| return false;
|
| }
|
| + // Return if the '/' operator is not defined in core, or if we don't know its static or propagated type
|
| MethodElement methodElement = node.bestElement;
|
| if (methodElement == null) {
|
| return false;
|
| @@ -2327,6 +2420,7 @@ class BestPracticesVerifier extends RecursiveASTVisitor<Object> {
|
| if (libraryElement != null && !libraryElement.isDartCore) {
|
| return false;
|
| }
|
| + // Report error if the (x/y) has toInt() invoked on it
|
| if (node.parent is ParenthesizedExpression) {
|
| ParenthesizedExpression parenthesizedExpression = wrapParenthesizedExpression(node.parent as ParenthesizedExpression);
|
| if (parenthesizedExpression.parent is MethodInvocation) {
|
| @@ -2385,16 +2479,23 @@ class BestPracticesVerifier extends RecursiveASTVisitor<Object> {
|
| * @see HintCode#OVERRIDDING_PRIVATE_MEMBER
|
| */
|
| bool checkForOverridingPrivateMember(MethodDeclaration node) {
|
| + // If not in an enclosing class, return false
|
| if (_enclosingClass == null) {
|
| return false;
|
| }
|
| + // If the member is not private, return false
|
| if (!Identifier.isPrivateName(node.name.name)) {
|
| return false;
|
| }
|
| + // Get the element of the member, if null, return false
|
| ExecutableElement executableElement = node.element;
|
| if (executableElement == null) {
|
| return false;
|
| }
|
| + // Loop through all of the superclasses looking for a matching method or accessor
|
| + // TODO(jwren) If the HintGenerator needs or has easy access to the InheritanceManager in the
|
| + // future then this could be refactored down to be more readable, however, since we are only
|
| + // looking through super classes (and not the entire interface graph) there is no pressing need
|
| String elementName = executableElement.name;
|
| bool isGetterOrSetter = executableElement is PropertyAccessorElement;
|
| InterfaceType superType = _enclosingClass.supertype;
|
| @@ -2450,6 +2551,8 @@ class BestPracticesVerifier extends RecursiveASTVisitor<Object> {
|
| TypeName typeName = node.type;
|
| Type2 lhsType = expression.staticType;
|
| Type2 rhsType = typeName.type;
|
| + // TODO(jwren) After dartbug.com/13732, revisit this, we should be able to remove the
|
| + // !(x instanceof TypeParameterType) checks.
|
| if (lhsType != null && rhsType != null && !lhsType.isDynamic && !rhsType.isDynamic && lhsType is! TypeParameterType && rhsType is! TypeParameterType && lhsType.isSubtypeOf(rhsType)) {
|
| _errorReporter.reportError3(HintCode.UNNECESSARY_CAST, node, []);
|
| return true;
|
| @@ -2507,6 +2610,15 @@ class Dart2JSVerifier extends RecursiveASTVisitor<Object> {
|
| Element element = type.element;
|
| String typeNameStr = element.name;
|
| LibraryElement libraryElement = element.library;
|
| + // if (typeNameStr.equals(INT_TYPE_NAME) && libraryElement != null
|
| + // && libraryElement.isDartCore()) {
|
| + // if (node.getNotOperator() == null) {
|
| + // errorReporter.reportError(HintCode.IS_INT, node);
|
| + // } else {
|
| + // errorReporter.reportError(HintCode.IS_NOT_INT, node);
|
| + // }
|
| + // return true;
|
| + // } else
|
| if (typeNameStr == _DOUBLE_TYPE_NAME && libraryElement != null && libraryElement.isDartCore) {
|
| if (node.notOperator == null) {
|
| _errorReporter.reportError3(HintCode.IS_DOUBLE, node, []);
|
| @@ -2551,11 +2663,15 @@ class DeadCodeVerifier extends RecursiveASTVisitor<Object> {
|
| ValidResult lhsResult = getConstantBooleanValue(lhsCondition);
|
| if (lhsResult != null) {
|
| if (lhsResult.isTrue && isBarBar) {
|
| + // report error on else block: true || !e!
|
| _errorReporter.reportError3(HintCode.DEAD_CODE, node.rightOperand, []);
|
| + // only visit the LHS:
|
| safelyVisit(lhsCondition);
|
| return null;
|
| } else if (lhsResult.isFalse && isAmpAmp) {
|
| + // report error on if block: false && !e!
|
| _errorReporter.reportError3(HintCode.DEAD_CODE, node.rightOperand, []);
|
| + // only visit the LHS:
|
| safelyVisit(lhsCondition);
|
| return null;
|
| }
|
| @@ -2596,10 +2712,12 @@ class DeadCodeVerifier extends RecursiveASTVisitor<Object> {
|
| ValidResult result = getConstantBooleanValue(conditionExpression);
|
| if (result != null) {
|
| if (result.isTrue) {
|
| + // report error on else block: true ? 1 : !2!
|
| _errorReporter.reportError3(HintCode.DEAD_CODE, node.elseExpression, []);
|
| safelyVisit(node.thenExpression);
|
| return null;
|
| } else {
|
| + // report error on if block: false ? !1! : 2
|
| _errorReporter.reportError3(HintCode.DEAD_CODE, node.thenExpression, []);
|
| safelyVisit(node.elseExpression);
|
| return null;
|
| @@ -2616,6 +2734,7 @@ class DeadCodeVerifier extends RecursiveASTVisitor<Object> {
|
| ValidResult result = getConstantBooleanValue(conditionExpression);
|
| if (result != null) {
|
| if (result.isTrue) {
|
| + // report error on else block: if(true) {} else {!}
|
| Statement elseStatement = node.elseStatement;
|
| if (elseStatement != null) {
|
| _errorReporter.reportError3(HintCode.DEAD_CODE, elseStatement, []);
|
| @@ -2623,6 +2742,7 @@ class DeadCodeVerifier extends RecursiveASTVisitor<Object> {
|
| return null;
|
| }
|
| } else {
|
| + // report error on if block: if (false) {!} else {}
|
| _errorReporter.reportError3(HintCode.DEAD_CODE, node.thenStatement, []);
|
| safelyVisit(node.elseStatement);
|
| return null;
|
| @@ -2641,12 +2761,18 @@ class DeadCodeVerifier extends RecursiveASTVisitor<Object> {
|
| for (int i = 0; i < numOfCatchClauses; i++) {
|
| CatchClause catchClause = catchClauses[i];
|
| if (catchClause.onKeyword != null) {
|
| + // on-catch clause found, verify that the exception type is not a subtype of a previous
|
| + // on-catch exception type
|
| TypeName typeName = catchClause.exceptionType;
|
| if (typeName != null && typeName.type != null) {
|
| Type2 currentType = typeName.type;
|
| if (currentType.isObject) {
|
| + // Found catch clause clause that has Object as an exception type, this is equivalent to
|
| + // having a catch clause that doesn't have an exception type, visit the block, but
|
| + // generate an error on any following catch clauses (and don't visit them).
|
| safelyVisit(catchClause);
|
| if (i + 1 != numOfCatchClauses) {
|
| + // this catch clause is not the last in the try statement
|
| CatchClause nextCatchClause = catchClauses[i + 1];
|
| CatchClause lastCatchClause = catchClauses[numOfCatchClauses - 1];
|
| int offset = nextCatchClause.offset;
|
| @@ -2668,8 +2794,11 @@ class DeadCodeVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| safelyVisit(catchClause);
|
| } else {
|
| + // Found catch clause clause that doesn't have an exception type, visit the block, but
|
| + // generate an error on any following catch clauses (and don't visit them).
|
| safelyVisit(catchClause);
|
| if (i + 1 != numOfCatchClauses) {
|
| + // this catch clause is not the last in the try statement
|
| CatchClause nextCatchClause = catchClauses[i + 1];
|
| CatchClause lastCatchClause = catchClauses[numOfCatchClauses - 1];
|
| int offset = nextCatchClause.offset;
|
| @@ -2689,6 +2818,7 @@ class DeadCodeVerifier extends RecursiveASTVisitor<Object> {
|
| ValidResult result = getConstantBooleanValue(conditionExpression);
|
| if (result != null) {
|
| if (result.isFalse) {
|
| + // report error on if block: while (false) {!}
|
| _errorReporter.reportError3(HintCode.DEAD_CODE, node.body, []);
|
| return null;
|
| }
|
| @@ -2716,6 +2846,17 @@ class DeadCodeVerifier extends RecursiveASTVisitor<Object> {
|
| return new ValidResult(new DartObjectImpl(null, BoolState.from(false)));
|
| }
|
| }
|
| + // Don't consider situations where we could evaluate to a constant boolean expression with the
|
| + // ConstantVisitor
|
| + // else {
|
| + // EvaluationResultImpl result = expression.accept(new ConstantVisitor());
|
| + // if (result == ValidResult.RESULT_TRUE) {
|
| + // return ValidResult.RESULT_TRUE;
|
| + // } else if (result == ValidResult.RESULT_FALSE) {
|
| + // return ValidResult.RESULT_FALSE;
|
| + // }
|
| + // return null;
|
| + // }
|
| return null;
|
| }
|
|
|
| @@ -2807,11 +2948,15 @@ class HintGenerator {
|
| void generateForCompilationUnit(CompilationUnit unit, Source source) {
|
| ErrorReporter errorReporter = new ErrorReporter(_errorListener, source);
|
| _importsVerifier.visitCompilationUnit(unit);
|
| + // dead code analysis
|
| new DeadCodeVerifier(errorReporter).visitCompilationUnit(unit);
|
| + // dart2js analysis
|
| if (_enableDart2JSHints) {
|
| new Dart2JSVerifier(errorReporter).visitCompilationUnit(unit);
|
| }
|
| + // Dart best practices
|
| new BestPracticesVerifier(errorReporter).visitCompilationUnit(unit);
|
| + // Find to-do comments
|
| new ToDoFinder(errorReporter).findIn(unit);
|
| }
|
| }
|
| @@ -2923,6 +3068,7 @@ class ImportsVerifier extends RecursiveASTVisitor<Object> {
|
| */
|
| void generateUnusedImportHints(ErrorReporter errorReporter) {
|
| for (ImportDirective unusedImport in _unusedImports) {
|
| + // Check that the import isn't dart:core
|
| ImportElement importElement = unusedImport.element;
|
| if (importElement != null) {
|
| LibraryElement libraryElement = importElement.importedLibrary;
|
| @@ -2943,6 +3089,9 @@ class ImportsVerifier extends RecursiveASTVisitor<Object> {
|
| LibraryElement libraryElement = importDirective.uriElement;
|
| if (libraryElement != null) {
|
| _unusedImports.add(importDirective);
|
| + //
|
| + // Initialize prefixElementMap
|
| + //
|
| if (importDirective.asToken != null) {
|
| SimpleIdentifier prefixIdentifier = importDirective.prefix;
|
| if (prefixIdentifier != null) {
|
| @@ -2953,22 +3102,34 @@ class ImportsVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| }
|
| }
|
| + //
|
| + // Initialize libraryMap: libraryElement -> importDirective
|
| + //
|
| putIntoLibraryMap(libraryElement, importDirective);
|
| + //
|
| + // For this new addition to the libraryMap, also recursively add any exports from the
|
| + // libraryElement
|
| + //
|
| addAdditionalLibrariesForExports(libraryElement, importDirective, new List<LibraryElement>());
|
| }
|
| }
|
| }
|
| }
|
| + // If there are no imports in this library, don't visit the identifiers in the library- there
|
| + // can be no unused imports.
|
| if (_unusedImports.isEmpty) {
|
| return null;
|
| }
|
| if (_unusedImports.length > 1) {
|
| + // order the list of unusedImports to find duplicates in faster than O(n^2) time
|
| List<ImportDirective> importDirectiveArray = new List.from(_unusedImports);
|
| importDirectiveArray.sort(ImportDirective.COMPARATOR);
|
| ImportDirective currentDirective = importDirectiveArray[0];
|
| for (int i = 1; i < importDirectiveArray.length; i++) {
|
| ImportDirective nextDirective = importDirectiveArray[i];
|
| if (ImportDirective.COMPARATOR(currentDirective, nextDirective) == 0) {
|
| + // Add either the currentDirective or nextDirective depending on which comes second, this
|
| + // guarantees that the first of the duplicates won't be highlighted.
|
| if (currentDirective.offset < nextDirective.offset) {
|
| _duplicateImports.add(nextDirective);
|
| } else {
|
| @@ -2997,12 +3158,16 @@ class ImportsVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
|
|
| Object visitPrefixedIdentifier(PrefixedIdentifier node) {
|
| + // If the prefixed identifier references some A.B, where A is a library prefix, then we can
|
| + // lookup the associated ImportDirective in prefixElementMap and remove it from the
|
| + // unusedImports list.
|
| SimpleIdentifier prefixIdentifier = node.prefix;
|
| Element element = prefixIdentifier.staticElement;
|
| if (element is PrefixElement) {
|
| _unusedImports.remove(_prefixElementMap[element]);
|
| return null;
|
| }
|
| + // Otherwise, pass the prefixed identifier element and name onto visitIdentifier.
|
| return visitIdentifier(element, prefixIdentifier.name);
|
| }
|
|
|
| @@ -3037,6 +3202,7 @@ class ImportsVerifier extends RecursiveASTVisitor<Object> {
|
| Namespace computeNamespace(ImportDirective importDirective) {
|
| Namespace namespace = _namespaceMap[importDirective];
|
| if (namespace == null) {
|
| + // If the namespace isn't in the namespaceMap, then compute and put it in the map
|
| ImportElement importElement = importDirective.element;
|
| if (importElement != null) {
|
| NamespaceBuilder builder = new NamespaceBuilder();
|
| @@ -3066,6 +3232,7 @@ class ImportsVerifier extends RecursiveASTVisitor<Object> {
|
| if (element == null) {
|
| return null;
|
| }
|
| + // If the element is multiply defined then call this method recursively for each of the conflicting elements.
|
| if (element is MultiplyDefinedElement) {
|
| MultiplyDefinedElement multiplyDefinedElement = element;
|
| for (Element elt in multiplyDefinedElement.conflictingElements) {
|
| @@ -3080,6 +3247,7 @@ class ImportsVerifier extends RecursiveASTVisitor<Object> {
|
| if (containingLibrary == null) {
|
| return null;
|
| }
|
| + // If the element is declared in the current library, return.
|
| if (_currentLibrary == containingLibrary) {
|
| return null;
|
| }
|
| @@ -3088,10 +3256,14 @@ class ImportsVerifier extends RecursiveASTVisitor<Object> {
|
| return null;
|
| }
|
| if (importsFromSameLibrary.length == 1) {
|
| + // If there is only one import directive for this library, then it must be the directive that
|
| + // this element is imported with, remove it from the unusedImports list.
|
| ImportDirective usedImportDirective = importsFromSameLibrary[0];
|
| _unusedImports.remove(usedImportDirective);
|
| } else {
|
| + // Otherwise, for each of the imported directives, use the namespaceMap to
|
| for (ImportDirective importDirective in importsFromSameLibrary) {
|
| + // Get the namespace for this import
|
| Namespace namespace = computeNamespace(importDirective);
|
| if (namespace != null && namespace.get(name) != null) {
|
| _unusedImports.remove(importDirective);
|
| @@ -3163,10 +3335,12 @@ class PubVerifier extends RecursiveASTVisitor<Object> {
|
| if (fullNameIndex < 4) {
|
| return false;
|
| }
|
| + // Check for "/lib" at a specified place in the fullName
|
| if (JavaString.startsWithBefore(fullName, "/lib", fullNameIndex - 4)) {
|
| String relativePubspecPath = path.substring(0, pathIndex + 3) + _PUBSPEC_YAML;
|
| Source pubspecSource = _context.sourceFactory.resolveUri(source, relativePubspecPath);
|
| if (pubspecSource != null && pubspecSource.exists()) {
|
| + // Files inside the lib directory hierarchy should not reference files outside
|
| _errorReporter.reportError3(PubSuggestionCode.FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE, uriLiteral, []);
|
| }
|
| return true;
|
| @@ -3213,6 +3387,8 @@ class PubVerifier extends RecursiveASTVisitor<Object> {
|
| String fullName = getSourceFullName(source);
|
| if (fullName != null) {
|
| if (!fullName.contains("/lib/")) {
|
| + // Files outside the lib directory hierarchy should not reference files inside
|
| + // ... use package: url instead
|
| _errorReporter.reportError3(PubSuggestionCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE, uriLiteral, []);
|
| return true;
|
| }
|
| @@ -3230,6 +3406,7 @@ class PubVerifier extends RecursiveASTVisitor<Object> {
|
| */
|
| bool checkForPackageImportContainsDotDot(StringLiteral uriLiteral, String path) {
|
| if (path.startsWith("../") || path.contains("/../")) {
|
| + // Package import should not to contain ".."
|
| _errorReporter.reportError3(PubSuggestionCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT, uriLiteral, []);
|
| return true;
|
| }
|
| @@ -4711,6 +4888,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| node.propagatedElement = propagatedMethod;
|
| bool shouldReportMissingMember_static = shouldReportMissingMember(staticType, staticMethod);
|
| bool shouldReportMissingMember_propagated = !shouldReportMissingMember_static && _enableHints ? shouldReportMissingMember(propagatedType, propagatedMethod) : false;
|
| + //
|
| + // If we are about to generate the hint (propagated version of this warning), then check
|
| + // that the member is not in a subtype of the propagated type.
|
| + //
|
| if (shouldReportMissingMember_propagated) {
|
| if (memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
|
| shouldReportMissingMember_propagated = false;
|
| @@ -4741,6 +4922,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| node.propagatedElement = propagatedMethod;
|
| bool shouldReportMissingMember_static = shouldReportMissingMember(staticType, staticMethod);
|
| bool shouldReportMissingMember_propagated = !shouldReportMissingMember_static && _enableHints ? shouldReportMissingMember(propagatedType, propagatedMethod) : false;
|
| + //
|
| + // If we are about to generate the hint (propagated version of this warning), then check
|
| + // that the member is not in a subtype of the propagated type.
|
| + //
|
| if (shouldReportMissingMember_propagated) {
|
| if (memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
|
| shouldReportMissingMember_propagated = false;
|
| @@ -4782,8 +4967,12 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| SimpleIdentifier simpleIdentifier = identifier;
|
| Element element = resolveSimpleIdentifier(simpleIdentifier);
|
| if (element == null) {
|
| + //
|
| + // This might be a reference to an imported name that is missing the prefix.
|
| + //
|
| element = findImportWithoutPrefix(simpleIdentifier);
|
| if (element is MultiplyDefinedElement) {
|
| + // TODO(brianwilkerson) Report this error?
|
| element = null;
|
| }
|
| }
|
| @@ -4812,12 +5001,14 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| } else {
|
| if (element is PrefixElement) {
|
| prefix.staticElement = element;
|
| + // TODO(brianwilkerson) Report this error?
|
| element = _resolver.nameScope.lookup(identifier, _definingLibrary);
|
| name.staticElement = element;
|
| return null;
|
| }
|
| LibraryElement library = element.library;
|
| if (library == null) {
|
| + // TODO(brianwilkerson) We need to understand how the library could ever be null.
|
| AnalysisEngine.instance.logger.logError("Found element with null library: ${element.name}");
|
| } else if (library != _definingLibrary) {
|
| }
|
| @@ -4857,11 +5048,13 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| ConstructorElement element = node.element;
|
| if (element is ConstructorElementImpl) {
|
| ConstructorElementImpl constructorElement = element;
|
| + // set redirected factory constructor
|
| ConstructorName redirectedNode = node.redirectedConstructor;
|
| if (redirectedNode != null) {
|
| ConstructorElement redirectedElement = redirectedNode.staticElement;
|
| constructorElement.redirectedConstructor = redirectedElement;
|
| }
|
| + // set redirected generate constructor
|
| for (ConstructorInitializer initializer in node.initializers) {
|
| if (initializer is RedirectingConstructorInvocation) {
|
| ConstructorElement redirectedElement = initializer.staticElement;
|
| @@ -4891,6 +5084,7 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| if (type != null && type.isDynamic) {
|
| return null;
|
| } else if (type is! InterfaceType) {
|
| + // TODO(brianwilkerson) Report these errors.
|
| ASTNode parent = node.parent;
|
| if (parent is InstanceCreationExpression) {
|
| if (parent.isConst) {
|
| @@ -4900,6 +5094,7 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| }
|
| return null;
|
| }
|
| + // look up ConstructorElement
|
| ConstructorElement constructor;
|
| SimpleIdentifier name = node.name;
|
| InterfaceType interfaceType = type as InterfaceType;
|
| @@ -4930,6 +5125,9 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| Object visitExportDirective(ExportDirective node) {
|
| Element element = node.element;
|
| if (element is ExportElement) {
|
| + // The element is null when the URI is invalid
|
| + // TODO(brianwilkerson) Figure out whether the element can ever be something other than an
|
| + // ExportElement
|
| resolveCombinators(element.exportedLibrary, node.combinators);
|
| setMetadata(element, node);
|
| }
|
| @@ -4958,6 +5156,8 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| } else if (fieldElement.isStatic) {
|
| _resolver.reportError7(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_STATIC_FIELD, node, [fieldName]);
|
| } else if (declaredType != null && fieldType != null && !declaredType.isAssignableTo(fieldType)) {
|
| + // TODO(brianwilkerson) We should implement a displayName() method for types that will
|
| + // work nicely with function types and then use that below.
|
| _resolver.reportError7(StaticWarningCode.FIELD_INITIALIZING_FORMAL_NOT_ASSIGNABLE, node, [declaredType.displayName, fieldType.displayName]);
|
| }
|
| } else {
|
| @@ -4969,6 +5169,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| }
|
| }
|
| }
|
| + // else {
|
| + // // TODO(jwren) Report error, constructor initializer variable is a top level element
|
| + // // (Either here or in ErrorVerifier#checkForAllFinalInitializedErrorCodes)
|
| + // }
|
| return super.visitFieldFormalParameter(node);
|
| }
|
|
|
| @@ -4978,6 +5182,7 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| }
|
|
|
| Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
|
| + // TODO(brianwilkerson) Can we ever resolve the function being invoked?
|
| Expression expression = node.function;
|
| if (expression is FunctionExpression) {
|
| FunctionExpression functionExpression = expression;
|
| @@ -5009,6 +5214,7 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| }
|
| ImportElement importElement = node.element;
|
| if (importElement != null) {
|
| + // The element is null when the URI is invalid
|
| LibraryElement library = importElement.importedLibrary;
|
| if (library != null) {
|
| resolveCombinators(library, node.combinators);
|
| @@ -5027,27 +5233,39 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| bool isInGetterContext = node.inGetterContext();
|
| bool isInSetterContext = node.inSetterContext();
|
| if (isInGetterContext && isInSetterContext) {
|
| + // lookup setter
|
| MethodElement setterStaticMethod = lookUpMethod(target, staticType, setterMethodName);
|
| MethodElement setterPropagatedMethod = lookUpMethod(target, propagatedType, setterMethodName);
|
| + // set setter element
|
| node.staticElement = setterStaticMethod;
|
| node.propagatedElement = setterPropagatedMethod;
|
| + // generate undefined method warning
|
| checkForUndefinedIndexOperator(node, target, getterMethodName, setterStaticMethod, setterPropagatedMethod, staticType, propagatedType);
|
| + // lookup getter method
|
| MethodElement getterStaticMethod = lookUpMethod(target, staticType, getterMethodName);
|
| MethodElement getterPropagatedMethod = lookUpMethod(target, propagatedType, getterMethodName);
|
| + // set getter element
|
| AuxiliaryElements auxiliaryElements = new AuxiliaryElements(getterStaticMethod, getterPropagatedMethod);
|
| node.auxiliaryElements = auxiliaryElements;
|
| + // generate undefined method warning
|
| checkForUndefinedIndexOperator(node, target, getterMethodName, getterStaticMethod, getterPropagatedMethod, staticType, propagatedType);
|
| } else if (isInGetterContext) {
|
| + // lookup getter method
|
| MethodElement staticMethod = lookUpMethod(target, staticType, getterMethodName);
|
| MethodElement propagatedMethod = lookUpMethod(target, propagatedType, getterMethodName);
|
| + // set getter element
|
| node.staticElement = staticMethod;
|
| node.propagatedElement = propagatedMethod;
|
| + // generate undefined method warning
|
| checkForUndefinedIndexOperator(node, target, getterMethodName, staticMethod, propagatedMethod, staticType, propagatedType);
|
| } else if (isInSetterContext) {
|
| + // lookup setter method
|
| MethodElement staticMethod = lookUpMethod(target, staticType, setterMethodName);
|
| MethodElement propagatedMethod = lookUpMethod(target, propagatedType, setterMethodName);
|
| + // set setter element
|
| node.staticElement = staticMethod;
|
| node.propagatedElement = propagatedMethod;
|
| + // generate undefined method warning
|
| checkForUndefinedIndexOperator(node, target, setterMethodName, staticMethod, propagatedMethod, staticType, propagatedType);
|
| }
|
| return null;
|
| @@ -5076,9 +5294,17 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
|
|
| Object visitMethodInvocation(MethodInvocation node) {
|
| SimpleIdentifier methodName = node.methodName;
|
| + //
|
| + // Synthetic identifiers have been already reported during parsing.
|
| + //
|
| if (methodName.isSynthetic) {
|
| return null;
|
| }
|
| + //
|
| + // We have a method invocation of one of two forms: 'e.m(a1, ..., an)' or 'm(a1, ..., an)'. The
|
| + // first step is to figure out which executable is being invoked, using both the static and the
|
| + // propagated type information.
|
| + //
|
| Expression target = node.realTarget;
|
| if (target is SuperExpression && !isSuperInValidContext(target)) {
|
| return null;
|
| @@ -5090,6 +5316,11 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| propagatedElement = null;
|
| } else {
|
| Type2 staticType = getStaticType(target);
|
| + //
|
| + // If this method invocation is of the form 'C.m' where 'C' is a class, then we don't call
|
| + // resolveInvokedElement(..) which walks up the class hierarchy, instead we just look for the
|
| + // member in the type only.
|
| + //
|
| ClassElementImpl typeReference = getTypeReference(target);
|
| if (typeReference != null) {
|
| staticElement = propagatedElement = resolveElement(typeReference, methodName);
|
| @@ -5100,6 +5331,9 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| }
|
| staticElement = convertSetterToGetter(staticElement);
|
| propagatedElement = convertSetterToGetter(propagatedElement);
|
| + //
|
| + // Record the results.
|
| + //
|
| methodName.staticElement = staticElement;
|
| methodName.propagatedElement = propagatedElement;
|
| ArgumentList argumentList = node.argumentList;
|
| @@ -5115,6 +5349,9 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| argumentList.correspondingPropagatedParameters = parameters;
|
| }
|
| }
|
| + //
|
| + // Then check for error conditions.
|
| + //
|
| ErrorCode errorCode = checkForInvocationError(target, true, staticElement);
|
| bool generatedWithTypePropagation = false;
|
| if (_enableHints && errorCode == null && staticElement == null) {
|
| @@ -5158,16 +5395,22 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| ErrorCode proxyErrorCode = (generatedWithTypePropagation ? HintCode.UNDEFINED_METHOD : StaticTypeWarningCode.UNDEFINED_METHOD) as ErrorCode;
|
| _resolver.reportErrorProxyConditionalAnalysisError(_resolver.enclosingClass, proxyErrorCode, methodName, [methodName.name, targetTypeName]);
|
| } else {
|
| + // ignore Function "call"
|
| + // (if we are about to create a hint using type propagation, then we can use type
|
| + // propagation here as well)
|
| Type2 targetType = null;
|
| if (!generatedWithTypePropagation) {
|
| targetType = getStaticType(target);
|
| } else {
|
| + // choose the best type
|
| targetType = getPropagatedType(target);
|
| if (targetType == null) {
|
| targetType = getStaticType(target);
|
| }
|
| }
|
| if (targetType != null && targetType.isDartCoreFunction && methodName.name == CALL_METHOD_NAME) {
|
| + // TODO(brianwilkerson) Can we ever resolve the function being invoked?
|
| + //resolveArgumentsToParameters(node.getArgumentList(), invokedFunction);
|
| return null;
|
| }
|
| targetTypeName = targetType == null ? null : targetType.displayName;
|
| @@ -5175,6 +5418,8 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| _resolver.reportErrorProxyConditionalAnalysisError(targetType.element, proxyErrorCode, methodName, [methodName.name, targetTypeName]);
|
| }
|
| } else if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_SUPER_METHOD)) {
|
| + // Generate the type name.
|
| + // The error code will never be generated via type propagation
|
| Type2 targetType = getStaticType(target);
|
| String targetTypeName = targetType == null ? null : targetType.name;
|
| _resolver.reportError7(StaticTypeWarningCode.UNDEFINED_SUPER_METHOD, methodName, [methodName.name, targetTypeName]);
|
| @@ -5203,6 +5448,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| node.propagatedElement = propagatedMethod;
|
| bool shouldReportMissingMember_static = shouldReportMissingMember(staticType, staticMethod);
|
| bool shouldReportMissingMember_propagated = !shouldReportMissingMember_static && _enableHints ? shouldReportMissingMember(propagatedType, propagatedMethod) : false;
|
| + //
|
| + // If we are about to generate the hint (propagated version of this warning), then check
|
| + // that the member is not in a subtype of the propagated type.
|
| + //
|
| if (shouldReportMissingMember_propagated) {
|
| if (memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
|
| shouldReportMissingMember_propagated = false;
|
| @@ -5220,6 +5469,9 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| Object visitPrefixedIdentifier(PrefixedIdentifier node) {
|
| SimpleIdentifier prefix = node.prefix;
|
| SimpleIdentifier identifier = node.identifier;
|
| + //
|
| + // First, check to see whether the prefix is really a prefix.
|
| + //
|
| Element prefixElement = prefix.staticElement;
|
| if (prefixElement is PrefixElement) {
|
| Element element = _resolver.nameScope.lookup(node, _definingLibrary);
|
| @@ -5247,7 +5499,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| }
|
| }
|
| }
|
| + // TODO(brianwilkerson) The prefix needs to be resolved to the element for the import that
|
| + // defines the prefix, not the prefix's element.
|
| identifier.staticElement = element;
|
| + // Validate annotation element.
|
| if (node.parent is Annotation) {
|
| Annotation annotation = node.parent as Annotation;
|
| resolveAnnotationElement(annotation);
|
| @@ -5255,10 +5510,15 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| }
|
| return null;
|
| }
|
| + // May be annotation, resolve invocation of "const" constructor.
|
| if (node.parent is Annotation) {
|
| Annotation annotation = node.parent as Annotation;
|
| resolveAnnotationElement(annotation);
|
| }
|
| + //
|
| + // Otherwise, the prefix is really an expression that happens to be a simple identifier and this
|
| + // is really equivalent to a property access node.
|
| + //
|
| resolvePropertyAccess(prefix, identifier);
|
| return null;
|
| }
|
| @@ -5277,6 +5537,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| node.propagatedElement = propagatedMethod;
|
| bool shouldReportMissingMember_static = shouldReportMissingMember(staticType, staticMethod);
|
| bool shouldReportMissingMember_propagated = !shouldReportMissingMember_static && _enableHints ? shouldReportMissingMember(propagatedType, propagatedMethod) : false;
|
| + //
|
| + // If we are about to generate the hint (propagated version of this warning), then check
|
| + // that the member is not in a subtype of the propagated type.
|
| + //
|
| if (shouldReportMissingMember_propagated) {
|
| if (memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
|
| shouldReportMissingMember_propagated = false;
|
| @@ -5305,6 +5569,7 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| Object visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) {
|
| ClassElement enclosingClass = _resolver.enclosingClass;
|
| if (enclosingClass == null) {
|
| + // TODO(brianwilkerson) Report this error.
|
| return null;
|
| }
|
| SimpleIdentifier name = node.constructorName;
|
| @@ -5315,6 +5580,7 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| element = enclosingClass.getNamedConstructor(name.name);
|
| }
|
| if (element == null) {
|
| + // TODO(brianwilkerson) Report this error and decide what element to associate with the node.
|
| return null;
|
| }
|
| if (name != null) {
|
| @@ -5330,17 +5596,30 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| }
|
|
|
| Object visitSimpleIdentifier(SimpleIdentifier node) {
|
| + //
|
| + // Synthetic identifiers have been already reported during parsing.
|
| + //
|
| if (node.isSynthetic) {
|
| return null;
|
| }
|
| + //
|
| + // We ignore identifiers that have already been resolved, such as identifiers representing the
|
| + // name in a declaration.
|
| + //
|
| if (node.staticElement != null) {
|
| return null;
|
| }
|
| + //
|
| + // The name dynamic denotes a Type object even though dynamic is not a class.
|
| + //
|
| if (node.name == _dynamicType.name) {
|
| node.staticElement = _dynamicType.element;
|
| node.staticType = _typeType;
|
| return null;
|
| }
|
| + //
|
| + // Otherwise, the node should be resolved.
|
| + //
|
| Element element = resolveSimpleIdentifier(node);
|
| ClassElement enclosingClass = _resolver.enclosingClass;
|
| if (isFactoryConstructorReturnType(node) && element != enclosingClass) {
|
| @@ -5349,6 +5628,7 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| _resolver.reportError7(CompileTimeErrorCode.INVALID_CONSTRUCTOR_NAME, node, []);
|
| element = null;
|
| } else if (element == null || (element is PrefixElement && !isValidAsPrefix(node))) {
|
| + // TODO(brianwilkerson) Recover from this error.
|
| if (isConstructorReturnType(node)) {
|
| _resolver.reportError7(CompileTimeErrorCode.INVALID_CONSTRUCTOR_NAME, node, []);
|
| } else if (node.parent is Annotation) {
|
| @@ -5364,6 +5644,9 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| AuxiliaryElements auxiliaryElements = new AuxiliaryElements(lookUpGetter(null, enclosingType, node.name), null);
|
| node.auxiliaryElements = auxiliaryElements;
|
| }
|
| + //
|
| + // Validate annotation element.
|
| + //
|
| if (node.parent is Annotation) {
|
| Annotation annotation = node.parent as Annotation;
|
| resolveAnnotationElement(annotation);
|
| @@ -5374,10 +5657,12 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| Object visitSuperConstructorInvocation(SuperConstructorInvocation node) {
|
| ClassElement enclosingClass = _resolver.enclosingClass;
|
| if (enclosingClass == null) {
|
| + // TODO(brianwilkerson) Report this error.
|
| return null;
|
| }
|
| InterfaceType superType = enclosingClass.supertype;
|
| if (superType == null) {
|
| + // TODO(brianwilkerson) Report this error.
|
| return null;
|
| }
|
| SimpleIdentifier name = node.constructorName;
|
| @@ -5458,10 +5743,15 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| * @return the error code that should be reported
|
| */
|
| ErrorCode checkForInvocationError(Expression target, bool useStaticContext, Element element) {
|
| + // Prefix is not declared, instead "prefix.id" are declared.
|
| if (element is PrefixElement) {
|
| element = null;
|
| }
|
| if (element is PropertyAccessorElement) {
|
| + //
|
| + // This is really a function expression invocation.
|
| + //
|
| + // TODO(brianwilkerson) Consider the possibility of re-writing the AST.
|
| FunctionType getterType = element.type;
|
| if (getterType != null) {
|
| Type2 returnType = getterType.returnType;
|
| @@ -5472,8 +5762,15 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| } else if (element is ExecutableElement) {
|
| return null;
|
| } else if (element == null && target is SuperExpression) {
|
| + // TODO(jwren) We should split the UNDEFINED_METHOD into two error codes, this one, and
|
| + // a code that describes the situation where the method was found, but it was not
|
| + // accessible from the current library.
|
| return StaticTypeWarningCode.UNDEFINED_SUPER_METHOD;
|
| } else {
|
| + //
|
| + // This is really a function expression invocation.
|
| + //
|
| + // TODO(brianwilkerson) Consider the possibility of re-writing the AST.
|
| if (element is PropertyInducingElement) {
|
| PropertyAccessorElement getter = element.getter;
|
| FunctionType getterType = getter.type;
|
| @@ -5494,6 +5791,7 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| if (enclosingClass == null) {
|
| return CompileTimeErrorCode.UNDEFINED_FUNCTION;
|
| } else if (element == null) {
|
| + // Proxy-conditional warning, based on state of resolver.getEnclosingClass()
|
| return StaticTypeWarningCode.UNDEFINED_METHOD;
|
| } else {
|
| return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION;
|
| @@ -5503,11 +5801,14 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| if (useStaticContext) {
|
| targetType = getStaticType(target);
|
| } else {
|
| + // Compute and use the propagated type, if it is null, then it may be the case that
|
| + // static type is some type, in which the static type should be used.
|
| targetType = target.bestType;
|
| }
|
| if (targetType == null) {
|
| return CompileTimeErrorCode.UNDEFINED_FUNCTION;
|
| } else if (!targetType.isDynamic && !targetType.isBottom) {
|
| + // Proxy-conditional warning, based on state of targetType.getElement()
|
| return StaticTypeWarningCode.UNDEFINED_METHOD;
|
| }
|
| }
|
| @@ -5529,6 +5830,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| bool checkForUndefinedIndexOperator(IndexExpression node, Expression target, String methodName, MethodElement staticMethod, MethodElement propagatedMethod, Type2 staticType, Type2 propagatedType) {
|
| bool shouldReportMissingMember_static = shouldReportMissingMember(staticType, staticMethod);
|
| bool shouldReportMissingMember_propagated = !shouldReportMissingMember_static && _enableHints ? shouldReportMissingMember(propagatedType, propagatedMethod) : false;
|
| + //
|
| + // If we are about to generate the hint (propagated version of this warning), then check
|
| + // that the member is not in a subtype of the propagated type.
|
| + //
|
| if (shouldReportMissingMember_propagated) {
|
| if (memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
|
| shouldReportMissingMember_propagated = false;
|
| @@ -5565,6 +5870,9 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| */
|
| List<ParameterElement> computeCorrespondingParameters(ArgumentList argumentList, Element element) {
|
| if (element is PropertyAccessorElement) {
|
| + //
|
| + // This is an invocation of the call method defined on the value returned by the getter.
|
| + //
|
| FunctionType getterType = element.type;
|
| if (getterType != null) {
|
| Type2 getterReturnType = getterType.returnType;
|
| @@ -5590,6 +5898,7 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| List<ParameterElement> parameters = functionType.parameters;
|
| return resolveArgumentsToParameters2(false, argumentList, parameters);
|
| } else if (type is InterfaceType) {
|
| + // "call" invocation
|
| MethodElement callMethod = type.lookUpMethod(CALL_METHOD_NAME, _definingLibrary);
|
| if (callMethod != null) {
|
| List<ParameterElement> parameters = callMethod.parameters;
|
| @@ -5608,6 +5917,7 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| * @return a non-setter element derived from the given element
|
| */
|
| Element convertSetterToGetter(Element element) {
|
| + // TODO(brianwilkerson) Determine whether and why the element could ever be a setter.
|
| if (element is PropertyAccessorElement) {
|
| return element.variable.getter;
|
| }
|
| @@ -5678,6 +5988,9 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| Type2 getPropagatedType(Expression expression) {
|
| Type2 propagatedType = resolveTypeParameter(expression.propagatedType);
|
| if (propagatedType is FunctionType) {
|
| + //
|
| + // All function types are subtypes of 'Function', which is itself a subclass of 'Object'.
|
| + //
|
| propagatedType = _resolver.typeProvider.functionType;
|
| }
|
| return propagatedType;
|
| @@ -5695,6 +6008,9 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| }
|
| Type2 staticType = resolveTypeParameter(expression.staticType);
|
| if (staticType is FunctionType) {
|
| + //
|
| + // All function types are subtypes of 'Function', which is itself a subclass of 'Object'.
|
| + //
|
| staticType = _resolver.typeProvider.functionType;
|
| }
|
| return staticType;
|
| @@ -5806,6 +6122,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| * @return the element representing the getter that was found
|
| */
|
| PropertyAccessorElement lookUpGetterInInterfaces(InterfaceType targetType, bool includeTargetType, String getterName, Set<ClassElement> visitedInterfaces) {
|
| + // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the specification (titled
|
| + // "Inheritance and Overriding" under "Interfaces") describes a much more complex scheme for
|
| + // finding the inherited member. We need to follow that scheme. The code below should cover the
|
| + // 80% case.
|
| ClassElement targetClass = targetType.element;
|
| if (visitedInterfaces.contains(targetClass)) {
|
| return null;
|
| @@ -5875,6 +6195,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| * @return the element representing the method or getter that was found
|
| */
|
| ExecutableElement lookUpGetterOrMethodInInterfaces(InterfaceType targetType, bool includeTargetType, String memberName, Set<ClassElement> visitedInterfaces) {
|
| + // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the specification (titled
|
| + // "Inheritance and Overriding" under "Interfaces") describes a much more complex scheme for
|
| + // finding the inherited member. We need to follow that scheme. The code below should cover the
|
| + // 80% case.
|
| ClassElement targetClass = targetType.element;
|
| if (visitedInterfaces.contains(targetClass)) {
|
| return null;
|
| @@ -5925,6 +6249,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| labelElement = labelScope.lookup2(LabelScope.EMPTY_LABEL) as LabelElementImpl;
|
| if (labelElement == null) {
|
| }
|
| + //
|
| + // The label element that was returned was a marker for look-up and isn't stored in the
|
| + // element model.
|
| + //
|
| labelElement = null;
|
| }
|
| } else {
|
| @@ -5989,6 +6317,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| * @return the element representing the method that was found
|
| */
|
| MethodElement lookUpMethodInInterfaces(InterfaceType targetType, bool includeTargetType, String methodName, Set<ClassElement> visitedInterfaces) {
|
| + // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the specification (titled
|
| + // "Inheritance and Overriding" under "Interfaces") describes a much more complex scheme for
|
| + // finding the inherited member. We need to follow that scheme. The code below should cover the
|
| + // 80% case.
|
| ClassElement targetClass = targetType.element;
|
| if (visitedInterfaces.contains(targetClass)) {
|
| return null;
|
| @@ -6059,6 +6391,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| * @return the element representing the setter that was found
|
| */
|
| PropertyAccessorElement lookUpSetterInInterfaces(InterfaceType targetType, bool includeTargetType, String setterName, Set<ClassElement> visitedInterfaces) {
|
| + // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the specification (titled
|
| + // "Inheritance and Overriding" under "Interfaces") describes a much more complex scheme for
|
| + // finding the inherited member. We need to follow that scheme. The code below should cover the
|
| + // 80% case.
|
| ClassElement targetClass = targetType.element;
|
| if (visitedInterfaces.contains(targetClass)) {
|
| return null;
|
| @@ -6150,15 +6486,18 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| }
|
| break;
|
| }
|
| + // Internal error: Unmapped assignment operator.
|
| AnalysisEngine.instance.logger.logError("Failed to map ${operator.lexeme} to it's corresponding operator");
|
| return operator;
|
| }
|
|
|
| void resolveAnnotationConstructorInvocationArguments(Annotation annotation, ConstructorElement constructor) {
|
| ArgumentList argumentList = annotation.arguments;
|
| + // error will be reported in ConstantVerifier
|
| if (argumentList == null) {
|
| return;
|
| }
|
| + // resolve arguments to parameters
|
| List<ParameterElement> parameters = resolveArgumentsToParameters(true, argumentList, constructor);
|
| if (parameters != null) {
|
| argumentList.correspondingStaticParameters = parameters;
|
| @@ -6186,45 +6525,62 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| }
|
| SimpleIdentifier nameNode3 = annotation.constructorName;
|
| ConstructorElement constructor = null;
|
| + //
|
| + // CONST or Class(args)
|
| + //
|
| if (nameNode1 != null && nameNode2 == null && nameNode3 == null) {
|
| Element element1 = nameNode1.staticElement;
|
| + // CONST
|
| if (element1 is PropertyAccessorElement) {
|
| resolveAnnotationElementGetter(annotation, element1);
|
| return;
|
| }
|
| + // Class(args)
|
| if (element1 is ClassElement) {
|
| ClassElement classElement = element1;
|
| constructor = new InterfaceTypeImpl.con1(classElement).lookUpConstructor(null, _definingLibrary);
|
| }
|
| }
|
| + //
|
| + // prefix.CONST or prefix.Class() or Class.CONST or Class.constructor(args)
|
| + //
|
| if (nameNode1 != null && nameNode2 != null && nameNode3 == null) {
|
| Element element1 = nameNode1.staticElement;
|
| Element element2 = nameNode2.staticElement;
|
| + // Class.CONST - not resolved yet
|
| if (element1 is ClassElement) {
|
| ClassElement classElement = element1;
|
| element2 = classElement.lookUpGetter(nameNode2.name, _definingLibrary);
|
| }
|
| + // prefix.CONST or Class.CONST
|
| if (element2 is PropertyAccessorElement) {
|
| nameNode2.staticElement = element2;
|
| annotation.element = element2;
|
| resolveAnnotationElementGetter(annotation, element2 as PropertyAccessorElement);
|
| return;
|
| }
|
| + // prefix.Class()
|
| if (element2 is ClassElement) {
|
| ClassElement classElement = element2 as ClassElement;
|
| constructor = classElement.unnamedConstructor;
|
| }
|
| + // Class.constructor(args)
|
| if (element1 is ClassElement) {
|
| ClassElement classElement = element1;
|
| constructor = new InterfaceTypeImpl.con1(classElement).lookUpConstructor(nameNode2.name, _definingLibrary);
|
| nameNode2.staticElement = constructor;
|
| }
|
| }
|
| + //
|
| + // prefix.Class.CONST or prefix.Class.constructor(args)
|
| + //
|
| if (nameNode1 != null && nameNode2 != null && nameNode3 != null) {
|
| Element element2 = nameNode2.staticElement;
|
| + // element2 should be ClassElement
|
| if (element2 is ClassElement) {
|
| ClassElement classElement = element2;
|
| String name3 = nameNode3.name;
|
| + // prefix.Class.CONST
|
| PropertyAccessorElement getter = classElement.lookUpGetter(name3, _definingLibrary);
|
| if (getter != null) {
|
| nameNode3.staticElement = getter;
|
| @@ -6232,27 +6588,34 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| resolveAnnotationElementGetter(annotation, getter);
|
| return;
|
| }
|
| + // prefix.Class.constructor(args)
|
| constructor = new InterfaceTypeImpl.con1(classElement).lookUpConstructor(name3, _definingLibrary);
|
| nameNode3.staticElement = constructor;
|
| }
|
| }
|
| + // we need constructor
|
| if (constructor == null) {
|
| _resolver.reportError7(CompileTimeErrorCode.INVALID_ANNOTATION, annotation, []);
|
| return;
|
| }
|
| + // record element
|
| annotation.element = constructor;
|
| + // resolve arguments
|
| resolveAnnotationConstructorInvocationArguments(annotation, constructor);
|
| }
|
|
|
| void resolveAnnotationElementGetter(Annotation annotation, PropertyAccessorElement accessorElement) {
|
| + // accessor should be synthetic
|
| if (!accessorElement.isSynthetic) {
|
| _resolver.reportError7(CompileTimeErrorCode.INVALID_ANNOTATION, annotation, []);
|
| return;
|
| }
|
| + // variable should be constant
|
| VariableElement variableElement = accessorElement.variable;
|
| if (!variableElement.isConst) {
|
| _resolver.reportError7(CompileTimeErrorCode.INVALID_ANNOTATION, annotation, []);
|
| }
|
| + // OK
|
| return;
|
| }
|
|
|
| @@ -6350,6 +6713,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| */
|
| void resolveCombinators(LibraryElement library, NodeList<Combinator> combinators) {
|
| if (library == null) {
|
| + //
|
| + // The library will be null if the directive containing the combinators has a URI that is not
|
| + // valid.
|
| + //
|
| return;
|
| }
|
| Namespace namespace = new NamespaceBuilder().createExportNamespace2(library);
|
| @@ -6408,20 +6775,30 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| InterfaceType classType = targetType;
|
| Element element = lookUpMethod(target, classType, 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);
|
| }
|
| return element;
|
| } else if (target is SimpleIdentifier) {
|
| Element targetElement = target.staticElement;
|
| if (targetElement is PrefixElement) {
|
| + //
|
| + // Look to see whether the name of the method is really part of a prefixed identifier for an
|
| + // imported top-level function or top-level getter that returns a function.
|
| + //
|
| String name = "${target.name}.${methodName}";
|
| Identifier functionName = new ElementResolver_SyntheticIdentifier(name);
|
| Element element = _resolver.nameScope.lookup(functionName, _definingLibrary);
|
| if (element != null) {
|
| + // TODO(brianwilkerson) This isn't a method invocation, it's a function invocation where
|
| + // the function name is a prefixed identifier. Consider re-writing the AST.
|
| return element;
|
| }
|
| }
|
| }
|
| + // TODO(brianwilkerson) Report this error.
|
| return null;
|
| }
|
|
|
| @@ -6435,17 +6812,28 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| * @return the element being invoked
|
| */
|
| Element resolveInvokedElement2(SimpleIdentifier methodName) {
|
| + //
|
| + // Look first in the lexical scope.
|
| + //
|
| Element element = _resolver.nameScope.lookup(methodName, _definingLibrary);
|
| if (element == null) {
|
| + //
|
| + // If it isn't defined in the lexical scope, and the invocation is within a class, then look
|
| + // in the inheritance scope.
|
| + //
|
| ClassElement enclosingClass = _resolver.enclosingClass;
|
| if (enclosingClass != null) {
|
| InterfaceType enclosingType = enclosingClass.type;
|
| element = lookUpMethod(null, enclosingType, 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(null, enclosingType, methodName.name);
|
| }
|
| }
|
| }
|
| + // TODO(brianwilkerson) Report this error.
|
| return element;
|
| }
|
|
|
| @@ -6477,6 +6865,11 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| Type2 propagatedType = getPropagatedType(target);
|
| Element staticElement = null;
|
| Element propagatedElement = null;
|
| + //
|
| + // If this property access is of the form 'C.m' where 'C' is a class, then we don't call
|
| + // resolveProperty(..) which walks up the class hierarchy, instead we just look for the
|
| + // member in the type only.
|
| + //
|
| ClassElementImpl typeReference = getTypeReference(target);
|
| if (typeReference != null) {
|
| staticElement = propagatedElement = resolveElement(typeReference, propertyName);
|
| @@ -6484,6 +6877,8 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| staticElement = resolveProperty(target, staticType, propertyName);
|
| propagatedElement = resolveProperty(target, propagatedType, propertyName);
|
| }
|
| + // May be part of annotation, record property element only if exists.
|
| + // Error was already reported in validateAnnotationElement().
|
| if (target.parent.parent is Annotation) {
|
| if (staticElement != null) {
|
| propertyName.staticElement = staticElement;
|
| @@ -6494,6 +6889,8 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| propertyName.propagatedElement = propagatedElement;
|
| bool shouldReportMissingMember_static = shouldReportMissingMember(staticType, staticElement);
|
| bool shouldReportMissingMember_propagated = !shouldReportMissingMember_static && _enableHints ? shouldReportMissingMember(propagatedType, propagatedElement) : false;
|
| + // If we are about to generate the hint (propagated version of this warning), then check
|
| + // that the member is not in a subtype of the propagated type.
|
| if (shouldReportMissingMember_propagated) {
|
| if (memberFoundInSubclass(propagatedType.element, propertyName.name, false, true)) {
|
| shouldReportMissingMember_propagated = false;
|
| @@ -6501,33 +6898,35 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| }
|
| if (shouldReportMissingMember_static || shouldReportMissingMember_propagated) {
|
| Element staticOrPropagatedEnclosingElt = shouldReportMissingMember_static ? staticType.element : propagatedType.element;
|
| - bool isStaticProperty = isStatic(staticOrPropagatedEnclosingElt);
|
| - if (propertyName.inSetterContext()) {
|
| - if (isStaticProperty) {
|
| - ErrorCode errorCode = (shouldReportMissingMember_static ? StaticWarningCode.UNDEFINED_SETTER : HintCode.UNDEFINED_SETTER) as ErrorCode;
|
| - _resolver.reportErrorProxyConditionalAnalysisError(staticOrPropagatedEnclosingElt, errorCode, propertyName, [
|
| - propertyName.name,
|
| - staticOrPropagatedEnclosingElt.displayName]);
|
| - } else {
|
| - ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_SETTER : HintCode.UNDEFINED_SETTER) as ErrorCode;
|
| - _resolver.reportErrorProxyConditionalAnalysisError(staticOrPropagatedEnclosingElt, errorCode, propertyName, [
|
| - propertyName.name,
|
| - staticOrPropagatedEnclosingElt.displayName]);
|
| - }
|
| - } else if (propertyName.inGetterContext()) {
|
| - if (isStaticProperty) {
|
| - ErrorCode errorCode = (shouldReportMissingMember_static ? StaticWarningCode.UNDEFINED_GETTER : HintCode.UNDEFINED_GETTER) as ErrorCode;
|
| - _resolver.reportErrorProxyConditionalAnalysisError(staticOrPropagatedEnclosingElt, errorCode, propertyName, [
|
| - propertyName.name,
|
| - staticOrPropagatedEnclosingElt.displayName]);
|
| + if (staticOrPropagatedEnclosingElt != null) {
|
| + bool isStaticProperty = isStatic(staticOrPropagatedEnclosingElt);
|
| + if (propertyName.inSetterContext()) {
|
| + if (isStaticProperty) {
|
| + ErrorCode errorCode = (shouldReportMissingMember_static ? StaticWarningCode.UNDEFINED_SETTER : HintCode.UNDEFINED_SETTER) as ErrorCode;
|
| + _resolver.reportErrorProxyConditionalAnalysisError(staticOrPropagatedEnclosingElt, errorCode, propertyName, [
|
| + propertyName.name,
|
| + staticOrPropagatedEnclosingElt.displayName]);
|
| + } else {
|
| + ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_SETTER : HintCode.UNDEFINED_SETTER) as ErrorCode;
|
| + _resolver.reportErrorProxyConditionalAnalysisError(staticOrPropagatedEnclosingElt, errorCode, propertyName, [
|
| + propertyName.name,
|
| + staticOrPropagatedEnclosingElt.displayName]);
|
| + }
|
| + } else if (propertyName.inGetterContext()) {
|
| + if (isStaticProperty) {
|
| + ErrorCode errorCode = (shouldReportMissingMember_static ? StaticWarningCode.UNDEFINED_GETTER : HintCode.UNDEFINED_GETTER) as ErrorCode;
|
| + _resolver.reportErrorProxyConditionalAnalysisError(staticOrPropagatedEnclosingElt, errorCode, propertyName, [
|
| + propertyName.name,
|
| + staticOrPropagatedEnclosingElt.displayName]);
|
| + } else {
|
| + ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_GETTER : HintCode.UNDEFINED_GETTER) as ErrorCode;
|
| + _resolver.reportErrorProxyConditionalAnalysisError(staticOrPropagatedEnclosingElt, errorCode, propertyName, [
|
| + propertyName.name,
|
| + staticOrPropagatedEnclosingElt.displayName]);
|
| + }
|
| } else {
|
| - ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_GETTER : HintCode.UNDEFINED_GETTER) as ErrorCode;
|
| - _resolver.reportErrorProxyConditionalAnalysisError(staticOrPropagatedEnclosingElt, errorCode, propertyName, [
|
| - propertyName.name,
|
| - staticOrPropagatedEnclosingElt.displayName]);
|
| + _resolver.reportErrorProxyConditionalAnalysisError(staticOrPropagatedEnclosingElt, StaticWarningCode.UNDEFINED_IDENTIFIER, propertyName, [propertyName.name]);
|
| }
|
| - } else {
|
| - _resolver.reportErrorProxyConditionalAnalysisError(staticOrPropagatedEnclosingElt, StaticWarningCode.UNDEFINED_IDENTIFIER, propertyName, [propertyName.name]);
|
| }
|
| }
|
| }
|
| @@ -6547,6 +6946,9 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| if (variable != null) {
|
| PropertyAccessorElement setter = variable.setter;
|
| if (setter == null) {
|
| + //
|
| + // Check to see whether there might be a locally defined getter and an inherited setter.
|
| + //
|
| ClassElement enclosingClass = _resolver.enclosingClass;
|
| if (enclosingClass != null) {
|
| setter = lookUpSetter(null, enclosingClass.type, node.name);
|
| @@ -6974,12 +7376,15 @@ class InheritanceManager {
|
| if (baseFunctionType == null) {
|
| return baseFunctionType;
|
| }
|
| + // First, generate the path from the defining type to the overridden member
|
| Queue<InterfaceType> inheritancePath = new Queue<InterfaceType>();
|
| computeInheritancePath(inheritancePath, definingType, memberName);
|
| if (inheritancePath == null || inheritancePath.isEmpty) {
|
| + // TODO(jwren) log analysis engine error
|
| return baseFunctionType;
|
| }
|
| FunctionType functionTypeToReturn = baseFunctionType;
|
| + // loop backward through the list substituting as we go:
|
| while (!inheritancePath.isEmpty) {
|
| InterfaceType lastType = inheritancePath.removeLast();
|
| List<Type2> parameterTypes = lastType.element.type.typeArguments;
|
| @@ -7012,6 +7417,7 @@ class InheritanceManager {
|
| if (supertype != null) {
|
| superclassElt = supertype.element;
|
| } else {
|
| + // classElt is Object
|
| _classLookup[classElt] = resultMap;
|
| return resultMap;
|
| }
|
| @@ -7020,12 +7426,23 @@ class InheritanceManager {
|
| visitedClasses.add(classElt);
|
| resultMap = new MemberMap.con2(computeClassChainLookupMap(superclassElt, visitedClasses));
|
| } else {
|
| + // This case happens only when the superclass was previously visited and not in the lookup,
|
| + // meaning this is meant to shorten the compute for recursive cases.
|
| _classLookup[superclassElt] = resultMap;
|
| return resultMap;
|
| }
|
| + //
|
| + // Substitute the supertypes down the hierarchy
|
| + //
|
| substituteTypeParametersDownHierarchy(supertype, resultMap);
|
| + //
|
| + // Include the members from the superclass in the resultMap
|
| + //
|
| recordMapWithClassMembers(resultMap, supertype);
|
| }
|
| + //
|
| + // Include the members from the mixins in the resultMap
|
| + //
|
| List<InterfaceType> mixins = classElt.mixins;
|
| for (int i = mixins.length - 1; i >= 0; i--) {
|
| recordMapWithClassMembers(resultMap, mixins[i]);
|
| @@ -7044,33 +7461,46 @@ class InheritanceManager {
|
| * @param memberName the name of the member that is being looked up the inheritance path
|
| */
|
| void computeInheritancePath(Queue<InterfaceType> chain, InterfaceType currentType, String memberName) {
|
| + // TODO (jwren) create a public version of this method which doesn't require the initial chain
|
| + // to be provided, then provided tests for this functionality in InheritanceManagerTest
|
| chain.add(currentType);
|
| ClassElement classElt = currentType.element;
|
| InterfaceType supertype = classElt.supertype;
|
| + // Base case- reached Object
|
| if (supertype == null) {
|
| + // Looked up the chain all the way to Object, return null.
|
| + // This should never happen.
|
| return;
|
| }
|
| + // If we are done, return the chain
|
| + // We are not done if this is the first recursive call on this method.
|
| if (chain.length != 1) {
|
| + // We are done however if the member is in this classElt
|
| if (lookupMemberInClass(classElt, memberName) != null) {
|
| return;
|
| }
|
| }
|
| + // Mixins- note that mixins call lookupMemberInClass, not lookupMember
|
| List<InterfaceType> mixins = classElt.mixins;
|
| for (int i = mixins.length - 1; i >= 0; i--) {
|
| ClassElement mixinElement = mixins[i].element;
|
| if (mixinElement != null) {
|
| ExecutableElement elt = lookupMemberInClass(mixinElement, memberName);
|
| if (elt != null) {
|
| + // this is equivalent (but faster than) calling this method recursively
|
| + // (return computeInheritancePath(chain, mixins[i], memberName);)
|
| chain.add(mixins[i]);
|
| return;
|
| }
|
| }
|
| }
|
| + // Superclass
|
| ClassElement superclassElt = supertype.element;
|
| if (lookupMember(superclassElt, memberName) != null) {
|
| computeInheritancePath(chain, supertype, memberName);
|
| return;
|
| }
|
| + // Interfaces
|
| List<InterfaceType> interfaces = classElt.interfaces;
|
| for (InterfaceType interfaceType in interfaces) {
|
| ClassElement interfaceElement = interfaceType.element;
|
| @@ -7103,14 +7533,25 @@ class InheritanceManager {
|
| ClassElement superclassElement = supertype != null ? supertype.element : null;
|
| List<InterfaceType> mixins = classElt.mixins;
|
| List<InterfaceType> interfaces = classElt.interfaces;
|
| + // Recursively collect the list of mappings from all of the interface types
|
| List<MemberMap> lookupMaps = new List<MemberMap>();
|
| + // Superclass element
|
| if (superclassElement != null) {
|
| if (!visitedInterfaces.contains(superclassElement)) {
|
| try {
|
| visitedInterfaces.add(superclassElement);
|
| + //
|
| + // Recursively compute the map for the supertype.
|
| + //
|
| MemberMap map = computeInterfaceLookupMap(superclassElement, visitedInterfaces);
|
| map = new MemberMap.con2(map);
|
| + //
|
| + // Substitute the supertypes down the hierarchy
|
| + //
|
| substituteTypeParametersDownHierarchy(supertype, map);
|
| + //
|
| + // Add any members from the supertype into the map as well.
|
| + //
|
| recordMapWithClassMembers(map, supertype);
|
| lookupMaps.add(map);
|
| } finally {
|
| @@ -7126,20 +7567,31 @@ class InheritanceManager {
|
| }
|
| }
|
| }
|
| + // Mixin elements
|
| for (InterfaceType mixinType in mixins) {
|
| MemberMap mapWithMixinMembers = new MemberMap();
|
| recordMapWithClassMembers(mapWithMixinMembers, mixinType);
|
| lookupMaps.add(mapWithMixinMembers);
|
| }
|
| + // Interface elements
|
| for (InterfaceType interfaceType in interfaces) {
|
| ClassElement interfaceElement = interfaceType.element;
|
| if (interfaceElement != null) {
|
| if (!visitedInterfaces.contains(interfaceElement)) {
|
| try {
|
| visitedInterfaces.add(interfaceElement);
|
| + //
|
| + // Recursively compute the map for the interfaces.
|
| + //
|
| MemberMap map = computeInterfaceLookupMap(interfaceElement, visitedInterfaces);
|
| map = new MemberMap.con2(map);
|
| + //
|
| + // Substitute the supertypes down the hierarchy
|
| + //
|
| substituteTypeParametersDownHierarchy(interfaceType, map);
|
| + //
|
| + // And add any members from the interface into the map as well.
|
| + //
|
| recordMapWithClassMembers(map, interfaceType);
|
| lookupMaps.add(map);
|
| } finally {
|
| @@ -7160,6 +7612,9 @@ class InheritanceManager {
|
| _interfaceLookup[classElt] = resultMap;
|
| return resultMap;
|
| }
|
| + //
|
| + // Union all of the maps together, grouping the ExecutableElements into sets.
|
| + //
|
| Map<String, Set<ExecutableElement>> unionMap = new Map<String, Set<ExecutableElement>>();
|
| for (MemberMap lookupMap in lookupMaps) {
|
| for (int i = 0; i < lookupMap.size; i++) {
|
| @@ -7175,6 +7630,9 @@ class InheritanceManager {
|
| set.add(lookupMap.getValue(i));
|
| }
|
| }
|
| + //
|
| + // Loop through the entries in the union map, adding them to the resultMap appropriately.
|
| + //
|
| for (MapEntry<String, Set<ExecutableElement>> entry in getMapEntrySet(unionMap)) {
|
| String key = entry.getKey();
|
| Set<ExecutableElement> set = entry.getValue();
|
| @@ -7199,6 +7657,7 @@ class InheritanceManager {
|
| }
|
| }
|
| if (allMethods || allGetters || allSetters) {
|
| + // Compute the element whose type is the subtype of all of the other types.
|
| List<ExecutableElement> elements = new List.from(set);
|
| List<FunctionType> executableElementTypes = new List<FunctionType>(numOfEltsWithMatchingNames);
|
| for (int i = 0; i < numOfEltsWithMatchingNames; i++) {
|
| @@ -7698,6 +8157,12 @@ class LibraryElementBuilder {
|
| List<Directive> directivesToResolve = new List<Directive>();
|
| List<CompilationUnitElementImpl> sourcedCompilationUnits = new List<CompilationUnitElementImpl>();
|
| for (Directive directive in directives) {
|
| + //
|
| + // We do not build the elements representing the import and export directives at this point.
|
| + // That is not done until we get to LibraryResolver.buildDirectiveModels() because we need the
|
| + // LibraryElements for the referenced libraries, which might not exist at this point (due to
|
| + // the possibility of circular references).
|
| + //
|
| if (directive is LibraryDirective) {
|
| if (libraryNameNode == null) {
|
| libraryNameNode = directive.name;
|
| @@ -7711,6 +8176,9 @@ class LibraryElementBuilder {
|
| hasPartDirective = true;
|
| CompilationUnitElementImpl part = builder.buildCompilationUnit(partSource, library.getAST(partSource));
|
| part.uri = library.getUri(partDirective);
|
| + //
|
| + // Validate that the part contains a part-of directive with the same name as the library.
|
| + //
|
| String partLibraryName = getPartLibraryName(library, partSource, directivesToResolve);
|
| if (partLibraryName == null) {
|
| _errorListener.onError(new AnalysisError.con2(librarySource, partUri.offset, partUri.length, CompileTimeErrorCode.PART_OF_NON_PART, [partUri.toSource()]));
|
| @@ -7729,6 +8197,9 @@ class LibraryElementBuilder {
|
| if (hasPartDirective && libraryNameNode == null) {
|
| _errorListener.onError(new AnalysisError.con1(librarySource, ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART, []));
|
| }
|
| + //
|
| + // Create and populate the library element.
|
| + //
|
| LibraryElementImpl libraryElement = new LibraryElementImpl(_analysisContext, libraryNameNode);
|
| libraryElement.definingCompilationUnit = definingCompilationUnitElement;
|
| if (entryPoint != null) {
|
| @@ -7922,14 +8393,35 @@ class LibraryResolver {
|
| try {
|
| instrumentation.metric("fullAnalysis", fullAnalysis);
|
| instrumentation.data3("fullName", librarySource.fullName);
|
| + //
|
| + // Create the objects representing the library being resolved and the core library.
|
| + //
|
| Library targetLibrary = createLibrary2(librarySource, modificationStamp, unit);
|
| _coreLibrary = _libraryMap[_coreLibrarySource];
|
| if (_coreLibrary == null) {
|
| + // This will be true unless the library being analyzed is the core library.
|
| _coreLibrary = createLibrary(_coreLibrarySource);
|
| }
|
| instrumentation.metric3("createLibrary", "complete");
|
| + //
|
| + // Compute the set of libraries that need to be resolved together.
|
| + //
|
| computeLibraryDependencies2(targetLibrary, unit);
|
| _librariesInCycles = computeLibrariesInCycles(targetLibrary);
|
| + //
|
| + // Build the element models representing the libraries being resolved. This is done in three
|
| + // steps:
|
| + //
|
| + // 1. Build the basic element models without making any connections between elements other than
|
| + // the basic parent/child relationships. This includes building the elements representing the
|
| + // libraries.
|
| + // 2. Build the elements for the import and export directives. This requires that we have the
|
| + // elements built for the referenced libraries, but because of the possibility of circular
|
| + // references needs to happen after all of the library elements have been created.
|
| + // 3. Build the rest of the type model by connecting superclasses, mixins, and interfaces. This
|
| + // requires that we be able to compute the names visible in the libraries being resolved,
|
| + // which in turn requires that we have resolved the import directives.
|
| + //
|
| buildElementModels();
|
| instrumentation.metric3("buildElementModels", "complete");
|
| LibraryElement coreElement = _coreLibrary.libraryElement;
|
| @@ -7941,8 +8433,21 @@ class LibraryResolver {
|
| _typeProvider = new TypeProviderImpl(coreElement);
|
| buildTypeHierarchies();
|
| instrumentation.metric3("buildTypeHierarchies", "complete");
|
| + //
|
| + // Perform resolution and type analysis.
|
| + //
|
| + // TODO(brianwilkerson) Decide whether we want to resolve all of the libraries or whether we
|
| + // want to only resolve the target library. The advantage to resolving everything is that we
|
| + // have already done part of the work so we'll avoid duplicated effort. The disadvantage of
|
| + // resolving everything is that we might do extra work that we don't really care about. Another
|
| + // possibility is to add a parameter to this method and punt the decision to the clients.
|
| + //
|
| + //if (analyzeAll) {
|
| resolveReferencesAndTypes();
|
| instrumentation.metric3("resolveReferencesAndTypes", "complete");
|
| + //} else {
|
| + // resolveReferencesAndTypes(targetLibrary);
|
| + //}
|
| performConstantEvaluation();
|
| instrumentation.metric3("performConstantEvaluation", "complete");
|
| return targetLibrary.libraryElement;
|
| @@ -7969,17 +8474,38 @@ class LibraryResolver {
|
| try {
|
| instrumentation.metric("fullAnalysis", fullAnalysis);
|
| instrumentation.data3("fullName", librarySource.fullName);
|
| + //
|
| + // Create the objects representing the library being resolved and the core library.
|
| + //
|
| Library targetLibrary = createLibrary(librarySource);
|
| _coreLibrary = _libraryMap[_coreLibrarySource];
|
| if (_coreLibrary == null) {
|
| + // This will be true unless the library being analyzed is the core library.
|
| _coreLibrary = createLibraryOrNull(_coreLibrarySource);
|
| if (_coreLibrary == null) {
|
| throw new AnalysisException.con1("Core library does not exist");
|
| }
|
| }
|
| instrumentation.metric3("createLibrary", "complete");
|
| + //
|
| + // Compute the set of libraries that need to be resolved together.
|
| + //
|
| computeLibraryDependencies(targetLibrary);
|
| _librariesInCycles = computeLibrariesInCycles(targetLibrary);
|
| + //
|
| + // Build the element models representing the libraries being resolved. This is done in three
|
| + // steps:
|
| + //
|
| + // 1. Build the basic element models without making any connections between elements other than
|
| + // the basic parent/child relationships. This includes building the elements representing the
|
| + // libraries.
|
| + // 2. Build the elements for the import and export directives. This requires that we have the
|
| + // elements built for the referenced libraries, but because of the possibility of circular
|
| + // references needs to happen after all of the library elements have been created.
|
| + // 3. Build the rest of the type model by connecting superclasses, mixins, and interfaces. This
|
| + // requires that we be able to compute the names visible in the libraries being resolved,
|
| + // which in turn requires that we have resolved the import directives.
|
| + //
|
| buildElementModels();
|
| instrumentation.metric3("buildElementModels", "complete");
|
| LibraryElement coreElement = _coreLibrary.libraryElement;
|
| @@ -7991,8 +8517,21 @@ class LibraryResolver {
|
| _typeProvider = new TypeProviderImpl(coreElement);
|
| buildTypeHierarchies();
|
| instrumentation.metric3("buildTypeHierarchies", "complete");
|
| + //
|
| + // Perform resolution and type analysis.
|
| + //
|
| + // TODO(brianwilkerson) Decide whether we want to resolve all of the libraries or whether we
|
| + // want to only resolve the target library. The advantage to resolving everything is that we
|
| + // have already done part of the work so we'll avoid duplicated effort. The disadvantage of
|
| + // resolving everything is that we might do extra work that we don't really care about. Another
|
| + // possibility is to add a parameter to this method and punt the decision to the clients.
|
| + //
|
| + //if (analyzeAll) {
|
| resolveReferencesAndTypes();
|
| instrumentation.metric3("resolveReferencesAndTypes", "complete");
|
| + //} else {
|
| + // resolveReferencesAndTypes(targetLibrary);
|
| + //}
|
| performConstantEvaluation();
|
| instrumentation.metric3("performConstantEvaluation", "complete");
|
| instrumentation.metric2("librariesInCycles", _librariesInCycles.length);
|
| @@ -8104,6 +8643,7 @@ class LibraryResolver {
|
| ImportDirective importDirective = directive;
|
| Source importedSource = library.getSource(importDirective);
|
| if (importedSource != null) {
|
| + // The imported source will be null if the URI in the import directive was invalid.
|
| Library importedLibrary = _libraryMap[importedSource];
|
| if (importedLibrary != null) {
|
| ImportElementImpl importElement = new ImportElementImpl(directive.offset);
|
| @@ -8140,6 +8680,7 @@ class LibraryResolver {
|
| ExportDirective exportDirective = directive;
|
| Source exportedSource = library.getSource(exportDirective);
|
| if (exportedSource != null) {
|
| + // The exported source will be null if the URI in the export directive was invalid.
|
| Library exportedLibrary = _libraryMap[exportedSource];
|
| if (exportedLibrary != null) {
|
| ExportElementImpl exportElement = new ExportElementImpl();
|
| @@ -8456,6 +8997,7 @@ class LibraryResolver {
|
| } finally {
|
| timeCounter.stop();
|
| }
|
| + // Angular
|
| timeCounter = PerformanceStatistics.angular.start();
|
| try {
|
| for (Source source in library.compilationUnitSources) {
|
| @@ -8589,12 +9131,14 @@ class MemberMap {
|
| * @param value the ExecutableElement value to store in the map
|
| */
|
| void put(String key, ExecutableElement value) {
|
| + // If we already have a value with this key, override the value
|
| for (int i = 0; i < _size; i++) {
|
| if (_keys[i] != null && _keys[i] == key) {
|
| _values[i] = value;
|
| return;
|
| }
|
| }
|
| + // If needed, double the size of our arrays and copy values over in both arrays
|
| if (_size == _keys.length) {
|
| int newArrayLength = _size * 2;
|
| List<String> keys_new_array = new List<String>(newArrayLength);
|
| @@ -8608,6 +9152,7 @@ class MemberMap {
|
| _keys = keys_new_array;
|
| _values = values_new_array;
|
| }
|
| + // Put new value at end of array
|
| _keys[_size] = key;
|
| _values[_size] = value;
|
| _size++;
|
| @@ -8836,10 +9381,12 @@ class ResolverVisitor extends ScopedVisitor {
|
| _overrideManager.enterScope();
|
| _promoteManager.enterScope();
|
| propagateTrueState(leftOperand);
|
| + // Type promotion.
|
| promoteTypes(leftOperand);
|
| clearTypePromotionsIfPotentiallyMutatedIn(leftOperand);
|
| clearTypePromotionsIfPotentiallyMutatedIn(rightOperand);
|
| clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated(rightOperand);
|
| + // Visit right operand.
|
| rightOperand.accept(this);
|
| } finally {
|
| _overrideManager.exitScope();
|
| @@ -8878,6 +9425,9 @@ class ResolverVisitor extends ScopedVisitor {
|
| }
|
|
|
| Object visitBreakStatement(BreakStatement node) {
|
| + //
|
| + // We do not visit the label because it needs to be visited in the context of the statement.
|
| + //
|
| node.accept(_elementResolver);
|
| node.accept(_typeAnalyzer);
|
| return null;
|
| @@ -8909,12 +9459,23 @@ class ResolverVisitor extends ScopedVisitor {
|
| }
|
|
|
| Object visitCommentReference(CommentReference node) {
|
| + //
|
| + // We do not visit the identifier because it needs to be visited in the context of the reference.
|
| + //
|
| node.accept(_elementResolver);
|
| node.accept(_typeAnalyzer);
|
| return null;
|
| }
|
|
|
| Object visitCompilationUnit(CompilationUnit node) {
|
| + //
|
| + // TODO(brianwilkerson) The goal of the code below is to visit the declarations in such an
|
| + // order that we can infer type information for top-level variables before we visit references
|
| + // to them. This is better than making no effort, but still doesn't completely satisfy that
|
| + // goal (consider for example "final var a = b; final var b = 0;"; we'll infer a type of 'int'
|
| + // for 'b', but not for 'a' because of the order of the visits). Ideally we would create a
|
| + // dependency graph, but that would require references to be resolved, which they are not.
|
| + //
|
| try {
|
| _overrideManager.enterScope();
|
| NodeList<Directive> directives = node.directives;
|
| @@ -8953,9 +9514,11 @@ class ResolverVisitor extends ScopedVisitor {
|
| _overrideManager.enterScope();
|
| _promoteManager.enterScope();
|
| propagateTrueState(condition);
|
| + // Type promotion.
|
| promoteTypes(condition);
|
| clearTypePromotionsIfPotentiallyMutatedIn(thenExpression);
|
| clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated(thenExpression);
|
| + // Visit "then" expression.
|
| thenExpression.accept(this);
|
| } finally {
|
| _overrideManager.exitScope();
|
| @@ -8998,6 +9561,10 @@ class ResolverVisitor extends ScopedVisitor {
|
| }
|
|
|
| Object visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
|
| + //
|
| + // We visit the expression, but do not visit the field name because it needs to be visited in
|
| + // the context of the constructor field initializer node.
|
| + //
|
| safelyVisit(node.expression);
|
| node.accept(_elementResolver);
|
| node.accept(_typeAnalyzer);
|
| @@ -9005,12 +9572,19 @@ class ResolverVisitor extends ScopedVisitor {
|
| }
|
|
|
| Object visitConstructorName(ConstructorName node) {
|
| + //
|
| + // We do not visit either the type name, because it won't be visited anyway, or the name,
|
| + // because it needs to be visited in the context of the constructor name.
|
| + //
|
| node.accept(_elementResolver);
|
| node.accept(_typeAnalyzer);
|
| return null;
|
| }
|
|
|
| Object visitContinueStatement(ContinueStatement node) {
|
| + //
|
| + // We do not visit the label because it needs to be visited in the context of the statement.
|
| + //
|
| node.accept(_elementResolver);
|
| node.accept(_typeAnalyzer);
|
| return null;
|
| @@ -9023,6 +9597,8 @@ class ResolverVisitor extends ScopedVisitor {
|
| } finally {
|
| _overrideManager.exitScope();
|
| }
|
| + // TODO(brianwilkerson) If the loop can only be exited because the condition is false, then
|
| + // propagateFalseState(node.getCondition());
|
| return null;
|
| }
|
|
|
| @@ -9120,9 +9696,11 @@ class ResolverVisitor extends ScopedVisitor {
|
| _overrideManager.enterScope();
|
| _promoteManager.enterScope();
|
| propagateTrueState(condition);
|
| + // Type promotion.
|
| promoteTypes(condition);
|
| clearTypePromotionsIfPotentiallyMutatedIn(thenStatement);
|
| clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated(thenStatement);
|
| + // Visit "then".
|
| visitStatementInScope(thenStatement);
|
| } finally {
|
| thenOverrides = _overrideManager.captureLocalOverrides();
|
| @@ -9176,6 +9754,10 @@ class ResolverVisitor extends ScopedVisitor {
|
| }
|
|
|
| Object visitMethodInvocation(MethodInvocation node) {
|
| + //
|
| + // We visit the target and argument list, but do not visit the method name because it needs to
|
| + // be visited in the context of the invocation.
|
| + //
|
| safelyVisit(node.target);
|
| node.accept(_elementResolver);
|
| inferFunctionExpressionsParametersTypes(node.argumentList);
|
| @@ -9192,6 +9774,10 @@ class ResolverVisitor extends ScopedVisitor {
|
| }
|
|
|
| Object visitPrefixedIdentifier(PrefixedIdentifier node) {
|
| + //
|
| + // We visit the prefix, but do not visit the identifier because it needs to be visited in the
|
| + // context of the prefix.
|
| + //
|
| safelyVisit(node.prefix);
|
| node.accept(_elementResolver);
|
| node.accept(_typeAnalyzer);
|
| @@ -9199,6 +9785,10 @@ class ResolverVisitor extends ScopedVisitor {
|
| }
|
|
|
| Object visitPropertyAccess(PropertyAccess node) {
|
| + //
|
| + // We visit the target, but do not visit the property name because it needs to be visited in the
|
| + // context of the property access node.
|
| + //
|
| safelyVisit(node.target);
|
| node.accept(_elementResolver);
|
| node.accept(_typeAnalyzer);
|
| @@ -9206,6 +9796,10 @@ class ResolverVisitor extends ScopedVisitor {
|
| }
|
|
|
| Object visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) {
|
| + //
|
| + // We visit the argument list, but do not visit the optional identifier because it needs to be
|
| + // visited in the context of the constructor invocation.
|
| + //
|
| safelyVisit(node.argumentList);
|
| node.accept(_elementResolver);
|
| node.accept(_typeAnalyzer);
|
| @@ -9215,6 +9809,10 @@ class ResolverVisitor extends ScopedVisitor {
|
| Object visitShowCombinator(ShowCombinator node) => null;
|
|
|
| Object visitSuperConstructorInvocation(SuperConstructorInvocation node) {
|
| + //
|
| + // We visit the argument list, but do not visit the optional identifier because it needs to be
|
| + // visited in the context of the constructor invocation.
|
| + //
|
| safelyVisit(node.argumentList);
|
| node.accept(_elementResolver);
|
| node.accept(_typeAnalyzer);
|
| @@ -9268,6 +9866,8 @@ class ResolverVisitor extends ScopedVisitor {
|
| _overrideManager.exitScope();
|
| }
|
| }
|
| + // TODO(brianwilkerson) If the loop can only be exited because the condition is false, then
|
| + // propagateFalseState(condition);
|
| node.accept(_elementResolver);
|
| node.accept(_typeAnalyzer);
|
| return null;
|
| @@ -9444,6 +10044,10 @@ class ResolverVisitor extends ScopedVisitor {
|
| }
|
|
|
| void visitForEachStatementInScope(ForEachStatement node) {
|
| + //
|
| + // We visit the iterator before the loop variable because the loop variable cannot be in scope
|
| + // while visiting the iterator.
|
| + //
|
| Expression iterator = node.iterator;
|
| safelyVisit(iterator);
|
| DeclaredIdentifier loopVariable = node.loopVariable;
|
| @@ -9556,6 +10160,7 @@ class ResolverVisitor extends ScopedVisitor {
|
| InterfaceType interfaceType = expressionType;
|
| FunctionType iteratorFunction = _inheritanceManager.lookupMemberType(interfaceType, "iterator");
|
| if (iteratorFunction == null) {
|
| + // TODO(brianwilkerson) Should we report this error?
|
| return null;
|
| }
|
| Type2 iteratorType = iteratorFunction.returnType;
|
| @@ -9563,6 +10168,7 @@ class ResolverVisitor extends ScopedVisitor {
|
| InterfaceType iteratorInterfaceType = iteratorType;
|
| FunctionType currentFunction = _inheritanceManager.lookupMemberType(iteratorInterfaceType, "current");
|
| if (currentFunction == null) {
|
| + // TODO(brianwilkerson) Should we report this error?
|
| return null;
|
| }
|
| return currentFunction.returnType;
|
| @@ -9576,21 +10182,26 @@ class ResolverVisitor extends ScopedVisitor {
|
| * required type is [FunctionType], then infer parameters types from [FunctionType].
|
| */
|
| void inferFunctionExpressionParametersTypes(Expression mayBeClosure, Type2 mayByFunctionType) {
|
| + // prepare closure
|
| if (mayBeClosure is! FunctionExpression) {
|
| return;
|
| }
|
| FunctionExpression closure = mayBeClosure as FunctionExpression;
|
| + // prepare expected closure type
|
| if (mayByFunctionType is! FunctionType) {
|
| return;
|
| }
|
| FunctionType expectedClosureType = mayByFunctionType as FunctionType;
|
| + // set propagated type for the closure
|
| closure.propagatedType = expectedClosureType;
|
| + // set inferred types for parameters
|
| NodeList<FormalParameter> parameters = closure.parameters.parameters;
|
| List<ParameterElement> expectedParameters = expectedClosureType.parameters;
|
| for (int i = 0; i < parameters.length && i < expectedParameters.length; i++) {
|
| FormalParameter parameter = parameters[i];
|
| ParameterElement element = parameter.element;
|
| Type2 currentType = getBestType(element);
|
| + // may be override the type
|
| Type2 expectedType = expectedParameters[i].type;
|
| if (currentType == null || expectedType.isMoreSpecificThan(currentType)) {
|
| _overrideManager.setType(element, expectedType);
|
| @@ -9621,6 +10232,9 @@ class ResolverVisitor extends ScopedVisitor {
|
| * @return `true` if the given expression terminates abruptly
|
| */
|
| bool isAbruptTermination(Expression expression) {
|
| + // TODO(brianwilkerson) This needs to be significantly improved. Ideally we would eventually
|
| + // turn this into a method on Expression that returns a termination indication (normal, abrupt
|
| + // with no exception, abrupt with an exception).
|
| while (expression is ParenthesizedExpression) {
|
| expression = (expression as ParenthesizedExpression).expression;
|
| }
|
| @@ -9635,6 +10249,9 @@ class ResolverVisitor extends ScopedVisitor {
|
| * @return `true` if the given statement terminates abruptly
|
| */
|
| bool isAbruptTermination2(Statement statement) {
|
| + // TODO(brianwilkerson) This needs to be significantly improved. Ideally we would eventually
|
| + // turn this into a method on Statement that returns a termination indication (normal, abrupt
|
| + // with no exception, abrupt with an exception).
|
| if (statement is ReturnStatement || statement is BreakStatement || statement is ContinueStatement) {
|
| return true;
|
| } else if (statement is ExpressionStatement) {
|
| @@ -9691,22 +10308,28 @@ class ResolverVisitor extends ScopedVisitor {
|
| void promote(Expression expression, Type2 potentialType) {
|
| VariableElement element = getPromotionStaticElement(expression);
|
| if (element != null) {
|
| + // may be mutated somewhere in closure
|
| if ((element as VariableElementImpl).isPotentiallyMutatedInClosure) {
|
| return;
|
| }
|
| + // prepare current variable type
|
| Type2 type = _promoteManager.getType(element);
|
| if (type == null) {
|
| type = expression.staticType;
|
| }
|
| + // Declared type should not be "dynamic".
|
| if (type == null || type.isDynamic) {
|
| return;
|
| }
|
| + // Promoted type should not be "dynamic".
|
| if (potentialType == null || potentialType.isDynamic) {
|
| return;
|
| }
|
| + // Promoted type should be more specific than declared.
|
| if (!potentialType.isMoreSpecificThan(type)) {
|
| return;
|
| }
|
| + // Do promote type of variable.
|
| _promoteManager.setType(element, potentialType);
|
| }
|
| }
|
| @@ -10105,6 +10728,7 @@ abstract class ScopedVisitor extends UnifyingASTVisitor<Object> {
|
|
|
| Object visitFormalParameterList(FormalParameterList node) {
|
| super.visitFormalParameterList(node);
|
| + // We finished resolving function signature, now include formal parameters scope.
|
| if (_nameScope is FunctionScope) {
|
| (_nameScope as FunctionScope).defineParameters();
|
| }
|
| @@ -10145,6 +10769,7 @@ abstract class ScopedVisitor extends UnifyingASTVisitor<Object> {
|
|
|
| Object visitFunctionExpression(FunctionExpression node) {
|
| if (node.parent is FunctionDeclaration) {
|
| + // We have already created a function scope and don't need to do so again.
|
| super.visitFunctionExpression(node);
|
| } else {
|
| Scope outerScope = _nameScope;
|
| @@ -10332,6 +10957,10 @@ abstract class ScopedVisitor extends UnifyingASTVisitor<Object> {
|
| * @param node the statement to be visited
|
| */
|
| void visitForEachStatementInScope(ForEachStatement node) {
|
| + //
|
| + // We visit the iterator before the loop variable because the loop variable cannot be in scope
|
| + // while visiting the iterator.
|
| + //
|
| safelyVisit(node.identifier);
|
| safelyVisit(node.iterator);
|
| safelyVisit(node.loopVariable);
|
| @@ -10361,6 +10990,7 @@ abstract class ScopedVisitor extends UnifyingASTVisitor<Object> {
|
| */
|
| void visitStatementInScope(Statement node) {
|
| if (node is Block) {
|
| + // Don't create a scope around a block because the block will create it's own scope.
|
| visitBlock(node);
|
| } else if (node != null) {
|
| Scope outerNameScope = _nameScope;
|
| @@ -10745,9 +11375,11 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| Type2 staticThenType = getStaticType(node.thenExpression);
|
| Type2 staticElseType = getStaticType(node.elseExpression);
|
| if (staticThenType == null) {
|
| + // TODO(brianwilkerson) Determine whether this can still happen.
|
| staticThenType = _dynamicType;
|
| }
|
| if (staticElseType == null) {
|
| + // TODO(brianwilkerson) Determine whether this can still happen.
|
| staticElseType = _dynamicType;
|
| }
|
| Type2 staticType = staticThenType.getLeastUpperBound(staticElseType);
|
| @@ -10822,6 +11454,7 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| */
|
| Object visitFunctionExpression(FunctionExpression node) {
|
| if (node.parent is FunctionDeclaration) {
|
| + // The function type will be resolved and set when we visit the parent node.
|
| return null;
|
| }
|
| ExecutableElementImpl functionElement = node.element as ExecutableElementImpl;
|
| @@ -10845,18 +11478,22 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| */
|
| Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
|
| ExecutableElement staticMethodElement = node.staticElement;
|
| + // Record static return type of the static element.
|
| Type2 staticStaticType = computeStaticReturnType(staticMethodElement);
|
| recordStaticType(node, staticStaticType);
|
| + // Record propagated return type of the static element.
|
| Type2 staticPropagatedType = computePropagatedReturnType(staticMethodElement);
|
| if (staticPropagatedType != null && (staticStaticType == null || staticPropagatedType.isMoreSpecificThan(staticStaticType))) {
|
| recordPropagatedType2(node, staticPropagatedType);
|
| }
|
| ExecutableElement propagatedMethodElement = node.propagatedElement;
|
| if (propagatedMethodElement != staticMethodElement) {
|
| + // Record static return type of the propagated element.
|
| Type2 propagatedStaticType = computeStaticReturnType(propagatedMethodElement);
|
| if (propagatedStaticType != null && (staticStaticType == null || propagatedStaticType.isMoreSpecificThan(staticStaticType)) && (staticPropagatedType == null || propagatedStaticType.isMoreSpecificThan(staticPropagatedType))) {
|
| recordPropagatedType2(node, propagatedStaticType);
|
| }
|
| + // Record propagated return type of the propagated element.
|
| Type2 propagatedPropagatedType = computePropagatedReturnType(propagatedMethodElement);
|
| if (propagatedPropagatedType != null && (staticStaticType == null || propagatedPropagatedType.isMoreSpecificThan(staticStaticType)) && (staticPropagatedType == null || propagatedPropagatedType.isMoreSpecificThan(staticPropagatedType)) && (propagatedStaticType == null || propagatedPropagatedType.isMoreSpecificThan(propagatedStaticType))) {
|
| recordPropagatedType2(node, propagatedPropagatedType);
|
| @@ -11106,6 +11743,7 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| Object visitMethodInvocation(MethodInvocation node) {
|
| SimpleIdentifier methodNameNode = node.methodName;
|
| Element staticMethodElement = methodNameNode.staticElement;
|
| + // Record types of the local variable invoked as a function.
|
| if (staticMethodElement is LocalVariableElement) {
|
| LocalVariableElement variable = staticMethodElement;
|
| Type2 staticType = variable.type;
|
| @@ -11115,24 +11753,31 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| recordPropagatedType2(methodNameNode, propagatedType);
|
| }
|
| }
|
| + // Record static return type of the static element.
|
| Type2 staticStaticType = computeStaticReturnType(staticMethodElement);
|
| recordStaticType(node, staticStaticType);
|
| + // Record propagated return type of the static element.
|
| Type2 staticPropagatedType = computePropagatedReturnType(staticMethodElement);
|
| if (staticPropagatedType != null && (staticStaticType == null || staticPropagatedType.isMoreSpecificThan(staticStaticType))) {
|
| recordPropagatedType2(node, staticPropagatedType);
|
| }
|
| String methodName = methodNameNode.name;
|
| + // Future.then(closure) return type is:
|
| + // 1) the returned Future type, if the closure returns a Future;
|
| + // 2) Future<valueType>, if the closure returns a value.
|
| if (methodName == "then") {
|
| Expression target = node.realTarget;
|
| Type2 targetType = target == null ? null : target.bestType;
|
| if (isAsyncFutureType(targetType)) {
|
| NodeList<Expression> arguments = node.argumentList.arguments;
|
| if (arguments.length == 1) {
|
| + // TODO(brianwilkerson) Handle the case where both arguments are provided.
|
| Expression closureArg = arguments[0];
|
| if (closureArg is FunctionExpression) {
|
| FunctionExpression closureExpr = closureArg;
|
| Type2 returnType = computePropagatedReturnType(closureExpr.element);
|
| if (returnType != null) {
|
| + // prepare the type of the returned Future
|
| InterfaceTypeImpl newFutureType;
|
| if (isAsyncFutureType(returnType)) {
|
| newFutureType = returnType as InterfaceTypeImpl;
|
| @@ -11141,6 +11786,7 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| newFutureType = new InterfaceTypeImpl.con1(futureType.element);
|
| newFutureType.typeArguments = <Type2> [returnType];
|
| }
|
| + // set the 'then' invocation type
|
| recordPropagatedType2(node, newFutureType);
|
| return null;
|
| }
|
| @@ -11207,10 +11853,12 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| } else {
|
| Element propagatedElement = methodNameNode.propagatedElement;
|
| if (propagatedElement != staticMethodElement) {
|
| + // Record static return type of the propagated element.
|
| Type2 propagatedStaticType = computeStaticReturnType(propagatedElement);
|
| if (propagatedStaticType != null && (staticStaticType == null || propagatedStaticType.isMoreSpecificThan(staticStaticType)) && (staticPropagatedType == null || propagatedStaticType.isMoreSpecificThan(staticPropagatedType))) {
|
| recordPropagatedType2(node, propagatedStaticType);
|
| }
|
| + // Record propagated return type of the propagated element.
|
| Type2 propagatedPropagatedType = computePropagatedReturnType(propagatedElement);
|
| if (propagatedPropagatedType != null && (staticStaticType == null || propagatedPropagatedType.isMoreSpecificThan(staticStaticType)) && (staticPropagatedType == null || propagatedPropagatedType.isMoreSpecificThan(staticPropagatedType)) && (propagatedStaticType == null || propagatedPropagatedType.isMoreSpecificThan(propagatedStaticType))) {
|
| recordPropagatedType2(node, propagatedPropagatedType);
|
| @@ -11358,6 +12006,7 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| if (identical(operator, sc.TokenType.BANG)) {
|
| recordStaticType(node, _typeProvider.boolType);
|
| } else {
|
| + // The other cases are equivalent to invoking a method.
|
| ExecutableElement staticMethodElement = node.staticElement;
|
| Type2 staticType = computeStaticReturnType(staticMethodElement);
|
| if (identical(operator, sc.TokenType.MINUS_MINUS) || identical(operator, sc.TokenType.PLUS_PLUS)) {
|
| @@ -11433,6 +12082,8 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| }
|
| recordStaticType(propertyName, staticType);
|
| recordStaticType(node, staticType);
|
| + // TODO(brianwilkerson) I think we want to repeat the logic above using the propagated element
|
| + // to get another candidate for the propagated type.
|
| Type2 propagatedType = _overrideManager.getType(element);
|
| if (propagatedType != null && propagatedType.isMoreSpecificThan(staticType)) {
|
| recordPropagatedType2(node, propagatedType);
|
| @@ -11513,6 +12164,7 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| } else if (element is ExecutableElement) {
|
| staticType = element.type;
|
| } else if (element is TypeParameterElement) {
|
| + // if (isTypeName(node)) {
|
| staticType = element.type;
|
| } else if (element is VariableElement) {
|
| VariableElement variable = element;
|
| @@ -11523,6 +12175,8 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| staticType = _dynamicType;
|
| }
|
| recordStaticType(node, staticType);
|
| + // TODO(brianwilkerson) I think we want to repeat the logic above using the propagated element
|
| + // to get another candidate for the propagated type.
|
| Type2 propagatedType = _overrideManager.getType(element);
|
| if (propagatedType != null && propagatedType.isMoreSpecificThan(staticType)) {
|
| recordPropagatedType2(node, propagatedType);
|
| @@ -11550,6 +12204,7 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
|
|
| Object visitSuperExpression(SuperExpression node) {
|
| if (_thisType == null) {
|
| + // TODO(brianwilkerson) Report this error if it hasn't already been reported
|
| recordStaticType(node, _dynamicType);
|
| } else {
|
| recordStaticType(node, _thisType);
|
| @@ -11568,6 +12223,7 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| */
|
| Object visitThisExpression(ThisExpression node) {
|
| if (_thisType == null) {
|
| + // TODO(brianwilkerson) Report this error if it hasn't already been reported
|
| recordStaticType(node, _dynamicType);
|
| } else {
|
| recordStaticType(node, _thisType);
|
| @@ -11656,6 +12312,10 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| */
|
| Type2 computeStaticReturnType(Element element) {
|
| if (element is PropertyAccessorElement) {
|
| + //
|
| + // This is a function invocation expression disguised as something else. We are invoking a
|
| + // getter and then invoking the returned function.
|
| + //
|
| FunctionType propertyType = element.type;
|
| if (propertyType != null) {
|
| Type2 returnType = propertyType.returnType;
|
| @@ -11679,6 +12339,7 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| } else if (element is ExecutableElement) {
|
| FunctionType type = element.type;
|
| if (type != null) {
|
| + // TODO(brianwilkerson) Figure out the conditions under which the type is null.
|
| return type.returnType;
|
| }
|
| } else if (element is VariableElement) {
|
| @@ -11757,9 +12418,16 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| Type2 getFirstArgumentAsQuery(LibraryElement library, ArgumentList argumentList) {
|
| String argumentValue = getFirstArgumentAsString(argumentList);
|
| if (argumentValue != null) {
|
| + //
|
| + // If the query has spaces, full parsing is required because it might be:
|
| + // E[text='warning text']
|
| + //
|
| if (argumentValue.contains(" ")) {
|
| return null;
|
| }
|
| + //
|
| + // Otherwise, try to extract the tag based on http://www.w3.org/TR/CSS2/selector.html.
|
| + //
|
| String tag = argumentValue;
|
| tag = StringUtilities.substringBefore(tag, ":");
|
| tag = StringUtilities.substringBefore(tag, "[");
|
| @@ -11824,6 +12492,7 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| Type2 getStaticType(Expression expression) {
|
| Type2 type = expression.staticType;
|
| if (type == null) {
|
| + // TODO(brianwilkerson) Determine the conditions for which the static type is null.
|
| return _dynamicType;
|
| }
|
| return type;
|
| @@ -11842,6 +12511,9 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| Type2 getType(PropertyAccessorElement accessor, Type2 context) {
|
| FunctionType functionType = accessor.type;
|
| if (functionType == null) {
|
| + // TODO(brianwilkerson) Report this internal error. This happens when we are analyzing a
|
| + // reference to a property before we have analyzed the declaration of the property or when
|
| + // the property does not have a defined type.
|
| return _dynamicType;
|
| }
|
| if (accessor.isSetter) {
|
| @@ -11860,7 +12532,10 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| }
|
| Type2 returnType = functionType.returnType;
|
| if (returnType is TypeParameterType && context is InterfaceType) {
|
| + // if the return type is a TypeParameter, we try to use the context [that the function is being
|
| + // called on] to get a more accurate returnType type
|
| InterfaceType interfaceTypeContext = context;
|
| + // Type[] argumentTypes = interfaceTypeContext.getTypeArguments();
|
| List<TypeParameterElement> typeParameterElements = interfaceTypeContext.element != null ? interfaceTypeContext.element.typeParameters : null;
|
| if (typeParameterElements != null) {
|
| for (int i = 0; i < typeParameterElements.length; i++) {
|
| @@ -11883,6 +12558,7 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| Type2 getType2(TypeName typeName) {
|
| Type2 type = typeName.type;
|
| if (type == null) {
|
| + //TODO(brianwilkerson) Determine the conditions for which the type is null.
|
| return _dynamicType;
|
| }
|
| return type;
|
| @@ -11935,13 +12611,16 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| if (propagatedReturnType == null) {
|
| return;
|
| }
|
| + // Ignore 'bottom' type.
|
| if (propagatedReturnType.isBottom) {
|
| return;
|
| }
|
| + // Record only if we inferred more specific type.
|
| Type2 staticReturnType = functionElement.returnType;
|
| if (!propagatedReturnType.isMoreSpecificThan(staticReturnType)) {
|
| return;
|
| }
|
| + // OK, do record.
|
| _propagatedReturnTypes[functionElement] = propagatedReturnType;
|
| }
|
|
|
| @@ -11980,23 +12659,27 @@ class StaticTypeAnalyzer extends SimpleASTVisitor<Object> {
|
| */
|
| Type2 refineBinaryExpressionType(BinaryExpression node, Type2 staticType) {
|
| sc.TokenType operator = node.operator.type;
|
| + // bool
|
| if (identical(operator, sc.TokenType.AMPERSAND_AMPERSAND) || identical(operator, sc.TokenType.BAR_BAR) || identical(operator, sc.TokenType.EQ_EQ) || identical(operator, sc.TokenType.BANG_EQ)) {
|
| return _typeProvider.boolType;
|
| }
|
| Type2 intType = _typeProvider.intType;
|
| if (getStaticType(node.leftOperand) == intType) {
|
| + // int op double
|
| if (identical(operator, sc.TokenType.MINUS) || identical(operator, sc.TokenType.PERCENT) || identical(operator, sc.TokenType.PLUS) || identical(operator, sc.TokenType.STAR)) {
|
| Type2 doubleType = _typeProvider.doubleType;
|
| if (getStaticType(node.rightOperand) == doubleType) {
|
| return doubleType;
|
| }
|
| }
|
| + // int op int
|
| if (identical(operator, sc.TokenType.MINUS) || identical(operator, sc.TokenType.PERCENT) || identical(operator, sc.TokenType.PLUS) || identical(operator, sc.TokenType.STAR) || identical(operator, sc.TokenType.TILDE_SLASH)) {
|
| if (getStaticType(node.rightOperand) == intType) {
|
| staticType = intType;
|
| }
|
| }
|
| }
|
| + // default
|
| return staticType;
|
| }
|
|
|
| @@ -12013,6 +12696,7 @@ class GeneralizingASTVisitor_StaticTypeAnalyzer_computePropagatedReturnType2 ext
|
| Object visitExpression(Expression node) => null;
|
|
|
| Object visitReturnStatement(ReturnStatement node) {
|
| + // prepare this 'return' type
|
| Type2 type;
|
| Expression expression = node.expression;
|
| if (expression != null) {
|
| @@ -12020,6 +12704,7 @@ class GeneralizingASTVisitor_StaticTypeAnalyzer_computePropagatedReturnType2 ext
|
| } else {
|
| type = BottomTypeImpl.instance;
|
| }
|
| + // merge types
|
| if (result[0] == null) {
|
| result[0] = type;
|
| } else {
|
| @@ -12052,7 +12737,9 @@ class SubtypeManager {
|
| * @param classElement the class to recursively return the set of subtypes of
|
| */
|
| Set<ClassElement> computeAllSubtypes(ClassElement classElement) {
|
| + // Ensure that we have generated the subtype map for the library
|
| computeSubtypesInLibrary(classElement.library);
|
| + // use the subtypeMap to compute the set of all subtypes and subtype's subtypes
|
| Set<ClassElement> allSubtypes = new Set<ClassElement>();
|
| computeAllSubtypes2(classElement, new Set<ClassElement>(), allSubtypes);
|
| return allSubtypes;
|
| @@ -12078,6 +12765,7 @@ class SubtypeManager {
|
| */
|
| void computeAllSubtypes2(ClassElement classElement, Set<ClassElement> visitedClasses, Set<ClassElement> allSubtypes) {
|
| if (!visitedClasses.add(classElement)) {
|
| + // if this class has already been called on this class element
|
| return;
|
| }
|
| Set<ClassElement> subtypes = _subtypeMap[classElement];
|
| @@ -12880,6 +13568,8 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| super.visitCatchClause(node);
|
| SimpleIdentifier exception = node.exceptionParameter;
|
| if (exception != null) {
|
| + // If an 'on' clause is provided the type of the exception parameter is the type in the 'on'
|
| + // clause. Otherwise, the type of the exception parameter is 'Object'.
|
| TypeName exceptionTypeName = node.exceptionType;
|
| Type2 exceptionType;
|
| if (exceptionTypeName == null) {
|
| @@ -12970,6 +13660,17 @@ class TypeResolverVisitor extends ScopedVisitor {
|
|
|
| Object visitDefaultFormalParameter(DefaultFormalParameter node) {
|
| super.visitDefaultFormalParameter(node);
|
| + // Expression defaultValue = node.getDefaultValue();
|
| + // if (defaultValue != null) {
|
| + // Type valueType = getType(defaultValue);
|
| + // Type parameterType = getType(node.getParameter());
|
| + // if (!valueType.isAssignableTo(parameterType)) {
|
| + // TODO(brianwilkerson) Determine whether this is really an error. I can't find in the spec
|
| + // anything that says it is, but a side comment from Gilad states that it should be a static
|
| + // warning.
|
| + // resolver.reportError(ResolverErrorCode.?, defaultValue);
|
| + // }
|
| + // }
|
| return null;
|
| }
|
|
|
| @@ -12983,6 +13684,7 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| Type2 type;
|
| TypeName typeName = node.type;
|
| if (typeName == null) {
|
| + // TODO(brianwilkerson) Find the field's declaration and use it's type.
|
| type = _dynamicType;
|
| } else {
|
| type = getType3(typeName);
|
| @@ -13079,6 +13781,10 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| TypeArgumentList argumentList = node.typeArguments;
|
| Element element = nameScope.lookup(typeName, definingLibrary);
|
| if (element == null) {
|
| + //
|
| + // Check to see whether the type name is either 'dynamic' or 'void', neither of which are in
|
| + // the name scope and hence will not be found by normal means.
|
| + //
|
| if (typeName.name == this._dynamicType.name) {
|
| setElement(typeName, this._dynamicType.element);
|
| if (argumentList != null) {
|
| @@ -13089,12 +13795,17 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| }
|
| VoidTypeImpl voidType = VoidTypeImpl.instance;
|
| if (typeName.name == voidType.name) {
|
| + // There is no element for 'void'.
|
| if (argumentList != null) {
|
| }
|
| typeName.staticType = voidType;
|
| node.type = voidType;
|
| return null;
|
| }
|
| + //
|
| + // If not, the look to see whether we might have created the wrong AST structure for a
|
| + // constructor name. If so, fix the AST structure and then proceed.
|
| + //
|
| ASTNode parent = node.parent;
|
| if (typeName is PrefixedIdentifier && parent is ConstructorName && argumentList == null) {
|
| ConstructorName name = parent;
|
| @@ -13104,13 +13815,22 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| element = nameScope.lookup(prefix, definingLibrary);
|
| if (element is PrefixElement) {
|
| if (parent.parent is InstanceCreationExpression && (parent.parent as InstanceCreationExpression).isConst) {
|
| + // If, if this is a const expression, then generate a
|
| + // CompileTimeErrorCode.CONST_WITH_NON_TYPE error.
|
| reportError7(CompileTimeErrorCode.CONST_WITH_NON_TYPE, prefixedIdentifier.identifier, [prefixedIdentifier.identifier.name]);
|
| } else {
|
| + // Else, if this expression is a new expression, report a NEW_WITH_NON_TYPE warning.
|
| reportError7(StaticWarningCode.NEW_WITH_NON_TYPE, prefixedIdentifier.identifier, [prefixedIdentifier.identifier.name]);
|
| }
|
| setElement(prefix, element);
|
| return null;
|
| } else if (element != null) {
|
| + //
|
| + // Rewrite the constructor name. The parser, when it sees a constructor named "a.b",
|
| + // cannot tell whether "a" is a prefix and "b" is a class name, or whether "a" is a
|
| + // class name and "b" is a constructor name. It arbitrarily chooses the former, but
|
| + // in this case was wrong.
|
| + //
|
| name.name = prefixedIdentifier.identifier;
|
| name.period = prefixedIdentifier.period;
|
| node.name = prefix;
|
| @@ -13119,6 +13839,7 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| }
|
| }
|
| }
|
| + // check element
|
| bool elementValid = element is! MultiplyDefinedElement;
|
| if (elementValid && element is! ClassElement && isTypeNameInInstanceCreationExpression(node)) {
|
| SimpleIdentifier typeNameSimple = getTypeSimpleIdentifier(typeName);
|
| @@ -13138,6 +13859,10 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| }
|
| }
|
| if (elementValid && element == null) {
|
| + // We couldn't resolve the type name.
|
| + // TODO(jwren) Consider moving the check for CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE
|
| + // from the ErrorVerifier, so that we don't have two errors on a built in identifier being
|
| + // used as a class name. See CompileTimeErrorCodeTest.test_builtInIdentifierAsType().
|
| SimpleIdentifier typeNameSimple = getTypeSimpleIdentifier(typeName);
|
| RedirectingConstructorKind redirectingConstructorKind;
|
| if (isBuiltInIdentifier(node) && isTypeAnnotation(node)) {
|
| @@ -13189,6 +13914,7 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| node.type = type;
|
| }
|
| } else {
|
| + // The name does not represent a type.
|
| RedirectingConstructorKind redirectingConstructorKind;
|
| if (isTypeNameInCatchClause(node)) {
|
| reportError7(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, typeName, [typeName.name]);
|
| @@ -13234,6 +13960,11 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| }
|
| argumentCount = typeArguments.length;
|
| if (argumentCount < parameterCount) {
|
| + //
|
| + // If there were too many arguments, we already handled it by not adding the values of the
|
| + // extra arguments to the list. If there are too few, we handle it by adding 'dynamic'
|
| + // enough times to make the count equal.
|
| + //
|
| for (int i = argumentCount; i < parameterCount; i++) {
|
| typeArguments.add(this._dynamicType);
|
| }
|
| @@ -13247,6 +13978,9 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| } else {
|
| }
|
| } else {
|
| + //
|
| + // Check for the case where there are no type arguments given for a parameterized type.
|
| + //
|
| List<Type2> parameters = getTypeArguments(type);
|
| int parameterCount = parameters.length;
|
| if (parameterCount > 0) {
|
| @@ -13326,11 +14060,16 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| * @return the class element that represents the class
|
| */
|
| ClassElementImpl getClassElement(SimpleIdentifier identifier) {
|
| + // TODO(brianwilkerson) Seems like we should be using ClassDeclaration.getElement().
|
| if (identifier == null) {
|
| + // TODO(brianwilkerson) Report this
|
| + // Internal error: We should never build a class declaration without a name.
|
| return null;
|
| }
|
| Element element = identifier.staticElement;
|
| if (element is! ClassElementImpl) {
|
| + // TODO(brianwilkerson) Report this
|
| + // Internal error: Failed to create an element for a class declaration.
|
| return null;
|
| }
|
| return element as ClassElementImpl;
|
| @@ -13347,6 +14086,7 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| List<ParameterElement> elements = new List<ParameterElement>();
|
| for (FormalParameter parameter in parameterList.parameters) {
|
| ParameterElement element = parameter.identifier.staticElement as ParameterElement;
|
| + // TODO(brianwilkerson) Understand why the element would be null.
|
| if (element != null) {
|
| elements.add(element);
|
| }
|
| @@ -13572,6 +14312,7 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| if (classElement != null) {
|
| classElement.interfaces = interfaceTypes;
|
| }
|
| + // TODO(brianwilkerson) Move the following checks to ErrorVerifier.
|
| List<TypeName> typeNames = new List.from(interfaces);
|
| List<bool> detectedRepeatOnIndex = new List<bool>.filled(typeNames.length, false);
|
| for (int i = 0; i < detectedRepeatOnIndex.length; i++) {
|
| @@ -13610,6 +14351,7 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| if (type is InterfaceType) {
|
| return type;
|
| }
|
| + // If the type is not an InterfaceType, then visitTypeName() sets the type to be a DynamicTypeImpl
|
| Identifier name = typeName.name;
|
| if (name.name == sc.Keyword.DYNAMIC.syntax) {
|
| reportError7(dynamicTypeError, name, [name.name]);
|
| @@ -13762,9 +14504,11 @@ class VariableResolverVisitor extends ScopedVisitor {
|
| }
|
|
|
| Object visitSimpleIdentifier(SimpleIdentifier node) {
|
| + // Ignore if already resolved - declaration or type.
|
| if (node.staticElement != null) {
|
| return null;
|
| }
|
| + // Ignore if qualified.
|
| ASTNode parent = node.parent;
|
| if (parent is PrefixedIdentifier && identical(parent.identifier, node)) {
|
| return null;
|
| @@ -13781,10 +14525,12 @@ class VariableResolverVisitor extends ScopedVisitor {
|
| if (parent is Label) {
|
| return null;
|
| }
|
| + // Prepare VariableElement.
|
| Element element = nameScope.lookup(node, definingLibrary);
|
| if (element is! VariableElement) {
|
| return null;
|
| }
|
| + // Must be local or parameter.
|
| ElementKind kind = element.kind;
|
| if (identical(kind, ElementKind.LOCAL_VARIABLE)) {
|
| node.staticElement = element;
|
| @@ -13800,6 +14546,7 @@ class VariableResolverVisitor extends ScopedVisitor {
|
| if (node.inSetterContext()) {
|
| ParameterElementImpl parameterImpl = element as ParameterElementImpl;
|
| parameterImpl.markPotentiallyMutatedInScope();
|
| + // If we are in some closure, check if it is not the same as where variable is declared.
|
| if (_enclosingFunction != null && (element.enclosingElement != _enclosingFunction)) {
|
| parameterImpl.markPotentiallyMutatedInClosure();
|
| }
|
| @@ -13919,6 +14666,7 @@ class EnclosedScope extends Scope {
|
| if (element != null) {
|
| return element;
|
| }
|
| + // May be there is a hidden Element.
|
| if (_hasHiddenName) {
|
| Element hiddenElement = _hiddenElements[name];
|
| if (hiddenElement != null) {
|
| @@ -13926,6 +14674,7 @@ class EnclosedScope extends Scope {
|
| return hiddenElement;
|
| }
|
| }
|
| + // Check enclosing scope.
|
| return enclosingScope.lookup3(identifier, name, referencingLibrary);
|
| }
|
| }
|
| @@ -14170,6 +14919,8 @@ class LibraryImportScope extends Scope {
|
| List<Element> conflictingMembers = (foundElement as MultiplyDefinedElementImpl).conflictingElements;
|
| String libName1 = getLibraryName(conflictingMembers[0], "");
|
| String libName2 = getLibraryName(conflictingMembers[1], "");
|
| + // TODO (jwren) Change the error message to include a list of all library names instead of
|
| + // just the first two
|
| errorListener.onError(new AnalysisError.con2(getSource(identifier), identifier.offset, identifier.length, StaticWarningCode.AMBIGUOUS_IMPORT, [foundEltName, libName1, libName2]));
|
| return foundElement;
|
| }
|
| @@ -14242,10 +14993,13 @@ class LibraryImportScope extends Scope {
|
| errorListener.onError(new AnalysisError.con2(getSource(identifier), identifier.offset, identifier.length, StaticWarningCode.CONFLICTING_DART_IMPORT, [name, sdkLibName, otherLibName]));
|
| }
|
| if (to == length) {
|
| + // None of the members were removed
|
| return foundElement;
|
| } else if (to == 1) {
|
| + // All but one member was removed
|
| return conflictingMembers[0];
|
| } else if (to == 0) {
|
| + // All members were removed
|
| AnalysisEngine.instance.logger.logInformation("Multiply defined SDK element: ${foundElement}");
|
| return foundElement;
|
| }
|
| @@ -14274,6 +15028,7 @@ class LibraryScope extends EnclosedScope {
|
|
|
| AnalysisError getErrorForDuplicate(Element existing, Element duplicate) {
|
| if (existing is PrefixElement) {
|
| + // TODO(scheglov) consider providing actual 'nameOffset' from the synthetic accessor
|
| int offset = duplicate.nameOffset;
|
| if (duplicate is PropertyAccessorElement) {
|
| PropertyAccessorElement accessor = duplicate;
|
| @@ -14386,6 +15141,9 @@ class NamespaceBuilder {
|
| Namespace createExportNamespace(ExportElement element) {
|
| LibraryElement exportedLibrary = element.exportedLibrary;
|
| if (exportedLibrary == null) {
|
| + //
|
| + // The exported library will be null if the URI does not reference a valid library.
|
| + //
|
| return Namespace.EMPTY;
|
| }
|
| Map<String, Element> definedNames = createExportMapping(exportedLibrary, new Set<LibraryElement>());
|
| @@ -14410,6 +15168,9 @@ class NamespaceBuilder {
|
| Namespace createImportNamespace(ImportElement element) {
|
| LibraryElement importedLibrary = element.importedLibrary;
|
| if (importedLibrary == null) {
|
| + //
|
| + // The imported library will be null if the URI does not reference a valid library.
|
| + //
|
| return Namespace.EMPTY;
|
| }
|
| Map<String, Element> definedNames = createExportMapping(importedLibrary, new Set<LibraryElement>());
|
| @@ -14506,6 +15267,7 @@ class NamespaceBuilder {
|
| } else if (combinator is ShowElementCombinator) {
|
| definedNames = show(definedNames, combinator.shownNames);
|
| } else {
|
| + // Internal error.
|
| AnalysisEngine.instance.logger.logError("Unknown type of combinator: ${combinator.runtimeType.toString()}");
|
| }
|
| }
|
| @@ -14547,6 +15309,9 @@ class NamespaceBuilder {
|
| for (ExportElement element in library.exports) {
|
| LibraryElement exportedLibrary = element.exportedLibrary;
|
| if (exportedLibrary != null && !visitedElements.contains(exportedLibrary)) {
|
| + //
|
| + // The exported library will be null if the URI does not reference a valid library.
|
| + //
|
| Map<String, Element> exportedNames = createExportMapping(exportedLibrary, visitedElements);
|
| exportedNames = apply(exportedNames, element.combinators);
|
| addAll(definedNames, exportedNames);
|
| @@ -14707,6 +15472,9 @@ abstract class Scope {
|
| * @return the error code used to report duplicate names within a scope
|
| */
|
| AnalysisError getErrorForDuplicate(Element existing, Element duplicate) {
|
| + // TODO(brianwilkerson) Customize the error message based on the types of elements that share
|
| + // the same name.
|
| + // TODO(jwren) There are 4 error codes for duplicate, but only 1 is being generated.
|
| Source source = duplicate.source;
|
| return new AnalysisError.con2(source, duplicate.nameOffset, duplicate.displayName.length, CompileTimeErrorCode.DUPLICATE_DEFINITION, [existing.displayName]);
|
| }
|
| @@ -14936,18 +15704,22 @@ class ConstantVerifier extends RecursiveASTVisitor<Object> {
|
|
|
| Object visitAnnotation(Annotation node) {
|
| super.visitAnnotation(node);
|
| + // check annotation creation
|
| Element element = node.element;
|
| if (element is ConstructorElement) {
|
| ConstructorElement constructorElement = element;
|
| + // should 'const' constructor
|
| if (!constructorElement.isConst) {
|
| _errorReporter.reportError3(CompileTimeErrorCode.NON_CONSTANT_ANNOTATION_CONSTRUCTOR, node, []);
|
| return null;
|
| }
|
| + // should have arguments
|
| ArgumentList argumentList = node.arguments;
|
| if (argumentList == null) {
|
| _errorReporter.reportError3(CompileTimeErrorCode.NO_ANNOTATION_CONSTRUCTOR_ARGUMENTS, node, []);
|
| return null;
|
| }
|
| + // arguments should be constants
|
| validateConstantArguments(argumentList);
|
| }
|
| return null;
|
| @@ -15042,6 +15814,11 @@ class ConstantVerifier extends RecursiveASTVisitor<Object> {
|
| VariableElementImpl element = node.element as VariableElementImpl;
|
| EvaluationResultImpl result = element.evaluationResult;
|
| if (result == null) {
|
| + //
|
| + // Normally we don't need to visit const variable declarations because we have already
|
| + // computed their values. But if we missed it for some reason, this gives us a second
|
| + // chance.
|
| + //
|
| result = validate(initializer, CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE);
|
| element.evaluationResult = result;
|
| } else if (result is ErrorResult) {
|
| @@ -15510,6 +16287,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| checkForRecursiveInterfaceInheritance(_enclosingClass);
|
| }
|
| }
|
| + // initialize initialFieldElementsMap
|
| ClassElement classElement = node.element;
|
| if (classElement != null) {
|
| List<FieldElement> fieldElements = classElement.fields;
|
| @@ -15674,6 +16452,8 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
|
|
| Object visitFunctionExpression(FunctionExpression node) {
|
| + // If this function expression is wrapped in a function declaration, don't change the
|
| + // enclosingFunction field.
|
| if (node.parent is! FunctionDeclaration) {
|
| ExecutableElement outerFunction = _enclosingFunction;
|
| try {
|
| @@ -15833,6 +16613,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
|
|
| Object visitNativeClause(NativeClause node) {
|
| + // TODO(brianwilkerson) Figure out the right rule for when 'native' is allowed.
|
| if (!_isInSystemLibrary) {
|
| _errorReporter.reportError3(ParserErrorCode.NATIVE_CLAUSE_IN_NON_SDK_CODE, node, []);
|
| }
|
| @@ -15964,8 +16745,11 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| Object visitVariableDeclaration(VariableDeclaration node) {
|
| SimpleIdentifier nameNode = node.name;
|
| Expression initializerNode = node.initializer;
|
| + // do checks
|
| checkForInvalidAssignment2(nameNode, initializerNode);
|
| + // visit name
|
| nameNode.accept(this);
|
| + // visit initializer
|
| String name = nameNode.name;
|
| _namesForReferenceToDeclaredVariableInInitializer.add(name);
|
| _isInInstanceVariableInitializer = _isInInstanceVariableDeclaration;
|
| @@ -15977,6 +16761,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| _isInInstanceVariableInitializer = false;
|
| _namesForReferenceToDeclaredVariableInInitializer.remove(name);
|
| }
|
| + // done
|
| return null;
|
| }
|
|
|
| @@ -16000,13 +16785,16 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticTypeWarningCode#EXPECTED_TWO_MAP_TYPE_ARGUMENTS
|
| */
|
| bool checkExpectedTwoMapTypeArguments(TypeArgumentList typeArguments) {
|
| + // has type arguments
|
| if (typeArguments == null) {
|
| return false;
|
| }
|
| + // check number of type arguments
|
| int num = typeArguments.arguments.length;
|
| if (num == 2) {
|
| return false;
|
| }
|
| + // report problem
|
| _errorReporter.reportError3(StaticTypeWarningCode.EXPECTED_TWO_MAP_TYPE_ARGUMENTS, typeArguments, [num]);
|
| return true;
|
| }
|
| @@ -16025,11 +16813,13 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (node.factoryKeyword != null || node.redirectedConstructor != null || node.externalKeyword != null) {
|
| return false;
|
| }
|
| + // Ignore if native class.
|
| if (_isInNativeClass) {
|
| return false;
|
| }
|
| bool foundError = false;
|
| Map<FieldElement, INIT_STATE> fieldElementsMap = new Map<FieldElement, INIT_STATE>.from(_initialFieldElementsMap);
|
| + // Visit all of the field formal parameters
|
| NodeList<FormalParameter> formalParameters = node.parameters.parameters;
|
| for (FormalParameter formalParameter in formalParameters) {
|
| FormalParameter parameter = formalParameter;
|
| @@ -16054,6 +16844,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| }
|
| }
|
| + // Visit all of the initializers
|
| NodeList<ConstructorInitializer> initializers = node.initializers;
|
| for (ConstructorInitializer constructorInitializer in initializers) {
|
| if (constructorInitializer is RedirectingConstructorInvocation) {
|
| @@ -16083,6 +16874,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| }
|
| }
|
| + // Visit all of the states in the map to ensure that none were never initialized.
|
| for (MapEntry<FieldElement, INIT_STATE> entry in getMapEntrySet(fieldElementsMap)) {
|
| if (identical(entry.getValue(), INIT_STATE.NOT_INIT)) {
|
| FieldElement fieldElement = entry.getKey();
|
| @@ -16128,6 +16920,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| isGetter = accessorElement.isGetter;
|
| isSetter = accessorElement.isSetter;
|
| }
|
| + // SWC.INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC
|
| if (overriddenExecutable == null) {
|
| if (!isGetter && !isSetter && !executableElement.isOperator) {
|
| Set<ClassElement> visitedClasses = new Set<ClassElement>();
|
| @@ -16136,14 +16929,18 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| while (superclassElement != null && !visitedClasses.contains(superclassElement)) {
|
| visitedClasses.add(superclassElement);
|
| LibraryElement superclassLibrary = superclassElement.library;
|
| + // Check fields.
|
| List<FieldElement> fieldElts = superclassElement.fields;
|
| for (FieldElement fieldElt in fieldElts) {
|
| + // We need the same name.
|
| if (fieldElt.name != executableElementName) {
|
| continue;
|
| }
|
| + // Ignore if private in a different library - cannot collide.
|
| if (executableElementPrivate && _currentLibrary != superclassLibrary) {
|
| continue;
|
| }
|
| + // instance vs. static
|
| if (fieldElt.isStatic) {
|
| _errorReporter.reportError3(StaticWarningCode.INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC, errorNameTarget, [
|
| executableElementName,
|
| @@ -16151,14 +16948,18 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| return true;
|
| }
|
| }
|
| + // Check methods.
|
| List<MethodElement> methodElements = superclassElement.methods;
|
| for (MethodElement methodElement in methodElements) {
|
| + // We need the same name.
|
| if (methodElement.name != executableElementName) {
|
| continue;
|
| }
|
| + // Ignore if private in a different library - cannot collide.
|
| if (executableElementPrivate && _currentLibrary != superclassLibrary) {
|
| continue;
|
| }
|
| + // instance vs. static
|
| if (methodElement.isStatic) {
|
| _errorReporter.reportError3(StaticWarningCode.INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC, errorNameTarget, [
|
| executableElementName,
|
| @@ -16187,6 +16988,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| List<Type2> overriddenPositionalPT = overriddenFT.optionalParameterTypes;
|
| Map<String, Type2> overridingNamedPT = overridingFT.namedParameterTypes;
|
| Map<String, Type2> overriddenNamedPT = overriddenFT.namedParameterTypes;
|
| + // CTEC.INVALID_OVERRIDE_REQUIRED, CTEC.INVALID_OVERRIDE_POSITIONAL and CTEC.INVALID_OVERRIDE_NAMED
|
| if (overridingNormalPT.length > overriddenNormalPT.length) {
|
| _errorReporter.reportError3(StaticWarningCode.INVALID_OVERRIDE_REQUIRED, errorNameTarget, [
|
| overriddenNormalPT.length,
|
| @@ -16199,17 +17001,22 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| overriddenExecutable.enclosingElement.displayName]);
|
| return true;
|
| }
|
| + // For each named parameter in the overridden method, verify that there is the same name in
|
| + // the overriding method, and in the same order.
|
| Set<String> overridingParameterNameSet = overridingNamedPT.keys.toSet();
|
| JavaIterator<String> overriddenParameterNameIterator = new JavaIterator(overriddenNamedPT.keys.toSet());
|
| while (overriddenParameterNameIterator.hasNext) {
|
| String overriddenParamName = overriddenParameterNameIterator.next();
|
| if (!overridingParameterNameSet.contains(overriddenParamName)) {
|
| + // The overridden method expected the overriding method to have overridingParamName,
|
| + // but it does not.
|
| _errorReporter.reportError3(StaticWarningCode.INVALID_OVERRIDE_NAMED, errorNameTarget, [
|
| overriddenParamName,
|
| overriddenExecutable.enclosingElement.displayName]);
|
| return true;
|
| }
|
| }
|
| + // SWC.INVALID_METHOD_OVERRIDE_RETURN_TYPE
|
| if (overriddenFTReturnType != VoidTypeImpl.instance && !overridingFTReturnType.isAssignableTo(overriddenFTReturnType)) {
|
| _errorReporter.reportError3(!isGetter ? StaticWarningCode.INVALID_METHOD_OVERRIDE_RETURN_TYPE : StaticWarningCode.INVALID_GETTER_OVERRIDE_RETURN_TYPE, errorNameTarget, [
|
| overridingFTReturnType.displayName,
|
| @@ -16217,6 +17024,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| overriddenExecutable.enclosingElement.displayName]);
|
| return true;
|
| }
|
| + // SWC.INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE
|
| if (parameterLocations == null) {
|
| return false;
|
| }
|
| @@ -16231,6 +17039,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| parameterIndex++;
|
| }
|
| + // SWC.INVALID_METHOD_OVERRIDE_OPTIONAL_PARAM_TYPE
|
| for (int i = 0; i < overriddenPositionalPT.length; i++) {
|
| if (!overridingPositionalPT[i].isAssignableTo(overriddenPositionalPT[i])) {
|
| _errorReporter.reportError3(StaticWarningCode.INVALID_METHOD_OVERRIDE_OPTIONAL_PARAM_TYPE, parameterLocations[parameterIndex], [
|
| @@ -16241,14 +17050,18 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| parameterIndex++;
|
| }
|
| + // SWC.INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE & SWC.INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES
|
| JavaIterator<MapEntry<String, Type2>> overriddenNamedPTIterator = new JavaIterator(getMapEntrySet(overriddenNamedPT));
|
| while (overriddenNamedPTIterator.hasNext) {
|
| MapEntry<String, Type2> overriddenNamedPTEntry = overriddenNamedPTIterator.next();
|
| Type2 overridingType = overridingNamedPT[overriddenNamedPTEntry.getKey()];
|
| if (overridingType == null) {
|
| + // Error, this is never reached- INVALID_OVERRIDE_NAMED would have been created above if
|
| + // this could be reached.
|
| continue;
|
| }
|
| if (!overriddenNamedPTEntry.getValue().isAssignableTo(overridingType)) {
|
| + // lookup the parameter for the error to select
|
| ParameterElement parameterToSelect = null;
|
| ASTNode parameterLocationToSelect = null;
|
| for (int i = 0; i < parameters.length; i++) {
|
| @@ -16268,6 +17081,12 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| }
|
| }
|
| + // SWC.INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES
|
| + //
|
| + // Create three arrays: an array of the optional parameter ASTs (FormalParameters), an array of
|
| + // the optional parameters elements from our method, and finally an array of the optional
|
| + // parameter elements from the method we are overriding.
|
| + //
|
| bool foundError = false;
|
| List<ASTNode> formalParameters = new List<ASTNode>();
|
| List<ParameterElementImpl> parameterElts = new List<ParameterElementImpl>();
|
| @@ -16287,11 +17106,17 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| }
|
| }
|
| + //
|
| + // Next compare the list of optional parameter elements to the list of overridden optional
|
| + // parameter elements.
|
| + //
|
| if (parameterElts.length > 0) {
|
| if (identical(parameterElts[0].parameterKind, ParameterKind.NAMED)) {
|
| + // Named parameters, consider the names when matching the parameterElts to the overriddenParameterElts
|
| for (int i = 0; i < parameterElts.length; i++) {
|
| ParameterElementImpl parameterElt = parameterElts[i];
|
| EvaluationResultImpl result = parameterElt.evaluationResult;
|
| + // TODO (jwren) Ignore Object types, see Dart bug 11287
|
| if (isUserDefinedObject(result)) {
|
| continue;
|
| }
|
| @@ -16315,9 +17140,11 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| }
|
| } else {
|
| + // Positional parameters, consider the positions when matching the parameterElts to the overriddenParameterElts
|
| for (int i = 0; i < parameterElts.length && i < overriddenParameterElts.length; i++) {
|
| ParameterElementImpl parameterElt = parameterElts[i];
|
| EvaluationResultImpl result = parameterElt.evaluationResult;
|
| + // TODO (jwren) Ignore Object types, see Dart bug 11287
|
| if (isUserDefinedObject(result)) {
|
| continue;
|
| }
|
| @@ -16435,15 +17262,27 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticWarningCode#REDIRECT_TO_MISSING_CONSTRUCTOR
|
| */
|
| bool checkForAllRedirectConstructorErrorCodes(ConstructorDeclaration node) {
|
| + //
|
| + // Prepare redirected constructor node
|
| + //
|
| ConstructorName redirectedConstructor = node.redirectedConstructor;
|
| if (redirectedConstructor == null) {
|
| return false;
|
| }
|
| + //
|
| + // Prepare redirected constructor type
|
| + //
|
| ConstructorElement redirectedElement = redirectedConstructor.staticElement;
|
| if (redirectedElement == null) {
|
| + //
|
| + // If the element is null, we check for the REDIRECT_TO_MISSING_CONSTRUCTOR case
|
| + //
|
| TypeName constructorTypeName = redirectedConstructor.type;
|
| Type2 redirectedType = constructorTypeName.type;
|
| if (redirectedType != null && redirectedType.element != null && !redirectedType.isDynamic) {
|
| + //
|
| + // Prepare the constructor name
|
| + //
|
| String constructorStrName = constructorTypeName.name.name;
|
| if (redirectedConstructor.name != null) {
|
| constructorStrName += ".${redirectedConstructor.name.name}";
|
| @@ -16456,12 +17295,18 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| FunctionType redirectedType = redirectedElement.type;
|
| Type2 redirectedReturnType = redirectedType.returnType;
|
| + //
|
| + // Report specific problem when return type is incompatible
|
| + //
|
| FunctionType constructorType = node.element.type;
|
| Type2 constructorReturnType = constructorType.returnType;
|
| if (!redirectedReturnType.isAssignableTo(constructorReturnType)) {
|
| _errorReporter.reportError3(StaticWarningCode.REDIRECT_TO_INVALID_RETURN_TYPE, redirectedConstructor, [redirectedReturnType, constructorReturnType]);
|
| return true;
|
| }
|
| + //
|
| + // Check parameters
|
| + //
|
| if (!redirectedType.isSubtypeOf(constructorType)) {
|
| _errorReporter.reportError3(StaticWarningCode.REDIRECT_TO_INVALID_FUNCTION_TYPE, redirectedConstructor, [redirectedType, constructorType]);
|
| return true;
|
| @@ -16490,6 +17335,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| FunctionType functionType = _enclosingFunction == null ? null : _enclosingFunction.type;
|
| Type2 expectedReturnType = functionType == null ? DynamicTypeImpl.instance : functionType.returnType;
|
| Expression returnExpression = node.expression;
|
| + // RETURN_IN_GENERATIVE_CONSTRUCTOR
|
| bool isGenerativeConstructor = _enclosingFunction is ConstructorElement && !(_enclosingFunction as ConstructorElement).isFactory;
|
| if (isGenerativeConstructor) {
|
| if (returnExpression == null) {
|
| @@ -16498,6 +17344,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| _errorReporter.reportError3(CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR, returnExpression, []);
|
| return true;
|
| }
|
| + // RETURN_WITHOUT_VALUE
|
| if (returnExpression == null) {
|
| if (VoidTypeImpl.instance.isAssignableTo(expectedReturnType)) {
|
| return false;
|
| @@ -16505,6 +17352,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| _errorReporter.reportError3(StaticWarningCode.RETURN_WITHOUT_VALUE, node, []);
|
| return true;
|
| }
|
| + // RETURN_OF_INVALID_TYPE
|
| return checkForReturnOfInvalidType(returnExpression, expectedReturnType);
|
| }
|
|
|
| @@ -16517,14 +17365,17 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#AMBIGUOUS_EXPORT
|
| */
|
| bool checkForAmbiguousExport(ExportDirective node) {
|
| + // prepare ExportElement
|
| if (node.element is! ExportElement) {
|
| return false;
|
| }
|
| ExportElement exportElement = node.element as ExportElement;
|
| + // prepare exported library
|
| LibraryElement exportedLibrary = exportElement.exportedLibrary;
|
| if (exportedLibrary == null) {
|
| return false;
|
| }
|
| + // check exported names
|
| Namespace namespace = new NamespaceBuilder().createExportNamespace(exportElement);
|
| Map<String, Element> definedNames = namespace.definedNames;
|
| for (MapEntry<String, Element> definedEntry in getMapEntrySet(definedNames)) {
|
| @@ -16576,6 +17427,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| for (Expression argument in argumentList.arguments) {
|
| problemReported = javaBooleanOr(problemReported, checkForArgumentTypeNotAssignable2(argument));
|
| }
|
| + // done
|
| return problemReported;
|
| }
|
|
|
| @@ -16621,6 +17473,9 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticWarningCode#ARGUMENT_TYPE_NOT_ASSIGNABLE
|
| */
|
| bool checkForArgumentTypeNotAssignable4(Expression expression, Type2 expectedStaticType, Type2 actualStaticType, Type2 expectedPropagatedType, Type2 actualPropagatedType, ErrorCode errorCode) {
|
| + //
|
| + // Test static type information
|
| + //
|
| if (actualStaticType == null || expectedStaticType == null) {
|
| return false;
|
| }
|
| @@ -16655,6 +17510,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticWarningCode#ASSIGNMENT_TO_METHOD
|
| */
|
| bool checkForAssignmentToFinal2(Expression expression) {
|
| + // prepare element
|
| Element element = null;
|
| if (expression is Identifier) {
|
| element = expression.staticElement;
|
| @@ -16662,6 +17518,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (expression is PropertyAccess) {
|
| element = expression.propertyName.staticElement;
|
| }
|
| + // check if element is assignable
|
| if (element is PropertyAccessorElement) {
|
| PropertyAccessorElement accessor = element as PropertyAccessorElement;
|
| element = accessor.variable;
|
| @@ -16720,6 +17577,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| bool checkForCaseBlockNotTerminated(SwitchCase node) {
|
| NodeList<Statement> statements = node.statements;
|
| if (statements.isEmpty) {
|
| + // fall-through without statements at all
|
| ASTNode parent = node.parent;
|
| if (parent is SwitchStatement) {
|
| SwitchStatement switchStatement = parent;
|
| @@ -16731,9 +17589,11 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| } else {
|
| Statement statement = statements[statements.length - 1];
|
| + // terminated with statement
|
| if (statement is BreakStatement || statement is ContinueStatement || statement is ReturnStatement) {
|
| return false;
|
| }
|
| + // terminated with 'throw' expression
|
| if (statement is ExpressionStatement) {
|
| Expression expression = statement.expression;
|
| if (expression is ThrowExpression) {
|
| @@ -16741,6 +17601,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| }
|
| }
|
| + // report error
|
| _errorReporter.reportError6(StaticWarningCode.CASE_BLOCK_NOT_TERMINATED, node.keyword, []);
|
| return true;
|
| }
|
| @@ -16779,6 +17640,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (!implementsEqualsWhenNotAllowed(type)) {
|
| return false;
|
| }
|
| + // report error
|
| _errorReporter.reportError6(CompileTimeErrorCode.CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS, node.keyword, [type.displayName]);
|
| return true;
|
| }
|
| @@ -16816,6 +17678,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| SimpleIdentifier constructorName = node.name;
|
| String name = constructorElement.name;
|
| ClassElement classElement = constructorElement.enclosingElement;
|
| + // constructors
|
| List<ConstructorElement> constructors = classElement.constructors;
|
| for (ConstructorElement otherConstructor in constructors) {
|
| if (identical(otherConstructor, constructorElement)) {
|
| @@ -16830,12 +17693,15 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| return true;
|
| }
|
| }
|
| + // conflict with class member
|
| if (constructorName != null && constructorElement != null && !constructorName.isSynthetic) {
|
| + // fields
|
| FieldElement field = classElement.getField(name);
|
| if (field != null) {
|
| _errorReporter.reportError3(CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_NAME_AND_FIELD, node, [name]);
|
| return true;
|
| }
|
| + // methods
|
| MethodElement method = classElement.getMethod(name);
|
| if (method != null) {
|
| _errorReporter.reportError3(CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_NAME_AND_METHOD, node, [name]);
|
| @@ -16858,33 +17724,40 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| return false;
|
| }
|
| bool hasProblem = false;
|
| + // method declared in the enclosing class vs. inherited getter
|
| for (MethodElement method in _enclosingClass.methods) {
|
| String name = method.name;
|
| + // find inherited property accessor (and can be only getter)
|
| ExecutableElement inherited = _inheritanceManager.lookupInheritance(_enclosingClass, name);
|
| if (inherited is! PropertyAccessorElement) {
|
| continue;
|
| }
|
| + // report problem
|
| hasProblem = true;
|
| _errorReporter.reportError5(CompileTimeErrorCode.CONFLICTING_GETTER_AND_METHOD, method.nameOffset, name.length, [
|
| _enclosingClass.displayName,
|
| inherited.enclosingElement.displayName,
|
| name]);
|
| }
|
| + // getter declared in the enclosing class vs. inherited method
|
| for (PropertyAccessorElement accessor in _enclosingClass.accessors) {
|
| if (!accessor.isGetter) {
|
| continue;
|
| }
|
| String name = accessor.name;
|
| + // find inherited method
|
| ExecutableElement inherited = _inheritanceManager.lookupInheritance(_enclosingClass, name);
|
| if (inherited is! MethodElement) {
|
| continue;
|
| }
|
| + // report problem
|
| hasProblem = true;
|
| _errorReporter.reportError5(CompileTimeErrorCode.CONFLICTING_METHOD_AND_GETTER, accessor.nameOffset, name.length, [
|
| _enclosingClass.displayName,
|
| inherited.enclosingElement.displayName,
|
| name]);
|
| }
|
| + // done
|
| return hasProblem;
|
| }
|
|
|
| @@ -16903,16 +17776,21 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| return false;
|
| }
|
| InterfaceType enclosingType = _enclosingClass.type;
|
| + // check every accessor
|
| bool hasProblem = false;
|
| for (PropertyAccessorElement accessor in _enclosingClass.accessors) {
|
| + // we analyze instance accessors here
|
| if (accessor.isStatic) {
|
| continue;
|
| }
|
| + // prepare accessor properties
|
| String name = accessor.displayName;
|
| bool getter = accessor.isGetter;
|
| + // if non-final variable, ignore setter - we alreay reported problem for getter
|
| if (accessor.isSetter && accessor.isSynthetic) {
|
| continue;
|
| }
|
| + // try to find super element
|
| ExecutableElement superElement;
|
| superElement = enclosingType.lookUpGetterInSuperclass(name, _currentLibrary);
|
| if (superElement == null) {
|
| @@ -16924,11 +17802,14 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (superElement == null) {
|
| continue;
|
| }
|
| + // OK, not static
|
| if (!superElement.isStatic) {
|
| continue;
|
| }
|
| + // prepare "super" type to report its name
|
| ClassElement superElementClass = superElement.enclosingElement as ClassElement;
|
| InterfaceType superElementType = superElementClass.type;
|
| + // report problem
|
| hasProblem = true;
|
| if (getter) {
|
| _errorReporter.reportError4(StaticWarningCode.CONFLICTING_INSTANCE_GETTER_AND_SUPERCLASS_MEMBER, accessor, [superElementType.displayName]);
|
| @@ -16936,6 +17817,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| _errorReporter.reportError4(StaticWarningCode.CONFLICTING_INSTANCE_SETTER_AND_SUPERCLASS_MEMBER, accessor, [superElementType.displayName]);
|
| }
|
| }
|
| + // done
|
| return hasProblem;
|
| }
|
|
|
| @@ -16951,18 +17833,22 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (node.isStatic) {
|
| return false;
|
| }
|
| + // prepare name
|
| SimpleIdentifier nameNode = node.name;
|
| if (nameNode == null) {
|
| return false;
|
| }
|
| String name = nameNode.name;
|
| + // ensure that we have enclosing class
|
| if (_enclosingClass == null) {
|
| return false;
|
| }
|
| + // try to find setter
|
| ExecutableElement setter = _inheritanceManager.lookupMember(_enclosingClass, "${name}=");
|
| if (setter == null) {
|
| return false;
|
| }
|
| + // report problem
|
| _errorReporter.reportError3(StaticWarningCode.CONFLICTING_INSTANCE_METHOD_SETTER, nameNode, [
|
| _enclosingClass.displayName,
|
| name,
|
| @@ -16982,24 +17868,30 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (!node.isStatic) {
|
| return false;
|
| }
|
| + // prepare name
|
| SimpleIdentifier nameNode = node.name;
|
| if (nameNode == null) {
|
| return false;
|
| }
|
| String name = nameNode.name;
|
| + // prepare enclosing type
|
| if (_enclosingClass == null) {
|
| return false;
|
| }
|
| InterfaceType enclosingType = _enclosingClass.type;
|
| + // try to find setter
|
| ExecutableElement setter = enclosingType.lookUpSetter(name, _currentLibrary);
|
| if (setter == null) {
|
| return false;
|
| }
|
| + // OK, also static
|
| if (setter.isStatic) {
|
| return false;
|
| }
|
| + // prepare "setter" type to report its name
|
| ClassElement setterClass = setter.enclosingElement as ClassElement;
|
| InterfaceType setterType = setterClass.type;
|
| + // report problem
|
| _errorReporter.reportError3(StaticWarningCode.CONFLICTING_STATIC_GETTER_AND_INSTANCE_SETTER, nameNode, [setterType.displayName]);
|
| return true;
|
| }
|
| @@ -17016,15 +17908,18 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (!node.isStatic) {
|
| return false;
|
| }
|
| + // prepare name
|
| SimpleIdentifier nameNode = node.name;
|
| if (nameNode == null) {
|
| return false;
|
| }
|
| String name = nameNode.name;
|
| + // prepare enclosing type
|
| if (_enclosingClass == null) {
|
| return false;
|
| }
|
| InterfaceType enclosingType = _enclosingClass.type;
|
| + // try to find member
|
| ExecutableElement member;
|
| member = enclosingType.lookUpMethod(name, _currentLibrary);
|
| if (member == null) {
|
| @@ -17036,11 +17931,14 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (member == null) {
|
| return false;
|
| }
|
| + // OK, also static
|
| if (member.isStatic) {
|
| return false;
|
| }
|
| + // prepare "member" type to report its name
|
| ClassElement memberClass = member.enclosingElement as ClassElement;
|
| InterfaceType memberType = memberClass.type;
|
| + // report problem
|
| _errorReporter.reportError3(StaticWarningCode.CONFLICTING_STATIC_SETTER_AND_INSTANCE_MEMBER, nameNode, [memberType.displayName]);
|
| return true;
|
| }
|
| @@ -17057,10 +17955,12 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| bool problemReported = false;
|
| for (TypeParameterElement typeParameter in _enclosingClass.typeParameters) {
|
| String name = typeParameter.name;
|
| + // name is same as the name of the enclosing class
|
| if (_enclosingClass.name == name) {
|
| _errorReporter.reportError5(CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_CLASS, typeParameter.nameOffset, name.length, [name]);
|
| problemReported = true;
|
| }
|
| + // check members
|
| if (_enclosingClass.getMethod(name) != null || _enclosingClass.getGetter(name) != null || _enclosingClass.getSetter(name) != null) {
|
| _errorReporter.reportError5(CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER, typeParameter.nameOffset, name.length, [name]);
|
| problemReported = true;
|
| @@ -17081,9 +17981,11 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (!_isEnclosingConstructorConst) {
|
| return false;
|
| }
|
| + // OK, const factory, checked elsewhere
|
| if (node.factoryKeyword != null) {
|
| return false;
|
| }
|
| + // try to find and check super constructor invocation
|
| for (ConstructorInitializer initializer in node.initializers) {
|
| if (initializer is SuperConstructorInvocation) {
|
| SuperConstructorInvocation superInvocation = initializer;
|
| @@ -17095,6 +17997,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| return true;
|
| }
|
| }
|
| + // no explicit super constructor invocation, check default constructor
|
| InterfaceType supertype = _enclosingClass.supertype;
|
| if (supertype == null) {
|
| return false;
|
| @@ -17109,6 +18012,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (unnamedConstructor.isConst) {
|
| return false;
|
| }
|
| + // default constructor is not 'const', report problem
|
| _errorReporter.reportError3(CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER, node, []);
|
| return true;
|
| }
|
| @@ -17125,11 +18029,13 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (!_isEnclosingConstructorConst) {
|
| return false;
|
| }
|
| + // check if there is non-final field
|
| ConstructorElement constructorElement = node.element;
|
| ClassElement classElement = constructorElement.enclosingElement;
|
| if (!classElement.hasNonFinalField()) {
|
| return false;
|
| }
|
| + // report problem
|
| _errorReporter.reportError3(CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD, node, []);
|
| return true;
|
| }
|
| @@ -17178,6 +18084,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (!implementsEqualsWhenNotAllowed(type)) {
|
| return false;
|
| }
|
| + // report error
|
| _errorReporter.reportError3(CompileTimeErrorCode.CONST_MAP_KEY_EXPRESSION_TYPE_IMPLEMENTS_EQUALS, key, [type.displayName]);
|
| return true;
|
| }
|
| @@ -17191,9 +18098,11 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#CONST_MAP_KEY_EXPRESSION_TYPE_IMPLEMENTS_EQUALS
|
| */
|
| bool checkForConstMapKeyExpressionTypeImplementsEquals2(MapLiteral node) {
|
| + // OK, not const.
|
| if (node.constKeyword == null) {
|
| return false;
|
| }
|
| + // Check every map entry.
|
| bool hasProblems = false;
|
| for (MapLiteralEntry entry in node.entries) {
|
| Expression key = entry.key;
|
| @@ -17275,6 +18184,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#CONST_WITH_TYPE_PARAMETERS
|
| */
|
| bool checkForConstWithTypeParameters2(TypeName typeName) {
|
| + // something wrong with AST
|
| if (typeName == null) {
|
| return false;
|
| }
|
| @@ -17282,9 +18192,11 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (name == null) {
|
| return false;
|
| }
|
| + // should not be a type parameter
|
| if (name.staticElement is TypeParameterElement) {
|
| _errorReporter.reportError3(CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS, name, []);
|
| }
|
| + // check type arguments
|
| TypeArgumentList typeArguments = typeName.typeArguments;
|
| if (typeArguments != null) {
|
| bool hasError = false;
|
| @@ -17293,6 +18205,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| return hasError;
|
| }
|
| + // OK
|
| return false;
|
| }
|
|
|
| @@ -17308,18 +18221,22 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT
|
| */
|
| bool checkForConstWithUndefinedConstructor(InstanceCreationExpression node) {
|
| + // OK if resolved
|
| if (node.staticElement != null) {
|
| return false;
|
| }
|
| + // prepare constructor name
|
| ConstructorName constructorName = node.constructorName;
|
| if (constructorName == null) {
|
| return false;
|
| }
|
| + // prepare class name
|
| TypeName type = constructorName.type;
|
| if (type == null) {
|
| return false;
|
| }
|
| Identifier className = type.name;
|
| + // report as named or default constructor absence
|
| SimpleIdentifier name = constructorName.name;
|
| if (name != null) {
|
| _errorReporter.reportError3(CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR, name, [className, name]);
|
| @@ -17361,12 +18278,15 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#DEFAULT_VALUE_IN_FUNCTION_TYPED_PARAMETER
|
| */
|
| bool checkForDefaultValueInFunctionTypedParameter(DefaultFormalParameter node) {
|
| + // OK, not in a function typed parameter.
|
| if (!_isInFunctionTypedFormalParameter) {
|
| return false;
|
| }
|
| + // OK, no default value.
|
| if (node.defaultValue == null) {
|
| return false;
|
| }
|
| + // Report problem.
|
| _errorReporter.reportError3(CompileTimeErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPED_PARAMETER, node, []);
|
| return true;
|
| }
|
| @@ -17407,17 +18327,21 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#DUPLICATE_DEFINITION_INHERITANCE
|
| */
|
| bool checkForDuplicateDefinitionInheritance2(ExecutableElement staticMember) {
|
| + // prepare name
|
| String name = staticMember.name;
|
| if (name == null) {
|
| return false;
|
| }
|
| + // try to find member
|
| ExecutableElement inheritedMember = _inheritanceManager.lookupInheritance(_enclosingClass, name);
|
| if (inheritedMember == null) {
|
| return false;
|
| }
|
| + // OK, also static
|
| if (inheritedMember.isStatic) {
|
| return false;
|
| }
|
| + // report problem
|
| _errorReporter.reportError5(CompileTimeErrorCode.DUPLICATE_DEFINITION_INHERITANCE, staticMember.nameOffset, name.length, [name, inheritedMember.enclosingElement.displayName]);
|
| return true;
|
| }
|
| @@ -17430,14 +18354,17 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticTypeWarningCode#EXPECTED_ONE_LIST_TYPE_ARGUMENTS
|
| */
|
| bool checkForExpectedOneListTypeArgument(ListLiteral node) {
|
| + // prepare type arguments
|
| TypeArgumentList typeArguments = node.typeArguments;
|
| if (typeArguments == null) {
|
| return false;
|
| }
|
| + // check number of type arguments
|
| int num = typeArguments.arguments.length;
|
| if (num == 1) {
|
| return false;
|
| }
|
| + // report problem
|
| _errorReporter.reportError3(StaticTypeWarningCode.EXPECTED_ONE_LIST_TYPE_ARGUMENTS, typeArguments, [num]);
|
| return true;
|
| }
|
| @@ -17450,16 +18377,19 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#EXPORT_DUPLICATED_LIBRARY_NAME
|
| */
|
| bool checkForExportDuplicateLibraryName(ExportDirective node) {
|
| + // prepare import element
|
| Element nodeElement = node.element;
|
| if (nodeElement is! ExportElement) {
|
| return false;
|
| }
|
| ExportElement nodeExportElement = nodeElement as ExportElement;
|
| + // prepare exported library
|
| LibraryElement nodeLibrary = nodeExportElement.exportedLibrary;
|
| if (nodeLibrary == null) {
|
| return false;
|
| }
|
| String name = nodeLibrary.name;
|
| + // check if there is other exported library with the same name
|
| LibraryElement prevLibrary = _nameToExportElement[name];
|
| if (prevLibrary != null) {
|
| if (prevLibrary != nodeLibrary) {
|
| @@ -17472,6 +18402,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| } else {
|
| _nameToExportElement[name] = nodeLibrary;
|
| }
|
| + // OK
|
| return false;
|
| }
|
|
|
| @@ -17487,11 +18418,13 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (_isInSystemLibrary) {
|
| return false;
|
| }
|
| + // prepare export element
|
| Element element = node.element;
|
| if (element is! ExportElement) {
|
| return false;
|
| }
|
| ExportElement exportElement = element as ExportElement;
|
| + // should be private
|
| DartSdk sdk = _currentLibrary.context.sourceFactory.dartSdk;
|
| String uri = exportElement.uri;
|
| SdkLibrary sdkLibrary = sdk.getSdkLibrary(uri);
|
| @@ -17501,6 +18434,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (!sdkLibrary.isInternal) {
|
| return false;
|
| }
|
| + // report problem
|
| _errorReporter.reportError3(CompileTimeErrorCode.EXPORT_INTERNAL_LIBRARY, node, [node.uri]);
|
| return true;
|
| }
|
| @@ -17537,8 +18471,14 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| Type2 superType = typeName.type;
|
| for (InterfaceType disallowedType in _DISALLOWED_TYPES_TO_EXTEND_OR_IMPLEMENT) {
|
| if (superType != null && superType == disallowedType) {
|
| + // if the violating type happens to be 'num', we need to rule out the case where the
|
| + // enclosing class is 'int' or 'double'
|
| if (superType == _typeProvider.numType) {
|
| ASTNode grandParent = typeName.parent.parent;
|
| + // Note: this is a corner case that won't happen often, so adding a field currentClass
|
| + // (see currentFunction) to ErrorVerifier isn't worth if for this case, but if the field
|
| + // currentClass is added, then this message should become a todo to not lookup the
|
| + // grandparent node
|
| if (grandParent is ClassDeclaration) {
|
| ClassElement classElement = grandParent.element;
|
| Type2 classType = classElement.type;
|
| @@ -17547,6 +18487,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| }
|
| }
|
| + // otherwise, report the error
|
| _errorReporter.reportError3(errorCode, typeName, [disallowedType.displayName]);
|
| return true;
|
| }
|
| @@ -17564,16 +18505,20 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticWarningCode#FIELD_INITIALIZER_NOT_ASSIGNABLE
|
| */
|
| bool checkForFieldInitializerNotAssignable(ConstructorFieldInitializer node) {
|
| + // prepare field element
|
| Element fieldNameElement = node.fieldName.staticElement;
|
| if (fieldNameElement is! FieldElement) {
|
| return false;
|
| }
|
| FieldElement fieldElement = fieldNameElement as FieldElement;
|
| + // prepare field type
|
| Type2 fieldType = fieldElement.type;
|
| + // prepare expression type
|
| Expression expression = node.expression;
|
| if (expression == null) {
|
| return false;
|
| }
|
| + // test the static type of the expression
|
| Type2 staticType = getStaticType(expression);
|
| if (staticType == null) {
|
| return false;
|
| @@ -17581,6 +18526,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (staticType.isAssignableTo(fieldType)) {
|
| return false;
|
| }
|
| + // report problem
|
| if (_isEnclosingConstructorConst) {
|
| _errorReporter.reportError3(CompileTimeErrorCode.CONST_FIELD_INITIALIZER_NOT_ASSIGNABLE, expression, [staticType.displayName, fieldType.displayName]);
|
| } else {
|
| @@ -17602,16 +18548,19 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| _errorReporter.reportError3(CompileTimeErrorCode.FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR, node, []);
|
| return true;
|
| }
|
| + // constructor cannot be a factory
|
| if (constructor.factoryKeyword != null) {
|
| _errorReporter.reportError3(CompileTimeErrorCode.FIELD_INITIALIZER_FACTORY_CONSTRUCTOR, node, []);
|
| return true;
|
| }
|
| + // constructor cannot have a redirection
|
| for (ConstructorInitializer initializer in constructor.initializers) {
|
| if (initializer is RedirectingConstructorInvocation) {
|
| _errorReporter.reportError3(CompileTimeErrorCode.FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR, node, []);
|
| return true;
|
| }
|
| }
|
| + // OK
|
| return false;
|
| }
|
|
|
| @@ -17707,28 +18656,34 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (!_isInConstructorInitializer && !_isInStaticMethod && !_isInInstanceVariableInitializer && !_isInStaticVariableDeclaration) {
|
| return false;
|
| }
|
| + // prepare element
|
| Element element = node.staticElement;
|
| if (!(element is MethodElement || element is PropertyAccessorElement)) {
|
| return false;
|
| }
|
| + // static element
|
| ExecutableElement executableElement = element as ExecutableElement;
|
| if (executableElement.isStatic) {
|
| return false;
|
| }
|
| + // not a class member
|
| Element enclosingElement = element.enclosingElement;
|
| if (enclosingElement is! ClassElement) {
|
| return false;
|
| }
|
| + // comment
|
| ASTNode parent = node.parent;
|
| if (parent is CommentReference) {
|
| return false;
|
| }
|
| + // qualified method invocation
|
| if (parent is MethodInvocation) {
|
| MethodInvocation invocation = parent;
|
| if (identical(invocation.methodName, node) && invocation.realTarget != null) {
|
| return false;
|
| }
|
| }
|
| + // qualified property access
|
| if (parent is PropertyAccess) {
|
| PropertyAccess access = parent;
|
| if (identical(access.propertyName, node) && access.realTarget != null) {
|
| @@ -17741,6 +18696,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| return false;
|
| }
|
| }
|
| + // report problem
|
| if (_isInStaticMethod) {
|
| _errorReporter.reportError3(CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC, node, []);
|
| } else {
|
| @@ -17757,15 +18713,18 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#IMPORT_DUPLICATED_LIBRARY_NAME
|
| */
|
| bool checkForImportDuplicateLibraryName(ImportDirective node) {
|
| + // prepare import element
|
| ImportElement nodeImportElement = node.element;
|
| if (nodeImportElement == null) {
|
| return false;
|
| }
|
| + // prepare imported library
|
| LibraryElement nodeLibrary = nodeImportElement.importedLibrary;
|
| if (nodeLibrary == null) {
|
| return false;
|
| }
|
| String name = nodeLibrary.name;
|
| + // check if there is other imported library with the same name
|
| LibraryElement prevLibrary = _nameToImportElement[name];
|
| if (prevLibrary != null) {
|
| if (prevLibrary != nodeLibrary) {
|
| @@ -17778,6 +18737,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| } else {
|
| _nameToImportElement[name] = nodeLibrary;
|
| }
|
| + // OK
|
| return false;
|
| }
|
|
|
| @@ -17793,10 +18753,12 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (_isInSystemLibrary) {
|
| return false;
|
| }
|
| + // prepare import element
|
| ImportElement importElement = node.element;
|
| if (importElement == null) {
|
| return false;
|
| }
|
| + // should be private
|
| DartSdk sdk = _currentLibrary.context.sourceFactory.dartSdk;
|
| String uri = importElement.uri;
|
| SdkLibrary sdkLibrary = sdk.getSdkLibrary(uri);
|
| @@ -17806,6 +18768,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (!sdkLibrary.isInternal) {
|
| return false;
|
| }
|
| + // report problem
|
| _errorReporter.reportError3(CompileTimeErrorCode.IMPORT_INTERNAL_LIBRARY, node, [node.uri]);
|
| return true;
|
| }
|
| @@ -17818,6 +18781,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#INCONSISTENT_CASE_EXPRESSION_TYPES
|
| */
|
| bool checkForInconsistentCaseExpressionTypes(SwitchStatement node) {
|
| + // TODO(jwren) Revisit this algorithm, should there up to n-1 errors?
|
| NodeList<SwitchMember> switchMembers = node.members;
|
| bool foundError = false;
|
| Type2 firstType = null;
|
| @@ -17826,6 +18790,9 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| SwitchCase switchCase = switchMember;
|
| Expression expression = switchCase.expression;
|
| if (firstType == null) {
|
| + // TODO(brianwilkerson) This is failing with const variables whose declared type is
|
| + // dynamic. The problem is that we don't have any way to propagate type information for
|
| + // the variable.
|
| firstType = expression.bestType;
|
| } else {
|
| Type2 nType = expression.bestType;
|
| @@ -17850,6 +18817,8 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticTypeWarningCode#INCONSISTENT_METHOD_INHERITANCE
|
| */
|
| bool checkForInconsistentMethodInheritance() {
|
| + // Ensure that the inheritance manager has a chance to generate all errors we may care about,
|
| + // note that we ensure that the interfaces data since there are no errors.
|
| _inheritanceManager.getMapOfMembersInheritedFromInterfaces(_enclosingClass);
|
| Set<AnalysisError> errors = _inheritanceManager.getErrors(_enclosingClass);
|
| if (errors == null || errors.isEmpty) {
|
| @@ -17873,23 +18842,29 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticTypeWarningCode#INSTANCE_ACCESS_TO_STATIC_MEMBER
|
| */
|
| bool checkForInstanceAccessToStaticMember(ClassElement typeReference, SimpleIdentifier name) {
|
| + // OK, in comment
|
| if (_isInComment) {
|
| return false;
|
| }
|
| + // OK, target is a type
|
| if (typeReference != null) {
|
| return false;
|
| }
|
| + // prepare member Element
|
| Element element = name.staticElement;
|
| if (element is! ExecutableElement) {
|
| return false;
|
| }
|
| ExecutableElement executableElement = element as ExecutableElement;
|
| + // OK, top-level element
|
| if (executableElement.enclosingElement is! ClassElement) {
|
| return false;
|
| }
|
| + // OK, instance member
|
| if (!executableElement.isStatic) {
|
| return false;
|
| }
|
| + // report problem
|
| _errorReporter.reportError3(StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, name, [name.name]);
|
| return true;
|
| }
|
| @@ -17964,6 +18939,17 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| _errorReporter.reportError3(StaticTypeWarningCode.INVALID_ASSIGNMENT, rhs, [staticRightType.displayName, leftType.displayName]);
|
| return true;
|
| }
|
| + // TODO(brianwilkerson) Define a hint corresponding to the warning and report it if appropriate.
|
| + // Type propagatedRightType = rhs.getPropagatedType();
|
| + // boolean isPropagatedAssignable = propagatedRightType.isAssignableTo(leftType);
|
| + // if (!isStaticAssignable && !isPropagatedAssignable) {
|
| + // errorReporter.reportError(
|
| + // StaticTypeWarningCode.INVALID_ASSIGNMENT,
|
| + // rhs,
|
| + // staticRightType.getDisplayName(),
|
| + // leftType.getDisplayName());
|
| + // return true;
|
| + // }
|
| return false;
|
| }
|
|
|
| @@ -18013,6 +18999,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticWarningCode#LIST_ELEMENT_TYPE_NOT_ASSIGNABLE
|
| */
|
| bool checkForListElementTypeNotAssignable(ListLiteral node) {
|
| + // Prepare list element type.
|
| TypeArgumentList typeArgumentList = node.typeArguments;
|
| if (typeArgumentList == null) {
|
| return false;
|
| @@ -18022,12 +19009,14 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| return false;
|
| }
|
| Type2 listElementType = typeArguments[0].type;
|
| + // Prepare problem to report.
|
| ErrorCode errorCode;
|
| if (node.constKeyword != null) {
|
| errorCode = CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE;
|
| } else {
|
| errorCode = StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE;
|
| }
|
| + // Check every list element.
|
| bool hasProblems = false;
|
| for (Expression element in node.elements) {
|
| hasProblems = javaBooleanOr(hasProblems, checkForArgumentTypeNotAssignable3(element, listElementType, null, errorCode));
|
| @@ -18047,6 +19036,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticWarningCode#MAP_VALUE_TYPE_NOT_ASSIGNABLE
|
| */
|
| bool checkForMapTypeNotAssignable(MapLiteral node) {
|
| + // Prepare maps key/value types.
|
| TypeArgumentList typeArgumentList = node.typeArguments;
|
| if (typeArgumentList == null) {
|
| return false;
|
| @@ -18057,6 +19047,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| Type2 keyType = typeArguments[0].type;
|
| Type2 valueType = typeArguments[1].type;
|
| + // Prepare problem to report.
|
| ErrorCode keyErrorCode;
|
| ErrorCode valueErrorCode;
|
| if (node.constKeyword != null) {
|
| @@ -18066,6 +19057,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| keyErrorCode = StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE;
|
| valueErrorCode = StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE;
|
| }
|
| + // Check every map entry.
|
| bool hasProblems = false;
|
| NodeList<MapLiteralEntry> entries = node.entries;
|
| for (MapLiteralEntry entry in entries) {
|
| @@ -18093,12 +19085,15 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| return false;
|
| }
|
| bool problemReported = false;
|
| + // check accessors
|
| for (PropertyAccessorElement accessor in _enclosingClass.accessors) {
|
| if (className == accessor.name) {
|
| _errorReporter.reportError5(CompileTimeErrorCode.MEMBER_WITH_CLASS_NAME, accessor.nameOffset, className.length, []);
|
| problemReported = true;
|
| }
|
| }
|
| + // don't check methods, they would be constructors
|
| + // done
|
| return problemReported;
|
| }
|
|
|
| @@ -18123,19 +19118,26 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| counterpartAccessor = propertyAccessorElement.correspondingSetter;
|
| } else {
|
| counterpartAccessor = propertyAccessorElement.correspondingGetter;
|
| + // If the setter and getter are in the same enclosing element, return, this prevents having
|
| + // MISMATCHED_GETTER_AND_SETTER_TYPES reported twice.
|
| if (counterpartAccessor != null && identical(counterpartAccessor.enclosingElement, propertyAccessorElement.enclosingElement)) {
|
| return false;
|
| }
|
| }
|
| if (counterpartAccessor == null) {
|
| + // If the accessor is declared in a class, check the superclasses.
|
| if (_enclosingClass != null) {
|
| + // Figure out the correct identifier to lookup in the inheritance graph, if 'x', then 'x=',
|
| + // or if 'x=', then 'x'.
|
| String lookupIdentifier = propertyAccessorElement.name;
|
| if (lookupIdentifier.endsWith("=")) {
|
| lookupIdentifier = lookupIdentifier.substring(0, lookupIdentifier.length - 1);
|
| } else {
|
| lookupIdentifier += "=";
|
| }
|
| + // lookup with the identifier.
|
| ExecutableElement elementFromInheritance = _inheritanceManager.lookupInheritance(_enclosingClass, lookupIdentifier);
|
| + // Verify that we found something, and that it is an accessor
|
| if (elementFromInheritance != null && elementFromInheritance is PropertyAccessorElement) {
|
| enclosingClassForCounterpart = elementFromInheritance.enclosingElement as ClassElement;
|
| counterpartAccessor = elementFromInheritance;
|
| @@ -18145,8 +19147,10 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| return false;
|
| }
|
| }
|
| + // Default of null == no accessor or no type (dynamic)
|
| Type2 getterType = null;
|
| Type2 setterType = null;
|
| + // Get an existing counterpart accessor if any.
|
| if (propertyAccessorElement.isGetter) {
|
| getterType = getGetterType(propertyAccessorElement);
|
| setterType = getSetterType(counterpartAccessor);
|
| @@ -18154,6 +19158,8 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| setterType = getSetterType(propertyAccessorElement);
|
| getterType = getGetterType(counterpartAccessor);
|
| }
|
| + // If either types are not assignable to each other, report an error (if the getter is null,
|
| + // it is dynamic which is assignable to everything).
|
| if (setterType != null && getterType != null && !getterType.isAssignableTo(setterType)) {
|
| if (enclosingClassForCounterpart == null) {
|
| _errorReporter.reportError3(StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES, accessorDeclaration, [
|
| @@ -18268,6 +19274,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see ParserErrorCode#NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE
|
| */
|
| bool checkForNativeFunctionBodyInNonSDKCode(NativeFunctionBody node) {
|
| + // TODO(brianwilkerson) Figure out the right rule for when 'native' is allowed.
|
| if (!_isInSystemLibrary) {
|
| _errorReporter.reportError3(ParserErrorCode.NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE, node, []);
|
| return true;
|
| @@ -18285,18 +19292,22 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticWarningCode#NEW_WITH_UNDEFINED_CONSTRUCTOR
|
| */
|
| bool checkForNewWithUndefinedConstructor(InstanceCreationExpression node) {
|
| + // OK if resolved
|
| if (node.staticElement != null) {
|
| return false;
|
| }
|
| + // prepare constructor name
|
| ConstructorName constructorName = node.constructorName;
|
| if (constructorName == null) {
|
| return false;
|
| }
|
| + // prepare class name
|
| TypeName type = constructorName.type;
|
| if (type == null) {
|
| return false;
|
| }
|
| Identifier className = type.name;
|
| + // report as named or default constructor absence
|
| SimpleIdentifier name = constructorName.name;
|
| if (name != null) {
|
| _errorReporter.reportError3(StaticWarningCode.NEW_WITH_UNDEFINED_CONSTRUCTOR, name, [className, name]);
|
| @@ -18315,15 +19326,18 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT
|
| */
|
| bool checkForNoDefaultSuperConstructorImplicit(ClassDeclaration node) {
|
| + // do nothing if there is explicit constructor
|
| List<ConstructorElement> constructors = _enclosingClass.constructors;
|
| if (!constructors[0].isSynthetic) {
|
| return false;
|
| }
|
| + // prepare super
|
| InterfaceType superType = _enclosingClass.supertype;
|
| if (superType == null) {
|
| return false;
|
| }
|
| ClassElement superElement = superType.element;
|
| + // try to find default generative super constructor
|
| ConstructorElement superUnnamedConstructor = superElement.unnamedConstructor;
|
| if (superUnnamedConstructor != null) {
|
| if (superUnnamedConstructor.isFactory) {
|
| @@ -18334,6 +19348,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| return true;
|
| }
|
| }
|
| + // report problem
|
| _errorReporter.reportError3(CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT, node.name, [superType.displayName]);
|
| return true;
|
| }
|
| @@ -18354,11 +19369,18 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (_enclosingClass.isAbstract) {
|
| return false;
|
| }
|
| + //
|
| + // Store in local sets the set of all method and accessor names
|
| + //
|
| List<MethodElement> methods = _enclosingClass.methods;
|
| List<PropertyAccessorElement> accessors = _enclosingClass.accessors;
|
| Set<String> methodsInEnclosingClass = new Set<String>();
|
| for (MethodElement method in methods) {
|
| String methodName = method.name;
|
| + // If the enclosing class declares the method noSuchMethod(), then return.
|
| + // From Spec: It is a static warning if a concrete class does not have an implementation for
|
| + // a method in any of its superinterfaces unless it declares its own noSuchMethod
|
| + // method (7.10).
|
| if (methodName == ElementResolver.NO_SUCH_METHOD_METHOD_NAME) {
|
| return false;
|
| }
|
| @@ -18369,6 +19391,9 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| accessorsInEnclosingClass.add(accessor.name);
|
| }
|
| Set<ExecutableElement> missingOverrides = new Set<ExecutableElement>();
|
| + //
|
| + // Loop through the set of all executable elements declared in the implicit interface.
|
| + //
|
| MemberMap membersInheritedFromInterfaces = _inheritanceManager.getMapOfMembersInheritedFromInterfaces(_enclosingClass);
|
| MemberMap membersInheritedFromSuperclasses = _inheritanceManager.getMapOfMembersInheritedFromClasses(_enclosingClass);
|
| for (int i = 0; i < membersInheritedFromInterfaces.size; i++) {
|
| @@ -18377,16 +19402,33 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (memberName == null) {
|
| break;
|
| }
|
| + // If the element is defined in Object, skip it.
|
| if ((executableElt.enclosingElement as ClassElement).type.isObject) {
|
| continue;
|
| }
|
| + // Reference the type of the enclosing class
|
| InterfaceType enclosingType = _enclosingClass.type;
|
| + // Check to see if some element is in local enclosing class that matches the name of the
|
| + // required member.
|
| if (isMemberInClassOrMixin(executableElt, _enclosingClass)) {
|
| + // We do not have to verify that this implementation of the found method matches the
|
| + // required function type: the set of StaticWarningCode.INVALID_METHOD_OVERRIDE_* warnings
|
| + // break out the different specific situations.
|
| continue;
|
| }
|
| + // First check to see if this element was declared in the superclass chain, in which case
|
| + // there is already a concrete implementation.
|
| ExecutableElement elt = membersInheritedFromSuperclasses.get(executableElt.name);
|
| + // Check to see if an element was found in the superclass chain with the correct name.
|
| if (elt != null) {
|
| + // Some element was found in the superclass chain that matches the name of the required
|
| + // member.
|
| + // If it is not abstract and it is the correct one (types match- the version of this method
|
| + // that we have has the correct number of parameters, etc), then this class has a valid
|
| + // implementation of this method, so skip it.
|
| if ((elt is MethodElement && !elt.isAbstract) || (elt is PropertyAccessorElement && !elt.isAbstract)) {
|
| + // Since we are comparing two function types, we need to do the appropriate type
|
| + // substitutions first ().
|
| FunctionType foundConcreteFT = _inheritanceManager.substituteTypeArgumentsInMemberFromInheritance(elt.type, executableElt.name, enclosingType);
|
| FunctionType requiredMemberFT = _inheritanceManager.substituteTypeArgumentsInMemberFromInheritance(executableElt.type, executableElt.name, enclosingType);
|
| if (foundConcreteFT.isSubtypeOf(requiredMemberFT)) {
|
| @@ -18394,8 +19436,10 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| }
|
| }
|
| + // The not qualifying concrete executable element was found, add it to the list.
|
| missingOverrides.add(executableElt);
|
| }
|
| + // Now that we have the set of missing overrides, generate a warning on this class
|
| int missingOverridesSize = missingOverrides.length;
|
| if (missingOverridesSize == 0) {
|
| return false;
|
| @@ -18509,19 +19553,24 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#NON_CONST_MAP_AS_EXPRESSION_STATEMENT
|
| */
|
| bool checkForNonConstMapAsExpressionStatement(MapLiteral node) {
|
| + // "const"
|
| if (node.constKeyword != null) {
|
| return false;
|
| }
|
| + // has type arguments
|
| if (node.typeArguments != null) {
|
| return false;
|
| }
|
| + // prepare statement
|
| Statement statement = node.getAncestor(ExpressionStatement);
|
| if (statement == null) {
|
| return false;
|
| }
|
| + // OK, statement does not start with map
|
| if (statement.beginToken != node.beginToken) {
|
| return false;
|
| }
|
| + // report problem
|
| _errorReporter.reportError3(CompileTimeErrorCode.NON_CONST_MAP_AS_EXPRESSION_STATEMENT, node, []);
|
| return true;
|
| }
|
| @@ -18535,10 +19584,12 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticWarningCode#NON_VOID_RETURN_FOR_OPERATOR
|
| */
|
| bool checkForNonVoidReturnTypeForOperator(MethodDeclaration node) {
|
| + // check that []= operator
|
| SimpleIdentifier name = node.name;
|
| if (name.name != "[]=") {
|
| return false;
|
| }
|
| + // check return type
|
| TypeName typeName = node.returnType;
|
| if (typeName != null) {
|
| Type2 type = typeName.type;
|
| @@ -18546,6 +19597,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| _errorReporter.reportError3(StaticWarningCode.NON_VOID_RETURN_FOR_OPERATOR, typeName, []);
|
| }
|
| }
|
| + // no warning
|
| return false;
|
| }
|
|
|
| @@ -18600,13 +19652,16 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#PRIVATE_OPTIONAL_PARAMETER
|
| */
|
| bool checkForPrivateOptionalParameter(FormalParameter node) {
|
| + // should be named parameter
|
| if (node.kind != ParameterKind.NAMED) {
|
| return false;
|
| }
|
| + // name should start with '_'
|
| SimpleIdentifier name = node.identifier;
|
| if (name.isSynthetic || !name.name.startsWith("_")) {
|
| return false;
|
| }
|
| + // report problem
|
| _errorReporter.reportError3(CompileTimeErrorCode.PRIVATE_OPTIONAL_PARAMETER, node, []);
|
| return true;
|
| }
|
| @@ -18620,19 +19675,24 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#RECURSIVE_CONSTRUCTOR_REDIRECT
|
| */
|
| bool checkForRecursiveConstructorRedirect(ConstructorDeclaration node) {
|
| + // we check generative constructor here
|
| if (node.factoryKeyword != null) {
|
| return false;
|
| }
|
| + // try to find redirecting constructor invocation and analyzer it for recursion
|
| for (ConstructorInitializer initializer in node.initializers) {
|
| if (initializer is RedirectingConstructorInvocation) {
|
| + // OK if no cycle
|
| ConstructorElement element = node.element;
|
| if (!hasRedirectingFactoryConstructorCycle(element)) {
|
| return false;
|
| }
|
| + // report error
|
| _errorReporter.reportError3(CompileTimeErrorCode.RECURSIVE_CONSTRUCTOR_REDIRECT, initializer, []);
|
| return true;
|
| }
|
| }
|
| + // OK, no redirecting constructor invocation
|
| return false;
|
| }
|
|
|
| @@ -18645,14 +19705,17 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#RECURSIVE_FACTORY_REDIRECT
|
| */
|
| bool checkForRecursiveFactoryRedirect(ConstructorDeclaration node) {
|
| + // prepare redirected constructor
|
| ConstructorName redirectedConstructorNode = node.redirectedConstructor;
|
| if (redirectedConstructorNode == null) {
|
| return false;
|
| }
|
| + // OK if no cycle
|
| ConstructorElement element = node.element;
|
| if (!hasRedirectingFactoryConstructorCycle(element)) {
|
| return false;
|
| }
|
| + // report error
|
| _errorReporter.reportError3(CompileTimeErrorCode.RECURSIVE_FACTORY_REDIRECT, redirectedConstructorNode, []);
|
| return true;
|
| }
|
| @@ -18684,10 +19747,14 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS
|
| */
|
| bool checkForRecursiveInterfaceInheritance2(ClassElement classElt, List<ClassElement> path) {
|
| + // Detect error condition.
|
| int size = path.length;
|
| + // If this is not the base case (size > 0), and the enclosing class is the passed class
|
| + // element then an error an error.
|
| if (size > 0 && _enclosingClass == classElt) {
|
| String enclosingClassName = _enclosingClass.displayName;
|
| if (size > 1) {
|
| + // Construct a string showing the cyclic implements path: "A, B, C, D, A"
|
| String separator = ", ";
|
| JavaStringBuilder builder = new JavaStringBuilder();
|
| for (int i = 0; i < size; i++) {
|
| @@ -18698,6 +19765,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| _errorReporter.reportError5(CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE, _enclosingClass.nameOffset, enclosingClassName.length, [enclosingClassName, builder.toString()]);
|
| return true;
|
| } else {
|
| + // RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS or RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS
|
| InterfaceType supertype = classElt.supertype;
|
| ErrorCode errorCode = (supertype != null && _enclosingClass == supertype.element ? CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS : CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS);
|
| _errorReporter.reportError5(errorCode, _enclosingClass.nameOffset, enclosingClassName.length, [enclosingClassName]);
|
| @@ -18708,6 +19776,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| return false;
|
| }
|
| path.add(classElt);
|
| + // n-case
|
| InterfaceType supertype = classElt.supertype;
|
| if (supertype != null && checkForRecursiveInterfaceInheritance2(supertype.element, path)) {
|
| return true;
|
| @@ -18735,6 +19804,9 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| */
|
| bool checkForRedirectingConstructorErrorCodes(ConstructorDeclaration node) {
|
| bool errorReported = false;
|
| + //
|
| + // Check for default values in the parameters
|
| + //
|
| ConstructorName redirectedConstructor = node.redirectedConstructor;
|
| if (redirectedConstructor != null) {
|
| for (FormalParameter parameter in node.parameters.parameters) {
|
| @@ -18744,6 +19816,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| }
|
| }
|
| + // check if there are redirected invocations
|
| int numRedirections = 0;
|
| for (ConstructorInitializer initializer in node.initializers) {
|
| if (initializer is RedirectingConstructorInvocation) {
|
| @@ -18754,6 +19827,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| numRedirections++;
|
| }
|
| }
|
| + // check for other initializers
|
| if (numRedirections > 0) {
|
| for (ConstructorInitializer initializer in node.initializers) {
|
| if (initializer is SuperConstructorInvocation) {
|
| @@ -18766,6 +19840,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| }
|
| }
|
| + // done
|
| return errorReported;
|
| }
|
|
|
| @@ -18778,24 +19853,30 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#REDIRECT_TO_NON_CONST_CONSTRUCTOR
|
| */
|
| bool checkForRedirectToNonConstConstructor(ConstructorDeclaration node) {
|
| + // prepare redirected constructor
|
| ConstructorName redirectedConstructorNode = node.redirectedConstructor;
|
| if (redirectedConstructorNode == null) {
|
| return false;
|
| }
|
| + // prepare element
|
| ConstructorElement element = node.element;
|
| if (element == null) {
|
| return false;
|
| }
|
| + // OK, it is not 'const'
|
| if (!element.isConst) {
|
| return false;
|
| }
|
| + // prepare redirected constructor
|
| ConstructorElement redirectedConstructor = element.redirectedConstructor;
|
| if (redirectedConstructor == null) {
|
| return false;
|
| }
|
| + // OK, it is also 'const'
|
| if (redirectedConstructor.isConst) {
|
| return false;
|
| }
|
| + // report error
|
| _errorReporter.reportError3(CompileTimeErrorCode.REDIRECT_TO_NON_CONST_CONSTRUCTOR, redirectedConstructorNode, []);
|
| return true;
|
| }
|
| @@ -18824,13 +19905,16 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#RETURN_IN_GENERATIVE_CONSTRUCTOR
|
| */
|
| bool checkForReturnInGenerativeConstructor(ConstructorDeclaration node) {
|
| + // ignore factory
|
| if (node.factoryKeyword != null) {
|
| return false;
|
| }
|
| + // block body (with possible return statement) is checked elsewhere
|
| FunctionBody body = node.body;
|
| if (body is! ExpressionFunctionBody) {
|
| return false;
|
| }
|
| + // report error
|
| _errorReporter.reportError3(CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR, body, []);
|
| return true;
|
| }
|
| @@ -18882,17 +19966,21 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticWarningCode#STATIC_ACCESS_TO_INSTANCE_MEMBER
|
| */
|
| bool checkForStaticAccessToInstanceMember(ClassElement typeReference, SimpleIdentifier name) {
|
| + // OK, target is not a type
|
| if (typeReference == null) {
|
| return false;
|
| }
|
| + // prepare member Element
|
| Element element = name.staticElement;
|
| if (element is! ExecutableElement) {
|
| return false;
|
| }
|
| ExecutableElement memberElement = element as ExecutableElement;
|
| + // OK, static
|
| if (memberElement.isStatic) {
|
| return false;
|
| }
|
| + // report problem
|
| _errorReporter.reportError3(StaticWarningCode.STATIC_ACCESS_TO_INSTANCE_MEMBER, name, [name.name]);
|
| return true;
|
| }
|
| @@ -18906,22 +19994,27 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticWarningCode#SWITCH_EXPRESSION_NOT_ASSIGNABLE
|
| */
|
| bool checkForSwitchExpressionNotAssignable(SwitchStatement node) {
|
| + // prepare 'switch' expression type
|
| Expression expression = node.expression;
|
| Type2 expressionType = getStaticType(expression);
|
| if (expressionType == null) {
|
| return false;
|
| }
|
| + // compare with type of the first 'case'
|
| NodeList<SwitchMember> members = node.members;
|
| for (SwitchMember switchMember in members) {
|
| if (switchMember is! SwitchCase) {
|
| continue;
|
| }
|
| SwitchCase switchCase = switchMember as SwitchCase;
|
| + // prepare 'case' type
|
| Expression caseExpression = switchCase.expression;
|
| Type2 caseType = getStaticType(caseExpression);
|
| + // check types
|
| if (expressionType.isAssignableTo(caseType)) {
|
| return false;
|
| }
|
| + // report problem
|
| _errorReporter.reportError3(StaticWarningCode.SWITCH_EXPRESSION_NOT_ASSIGNABLE, expression, [expressionType, caseType]);
|
| return true;
|
| }
|
| @@ -18970,17 +20063,21 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (node.typeArguments == null) {
|
| return false;
|
| }
|
| + // prepare Type
|
| Type2 type = node.type;
|
| if (type == null) {
|
| return false;
|
| }
|
| + // prepare ClassElement
|
| Element element = type.element;
|
| if (element is! ClassElement) {
|
| return false;
|
| }
|
| ClassElement classElement = element as ClassElement;
|
| + // prepare type parameters
|
| List<Type2> typeParameters = classElement.type.typeArguments;
|
| List<TypeParameterElement> boundingElts = classElement.typeParameters;
|
| + // iterate over each bounded type parameter and corresponding argument
|
| NodeList<TypeName> typeNameArgList = node.typeArguments.arguments;
|
| List<Type2> typeArguments = (type as InterfaceType).typeArguments;
|
| int loopThroughIndex = Math.min(typeNameArgList.length, boundingElts.length);
|
| @@ -19034,13 +20131,16 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| */
|
| bool checkForTypeParameterSupertypeOfItsBound(TypeParameter node) {
|
| TypeParameterElement element = node.element;
|
| + // prepare bound
|
| Type2 bound = element.bound;
|
| if (bound == null) {
|
| return false;
|
| }
|
| + // OK, type parameter is not supertype of its bound
|
| if (!bound.isMoreSpecificThan(element.type)) {
|
| return false;
|
| }
|
| + // report problem
|
| _errorReporter.reportError3(StaticTypeWarningCode.TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND, node, [element.displayName]);
|
| return true;
|
| }
|
| @@ -19057,14 +20157,24 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticWarningCode#NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT
|
| */
|
| bool checkForUndefinedConstructorInInitializerImplicit(ConstructorDeclaration node) {
|
| + //
|
| + // Ignore if the constructor is not generative.
|
| + //
|
| if (node.factoryKeyword != null) {
|
| return false;
|
| }
|
| + //
|
| + // Ignore if the constructor has either an implicit super constructor invocation or a
|
| + // redirecting constructor invocation.
|
| + //
|
| for (ConstructorInitializer constructorInitializer in node.initializers) {
|
| if (constructorInitializer is SuperConstructorInvocation || constructorInitializer is RedirectingConstructorInvocation) {
|
| return false;
|
| }
|
| }
|
| + //
|
| + // Check to see whether the superclass has a non-factory unnamed constructor.
|
| + //
|
| if (_enclosingClass == null) {
|
| return false;
|
| }
|
| @@ -19134,16 +20244,19 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR
|
| */
|
| bool checkForWrongNumberOfParametersForOperator(MethodDeclaration node) {
|
| + // prepare number of parameters
|
| FormalParameterList parameterList = node.parameters;
|
| if (parameterList == null) {
|
| return false;
|
| }
|
| int numParameters = parameterList.parameters.length;
|
| + // prepare operator name
|
| SimpleIdentifier nameNode = node.name;
|
| if (nameNode == null) {
|
| return false;
|
| }
|
| String name = nameNode.name;
|
| + // check for exact number of parameters
|
| int expected = -1;
|
| if ("[]=" == name) {
|
| expected = 2;
|
| @@ -19156,10 +20269,12 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| _errorReporter.reportError3(CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR, nameNode, [name, expected, numParameters]);
|
| return true;
|
| }
|
| + // check for operator "-"
|
| if ("-" == name && numParameters > 1) {
|
| _errorReporter.reportError3(CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR_MINUS, nameNode, [numParameters]);
|
| return true;
|
| }
|
| + // OK
|
| return false;
|
| }
|
|
|
| @@ -19222,14 +20337,17 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see CompileTimeErrorCode#IMPLEMENTS_SUPER_CLASS
|
| */
|
| bool checkImplementsSuperClass(ClassDeclaration node) {
|
| + // prepare super type
|
| InterfaceType superType = _enclosingClass.supertype;
|
| if (superType == null) {
|
| return false;
|
| }
|
| + // prepare interfaces
|
| ImplementsClause implementsClause = node.implementsClause;
|
| if (implementsClause == null) {
|
| return false;
|
| }
|
| + // check interfaces
|
| bool hasProblem = false;
|
| for (TypeName interfaceNode in implementsClause.interfaces) {
|
| if (interfaceNode.type == superType) {
|
| @@ -19237,6 +20355,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| _errorReporter.reportError3(CompileTimeErrorCode.IMPLEMENTS_SUPER_CLASS, interfaceNode, [superType.displayName]);
|
| }
|
| }
|
| + // done
|
| return hasProblem;
|
| }
|
|
|
| @@ -19262,7 +20381,9 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @return The type of the given setter.
|
| */
|
| Type2 getSetterType(PropertyAccessorElement propertyAccessorElement) {
|
| + // Get the parameters for MethodDeclaration or FunctionDeclaration
|
| List<ParameterElement> setterParameters = propertyAccessorElement.parameters;
|
| + // If there are no setter parameters, return no type.
|
| if (setterParameters.length == 0) {
|
| return null;
|
| }
|
| @@ -19278,6 +20399,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| Type2 getStaticType(Expression expression) {
|
| Type2 type = expression.staticType;
|
| if (type == null) {
|
| + // TODO(brianwilkerson) This should never happen.
|
| return _dynamicType;
|
| }
|
| return type;
|
| @@ -19330,10 +20452,13 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| bool firstIteration = true;
|
| while (true) {
|
| Element current;
|
| + // get next element
|
| while (true) {
|
| + // may be no more elements to check
|
| if (toCheck.isEmpty) {
|
| return false;
|
| }
|
| + // try to get next element
|
| current = toCheck.removeAt(toCheck.length - 1);
|
| if (target == current) {
|
| if (firstIteration) {
|
| @@ -19347,6 +20472,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| break;
|
| }
|
| }
|
| + // check current element
|
| current.accept(new GeneralizingElementVisitor_ErrorVerifier_hasTypedefSelfReference(target, toCheck));
|
| checked.add(current);
|
| }
|
| @@ -19357,18 +20483,22 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * <i>int</i> or <i>String</i>.
|
| */
|
| bool implementsEqualsWhenNotAllowed(Type2 type) {
|
| + // ignore int or String
|
| if (type == null || type == _typeProvider.intType || type == _typeProvider.stringType) {
|
| return false;
|
| }
|
| + // prepare ClassElement
|
| Element element = type.element;
|
| if (element is! ClassElement) {
|
| return false;
|
| }
|
| ClassElement classElement = element as ClassElement;
|
| + // lookup for ==
|
| MethodElement method = classElement.lookUpMethod("==", _currentLibrary);
|
| if (method == null || method.enclosingElement.type.isObject) {
|
| return false;
|
| }
|
| + // there is == that we don't like
|
| return true;
|
| }
|
|
|
| @@ -19664,10 +20794,13 @@ class GeneralizingElementVisitor_ErrorVerifier_hasTypedefSelfReference extends G
|
| return;
|
| }
|
| Element element = type.element;
|
| + // it is OK to reference target from class
|
| if (_inClass && target == element) {
|
| return;
|
| }
|
| + // schedule for checking
|
| toCheck.add(element);
|
| + // type arguments
|
| if (type is InterfaceType) {
|
| InterfaceType interfaceType = type;
|
| for (Type2 typeArgument in interfaceType.typeArguments) {
|
|
|