Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(38)

Unified Diff: pkg/analyzer/lib/src/generated/resolver.dart

Issue 137863002: Issue 8742. Preserve leading line comments during java2dart translation. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Test for block-style comment translation. Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/analyzer/lib/src/generated/parser.dart ('k') | pkg/analyzer/lib/src/generated/scanner.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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) {
« no previous file with comments | « pkg/analyzer/lib/src/generated/parser.dart ('k') | pkg/analyzer/lib/src/generated/scanner.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698