Index: pkg/analyzer_experimental/lib/src/generated/resolver.dart |
=================================================================== |
--- pkg/analyzer_experimental/lib/src/generated/resolver.dart (revision 23549) |
+++ pkg/analyzer_experimental/lib/src/generated/resolver.dart (working copy) |
@@ -13,26 +13,22 @@ |
import 'utilities_dart.dart'; |
import 'ast.dart'; |
import 'parser.dart' show Parser, ParserErrorCode; |
-import 'sdk.dart' show DartSdk; |
-import 'element.dart' hide HideCombinator, ShowCombinator; |
+import 'sdk.dart' show DartSdk, SdkLibrary; |
+import 'element.dart' hide Annotation, HideCombinator, ShowCombinator; |
import 'html.dart' as ht; |
import 'engine.dart'; |
import 'constant.dart'; |
import 'element.dart' as __imp_combi show HideCombinator, ShowCombinator; |
+ |
/** |
* Instances of the class {@code CompilationUnitBuilder} build an element model for a single |
* compilation unit. |
* @coverage dart.engine.resolver |
*/ |
class CompilationUnitBuilder { |
+ |
/** |
- * Initialize a newly created compilation unit element builder. |
- * @param analysisContext the analysis context in which the element model will be built |
- */ |
- CompilationUnitBuilder() : super() { |
- } |
- /** |
* Build the compilation unit element for the given source. |
* @param source the source describing the compilation unit |
* @param unit the AST structure representing the compilation unit |
@@ -57,28 +53,34 @@ |
return element; |
} |
} |
+ |
/** |
* Instances of the class {@code ElementBuilder} traverse an AST structure and build the element |
* model representing the AST structure. |
* @coverage dart.engine.resolver |
*/ |
class ElementBuilder extends RecursiveASTVisitor<Object> { |
+ |
/** |
* The element holder associated with the element that is currently being built. |
*/ |
ElementHolder _currentHolder; |
+ |
/** |
* A flag indicating whether a variable declaration is in the context of a field declaration. |
*/ |
bool _inFieldContext = false; |
+ |
/** |
* A flag indicating whether a variable declaration is within the body of a method or function. |
*/ |
bool _inFunction = false; |
+ |
/** |
* A flag indicating whether the class currently being visited can be used as a mixin. |
*/ |
bool _isValidMixin = false; |
+ |
/** |
* Initialize a newly created element builder to build the elements for a compilation unit. |
* @param initialHolder the element holder associated with the compilation unit being built |
@@ -123,12 +125,7 @@ |
element.type = interfaceType; |
List<ConstructorElement> constructors2 = holder.constructors; |
if (constructors2.length == 0) { |
- ConstructorElementImpl constructor = new ConstructorElementImpl(null); |
- constructor.synthetic = true; |
- FunctionTypeImpl type = new FunctionTypeImpl.con1(constructor); |
- type.returnType = interfaceType; |
- constructor.type = type; |
- constructors2 = <ConstructorElement> [constructor]; |
+ constructors2 = createDefaultConstructors(interfaceType); |
} |
element.abstract = node.abstractKeyword != null; |
element.accessors = holder.accessors; |
@@ -153,6 +150,7 @@ |
InterfaceTypeImpl interfaceType = new InterfaceTypeImpl.con1(element); |
interfaceType.typeArguments = createTypeVariableTypes(typeVariables2); |
element.type = interfaceType; |
+ element.constructors = createDefaultConstructors(interfaceType); |
_currentHolder.addType(element); |
className.element = element; |
return null; |
@@ -248,7 +246,6 @@ |
SimpleIdentifier parameterName = node.identifier; |
FieldFormalParameterElementImpl parameter = new FieldFormalParameterElementImpl(parameterName); |
parameter.const3 = node.isConst(); |
- parameter.initializingFormal = true; |
parameter.final2 = node.isFinal(); |
parameter.parameterKind = node.kind; |
_currentHolder.addParameter(parameter); |
@@ -290,6 +287,7 @@ |
if (field == null) { |
field = new FieldElementImpl.con2(node.name.name); |
field.final2 = true; |
+ field.static = true; |
_currentHolder.addField(field); |
} |
if (matches(property, sc.Keyword.GET)) { |
@@ -299,6 +297,7 @@ |
getter.localVariables = holder.localVariables; |
getter.variable = field; |
getter.getter = true; |
+ getter.static = true; |
field.getter = getter; |
_currentHolder.addAccessor(getter); |
propertyNameNode.element = getter; |
@@ -310,6 +309,7 @@ |
setter.parameters = holder.parameters; |
setter.variable = field; |
setter.setter = true; |
+ setter.static = true; |
field.setter = setter; |
field.final2 = false; |
_currentHolder.addAccessor(setter); |
@@ -395,6 +395,7 @@ |
} finally { |
_inFunction = wasInFunction; |
} |
+ bool isStatic2 = node.isStatic(); |
sc.Token property = node.propertyKeyword; |
if (property == null) { |
SimpleIdentifier methodName = node.name; |
@@ -408,7 +409,7 @@ |
element.labels = holder.labels; |
element.localVariables = holder.localVariables; |
element.parameters = holder.parameters; |
- element.static = node.isStatic(); |
+ element.static = isStatic2; |
_currentHolder.addMethod(element); |
methodName.element = element; |
} else { |
@@ -418,7 +419,7 @@ |
if (field == null) { |
field = new FieldElementImpl.con2(node.name.name); |
field.final2 = true; |
- field.static = matches(node.modifierKeyword, sc.Keyword.STATIC); |
+ field.static = isStatic2; |
_currentHolder.addField(field); |
} |
if (matches(property, sc.Keyword.GET)) { |
@@ -427,7 +428,9 @@ |
getter.labels = holder.labels; |
getter.localVariables = holder.localVariables; |
getter.variable = field; |
+ getter.abstract = node.body is EmptyFunctionBody && node.externalKeyword == null; |
getter.getter = true; |
+ getter.static = isStatic2; |
field.getter = getter; |
_currentHolder.addAccessor(getter); |
propertyNameNode.element = getter; |
@@ -438,7 +441,9 @@ |
setter.localVariables = holder.localVariables; |
setter.parameters = holder.parameters; |
setter.variable = field; |
+ setter.abstract = node.body is EmptyFunctionBody && !matches(node.externalKeyword, sc.Keyword.EXTERNAL); |
setter.setter = true; |
+ setter.static = isStatic2; |
field.setter = setter; |
field.final2 = false; |
_currentHolder.addAccessor(setter); |
@@ -554,22 +559,38 @@ |
} |
if (element is PropertyInducingElementImpl) { |
PropertyInducingElementImpl variable = element as PropertyInducingElementImpl; |
+ if (_inFieldContext) { |
+ ((variable as FieldElementImpl)).static = matches(((node.parent.parent as FieldDeclaration)).keyword, sc.Keyword.STATIC); |
+ } |
PropertyAccessorElementImpl getter = new PropertyAccessorElementImpl.con2(variable); |
getter.getter = true; |
+ getter.static = variable.isStatic(); |
_currentHolder.addAccessor(getter); |
variable.getter = getter; |
if (!isFinal) { |
PropertyAccessorElementImpl setter = new PropertyAccessorElementImpl.con2(variable); |
setter.setter = true; |
+ setter.static = variable.isStatic(); |
_currentHolder.addAccessor(setter); |
variable.setter = setter; |
} |
- if (_inFieldContext) { |
- ((variable as FieldElementImpl)).static = matches(((node.parent.parent as FieldDeclaration)).keyword, sc.Keyword.STATIC); |
- } |
} |
return null; |
} |
+ |
+ /** |
+ * Creates the {@link ConstructorElement}s array with the single default constructor element. |
+ * @param interfaceType the interface type for which to create a default constructor |
+ * @return the {@link ConstructorElement}s array with the single default constructor element |
+ */ |
+ List<ConstructorElement> createDefaultConstructors(InterfaceTypeImpl interfaceType) { |
+ ConstructorElementImpl constructor = new ConstructorElementImpl(null); |
+ constructor.synthetic = true; |
+ FunctionTypeImpl type = new FunctionTypeImpl.con1(constructor); |
+ type.returnType = interfaceType; |
+ constructor.type = type; |
+ return <ConstructorElement> [constructor]; |
+ } |
List<Type2> createTypeVariableTypes(List<TypeVariableElement> typeVariables) { |
int typeVariableCount = typeVariables.length; |
List<Type2> typeArguments = new List<Type2>(typeVariableCount); |
@@ -581,6 +602,7 @@ |
} |
return typeArguments; |
} |
+ |
/** |
* Return the body of the function that contains the given parameter, or {@code null} if no |
* function body could be found. |
@@ -599,6 +621,7 @@ |
} |
return null; |
} |
+ |
/** |
* Return {@code true} if the given token is a token for the given keyword. |
* @param token the token being tested |
@@ -606,6 +629,7 @@ |
* @return {@code true} if the given token is a token for the given keyword |
*/ |
bool matches(sc.Token token, sc.Keyword keyword2) => token != null && identical(token.type, sc.TokenType.KEYWORD) && identical(((token as sc.KeywordToken)).keyword, keyword2); |
+ |
/** |
* Make the given holder be the current holder while visiting the given node. |
* @param holder the holder that will gather elements that are built while visiting the children |
@@ -622,6 +646,7 @@ |
} |
} |
} |
+ |
/** |
* Make the given holder be the current holder while visiting the children of the given node. |
* @param holder the holder that will gather elements that are built while visiting the children |
@@ -639,6 +664,7 @@ |
} |
} |
} |
+ |
/** |
* Instances of the class {@code ElementHolder} hold on to elements created while traversing an AST |
* structure so that they can be accessed when creating their enclosing element. |
@@ -657,11 +683,6 @@ |
List<VariableElement> _topLevelVariables = new List<VariableElement>(); |
List<ClassElement> _types = new List<ClassElement>(); |
List<TypeVariableElement> _typeVariables = new List<TypeVariableElement>(); |
- /** |
- * Initialize a newly created element holder. |
- */ |
- ElementHolder() : super() { |
- } |
void addAccessor(PropertyAccessorElement element) { |
_accessors.add(element); |
} |
@@ -779,6 +800,7 @@ |
return new List.from(_typeVariables); |
} |
} |
+ |
/** |
* Instances of the class {@code HtmlUnitBuilder} build an element model for a single HTML unit. |
*/ |
@@ -788,35 +810,51 @@ |
static String _SCRIPT = "script"; |
static String _SRC = "src"; |
static String _TYPE = "type"; |
+ |
/** |
* The analysis context in which the element model will be built. |
*/ |
InternalAnalysisContext _context; |
+ |
/** |
* The error listener to which errors will be reported. |
*/ |
- AnalysisErrorListener _errorListener; |
+ RecordingErrorListener _errorListener; |
+ |
/** |
* The line information associated with the source for which an element is being built, or{@code null} if we are not building an element. |
*/ |
LineInfo _lineInfo; |
+ |
/** |
* The HTML element being built. |
*/ |
HtmlElementImpl _htmlElement; |
+ |
/** |
+ * The elements in the path from the HTML unit to the current tag node. |
+ */ |
+ List<ht.XmlTagNode> _parentNodes; |
+ |
+ /** |
* The script elements being built. |
*/ |
List<HtmlScriptElement> _scripts; |
+ |
/** |
+ * A set of the libraries that were resolved while resolving the HTML unit. |
+ */ |
+ Set<Library> _resolvedLibraries = new Set<Library>(); |
+ |
+ /** |
* Initialize a newly created HTML unit builder. |
* @param context the analysis context in which the element model will be built |
- * @param errorListener the error listener to which errors will be reported |
*/ |
- HtmlUnitBuilder(InternalAnalysisContext context, AnalysisErrorListener errorListener) { |
+ HtmlUnitBuilder(InternalAnalysisContext context) { |
this._context = context; |
- this._errorListener = errorListener; |
+ this._errorListener = new RecordingErrorListener(); |
} |
+ |
/** |
* Build the HTML element for the given source. |
* @param source the source describing the compilation unit |
@@ -824,6 +862,7 @@ |
* @throws AnalysisException if the analysis could not be performed |
*/ |
HtmlElementImpl buildHtmlElement(Source source) => buildHtmlElement2(source, _context.parseHtmlUnit(source)); |
+ |
/** |
* Build the HTML element for the given source. |
* @param source the source describing the compilation unit |
@@ -840,62 +879,107 @@ |
unit.element = result; |
return result; |
} |
+ |
+ /** |
+ * Return the listener to which analysis errors will be reported. |
+ * @return the listener to which analysis errors will be reported |
+ */ |
+ RecordingErrorListener get errorListener => _errorListener; |
+ |
+ /** |
+ * Return an array containing information about all of the libraries that were resolved. |
+ * @return an array containing the libraries that were resolved |
+ */ |
+ Set<Library> get resolvedLibraries => _resolvedLibraries; |
Object visitHtmlUnit(ht.HtmlUnit node) { |
+ _parentNodes = new List<ht.XmlTagNode>(); |
_scripts = new List<HtmlScriptElement>(); |
- node.visitChildren(this); |
- _htmlElement.scripts = new List.from(_scripts); |
- _scripts = null; |
+ try { |
+ node.visitChildren(this); |
+ _htmlElement.scripts = new List.from(_scripts); |
+ } finally { |
+ _scripts = null; |
+ _parentNodes = null; |
+ } |
return null; |
} |
Object visitXmlAttributeNode(ht.XmlAttributeNode node) => null; |
Object visitXmlTagNode(ht.XmlTagNode node) { |
- if (isScriptNode(node)) { |
- Source htmlSource = _htmlElement.source; |
- ht.XmlAttributeNode scriptAttribute = getScriptSourcePath(node); |
- String scriptSourcePath = scriptAttribute == null ? null : scriptAttribute.text; |
- if (identical(node.attributeEnd.type, ht.TokenType.GT) && scriptSourcePath == null) { |
- EmbeddedHtmlScriptElementImpl script = new EmbeddedHtmlScriptElementImpl(node); |
- String contents = node.content; |
- int attributeEnd2 = node.attributeEnd.end; |
- LineInfo_Location location = _lineInfo.getLocation(attributeEnd2); |
- sc.StringScanner scanner = new sc.StringScanner(htmlSource, contents, _errorListener); |
- scanner.setSourceStart(location.lineNumber, location.columnNumber, attributeEnd2); |
- sc.Token firstToken = scanner.tokenize(); |
- List<int> lineStarts2 = scanner.lineStarts; |
- Parser parser = new Parser(null, _errorListener); |
- CompilationUnit unit = parser.parseCompilationUnit(firstToken); |
- unit.lineInfo = new LineInfo(lineStarts2); |
- try { |
- CompilationUnitBuilder builder = new CompilationUnitBuilder(); |
- CompilationUnitElementImpl elem = builder.buildCompilationUnit(htmlSource, unit); |
- LibraryElementImpl library = new LibraryElementImpl(_context, null); |
- library.definingCompilationUnit = elem; |
- script.scriptLibrary = library; |
- } on AnalysisException catch (exception) { |
- print(exception); |
+ if (_parentNodes.contains(node)) { |
+ JavaStringBuilder builder = new JavaStringBuilder(); |
+ builder.append("Found circularity in XML nodes: "); |
+ bool first = true; |
+ for (ht.XmlTagNode pathNode in _parentNodes) { |
+ if (first) { |
+ first = false; |
+ } else { |
+ builder.append(", "); |
} |
- _scripts.add(script); |
- } else { |
- ExternalHtmlScriptElementImpl script = new ExternalHtmlScriptElementImpl(node); |
- if (scriptSourcePath != null) { |
+ String tagName = pathNode.tag.lexeme; |
+ if (identical(pathNode, node)) { |
+ builder.append("*"); |
+ builder.append(tagName); |
+ builder.append("*"); |
+ } else { |
+ builder.append(tagName); |
+ } |
+ } |
+ AnalysisEngine.instance.logger.logError(builder.toString()); |
+ return null; |
+ } |
+ _parentNodes.add(node); |
+ try { |
+ if (isScriptNode(node)) { |
+ Source htmlSource = _htmlElement.source; |
+ ht.XmlAttributeNode scriptAttribute = getScriptSourcePath(node); |
+ String scriptSourcePath = scriptAttribute == null ? null : scriptAttribute.text; |
+ if (identical(node.attributeEnd.type, ht.TokenType.GT) && scriptSourcePath == null) { |
+ EmbeddedHtmlScriptElementImpl script = new EmbeddedHtmlScriptElementImpl(node); |
+ String contents = node.content; |
+ int attributeEnd2 = node.attributeEnd.end; |
+ LineInfo_Location location = _lineInfo.getLocation(attributeEnd2); |
+ sc.StringScanner scanner = new sc.StringScanner(htmlSource, contents, _errorListener); |
+ scanner.setSourceStart(location.lineNumber, location.columnNumber, attributeEnd2); |
+ sc.Token firstToken = scanner.tokenize(); |
+ List<int> lineStarts2 = scanner.lineStarts; |
+ Parser parser = new Parser(htmlSource, _errorListener); |
+ CompilationUnit unit = parser.parseCompilationUnit(firstToken); |
+ unit.lineInfo = new LineInfo(lineStarts2); |
try { |
- Uri.parse(scriptSourcePath); |
- Source scriptSource = _context.sourceFactory.resolveUri(htmlSource, scriptSourcePath); |
- script.scriptSource = scriptSource; |
- if (!scriptSource.exists()) { |
- reportError(HtmlWarningCode.URI_DOES_NOT_EXIST, scriptAttribute.offset + 1, scriptSourcePath.length, []); |
+ LibraryResolver resolver = new LibraryResolver(_context); |
+ LibraryElementImpl library = resolver.resolveEmbeddedLibrary(htmlSource, unit, true) as LibraryElementImpl; |
+ script.scriptLibrary = library; |
+ _resolvedLibraries.addAll(resolver.resolvedLibraries); |
+ _errorListener.addAll(resolver.errorListener); |
+ } on AnalysisException catch (exception) { |
+ AnalysisEngine.instance.logger.logError3(exception); |
+ } |
+ _scripts.add(script); |
+ } else { |
+ ExternalHtmlScriptElementImpl script = new ExternalHtmlScriptElementImpl(node); |
+ if (scriptSourcePath != null) { |
+ try { |
+ parseUriWithException(scriptSourcePath); |
+ Source scriptSource = _context.sourceFactory.resolveUri(htmlSource, scriptSourcePath); |
+ script.scriptSource = scriptSource; |
+ if (!scriptSource.exists()) { |
+ reportError(HtmlWarningCode.URI_DOES_NOT_EXIST, scriptAttribute.offset + 1, scriptSourcePath.length, [scriptSourcePath]); |
+ } |
+ } on URISyntaxException catch (exception) { |
+ reportError(HtmlWarningCode.INVALID_URI, scriptAttribute.offset + 1, scriptSourcePath.length, [scriptSourcePath]); |
} |
- } on URISyntaxException catch (exception) { |
- reportError(HtmlWarningCode.INVALID_URI, scriptAttribute.offset + 1, scriptSourcePath.length, []); |
} |
+ _scripts.add(script); |
} |
- _scripts.add(script); |
+ } else { |
+ node.visitChildren(this); |
} |
- } else { |
- node.visitChildren(this); |
+ } finally { |
+ _parentNodes.remove(node); |
} |
return null; |
} |
+ |
/** |
* Return the first source attribute for the given tag node, or {@code null} if it does not exist. |
* @param node the node containing attributes |
@@ -909,6 +993,7 @@ |
} |
return null; |
} |
+ |
/** |
* Determine if the specified node is a Dart script. |
* @param node the node to be tested (not {@code null}) |
@@ -931,6 +1016,7 @@ |
} |
return false; |
} |
+ |
/** |
* Report an error with the given error code at the given location. Use the given arguments to |
* compose the error message. |
@@ -943,41 +1029,43 @@ |
_errorListener.onError(new AnalysisError.con2(_htmlElement.source, offset, length, errorCode, arguments)); |
} |
} |
+ |
/** |
* Instances of the class {@code DeclarationResolver} are used to resolve declarations in an AST |
* structure to already built elements. |
*/ |
class DeclarationResolver extends RecursiveASTVisitor<Object> { |
+ |
/** |
* The compilation unit containing the AST nodes being visited. |
*/ |
CompilationUnitElement _enclosingUnit; |
+ |
/** |
* The function type alias containing the AST nodes being visited, or {@code null} if we are not |
* in the scope of a function type alias. |
*/ |
FunctionTypeAliasElement _enclosingAlias; |
+ |
/** |
* The class containing the AST nodes being visited, or {@code null} if we are not in the scope of |
* a class. |
*/ |
ClassElement _enclosingClass; |
+ |
/** |
* The method or function containing the AST nodes being visited, or {@code null} if we are not in |
* the scope of a method or function. |
*/ |
ExecutableElement _enclosingExecutable; |
+ |
/** |
* The parameter containing the AST nodes being visited, or {@code null} if we are not in the |
* scope of a parameter. |
*/ |
ParameterElement _enclosingParameter; |
+ |
/** |
- * Initialize a newly created resolver. |
- */ |
- DeclarationResolver() : super() { |
- } |
- /** |
* Resolve the declarations within the given compilation unit to the elements rooted at the given |
* element. |
* @param unit the compilation unit to be resolved |
@@ -1297,6 +1385,7 @@ |
} |
return super.visitVariableDeclaration(node); |
} |
+ |
/** |
* Append the value of the given string literal to the given string builder. |
* @param builder the builder to which the string's value is to be appended |
@@ -1315,6 +1404,7 @@ |
throw new IllegalArgumentException(); |
} |
} |
+ |
/** |
* Return the element for the part with the given source, or {@code null} if there is no element |
* for the given source. |
@@ -1330,6 +1420,7 @@ |
} |
return null; |
} |
+ |
/** |
* Return the element in the given array of elements that was created for the declaration at the |
* given offset. This method should only be used when there is no name |
@@ -1338,6 +1429,7 @@ |
* @return the element at the given offset |
*/ |
Element find2(List<Element> elements, int offset) => find4(elements, "", offset); |
+ |
/** |
* Return the element in the given array of elements that was created for the declaration with the |
* given name. |
@@ -1350,6 +1442,7 @@ |
identifier.element = element; |
return element; |
} |
+ |
/** |
* Return the element in the given array of elements that was created for the declaration with the |
* given name at the given offset. |
@@ -1358,14 +1451,15 @@ |
* @param offset the offset of the name of the element to be returned |
* @return the element with the given name and offset |
*/ |
- Element find4(List<Element> elements, String name2, int offset) { |
+ Element find4(List<Element> elements, String name, int offset) { |
for (Element element in elements) { |
- if (element.name == name2 && element.nameOffset == offset) { |
+ if (element.displayName == name && element.nameOffset == offset) { |
return element; |
} |
} |
return null; |
} |
+ |
/** |
* Return the export element from the given array whose library has the given source, or{@code null} if there is no such export. |
* @param exports the export elements being searched |
@@ -1381,6 +1475,7 @@ |
} |
return null; |
} |
+ |
/** |
* Return the import element from the given array whose library has the given source and that has |
* the given prefix, or {@code null} if there is no such import. |
@@ -1399,7 +1494,7 @@ |
return element; |
} |
} else { |
- if (prefixElement != null && prefix2.name == prefixElement.name) { |
+ if (prefixElement != null && prefix2.name == prefixElement.displayName) { |
return element; |
} |
} |
@@ -1407,6 +1502,7 @@ |
} |
return null; |
} |
+ |
/** |
* Return the value of the given string literal, or {@code null} if the string is not a constant |
* string without any string interpolation. |
@@ -1426,6 +1522,7 @@ |
return builder.toString().trim(); |
} |
} |
+ |
/** |
* Instances of the class {@code ElementResolver} are used by instances of {@link ResolverVisitor}to resolve references within the AST structure to the elements being referenced. The requirements |
* for the element resolver are: |
@@ -1474,11 +1571,75 @@ |
* @coverage dart.engine.resolver |
*/ |
class ElementResolver extends SimpleASTVisitor<Object> { |
+ |
/** |
+ * @return {@code true} if the given identifier is the return type of a constructor declaration. |
+ */ |
+ static bool isConstructorReturnType(SimpleIdentifier node) { |
+ ASTNode parent2 = node.parent; |
+ if (parent2 is ConstructorDeclaration) { |
+ ConstructorDeclaration constructor = parent2 as ConstructorDeclaration; |
+ return identical(constructor.returnType, node); |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * @return {@code true} if the given identifier is the return type of a factory constructor |
+ * declaration. |
+ */ |
+ static bool isFactoryConstructorReturnType(SimpleIdentifier node) { |
+ ASTNode parent2 = node.parent; |
+ if (parent2 is ConstructorDeclaration) { |
+ ConstructorDeclaration constructor = parent2 as ConstructorDeclaration; |
+ return identical(constructor.returnType, node) && constructor.factoryKeyword != null; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Checks if the given 'super' expression is used in the valid context. |
+ * @param node the 'super' expression to analyze |
+ * @return {@code true} if the given 'super' expression is in the valid context |
+ */ |
+ static bool isSuperInValidContext(SuperExpression node) { |
+ for (ASTNode n = node; n != null; n = n.parent) { |
+ if (n is CompilationUnit) { |
+ return false; |
+ } |
+ if (n is ConstructorDeclaration) { |
+ ConstructorDeclaration constructor = n as ConstructorDeclaration; |
+ return constructor.factoryKeyword == null; |
+ } |
+ if (n is ConstructorFieldInitializer) { |
+ return false; |
+ } |
+ if (n is MethodDeclaration) { |
+ MethodDeclaration method = n as MethodDeclaration; |
+ return !method.isStatic(); |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
* The resolver driving this participant. |
*/ |
ResolverVisitor _resolver; |
+ |
/** |
+ * The name of the method that can be implemented by a class to allow its instances to be invoked |
+ * as if they were a function. |
+ */ |
+ static String CALL_METHOD_NAME = "call"; |
+ |
+ /** |
+ * The name of the method that will be invoked if an attempt is made to invoke an undefined method |
+ * on an object. |
+ */ |
+ static String _NO_SUCH_METHOD_METHOD_NAME = "noSuchMethod"; |
+ |
+ /** |
* Initialize a newly created visitor to resolve the nodes in a compilation unit. |
* @param resolver the resolver driving this participant |
*/ |
@@ -1486,18 +1647,21 @@ |
this._resolver = resolver; |
} |
Object visitAssignmentExpression(AssignmentExpression node) { |
- sc.TokenType operator2 = node.operator.type; |
- if (operator2 != sc.TokenType.EQ) { |
- operator2 = operatorFromCompoundAssignment(operator2); |
- Expression leftNode = node.leftHandSide; |
- if (leftNode != null) { |
- Type2 leftType = getType(leftNode); |
- if (leftType != null) { |
- MethodElement method = lookUpMethod(leftType, operator2.lexeme); |
- if (method != null) { |
- node.element = method; |
- } else { |
- } |
+ sc.Token operator2 = node.operator; |
+ sc.TokenType operatorType = operator2.type; |
+ if (operatorType != sc.TokenType.EQ) { |
+ operatorType = operatorFromCompoundAssignment(operatorType); |
+ Expression leftHandSide2 = node.leftHandSide; |
+ if (leftHandSide2 != null) { |
+ String methodName = operatorType.lexeme; |
+ Type2 staticType = getStaticType(leftHandSide2); |
+ MethodElement staticMethod = lookUpMethod(leftHandSide2, staticType, methodName); |
+ node.staticElement = staticMethod; |
+ Type2 propagatedType = getPropagatedType(leftHandSide2); |
+ MethodElement propagatedMethod = lookUpMethod(leftHandSide2, propagatedType, methodName); |
+ node.element = select3(staticMethod, propagatedMethod); |
+ if (shouldReportMissingMember(staticType, staticMethod) && (propagatedType == null || shouldReportMissingMember(propagatedType, propagatedMethod))) { |
+ _resolver.reportError6(StaticTypeWarningCode.UNDEFINED_METHOD, operator2, [methodName, staticType.displayName]); |
} |
} |
} |
@@ -1506,19 +1670,19 @@ |
Object visitBinaryExpression(BinaryExpression node) { |
sc.Token operator2 = node.operator; |
if (operator2.isUserDefinableOperator()) { |
- Type2 leftType = getType(node.leftOperand); |
- if (leftType == null || leftType.isDynamic()) { |
- return null; |
- } else if (leftType is FunctionType) { |
- leftType = _resolver.typeProvider.functionType; |
+ Expression leftOperand2 = node.leftOperand; |
+ if (leftOperand2 != null) { |
+ String methodName = operator2.lexeme; |
+ Type2 staticType = getStaticType(leftOperand2); |
+ MethodElement staticMethod = lookUpMethod(leftOperand2, staticType, methodName); |
+ node.staticElement = staticMethod; |
+ Type2 propagatedType = getPropagatedType(leftOperand2); |
+ MethodElement propagatedMethod = lookUpMethod(leftOperand2, propagatedType, methodName); |
+ node.element = select3(staticMethod, propagatedMethod); |
+ if (shouldReportMissingMember(staticType, staticMethod) && (propagatedType == null || shouldReportMissingMember(propagatedType, propagatedMethod))) { |
+ _resolver.reportError6(StaticTypeWarningCode.UNDEFINED_OPERATOR, operator2, [methodName, staticType.displayName]); |
+ } |
} |
- String methodName = operator2.lexeme; |
- MethodElement member = lookUpMethod(leftType, methodName); |
- if (member == null) { |
- _resolver.reportError3(StaticWarningCode.UNDEFINED_OPERATOR, operator2, [methodName, leftType.name]); |
- } else { |
- node.element = member; |
- } |
} |
return null; |
} |
@@ -1530,19 +1694,37 @@ |
} |
return null; |
} |
+ Object visitClassDeclaration(ClassDeclaration node) { |
+ setMetadata(node.element, node); |
+ return null; |
+ } |
+ Object visitClassTypeAlias(ClassTypeAlias node) { |
+ setMetadata(node.element, node); |
+ return null; |
+ } |
Object visitCommentReference(CommentReference node) { |
Identifier identifier2 = node.identifier; |
if (identifier2 is SimpleIdentifier) { |
SimpleIdentifier simpleIdentifier = identifier2 as SimpleIdentifier; |
- visitSimpleIdentifier(simpleIdentifier); |
- Element element2 = simpleIdentifier.element; |
- if (element2 != null) { |
- if (element2.library != _resolver.definingLibrary) { |
+ Element element = resolveSimpleIdentifier(simpleIdentifier); |
+ if (element == null) { |
+ element = findImportWithoutPrefix(simpleIdentifier); |
+ if (element is MultiplyDefinedElement) { |
+ element = null; |
} |
+ } |
+ if (element == null) { |
+ } else { |
+ if (element.library != _resolver.definingLibrary) { |
+ } |
+ recordResolution(simpleIdentifier, element); |
if (node.newKeyword != null) { |
- if (element2 is ClassElement) { |
- ConstructorElement constructor = ((element2 as ClassElement)).unnamedConstructor; |
- recordResolution(simpleIdentifier, constructor); |
+ if (element is ClassElement) { |
+ ConstructorElement constructor = ((element as ClassElement)).unnamedConstructor; |
+ if (constructor == null) { |
+ } else { |
+ recordResolution(simpleIdentifier, constructor); |
+ } |
} else { |
} |
} |
@@ -1551,33 +1733,41 @@ |
PrefixedIdentifier prefixedIdentifier = identifier2 as PrefixedIdentifier; |
SimpleIdentifier prefix2 = prefixedIdentifier.prefix; |
SimpleIdentifier name = prefixedIdentifier.identifier; |
- visitSimpleIdentifier(prefix2); |
- Element element3 = prefix2.element; |
- if (element3 != null) { |
- if (element3 is PrefixElement) { |
- element3 = _resolver.nameScope.lookup(identifier2, _resolver.definingLibrary); |
- recordResolution(name, element3); |
+ Element element = resolveSimpleIdentifier(prefix2); |
+ if (element == null) { |
+ } else { |
+ if (element is PrefixElement) { |
+ recordResolution(prefix2, element); |
+ element = _resolver.nameScope.lookup(identifier2, _resolver.definingLibrary); |
+ recordResolution(name, element); |
return null; |
} |
- if (element3.library != _resolver.definingLibrary) { |
+ LibraryElement library2 = element.library; |
+ if (library2 == null) { |
+ AnalysisEngine.instance.logger.logError("Found element with null library: ${element.name}"); |
+ } else if (library2 != _resolver.definingLibrary) { |
} |
+ recordResolution(name, element); |
if (node.newKeyword == null) { |
- if (element3 is ClassElement) { |
- Element memberElement = lookupGetterOrMethod(((element3 as ClassElement)).type, name.name); |
+ if (element is ClassElement) { |
+ Element memberElement = lookupGetterOrMethod(((element as ClassElement)).type, name.name); |
if (memberElement == null) { |
- memberElement = ((element3 as ClassElement)).getNamedConstructor(name.name); |
+ memberElement = ((element as ClassElement)).getNamedConstructor(name.name); |
+ if (memberElement == null) { |
+ memberElement = lookUpSetter(prefix2, ((element as ClassElement)).type, name.name); |
+ } |
} |
if (memberElement == null) { |
- reportGetterOrSetterNotFound(prefixedIdentifier, name, element3.name); |
} else { |
recordResolution(name, memberElement); |
} |
} else { |
} |
} else { |
- if (element3 is ClassElement) { |
- ConstructorElement constructor = ((element3 as ClassElement)).getNamedConstructor(name.name); |
- if (constructor != null) { |
+ if (element is ClassElement) { |
+ ConstructorElement constructor = ((element as ClassElement)).getNamedConstructor(name.name); |
+ if (constructor == null) { |
+ } else { |
recordResolution(name, constructor); |
} |
} else { |
@@ -1587,24 +1777,41 @@ |
} |
return null; |
} |
+ Object visitConstructorDeclaration(ConstructorDeclaration node) { |
+ super.visitConstructorDeclaration(node); |
+ ConstructorElement element2 = node.element; |
+ if (element2 is ConstructorElementImpl) { |
+ ConstructorElementImpl constructorElement = element2 as ConstructorElementImpl; |
+ ConstructorName redirectedNode = node.redirectedConstructor; |
+ if (redirectedNode != null) { |
+ ConstructorElement redirectedElement = redirectedNode.element; |
+ constructorElement.redirectedConstructor = redirectedElement; |
+ } |
+ for (ConstructorInitializer initializer in node.initializers) { |
+ if (initializer is RedirectingConstructorInvocation) { |
+ ConstructorElement redirectedElement = ((initializer as RedirectingConstructorInvocation)).element; |
+ constructorElement.redirectedConstructor = redirectedElement; |
+ } |
+ } |
+ setMetadata(constructorElement, node); |
+ } |
+ return null; |
+ } |
Object visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
- FieldElement fieldElement = null; |
SimpleIdentifier fieldName2 = node.fieldName; |
ClassElement enclosingClass2 = _resolver.enclosingClass; |
- fieldElement = ((enclosingClass2 as ClassElementImpl)).getField(fieldName2.name); |
- if (fieldElement == null) { |
+ FieldElement fieldElement = ((enclosingClass2 as ClassElementImpl)).getField(fieldName2.name); |
+ recordResolution(fieldName2, fieldElement); |
+ if (fieldElement == null || fieldElement.isSynthetic()) { |
_resolver.reportError(CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTANT_FIELD, node, [fieldName2]); |
- } else if (!fieldElement.isSynthetic()) { |
- recordResolution(fieldName2, fieldElement); |
- if (fieldElement.isStatic()) { |
- _resolver.reportError(CompileTimeErrorCode.INITIALIZER_FOR_STATIC_FIELD, node, [fieldName2]); |
- } |
+ } else if (fieldElement.isStatic()) { |
+ _resolver.reportError(CompileTimeErrorCode.INITIALIZER_FOR_STATIC_FIELD, node, [fieldName2]); |
} |
return null; |
} |
Object visitConstructorName(ConstructorName node) { |
Type2 type2 = node.type.type; |
- if (type2 is DynamicTypeImpl) { |
+ if (type2 != null && type2.isDynamic()) { |
return null; |
} else if (type2 is! InterfaceType) { |
ASTNode parent2 = node.parent; |
@@ -1616,15 +1823,18 @@ |
} |
return null; |
} |
- ClassElement classElement = ((type2 as InterfaceType)).element; |
ConstructorElement constructor; |
SimpleIdentifier name2 = node.name; |
+ InterfaceType interfaceType = type2 as InterfaceType; |
+ LibraryElement definingLibrary2 = _resolver.definingLibrary; |
if (name2 == null) { |
- constructor = classElement.unnamedConstructor; |
+ constructor = interfaceType.lookUpConstructor(null, definingLibrary2); |
} else { |
- constructor = classElement.getNamedConstructor(name2.name); |
+ constructor = interfaceType.lookUpConstructor(name2.name, definingLibrary2); |
+ name2.staticElement = constructor; |
name2.element = constructor; |
} |
+ node.staticElement = constructor; |
node.element = constructor; |
return null; |
} |
@@ -1636,10 +1846,15 @@ |
} |
return null; |
} |
+ Object visitDeclaredIdentifier(DeclaredIdentifier node) { |
+ setMetadata(node.element, node); |
+ return null; |
+ } |
Object visitExportDirective(ExportDirective node) { |
Element element2 = node.element; |
if (element2 is ExportElement) { |
resolveCombinators(((element2 as ExportElement)).exportedLibrary, node.combinators); |
+ setMetadata(element2, node); |
} |
return null; |
} |
@@ -1648,37 +1863,51 @@ |
ClassElement classElement = _resolver.enclosingClass; |
if (classElement != null) { |
FieldElement fieldElement = ((classElement as ClassElementImpl)).getField(fieldName); |
- if (fieldElement != null) { |
- if (!fieldElement.isSynthetic()) { |
- ParameterElement parameterElement = node.element; |
- if (parameterElement is FieldFormalParameterElementImpl) { |
- FieldFormalParameterElementImpl fieldFormal = parameterElement as FieldFormalParameterElementImpl; |
- fieldFormal.field = fieldElement; |
- Type2 declaredType = fieldFormal.type; |
- Type2 fieldType = fieldElement.type; |
- if (node.type == null) { |
- fieldFormal.type = fieldType; |
- } |
- if (fieldElement.isStatic()) { |
- _resolver.reportError(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_STATIC_FIELD, node, [fieldName]); |
- } else if (declaredType != null && fieldType != null && !declaredType.isAssignableTo(fieldType)) { |
- _resolver.reportError(StaticWarningCode.FIELD_INITIALIZER_WITH_INVALID_TYPE, node, [declaredType.name, fieldType.name]); |
- } |
+ if (fieldElement == null) { |
+ _resolver.reportError(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTANT_FIELD, node, [fieldName]); |
+ } else { |
+ ParameterElement parameterElement = node.element; |
+ if (parameterElement is FieldFormalParameterElementImpl) { |
+ FieldFormalParameterElementImpl fieldFormal = parameterElement as FieldFormalParameterElementImpl; |
+ fieldFormal.field = fieldElement; |
+ Type2 declaredType = fieldFormal.type; |
+ Type2 fieldType = fieldElement.type; |
+ if (node.type == null) { |
+ fieldFormal.type = fieldType; |
} |
+ if (fieldElement.isSynthetic()) { |
+ _resolver.reportError(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTANT_FIELD, node, [fieldName]); |
+ } else if (fieldElement.isStatic()) { |
+ _resolver.reportError(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_STATIC_FIELD, node, [fieldName]); |
+ } else if (declaredType != null && fieldType != null && !declaredType.isAssignableTo(fieldType)) { |
+ _resolver.reportError(StaticWarningCode.FIELD_INITIALIZING_FORMAL_NOT_ASSIGNABLE, node, [declaredType.displayName, fieldType.displayName]); |
+ } |
+ } else { |
+ if (fieldElement.isSynthetic()) { |
+ _resolver.reportError(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTANT_FIELD, node, [fieldName]); |
+ } else if (fieldElement.isStatic()) { |
+ _resolver.reportError(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_STATIC_FIELD, node, [fieldName]); |
+ } |
} |
- } else { |
- _resolver.reportError(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTANT_FIELD, node, [fieldName]); |
} |
} |
return super.visitFieldFormalParameter(node); |
} |
+ Object visitFunctionDeclaration(FunctionDeclaration node) { |
+ setMetadata(node.element, node); |
+ return null; |
+ } |
Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) => null; |
+ Object visitFunctionTypeAlias(FunctionTypeAlias node) { |
+ setMetadata(node.element, node); |
+ return null; |
+ } |
Object visitImportDirective(ImportDirective node) { |
SimpleIdentifier prefixNode = node.prefix; |
if (prefixNode != null) { |
String prefixName = prefixNode.name; |
for (PrefixElement prefixElement in _resolver.definingLibrary.prefixes) { |
- if (prefixElement.name == prefixName) { |
+ if (prefixElement.displayName == prefixName) { |
recordResolution(prefixNode, prefixElement); |
break; |
} |
@@ -1686,177 +1915,160 @@ |
} |
Element element2 = node.element; |
if (element2 is ImportElement) { |
- resolveCombinators(((element2 as ImportElement)).importedLibrary, node.combinators); |
+ ImportElement importElement = element2 as ImportElement; |
+ LibraryElement library = importElement.importedLibrary; |
+ if (library != null) { |
+ resolveCombinators(library, node.combinators); |
+ } |
+ setMetadata(element2, node); |
} |
return null; |
} |
Object visitIndexExpression(IndexExpression node) { |
- Type2 arrayType = getType(node.realTarget); |
- if (arrayType == null || arrayType.isDynamic()) { |
- return null; |
+ Expression target = node.realTarget; |
+ Type2 staticType = getStaticType(target); |
+ Type2 propagatedType = getPropagatedType(target); |
+ if (node.inGetterContext()) { |
+ String methodName = sc.TokenType.INDEX.lexeme; |
+ bool error = lookUpCheckIndexOperator(node, target, methodName, staticType, propagatedType); |
+ if (error) { |
+ return null; |
+ } |
} |
- String operator; |
if (node.inSetterContext()) { |
- operator = sc.TokenType.INDEX_EQ.lexeme; |
- } else { |
- operator = sc.TokenType.INDEX.lexeme; |
+ String methodName = sc.TokenType.INDEX_EQ.lexeme; |
+ lookUpCheckIndexOperator(node, target, methodName, staticType, propagatedType); |
} |
- MethodElement member = lookUpMethod(arrayType, operator); |
- if (member == null) { |
- _resolver.reportError(StaticWarningCode.UNDEFINED_OPERATOR, node, [operator, arrayType.name]); |
- } else { |
- node.element = member; |
- } |
return null; |
} |
Object visitInstanceCreationExpression(InstanceCreationExpression node) { |
ConstructorElement invokedConstructor = node.constructorName.element; |
+ node.staticElement = invokedConstructor; |
node.element = invokedConstructor; |
- resolveNamedArguments(node.argumentList, invokedConstructor); |
+ resolveArgumentsToParameters(node.isConst(), node.argumentList, invokedConstructor); |
return null; |
} |
+ Object visitLibraryDirective(LibraryDirective node) { |
+ setMetadata(node.element, node); |
+ return null; |
+ } |
+ Object visitMethodDeclaration(MethodDeclaration node) { |
+ setMetadata(node.element, node); |
+ return null; |
+ } |
Object visitMethodInvocation(MethodInvocation node) { |
SimpleIdentifier methodName2 = node.methodName; |
Expression target = node.realTarget; |
- Element element; |
+ Element staticElement; |
+ Element propagatedElement; |
+ if (target is SuperExpression && !isSuperInValidContext((target as SuperExpression))) { |
+ return null; |
+ } |
if (target == null) { |
- element = _resolver.nameScope.lookup(methodName2, _resolver.definingLibrary); |
- if (element == null) { |
- ClassElement enclosingClass2 = _resolver.enclosingClass; |
- if (enclosingClass2 != null) { |
- InterfaceType enclosingType = enclosingClass2.type; |
- element = lookUpMethod(enclosingType, methodName2.name); |
- if (element == null) { |
- PropertyAccessorElement getter = lookUpGetter(enclosingType, methodName2.name); |
- if (getter != null) { |
- FunctionType getterType = getter.type; |
- if (getterType != null) { |
- Type2 returnType2 = getterType.returnType; |
- if (!isExecutableType(returnType2)) { |
- _resolver.reportError(StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION, methodName2, [methodName2.name]); |
- } |
- } |
- recordResolution(methodName2, getter); |
- return null; |
- } |
+ staticElement = resolveInvokedElement2(methodName2); |
+ propagatedElement = null; |
+ } else { |
+ Type2 targetType = getStaticType(target); |
+ staticElement = resolveInvokedElement(target, targetType, methodName2); |
+ propagatedElement = resolveInvokedElement(target, getPropagatedType(target), methodName2); |
+ } |
+ staticElement = convertSetterToGetter(staticElement); |
+ propagatedElement = convertSetterToGetter(propagatedElement); |
+ Element recordedElement = recordResolution2(methodName2, staticElement, propagatedElement); |
+ if (recordedElement is PropertyAccessorElement) { |
+ FunctionType getterType = ((recordedElement as PropertyAccessorElement)).type; |
+ if (getterType != null) { |
+ Type2 getterReturnType = getterType.returnType; |
+ if (getterReturnType is InterfaceType) { |
+ MethodElement callMethod = ((getterReturnType as InterfaceType)).lookUpMethod(CALL_METHOD_NAME, _resolver.definingLibrary); |
+ if (callMethod != null) { |
+ resolveArgumentsToParameters(false, node.argumentList, callMethod); |
} |
+ } else if (getterReturnType is FunctionType) { |
+ Element functionElement = ((getterReturnType as FunctionType)).element; |
+ if (functionElement is ExecutableElement) { |
+ resolveArgumentsToParameters(false, node.argumentList, (functionElement as ExecutableElement)); |
+ } |
} |
} |
- } else { |
- Type2 targetType = getType(target); |
- if (targetType is InterfaceType) { |
- InterfaceType classType = targetType as InterfaceType; |
- element = lookUpMethod(classType, methodName2.name); |
- if (element == null) { |
- PropertyAccessorElement accessor = classType.getGetter(methodName2.name); |
- if (accessor != null) { |
- Type2 returnType3 = accessor.type.returnType; |
- if (!isExecutableType(returnType3)) { |
- _resolver.reportError(StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION, methodName2, [methodName2.name]); |
- return null; |
- } |
- element = accessor; |
- } |
+ } else if (recordedElement is ExecutableElement) { |
+ resolveArgumentsToParameters(false, node.argumentList, (recordedElement as ExecutableElement)); |
+ } else if (recordedElement is VariableElement) { |
+ VariableElement variable = recordedElement as VariableElement; |
+ Type2 type2 = variable.type; |
+ if (type2 is FunctionType) { |
+ FunctionType functionType = type2 as FunctionType; |
+ List<ParameterElement> parameters2 = functionType.parameters; |
+ resolveArgumentsToParameters2(false, node.argumentList, parameters2); |
+ } else if (type2 is InterfaceType) { |
+ MethodElement callMethod = ((type2 as InterfaceType)).lookUpMethod(CALL_METHOD_NAME, _resolver.definingLibrary); |
+ if (callMethod != null) { |
+ List<ParameterElement> parameters3 = callMethod.parameters; |
+ resolveArgumentsToParameters2(false, node.argumentList, parameters3); |
} |
- if (element == null && target is SuperExpression) { |
- _resolver.reportError(StaticTypeWarningCode.UNDEFINED_SUPER_METHOD, methodName2, [methodName2.name, targetType.element.name]); |
- return null; |
- } |
- } else if (target is SimpleIdentifier) { |
- Element targetElement = ((target as SimpleIdentifier)).element; |
- if (targetElement is PrefixElement) { |
- String name3 = "${((target as SimpleIdentifier)).name}.${methodName2}"; |
- Identifier functionName = new Identifier_8(name3); |
- element = _resolver.nameScope.lookup(functionName, _resolver.definingLibrary); |
- } else { |
- return null; |
- } |
- } else { |
- return null; |
} |
} |
- ExecutableElement invokedMethod = null; |
- if (element is PropertyAccessorElement) { |
- PropertyAccessorElement getter = element as PropertyAccessorElement; |
- FunctionType getterType = getter.type; |
- if (getterType != null) { |
- Type2 returnType4 = getterType.returnType; |
- if (!isExecutableType(returnType4)) { |
- _resolver.reportError(StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION, methodName2, [methodName2.name]); |
- } |
+ ErrorCode errorCode; |
+ if (staticElement == null) { |
+ if (propagatedElement == null) { |
+ errorCode = checkForInvocationError(target, staticElement); |
+ } else { |
+ errorCode = checkForInvocationError(target, propagatedElement); |
} |
- recordResolution(methodName2, element); |
- return null; |
- } else if (element is ExecutableElement) { |
- invokedMethod = element as ExecutableElement; |
} else { |
- if (element is PropertyInducingElement) { |
- PropertyAccessorElement getter2 = ((element as PropertyInducingElement)).getter; |
- FunctionType getterType = getter2.type; |
- if (getterType != null) { |
- Type2 returnType5 = getterType.returnType; |
- if (!isExecutableType(returnType5)) { |
- _resolver.reportError(StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION, methodName2, [methodName2.name]); |
- } |
- } |
- recordResolution(methodName2, element); |
- return null; |
- } else if (element is VariableElement) { |
- Type2 variableType = _resolver.overrideManager.getType(element); |
- if (variableType == null) { |
- variableType = ((element as VariableElement)).type; |
- } |
- if (!isExecutableType(variableType)) { |
- _resolver.reportError(StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION, methodName2, [methodName2.name]); |
- } |
- recordResolution(methodName2, element); |
- return null; |
+ errorCode = checkForInvocationError(target, staticElement); |
+ if (propagatedElement != null) { |
+ ErrorCode propagatedError = checkForInvocationError(target, propagatedElement); |
+ errorCode = select(errorCode, propagatedError); |
+ } |
+ } |
+ if (identical(errorCode, StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION)) { |
+ _resolver.reportError(StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION, methodName2, [methodName2.name]); |
+ } else if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_FUNCTION)) { |
+ _resolver.reportError(StaticTypeWarningCode.UNDEFINED_FUNCTION, methodName2, [methodName2.name]); |
+ } else if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_METHOD)) { |
+ String targetTypeName; |
+ if (target == null) { |
+ ClassElement enclosingClass2 = _resolver.enclosingClass; |
+ targetTypeName = enclosingClass2.displayName; |
} else { |
- if (target == null) { |
- ClassElement enclosingClass3 = _resolver.enclosingClass; |
- if (enclosingClass3 == null) { |
- _resolver.reportError(StaticTypeWarningCode.UNDEFINED_FUNCTION, methodName2, [methodName2.name]); |
- } else if (element == null) { |
- _resolver.reportError(StaticTypeWarningCode.UNDEFINED_METHOD, methodName2, [methodName2.name, enclosingClass3.name]); |
- } else { |
- _resolver.reportError(StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION, methodName2, [methodName2.name]); |
- } |
- } else { |
- Type2 targetType = getType(target); |
- String targetTypeName = targetType == null ? null : targetType.name; |
- if (targetTypeName == null) { |
- _resolver.reportError(StaticTypeWarningCode.UNDEFINED_FUNCTION, methodName2, [methodName2.name]); |
- } else { |
- if (!doesClassDeclareNoSuchMethod(targetType.element)) { |
- _resolver.reportError(StaticTypeWarningCode.UNDEFINED_METHOD, methodName2, [methodName2.name, targetTypeName]); |
- } |
- } |
+ Type2 targetType = getPropagatedType(target); |
+ if (targetType == null) { |
+ targetType = getStaticType(target); |
} |
- return null; |
+ targetTypeName = targetType == null ? null : targetType.displayName; |
} |
+ _resolver.reportError(StaticTypeWarningCode.UNDEFINED_METHOD, methodName2, [methodName2.name, targetTypeName]); |
+ } else if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_SUPER_METHOD)) { |
+ Type2 targetType = getPropagatedType(target); |
+ if (targetType == null) { |
+ targetType = getStaticType(target); |
+ } |
+ String targetTypeName = targetType == null ? null : targetType.name; |
+ _resolver.reportError(StaticTypeWarningCode.UNDEFINED_SUPER_METHOD, methodName2, [methodName2.name, targetTypeName]); |
} |
- recordResolution(methodName2, invokedMethod); |
- resolveNamedArguments(node.argumentList, invokedMethod); |
return null; |
} |
+ Object visitPartDirective(PartDirective node) { |
+ setMetadata(node.element, node); |
+ return null; |
+ } |
+ Object visitPartOfDirective(PartOfDirective node) { |
+ setMetadata(node.element, node); |
+ return null; |
+ } |
Object visitPostfixExpression(PostfixExpression node) { |
- sc.Token operator2 = node.operator; |
- Type2 operandType = getType(node.operand); |
- if (operandType == null || operandType.isDynamic()) { |
- return null; |
+ Expression operand2 = node.operand; |
+ String methodName = getPostfixOperator(node); |
+ Type2 staticType = getStaticType(operand2); |
+ MethodElement staticMethod = lookUpMethod(operand2, staticType, methodName); |
+ node.staticElement = staticMethod; |
+ Type2 propagatedType = getPropagatedType(operand2); |
+ MethodElement propagatedMethod = lookUpMethod(operand2, propagatedType, methodName); |
+ node.element = select3(staticMethod, propagatedMethod); |
+ if (shouldReportMissingMember(staticType, staticMethod) && (propagatedType == null || shouldReportMissingMember(propagatedType, propagatedMethod))) { |
+ _resolver.reportError6(StaticTypeWarningCode.UNDEFINED_OPERATOR, node.operator, [methodName, staticType.displayName]); |
} |
- String methodName; |
- if (identical(operator2.type, sc.TokenType.PLUS_PLUS)) { |
- methodName = sc.TokenType.PLUS.lexeme; |
- } else { |
- methodName = sc.TokenType.MINUS.lexeme; |
- } |
- MethodElement member = lookUpMethod(operandType, methodName); |
- if (member == null) { |
- _resolver.reportError3(StaticWarningCode.UNDEFINED_OPERATOR, operator2, [methodName, operandType.name]); |
- } else { |
- node.element = member; |
- } |
return null; |
} |
Object visitPrefixedIdentifier(PrefixedIdentifier node) { |
@@ -1868,138 +2080,46 @@ |
if (element == null) { |
return null; |
} |
- recordResolution(identifier2, element); |
- return null; |
- } |
- if (prefixElement is ClassElement) { |
- Element memberElement; |
- if (node.identifier.inSetterContext()) { |
- memberElement = ((prefixElement as ClassElementImpl)).getSetter(identifier2.name); |
- } else { |
- memberElement = ((prefixElement as ClassElementImpl)).getGetter(identifier2.name); |
- } |
- if (memberElement == null) { |
- MethodElement methodElement = lookUpMethod(((prefixElement as ClassElement)).type, identifier2.name); |
- if (methodElement != null) { |
- recordResolution(identifier2, methodElement); |
- return null; |
+ if (element is PropertyAccessorElement && identifier2.inSetterContext()) { |
+ PropertyInducingElement variable2 = ((element as PropertyAccessorElement)).variable; |
+ if (variable2 != null) { |
+ PropertyAccessorElement setter2 = variable2.setter; |
+ if (setter2 != null) { |
+ element = setter2; |
+ } |
} |
} |
- if (memberElement == null) { |
- reportGetterOrSetterNotFound(node, identifier2, prefixElement.name); |
- } else { |
- recordResolution(identifier2, memberElement); |
- } |
+ recordResolution(identifier2, element); |
return null; |
} |
- Type2 variableType; |
- if (prefixElement is PropertyAccessorElement) { |
- PropertyAccessorElement accessor = prefixElement as PropertyAccessorElement; |
- FunctionType type2 = accessor.type; |
- if (type2 == null) { |
- return null; |
- } |
- if (accessor.isGetter()) { |
- variableType = type2.returnType; |
- } else { |
- variableType = type2.normalParameterTypes[0]; |
- } |
- if (variableType == null || variableType.isDynamic()) { |
- return null; |
- } |
- } else if (prefixElement is VariableElement) { |
- variableType = _resolver.overrideManager.getType(prefixElement); |
- if (variableType == null) { |
- variableType = ((prefixElement as VariableElement)).type; |
- } |
- if (variableType == null || variableType.isDynamic()) { |
- return null; |
- } |
- } else { |
- return null; |
- } |
- PropertyAccessorElement memberElement = null; |
- if (node.identifier.inSetterContext()) { |
- memberElement = lookUpSetter(variableType, identifier2.name); |
- } |
- if (memberElement == null && node.identifier.inGetterContext()) { |
- memberElement = lookUpGetter(variableType, identifier2.name); |
- } |
- if (memberElement == null) { |
- MethodElement methodElement = lookUpMethod(variableType, identifier2.name); |
- if (methodElement != null) { |
- recordResolution(identifier2, methodElement); |
- return null; |
- } |
- } |
- if (memberElement == null) { |
- reportGetterOrSetterNotFound(node, identifier2, variableType.element.name); |
- } else { |
- recordResolution(identifier2, memberElement); |
- } |
+ resolvePropertyAccess(prefix2, identifier2); |
return null; |
} |
Object visitPrefixExpression(PrefixExpression node) { |
sc.Token operator2 = node.operator; |
sc.TokenType operatorType = operator2.type; |
if (operatorType.isUserDefinableOperator() || identical(operatorType, sc.TokenType.PLUS_PLUS) || identical(operatorType, sc.TokenType.MINUS_MINUS)) { |
- Type2 operandType = getType(node.operand); |
- if (operandType == null || operandType.isDynamic()) { |
- return null; |
+ Expression operand2 = node.operand; |
+ String methodName = getPrefixOperator(node); |
+ Type2 staticType = getStaticType(operand2); |
+ MethodElement staticMethod = lookUpMethod(operand2, staticType, methodName); |
+ node.staticElement = staticMethod; |
+ Type2 propagatedType = getPropagatedType(operand2); |
+ MethodElement propagatedMethod = lookUpMethod(operand2, propagatedType, methodName); |
+ node.element = select3(staticMethod, propagatedMethod); |
+ if (shouldReportMissingMember(staticType, staticMethod) && (propagatedType == null || shouldReportMissingMember(propagatedType, propagatedMethod))) { |
+ _resolver.reportError6(StaticTypeWarningCode.UNDEFINED_OPERATOR, operator2, [methodName, staticType.displayName]); |
} |
- String methodName; |
- if (identical(operatorType, sc.TokenType.PLUS_PLUS)) { |
- methodName = sc.TokenType.PLUS.lexeme; |
- } else if (identical(operatorType, sc.TokenType.MINUS_MINUS)) { |
- methodName = sc.TokenType.MINUS.lexeme; |
- } else if (identical(operatorType, sc.TokenType.MINUS)) { |
- methodName = "unary-"; |
- } else { |
- methodName = operator2.lexeme; |
- } |
- MethodElement member = lookUpMethod(operandType, methodName); |
- if (member == null) { |
- _resolver.reportError3(StaticWarningCode.UNDEFINED_OPERATOR, operator2, [methodName, operandType.name]); |
- } else { |
- node.element = member; |
- } |
} |
return null; |
} |
Object visitPropertyAccess(PropertyAccess node) { |
- Type2 targetType = getType(node.realTarget); |
- if (targetType is! InterfaceType) { |
+ Expression target = node.realTarget; |
+ if (target is SuperExpression && !isSuperInValidContext((target as SuperExpression))) { |
return null; |
} |
- SimpleIdentifier identifier = node.propertyName; |
- PropertyAccessorElement memberElement = null; |
- if (identifier.inSetterContext()) { |
- memberElement = lookUpSetter(targetType, identifier.name); |
- } |
- if (memberElement == null && identifier.inGetterContext()) { |
- memberElement = lookUpGetter(targetType, identifier.name); |
- } |
- if (memberElement == null) { |
- MethodElement methodElement = lookUpMethod(targetType, identifier.name); |
- if (methodElement != null) { |
- recordResolution(identifier, methodElement); |
- return null; |
- } |
- } |
- if (memberElement == null) { |
- if (!doesClassDeclareNoSuchMethod(targetType.element)) { |
- if (identifier.inSetterContext()) { |
- _resolver.reportError(StaticWarningCode.UNDEFINED_SETTER, identifier, [identifier.name, targetType.name]); |
- } else if (identifier.inGetterContext()) { |
- _resolver.reportError(StaticWarningCode.UNDEFINED_GETTER, identifier, [identifier.name, targetType.name]); |
- } else { |
- print("two ${identifier.name}"); |
- _resolver.reportError(StaticWarningCode.UNDEFINED_IDENTIFIER, identifier, [identifier.name]); |
- } |
- } |
- } else { |
- recordResolution(identifier, memberElement); |
- } |
+ SimpleIdentifier propertyName2 = node.propertyName; |
+ resolvePropertyAccess(target, propertyName2); |
return null; |
} |
Object visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) { |
@@ -2020,39 +2140,22 @@ |
if (name != null) { |
recordResolution(name, element); |
} |
+ node.staticElement = element; |
node.element = element; |
- resolveNamedArguments(node.argumentList, element); |
+ resolveArgumentsToParameters(false, node.argumentList, element); |
return null; |
} |
Object visitSimpleIdentifier(SimpleIdentifier node) { |
if (node.element != null) { |
return null; |
} |
- Element element = _resolver.nameScope.lookup(node, _resolver.definingLibrary); |
- if (element is PropertyAccessorElement && node.inSetterContext()) { |
- PropertyInducingElement variable2 = ((element as PropertyAccessorElement)).variable; |
- if (variable2 != null) { |
- PropertyAccessorElement setter2 = variable2.setter; |
- if (setter2 != null) { |
- element = setter2; |
- } |
- } |
- } |
- ClassElement enclosingClass2 = _resolver.enclosingClass; |
- if (element == null && enclosingClass2 != null) { |
- InterfaceType enclosingType = enclosingClass2.type; |
- if (element == null && node.inSetterContext()) { |
- element = lookUpSetter(enclosingType, node.name); |
- } |
- if (element == null && node.inGetterContext()) { |
- element = lookUpGetter(enclosingType, node.name); |
- } |
- if (element == null) { |
- element = lookUpMethod(enclosingType, node.name); |
- } |
- } |
- if (element == null) { |
- if (!doesClassDeclareNoSuchMethod(enclosingClass2)) { |
+ Element element = resolveSimpleIdentifier(node); |
+ if (isFactoryConstructorReturnType(node) && element != _resolver.enclosingClass) { |
+ _resolver.reportError(CompileTimeErrorCode.INVALID_FACTORY_NAME_NOT_A_CLASS, node, []); |
+ } else if (element == null) { |
+ if (isConstructorReturnType(node)) { |
+ _resolver.reportError(CompileTimeErrorCode.INVALID_CONSTRUCTOR_NAME, node, []); |
+ } else if (!classDeclaresNoSuchMethod(_resolver.enclosingClass)) { |
_resolver.reportError(StaticWarningCode.UNDEFINED_IDENTIFIER, node, [node.name]); |
} |
} |
@@ -2076,15 +2179,31 @@ |
element = superclass.getNamedConstructor(name.name); |
} |
if (element == null) { |
+ if (name != null) { |
+ _resolver.reportError(CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER, node, [superclass.name, name]); |
+ } else { |
+ _resolver.reportError(CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT, node, [superclass.name]); |
+ } |
return null; |
+ } else { |
+ if (element.isFactory()) { |
+ _resolver.reportError(CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR, node, [element]); |
+ } |
} |
if (name != null) { |
recordResolution(name, element); |
} |
+ node.staticElement = element; |
node.element = element; |
- resolveNamedArguments(node.argumentList, element); |
+ resolveArgumentsToParameters(false, node.argumentList, element); |
return null; |
} |
+ Object visitSuperExpression(SuperExpression node) { |
+ if (!isSuperInValidContext(node)) { |
+ _resolver.reportError(CompileTimeErrorCode.SUPER_IN_INVALID_CONTEXT, node, []); |
+ } |
+ return super.visitSuperExpression(node); |
+ } |
Object visitTypeParameter(TypeParameter node) { |
TypeName bound2 = node.bound; |
if (bound2 != null) { |
@@ -2093,48 +2212,216 @@ |
variable.bound = bound2.type; |
} |
} |
+ setMetadata(node.element, node); |
return null; |
} |
+ Object visitVariableDeclaration(VariableDeclaration node) { |
+ setMetadata(node.element, node); |
+ return null; |
+ } |
+ |
/** |
- * Return {@code true} if the passed {@link Element} is a {@link ClassElement} that declares a |
- * method "noSuchMethod". |
- * @param element the {@link Element} to evaluate |
- * @return {@code true} if the passed {@link Element} is a {@link ClassElement} that declares a |
- * method "noSuchMethod" |
+ * Generate annotation elements for each of the annotations in the given node list and add them to |
+ * the given list of elements. |
+ * @param annotationList the list of elements to which new elements are to be added |
+ * @param annotations the AST nodes used to generate new elements |
*/ |
- bool doesClassDeclareNoSuchMethod(Element element) { |
- if (element == null) { |
- return false; |
+ void addAnnotations(List<AnnotationImpl> annotationList, NodeList<Annotation> annotations) { |
+ for (Annotation annotationNode in annotations) { |
+ Element resolvedElement = annotationNode.element; |
+ if (resolvedElement != null) { |
+ annotationList.add(new AnnotationImpl(resolvedElement)); |
+ } |
} |
- if (element is! ClassElementImpl) { |
- return false; |
+ } |
+ |
+ /** |
+ * Given that we have found code to invoke the given element, return the error code that should be |
+ * reported, or {@code null} if no error should be reported. |
+ * @param target the target of the invocation, or {@code null} if there was no target |
+ * @param element the element to be invoked |
+ * @return the error code that should be reported |
+ */ |
+ ErrorCode checkForInvocationError(Expression target, Element element2) { |
+ if (element2 is PropertyAccessorElement) { |
+ FunctionType getterType = ((element2 as PropertyAccessorElement)).type; |
+ if (getterType != null) { |
+ Type2 returnType2 = getterType.returnType; |
+ if (!isExecutableType(returnType2)) { |
+ return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION; |
+ } |
+ } |
+ } else if (element2 is ExecutableElement) { |
+ return null; |
+ } else if (element2 == null && target is SuperExpression) { |
+ return StaticTypeWarningCode.UNDEFINED_SUPER_METHOD; |
+ } else { |
+ if (element2 is PropertyInducingElement) { |
+ PropertyAccessorElement getter2 = ((element2 as PropertyInducingElement)).getter; |
+ FunctionType getterType = getter2.type; |
+ if (getterType != null) { |
+ Type2 returnType3 = getterType.returnType; |
+ if (!isExecutableType(returnType3)) { |
+ return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION; |
+ } |
+ } |
+ } else if (element2 is VariableElement) { |
+ Type2 variableType = _resolver.overrideManager.getType(element2); |
+ if (variableType == null) { |
+ variableType = ((element2 as VariableElement)).type; |
+ } |
+ if (!isExecutableType(variableType)) { |
+ return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION; |
+ } |
+ } else { |
+ if (target == null) { |
+ ClassElement enclosingClass2 = _resolver.enclosingClass; |
+ if (enclosingClass2 == null) { |
+ return StaticTypeWarningCode.UNDEFINED_FUNCTION; |
+ } else if (element2 == null) { |
+ if (!classDeclaresNoSuchMethod(enclosingClass2)) { |
+ return StaticTypeWarningCode.UNDEFINED_METHOD; |
+ } |
+ } else { |
+ return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION; |
+ } |
+ } else { |
+ Type2 targetType = getStaticType(target); |
+ if (targetType == null) { |
+ return StaticTypeWarningCode.UNDEFINED_FUNCTION; |
+ } else if (!targetType.isDynamic() && !classDeclaresNoSuchMethod2(targetType.element)) { |
+ return StaticTypeWarningCode.UNDEFINED_METHOD; |
+ } |
+ } |
+ } |
} |
- ClassElementImpl classElement = element as ClassElementImpl; |
- MethodElement method = classElement.lookUpMethod("noSuchMethod", _resolver.definingLibrary); |
- if (method == null) { |
+ return null; |
+ } |
+ |
+ /** |
+ * Return {@code true} if the given class declares a method named "noSuchMethod" and is not the |
+ * class 'Object'. |
+ * @param element the class being tested |
+ * @return {@code true} if the given class declares a method named "noSuchMethod" |
+ */ |
+ bool classDeclaresNoSuchMethod(ClassElement classElement) { |
+ if (classElement == null) { |
return false; |
} |
- return true; |
+ MethodElement methodElement = classElement.lookUpMethod(_NO_SUCH_METHOD_METHOD_NAME, _resolver.definingLibrary); |
+ return methodElement != null && methodElement.enclosingElement.supertype != null; |
} |
+ |
/** |
- * Search through the array of parameters for a parameter whose name matches the given name. |
- * Return the parameter with the given name, or {@code null} if there is no such parameter. |
- * @param parameters the parameters being searched |
- * @param name the name being searched for |
- * @return the parameter with the given name |
+ * Return {@code true} if the given element represents a class that declares a method named |
+ * "noSuchMethod" and is not the class 'Object'. |
+ * @param element the element being tested |
+ * @return {@code true} if the given element represents a class that declares a method named |
+ * "noSuchMethod" |
*/ |
- ParameterElement findNamedParameter(List<ParameterElement> parameters, String name2) { |
- for (ParameterElement parameter in parameters) { |
- if (identical(parameter.parameterKind, ParameterKind.NAMED)) { |
- String parameteName = parameter.name; |
- if (parameteName != null && parameteName == name2) { |
- return parameter; |
+ bool classDeclaresNoSuchMethod2(Element element) { |
+ if (element is ClassElement) { |
+ return classDeclaresNoSuchMethod((element as ClassElement)); |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * If the given element is a setter, return the getter associated with it. Otherwise, return the |
+ * element unchanged. |
+ * @param element the element to be normalized |
+ * @return a non-setter element derived from the given element |
+ */ |
+ Element convertSetterToGetter(Element element) { |
+ if (element is PropertyAccessorElement) { |
+ return ((element as PropertyAccessorElement)).variable.getter; |
+ } |
+ return element; |
+ } |
+ |
+ /** |
+ * Look for any declarations of the given identifier that are imported using a prefix. Return the |
+ * element that was found, or {@code null} if the name is not imported using a prefix. |
+ * @param identifier the identifier that might have been imported using a prefix |
+ * @return the element that was found |
+ */ |
+ Element findImportWithoutPrefix(SimpleIdentifier identifier) { |
+ Element element = null; |
+ Scope nameScope2 = _resolver.nameScope; |
+ LibraryElement definingLibrary2 = _resolver.definingLibrary; |
+ for (ImportElement importElement in definingLibrary2.imports) { |
+ PrefixElement prefixElement = importElement.prefix; |
+ if (prefixElement != null) { |
+ Identifier prefixedIdentifier = new ElementResolver_SyntheticIdentifier("${prefixElement.name}.${identifier.name}"); |
+ Element importedElement = nameScope2.lookup(prefixedIdentifier, definingLibrary2); |
+ if (importedElement != null) { |
+ if (element == null) { |
+ element = importedElement; |
+ } else { |
+ element = new MultiplyDefinedElementImpl(definingLibrary2.context, element, importedElement); |
+ } |
} |
} |
} |
- return null; |
+ return element; |
} |
+ |
/** |
+ * Return the name of the method invoked by the given postfix expression. |
+ * @param node the postfix expression being invoked |
+ * @return the name of the method invoked by the expression |
+ */ |
+ String getPostfixOperator(PostfixExpression node) => (identical(node.operator.type, sc.TokenType.PLUS_PLUS)) ? sc.TokenType.PLUS.lexeme : sc.TokenType.MINUS.lexeme; |
+ |
+ /** |
+ * Return the name of the method invoked by the given postfix expression. |
+ * @param node the postfix expression being invoked |
+ * @return the name of the method invoked by the expression |
+ */ |
+ String getPrefixOperator(PrefixExpression node) { |
+ sc.Token operator2 = node.operator; |
+ sc.TokenType operatorType = operator2.type; |
+ if (identical(operatorType, sc.TokenType.PLUS_PLUS)) { |
+ return sc.TokenType.PLUS.lexeme; |
+ } else if (identical(operatorType, sc.TokenType.MINUS_MINUS)) { |
+ return sc.TokenType.MINUS.lexeme; |
+ } else if (identical(operatorType, sc.TokenType.MINUS)) { |
+ return "unary-"; |
+ } else { |
+ return operator2.lexeme; |
+ } |
+ } |
+ |
+ /** |
+ * Return the propagated type of the given expression that is to be used for type analysis. |
+ * @param expression the expression whose type is to be returned |
+ * @return the type of the given expression |
+ */ |
+ Type2 getPropagatedType(Expression expression) { |
+ Type2 propagatedType2 = resolveTypeVariable(expression.propagatedType); |
+ if (propagatedType2 is FunctionType) { |
+ propagatedType2 = _resolver.typeProvider.functionType; |
+ } |
+ return propagatedType2; |
+ } |
+ |
+ /** |
+ * Return the static type of the given expression that is to be used for type analysis. |
+ * @param expression the expression whose type is to be returned |
+ * @return the type of the given expression |
+ */ |
+ Type2 getStaticType(Expression expression) { |
+ if (expression is NullLiteral) { |
+ return _resolver.typeProvider.objectType; |
+ } |
+ Type2 staticType2 = resolveTypeVariable(expression.staticType); |
+ if (staticType2 is FunctionType) { |
+ staticType2 = _resolver.typeProvider.functionType; |
+ } |
+ return staticType2; |
+ } |
+ |
+ /** |
* Return the element representing the superclass of the given class. |
* @param targetClass the class whose superclass is to be returned |
* @return the element representing the superclass of the given class |
@@ -2146,18 +2433,8 @@ |
} |
return superType.element; |
} |
+ |
/** |
- * Return the type of the given expression that is to be used for type analysis. |
- * @param expression the expression whose type is to be returned |
- * @return the type of the given expression |
- */ |
- Type2 getType(Expression expression) { |
- if (expression is NullLiteral) { |
- return _resolver.typeProvider.objectType; |
- } |
- return expression.staticType; |
- } |
- /** |
* Return {@code true} if the given type represents an object that could be invoked using the call |
* operator '()'. |
* @param type the type being tested |
@@ -2168,51 +2445,105 @@ |
return true; |
} else if (type is InterfaceType) { |
ClassElement classElement = ((type as InterfaceType)).element; |
- MethodElement methodElement = classElement.lookUpMethod("call", _resolver.definingLibrary); |
+ MethodElement methodElement = classElement.lookUpMethod(CALL_METHOD_NAME, _resolver.definingLibrary); |
return methodElement != null; |
} |
return false; |
} |
+ |
/** |
+ * Return {@code true} if the given element is a static element. |
+ * @param element the element being tested |
+ * @return {@code true} if the given element is a static element |
+ */ |
+ bool isStatic(Element element) { |
+ if (element is ExecutableElement) { |
+ return ((element as ExecutableElement)).isStatic(); |
+ } else if (element is PropertyInducingElement) { |
+ return ((element as PropertyInducingElement)).isStatic(); |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Looks up the method element with the given name for index expression, reports{@link StaticWarningCode#UNDEFINED_OPERATOR} if not found. |
+ * @param node the index expression to resolve |
+ * @param target the target of the expression |
+ * @param methodName the name of the operator associated with the context of using of the given |
+ * index expression |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ */ |
+ bool lookUpCheckIndexOperator(IndexExpression node, Expression target, String methodName, Type2 staticType, Type2 propagatedType) { |
+ MethodElement staticMethod = lookUpMethod(target, staticType, methodName); |
+ MethodElement propagatedMethod = lookUpMethod(target, propagatedType, methodName); |
+ node.staticElement = staticMethod; |
+ node.element = select3(staticMethod, propagatedMethod); |
+ if (shouldReportMissingMember(staticType, staticMethod) && (propagatedType == null || shouldReportMissingMember(propagatedType, propagatedMethod))) { |
+ sc.Token leftBracket2 = node.leftBracket; |
+ sc.Token rightBracket2 = node.rightBracket; |
+ if (leftBracket2 == null || rightBracket2 == null) { |
+ _resolver.reportError(StaticTypeWarningCode.UNDEFINED_OPERATOR, node, [methodName, staticType.displayName]); |
+ return true; |
+ } else { |
+ int offset2 = leftBracket2.offset; |
+ int length = rightBracket2.offset - offset2 + 1; |
+ _resolver.reportError5(StaticTypeWarningCode.UNDEFINED_OPERATOR, offset2, length, [methodName, staticType.displayName]); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
* Look up the getter with the given name in the given type. Return the element representing the |
* getter that was found, or {@code null} if there is no getter with the given name. |
+ * @param target the target of the invocation, or {@code null} if there is no target |
* @param type the type in which the getter is defined |
* @param getterName the name of the getter being looked up |
* @return the element representing the getter that was found |
*/ |
- PropertyAccessorElement lookUpGetter(Type2 type, String getterName) { |
+ PropertyAccessorElement lookUpGetter(Expression target, Type2 type, String getterName) { |
type = resolveTypeVariable(type); |
if (type is InterfaceType) { |
InterfaceType interfaceType = type as InterfaceType; |
- PropertyAccessorElement accessor = interfaceType.lookUpGetter(getterName, _resolver.definingLibrary); |
+ PropertyAccessorElement accessor; |
+ if (target is SuperExpression) { |
+ accessor = interfaceType.lookUpGetterInSuperclass(getterName, _resolver.definingLibrary); |
+ } else { |
+ accessor = interfaceType.lookUpGetter(getterName, _resolver.definingLibrary); |
+ } |
if (accessor != null) { |
return accessor; |
} |
- return lookUpGetterInInterfaces(interfaceType, getterName, new Set<ClassElement>()); |
+ return lookUpGetterInInterfaces(interfaceType, false, getterName, new Set<ClassElement>()); |
} |
return null; |
} |
+ |
/** |
* Look up the getter with the given name in the interfaces implemented by the given type, either |
* directly or indirectly. Return the element representing the getter that was found, or{@code null} if there is no getter with the given name. |
* @param targetType the type in which the getter might be defined |
+ * @param includeTargetType {@code true} if the search should include the target type |
* @param getterName the name of the getter being looked up |
* @param visitedInterfaces a set containing all of the interfaces that have been examined, used |
* to prevent infinite recursion and to optimize the search |
* @return the element representing the getter that was found |
*/ |
- PropertyAccessorElement lookUpGetterInInterfaces(InterfaceType targetType, String getterName, Set<ClassElement> visitedInterfaces) { |
+ PropertyAccessorElement lookUpGetterInInterfaces(InterfaceType targetType, bool includeTargetType, String getterName, Set<ClassElement> visitedInterfaces) { |
ClassElement targetClass = targetType.element; |
if (visitedInterfaces.contains(targetClass)) { |
return null; |
} |
javaSetAdd(visitedInterfaces, targetClass); |
- PropertyAccessorElement getter = targetType.getGetter(getterName); |
- if (getter != null) { |
- return getter; |
+ if (includeTargetType) { |
+ PropertyAccessorElement getter = targetType.getGetter(getterName); |
+ if (getter != null) { |
+ return getter; |
+ } |
} |
for (InterfaceType interfaceType in targetType.interfaces) { |
- getter = lookUpGetterInInterfaces(interfaceType, getterName, visitedInterfaces); |
+ PropertyAccessorElement getter = lookUpGetterInInterfaces(interfaceType, true, getterName, visitedInterfaces); |
if (getter != null) { |
return getter; |
} |
@@ -2221,8 +2552,9 @@ |
if (superclass2 == null) { |
return null; |
} |
- return lookUpGetterInInterfaces(superclass2, getterName, visitedInterfaces); |
+ return lookUpGetterInInterfaces(superclass2, true, getterName, visitedInterfaces); |
} |
+ |
/** |
* Look up the method or getter with the given name in the given type. Return the element |
* representing the method or getter that was found, or {@code null} if there is no method or |
@@ -2243,36 +2575,40 @@ |
if (member != null) { |
return member; |
} |
- return lookUpGetterOrMethodInInterfaces(interfaceType, memberName, new Set<ClassElement>()); |
+ return lookUpGetterOrMethodInInterfaces(interfaceType, false, memberName, new Set<ClassElement>()); |
} |
return null; |
} |
+ |
/** |
* Look up the method or getter with the given name in the interfaces implemented by the given |
* type, either directly or indirectly. Return the element representing the method or getter that |
* was found, or {@code null} if there is no method or getter with the given name. |
* @param targetType the type in which the method or getter might be defined |
+ * @param includeTargetType {@code true} if the search should include the target type |
* @param memberName the name of the method or getter being looked up |
* @param visitedInterfaces a set containing all of the interfaces that have been examined, used |
* to prevent infinite recursion and to optimize the search |
* @return the element representing the method or getter that was found |
*/ |
- ExecutableElement lookUpGetterOrMethodInInterfaces(InterfaceType targetType, String memberName, Set<ClassElement> visitedInterfaces) { |
+ ExecutableElement lookUpGetterOrMethodInInterfaces(InterfaceType targetType, bool includeTargetType, String memberName, Set<ClassElement> visitedInterfaces) { |
ClassElement targetClass = targetType.element; |
if (visitedInterfaces.contains(targetClass)) { |
return null; |
} |
javaSetAdd(visitedInterfaces, targetClass); |
- ExecutableElement member = targetType.getMethod(memberName); |
- if (member != null) { |
- return member; |
+ if (includeTargetType) { |
+ ExecutableElement member = targetType.getMethod(memberName); |
+ if (member != null) { |
+ return member; |
+ } |
+ member = targetType.getGetter(memberName); |
+ if (member != null) { |
+ return member; |
+ } |
} |
- member = targetType.getGetter(memberName); |
- if (member != null) { |
- return member; |
- } |
for (InterfaceType interfaceType in targetType.interfaces) { |
- member = lookUpGetterOrMethodInInterfaces(interfaceType, memberName, visitedInterfaces); |
+ ExecutableElement member = lookUpGetterOrMethodInInterfaces(interfaceType, true, memberName, visitedInterfaces); |
if (member != null) { |
return member; |
} |
@@ -2281,8 +2617,9 @@ |
if (superclass2 == null) { |
return null; |
} |
- return lookUpGetterInInterfaces(superclass2, memberName, visitedInterfaces); |
+ return lookUpGetterOrMethodInInterfaces(superclass2, true, memberName, visitedInterfaces); |
} |
+ |
/** |
* Find the element corresponding to the given label node in the current label scope. |
* @param parentNode the node containing the given label |
@@ -2321,46 +2658,57 @@ |
} |
return labelElement; |
} |
+ |
/** |
* Look up the method with the given name in the given type. Return the element representing the |
* method that was found, or {@code null} if there is no method with the given name. |
+ * @param target the target of the invocation, or {@code null} if there is no target |
* @param type the type in which the method is defined |
* @param methodName the name of the method being looked up |
* @return the element representing the method that was found |
*/ |
- MethodElement lookUpMethod(Type2 type, String methodName) { |
+ MethodElement lookUpMethod(Expression target, Type2 type, String methodName) { |
type = resolveTypeVariable(type); |
if (type is InterfaceType) { |
InterfaceType interfaceType = type as InterfaceType; |
- MethodElement method = interfaceType.lookUpMethod(methodName, _resolver.definingLibrary); |
+ MethodElement method; |
+ if (target is SuperExpression) { |
+ method = interfaceType.lookUpMethodInSuperclass(methodName, _resolver.definingLibrary); |
+ } else { |
+ method = interfaceType.lookUpMethod(methodName, _resolver.definingLibrary); |
+ } |
if (method != null) { |
return method; |
} |
- return lookUpMethodInInterfaces(interfaceType, methodName, new Set<ClassElement>()); |
+ return lookUpMethodInInterfaces(interfaceType, false, methodName, new Set<ClassElement>()); |
} |
return null; |
} |
+ |
/** |
* Look up the method with the given name in the interfaces implemented by the given type, either |
* directly or indirectly. Return the element representing the method that was found, or{@code null} if there is no method with the given name. |
* @param targetType the type in which the member might be defined |
+ * @param includeTargetType {@code true} if the search should include the target type |
* @param methodName the name of the method being looked up |
* @param visitedInterfaces a set containing all of the interfaces that have been examined, used |
* to prevent infinite recursion and to optimize the search |
* @return the element representing the method that was found |
*/ |
- MethodElement lookUpMethodInInterfaces(InterfaceType targetType, String methodName, Set<ClassElement> visitedInterfaces) { |
+ MethodElement lookUpMethodInInterfaces(InterfaceType targetType, bool includeTargetType, String methodName, Set<ClassElement> visitedInterfaces) { |
ClassElement targetClass = targetType.element; |
if (visitedInterfaces.contains(targetClass)) { |
return null; |
} |
javaSetAdd(visitedInterfaces, targetClass); |
- MethodElement method = targetType.getMethod(methodName); |
- if (method != null) { |
- return method; |
+ if (includeTargetType) { |
+ MethodElement method = targetType.getMethod(methodName); |
+ if (method != null) { |
+ return method; |
+ } |
} |
for (InterfaceType interfaceType in targetType.interfaces) { |
- method = lookUpMethodInInterfaces(interfaceType, methodName, visitedInterfaces); |
+ MethodElement method = lookUpMethodInInterfaces(interfaceType, true, methodName, visitedInterfaces); |
if (method != null) { |
return method; |
} |
@@ -2369,48 +2717,59 @@ |
if (superclass2 == null) { |
return null; |
} |
- return lookUpMethodInInterfaces(superclass2, methodName, visitedInterfaces); |
+ return lookUpMethodInInterfaces(superclass2, true, methodName, visitedInterfaces); |
} |
+ |
/** |
* Look up the setter with the given name in the given type. Return the element representing the |
* setter that was found, or {@code null} if there is no setter with the given name. |
+ * @param target the target of the invocation, or {@code null} if there is no target |
* @param type the type in which the setter is defined |
* @param setterName the name of the setter being looked up |
* @return the element representing the setter that was found |
*/ |
- PropertyAccessorElement lookUpSetter(Type2 type, String setterName) { |
+ PropertyAccessorElement lookUpSetter(Expression target, Type2 type, String setterName) { |
type = resolveTypeVariable(type); |
if (type is InterfaceType) { |
InterfaceType interfaceType = type as InterfaceType; |
- PropertyAccessorElement accessor = interfaceType.lookUpSetter(setterName, _resolver.definingLibrary); |
+ PropertyAccessorElement accessor; |
+ if (target is SuperExpression) { |
+ accessor = interfaceType.lookUpSetterInSuperclass(setterName, _resolver.definingLibrary); |
+ } else { |
+ accessor = interfaceType.lookUpSetter(setterName, _resolver.definingLibrary); |
+ } |
if (accessor != null) { |
return accessor; |
} |
- return lookUpSetterInInterfaces(interfaceType, setterName, new Set<ClassElement>()); |
+ return lookUpSetterInInterfaces(interfaceType, false, setterName, new Set<ClassElement>()); |
} |
return null; |
} |
+ |
/** |
* Look up the setter with the given name in the interfaces implemented by the given type, either |
* directly or indirectly. Return the element representing the setter that was found, or{@code null} if there is no setter with the given name. |
* @param targetType the type in which the setter might be defined |
+ * @param includeTargetType {@code true} if the search should include the target type |
* @param setterName the name of the setter being looked up |
* @param visitedInterfaces a set containing all of the interfaces that have been examined, used |
* to prevent infinite recursion and to optimize the search |
* @return the element representing the setter that was found |
*/ |
- PropertyAccessorElement lookUpSetterInInterfaces(InterfaceType targetType, String setterName, Set<ClassElement> visitedInterfaces) { |
+ PropertyAccessorElement lookUpSetterInInterfaces(InterfaceType targetType, bool includeTargetType, String setterName, Set<ClassElement> visitedInterfaces) { |
ClassElement targetClass = targetType.element; |
if (visitedInterfaces.contains(targetClass)) { |
return null; |
} |
javaSetAdd(visitedInterfaces, targetClass); |
- PropertyAccessorElement setter = targetType.getGetter(setterName); |
- if (setter != null) { |
- return setter; |
+ if (includeTargetType) { |
+ PropertyAccessorElement setter = targetType.getSetter(setterName); |
+ if (setter != null) { |
+ return setter; |
+ } |
} |
for (InterfaceType interfaceType in targetType.interfaces) { |
- setter = lookUpSetterInInterfaces(interfaceType, setterName, visitedInterfaces); |
+ PropertyAccessorElement setter = lookUpSetterInInterfaces(interfaceType, true, setterName, visitedInterfaces); |
if (setter != null) { |
return setter; |
} |
@@ -2419,8 +2778,9 @@ |
if (superclass2 == null) { |
return null; |
} |
- return lookUpSetterInInterfaces(superclass2, setterName, visitedInterfaces); |
+ return lookUpSetterInInterfaces(superclass2, true, setterName, visitedInterfaces); |
} |
+ |
/** |
* Return the binary operator that is invoked by the given compound assignment operator. |
* @param operator the assignment operator being mapped |
@@ -2456,33 +2816,112 @@ |
AnalysisEngine.instance.logger.logError("Failed to map ${operator.lexeme} to it's corresponding operator"); |
return operator; |
} |
+ |
/** |
* Record the fact that the given AST node was resolved to the given element. |
* @param node the AST node that was resolved |
* @param element the element to which the AST node was resolved |
*/ |
void recordResolution(SimpleIdentifier node, Element element2) { |
- if (element2 != null) { |
- node.element = element2; |
- } |
+ node.staticElement = element2; |
+ node.element = element2; |
} |
+ |
/** |
- * Report the {@link StaticTypeWarningCode}s <code>UNDEFINED_SETTER</code> and <code>UNDEFINED_GETTER</code>. |
- * @param node the prefixed identifier that gives the context to determine if the error on the |
- * undefined identifier is a getter or a setter |
- * @param identifier the identifier in the passed prefix identifier |
- * @param typeName the name of the type of the left hand side of the passed prefixed identifier |
+ * Record the fact that the given AST node was resolved to the given elements. |
+ * @param node the AST node that was resolved |
+ * @param staticElement the element to which the AST node was resolved using static type |
+ * information |
+ * @param propagatedElement the element to which the AST node was resolved using propagated type |
+ * information |
+ * @return the element that was associated with the node |
*/ |
- void reportGetterOrSetterNotFound(PrefixedIdentifier node, SimpleIdentifier identifier2, String typeName) { |
- Type2 targetType = getType(node); |
- if (targetType != null && doesClassDeclareNoSuchMethod(targetType.element)) { |
+ Element recordResolution2(SimpleIdentifier node, Element staticElement2, Element propagatedElement) { |
+ node.staticElement = staticElement2; |
+ Element element = propagatedElement == null ? staticElement2 : propagatedElement; |
+ node.element = element; |
+ return element; |
+ } |
+ |
+ /** |
+ * Given a list of arguments and the element that will be invoked using those argument, compute |
+ * the list of parameters that correspond to the list of arguments. |
+ * @param reportError if {@code true} then compile-time error should be reported; if {@code false}then compile-time warning |
+ * @param argumentList the list of arguments being passed to the element |
+ * @param executableElement the element that will be invoked with the arguments |
+ */ |
+ void resolveArgumentsToParameters(bool reportError, ArgumentList argumentList, ExecutableElement executableElement) { |
+ if (executableElement == null) { |
return; |
} |
- bool isSetterContext = node.identifier.inSetterContext(); |
- ErrorCode errorCode = isSetterContext ? StaticTypeWarningCode.UNDEFINED_SETTER : StaticTypeWarningCode.UNDEFINED_GETTER; |
- _resolver.reportError(errorCode, identifier2, [identifier2.name, typeName]); |
+ List<ParameterElement> parameters2 = executableElement.parameters; |
+ resolveArgumentsToParameters2(reportError, argumentList, parameters2); |
} |
+ |
/** |
+ * Given a list of arguments and the element that will be invoked using those argument, compute |
+ * the list of parameters that correspond to the list of arguments. |
+ * @param reportError if {@code true} then compile-time error should be reported; if {@code false}then compile-time warning |
+ * @param argumentList the list of arguments being passed to the element |
+ * @param parameters the of the function that will be invoked with the arguments |
+ */ |
+ void resolveArgumentsToParameters2(bool reportError2, ArgumentList argumentList, List<ParameterElement> parameters) { |
+ List<ParameterElement> requiredParameters = new List<ParameterElement>(); |
+ List<ParameterElement> positionalParameters = new List<ParameterElement>(); |
+ Map<String, ParameterElement> namedParameters = new Map<String, ParameterElement>(); |
+ for (ParameterElement parameter in parameters) { |
+ ParameterKind kind = parameter.parameterKind; |
+ if (identical(kind, ParameterKind.REQUIRED)) { |
+ requiredParameters.add(parameter); |
+ } else if (identical(kind, ParameterKind.POSITIONAL)) { |
+ positionalParameters.add(parameter); |
+ } else { |
+ namedParameters[parameter.name] = parameter; |
+ } |
+ } |
+ List<ParameterElement> unnamedParameters = new List<ParameterElement>.from(requiredParameters); |
+ unnamedParameters.addAll(positionalParameters); |
+ int unnamedParameterCount = unnamedParameters.length; |
+ int unnamedIndex = 0; |
+ NodeList<Expression> arguments2 = argumentList.arguments; |
+ int argumentCount = arguments2.length; |
+ List<ParameterElement> resolvedParameters = new List<ParameterElement>(argumentCount); |
+ int positionalArgumentCount = 0; |
+ Set<String> usedNames = new Set<String>(); |
+ for (int i = 0; i < argumentCount; i++) { |
+ Expression argument = arguments2[i]; |
+ if (argument is NamedExpression) { |
+ SimpleIdentifier nameNode = ((argument as NamedExpression)).name.label; |
+ String name2 = nameNode.name; |
+ ParameterElement element = namedParameters[name2]; |
+ if (element == null) { |
+ ErrorCode errorCode = reportError2 ? CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER : StaticWarningCode.UNDEFINED_NAMED_PARAMETER; |
+ _resolver.reportError(errorCode, nameNode, [name2]); |
+ } else { |
+ resolvedParameters[i] = element; |
+ recordResolution(nameNode, element); |
+ } |
+ if (!javaSetAdd(usedNames, name2)) { |
+ _resolver.reportError(CompileTimeErrorCode.DUPLICATE_NAMED_ARGUMENT, nameNode, [name2]); |
+ } |
+ } else { |
+ positionalArgumentCount++; |
+ if (unnamedIndex < unnamedParameterCount) { |
+ resolvedParameters[i] = unnamedParameters[unnamedIndex++]; |
+ } |
+ } |
+ } |
+ if (positionalArgumentCount < requiredParameters.length) { |
+ ErrorCode errorCode = reportError2 ? CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS : StaticWarningCode.NOT_ENOUGH_REQUIRED_ARGUMENTS; |
+ _resolver.reportError(errorCode, argumentList, [requiredParameters.length, positionalArgumentCount]); |
+ } else if (positionalArgumentCount > unnamedParameterCount) { |
+ ErrorCode errorCode = reportError2 ? CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS : StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS; |
+ _resolver.reportError(errorCode, argumentList, [unnamedParameterCount, positionalArgumentCount]); |
+ } |
+ argumentList.correspondingParameters = resolvedParameters; |
+ } |
+ |
+ /** |
* Resolve the names in the given combinators in the scope of the given library. |
* @param library the library that defines the names |
* @param combinators the combinators containing the names to be resolved |
@@ -2491,7 +2930,7 @@ |
if (library == null) { |
return; |
} |
- Namespace namespace = new NamespaceBuilder().createExportNamespace(library); |
+ Namespace namespace = new NamespaceBuilder().createExportNamespace2(library); |
for (Combinator combinator in combinators) { |
NodeList<SimpleIdentifier> names; |
if (combinator is HideCombinator) { |
@@ -2507,29 +2946,158 @@ |
} |
} |
} |
+ |
/** |
- * Resolve the names associated with any named arguments to the parameter elements named by the |
- * argument. |
- * @param argumentList the arguments to be resolved |
- * @param invokedMethod the method or function defining the parameters to which the named |
- * arguments are to be resolved |
+ * Given an invocation of the form 'e.m(a1, ..., an)', resolve 'e.m' to the element being invoked. |
+ * If the returned element is a method, then the method will be invoked. If the returned element |
+ * is a getter, the getter will be invoked without arguments and the result of that invocation |
+ * will then be invoked with the arguments. |
+ * @param target the target of the invocation ('e') |
+ * @param targetType the type of the target |
+ * @param methodName the name of the method being invoked ('m') |
+ * @return the element being invoked |
*/ |
- void resolveNamedArguments(ArgumentList argumentList, ExecutableElement invokedMethod) { |
- if (invokedMethod == null) { |
- return; |
+ Element resolveInvokedElement(Expression target, Type2 targetType, SimpleIdentifier methodName) { |
+ if (targetType is InterfaceType) { |
+ InterfaceType classType = targetType as InterfaceType; |
+ Element element = lookUpMethod(target, classType, methodName.name); |
+ if (element == null) { |
+ element = classType.getGetter(methodName.name); |
+ } |
+ return element; |
+ } else if (target is SimpleIdentifier) { |
+ Element targetElement = ((target as SimpleIdentifier)).element; |
+ if (targetElement is PrefixElement) { |
+ String name2 = "${((target as SimpleIdentifier)).name}.${methodName}"; |
+ Identifier functionName = new ElementResolver_SyntheticIdentifier(name2); |
+ Element element = _resolver.nameScope.lookup(functionName, _resolver.definingLibrary); |
+ if (element != null) { |
+ return element; |
+ } |
+ } |
} |
- List<ParameterElement> parameters2 = invokedMethod.parameters; |
- for (Expression argument in argumentList.arguments) { |
- if (argument is NamedExpression) { |
- SimpleIdentifier name2 = ((argument as NamedExpression)).name.label; |
- ParameterElement parameter = findNamedParameter(parameters2, name2.name); |
- if (parameter != null) { |
- recordResolution(name2, parameter); |
+ return null; |
+ } |
+ |
+ /** |
+ * Given an invocation of the form 'm(a1, ..., an)', resolve 'm' to the element being invoked. If |
+ * the returned element is a method, then the method will be invoked. If the returned element is a |
+ * getter, the getter will be invoked without arguments and the result of that invocation will |
+ * then be invoked with the arguments. |
+ * @param methodName the name of the method being invoked ('m') |
+ * @return the element being invoked |
+ */ |
+ Element resolveInvokedElement2(SimpleIdentifier methodName) { |
+ Element element = _resolver.nameScope.lookup(methodName, _resolver.definingLibrary); |
+ if (element == null) { |
+ ClassElement enclosingClass2 = _resolver.enclosingClass; |
+ if (enclosingClass2 != null) { |
+ InterfaceType enclosingType = enclosingClass2.type; |
+ element = lookUpMethod(null, enclosingType, methodName.name); |
+ if (element == null) { |
+ element = lookUpGetter(null, enclosingType, methodName.name); |
} |
} |
} |
+ return element; |
} |
+ |
/** |
+ * Given that we are accessing a property of the given type with the given name, return the |
+ * element that represents the property. |
+ * @param target the target of the invocation ('e') |
+ * @param targetType the type in which the search for the property should begin |
+ * @param propertyName the name of the property being accessed |
+ * @return the element that represents the property |
+ */ |
+ ExecutableElement resolveProperty(Expression target, Type2 targetType, SimpleIdentifier propertyName) { |
+ ExecutableElement memberElement = null; |
+ if (propertyName.inSetterContext()) { |
+ memberElement = lookUpSetter(target, targetType, propertyName.name); |
+ } |
+ if (memberElement == null) { |
+ memberElement = lookUpGetter(target, targetType, propertyName.name); |
+ } |
+ if (memberElement == null) { |
+ memberElement = lookUpMethod(target, targetType, propertyName.name); |
+ } |
+ return memberElement; |
+ } |
+ void resolvePropertyAccess(Expression target, SimpleIdentifier propertyName) { |
+ Type2 staticType = getStaticType(target); |
+ ExecutableElement staticElement = resolveProperty(target, staticType, propertyName); |
+ propertyName.staticElement = staticElement; |
+ Type2 propagatedType = getPropagatedType(target); |
+ ExecutableElement propagatedElement = resolveProperty(target, propagatedType, propertyName); |
+ Element selectedElement = select2(staticElement, propagatedElement); |
+ propertyName.element = selectedElement; |
+ if (shouldReportMissingMember(staticType, staticElement) && (propagatedType == null || shouldReportMissingMember(propagatedType, propagatedElement))) { |
+ bool staticNoSuchMethod = staticType != null && classDeclaresNoSuchMethod2(staticType.element); |
+ bool propagatedNoSuchMethod = propagatedType != null && classDeclaresNoSuchMethod2(propagatedType.element); |
+ if (!staticNoSuchMethod && !propagatedNoSuchMethod) { |
+ bool isStaticProperty = isStatic(selectedElement); |
+ if (propertyName.inSetterContext()) { |
+ if (isStaticProperty) { |
+ _resolver.reportError(StaticWarningCode.UNDEFINED_SETTER, propertyName, [propertyName.name, staticType.displayName]); |
+ } else { |
+ _resolver.reportError(StaticTypeWarningCode.UNDEFINED_SETTER, propertyName, [propertyName.name, staticType.displayName]); |
+ } |
+ } else if (propertyName.inGetterContext()) { |
+ if (isStaticProperty) { |
+ _resolver.reportError(StaticWarningCode.UNDEFINED_GETTER, propertyName, [propertyName.name, staticType.displayName]); |
+ } else { |
+ _resolver.reportError(StaticTypeWarningCode.UNDEFINED_GETTER, propertyName, [propertyName.name, staticType.displayName]); |
+ } |
+ } else { |
+ _resolver.reportError(StaticWarningCode.UNDEFINED_IDENTIFIER, propertyName, [propertyName.name]); |
+ } |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Resolve the given simple identifier if possible. Return the element to which it could be |
+ * resolved, or {@code null} if it could not be resolved. This does not record the results of the |
+ * resolution. |
+ * @param node the identifier to be resolved |
+ * @return the element to which the identifier could be resolved |
+ */ |
+ Element resolveSimpleIdentifier(SimpleIdentifier node) { |
+ Element element = _resolver.nameScope.lookup(node, _resolver.definingLibrary); |
+ if (element is PropertyAccessorElement && node.inSetterContext()) { |
+ PropertyInducingElement variable2 = ((element as PropertyAccessorElement)).variable; |
+ if (variable2 != null) { |
+ PropertyAccessorElement setter2 = variable2.setter; |
+ if (setter2 == null) { |
+ ClassElement enclosingClass2 = _resolver.enclosingClass; |
+ if (enclosingClass2 != null) { |
+ setter2 = lookUpSetter(null, enclosingClass2.type, node.name); |
+ } |
+ } |
+ if (setter2 != null) { |
+ element = setter2; |
+ } |
+ } |
+ } else if (element == null && node.inSetterContext()) { |
+ element = _resolver.nameScope.lookup(new ElementResolver_SyntheticIdentifier("${node.name}="), _resolver.definingLibrary); |
+ } |
+ ClassElement enclosingClass3 = _resolver.enclosingClass; |
+ if (element == null && enclosingClass3 != null) { |
+ InterfaceType enclosingType = enclosingClass3.type; |
+ if (element == null && node.inSetterContext()) { |
+ element = lookUpSetter(null, enclosingType, node.name); |
+ } |
+ if (element == null && node.inGetterContext()) { |
+ element = lookUpGetter(null, enclosingType, node.name); |
+ } |
+ if (element == null) { |
+ element = lookUpMethod(null, enclosingType, node.name); |
+ } |
+ } |
+ return element; |
+ } |
+ |
+ /** |
* If the given type is a type variable, resolve it to the type that should be used when looking |
* up members. Otherwise, return the original type. |
* @param type the type that is to be resolved if it is a type variable |
@@ -2546,62 +3114,621 @@ |
} |
return type; |
} |
+ |
+ /** |
+ * Given two possible error codes for the same piece of code, one computed using static type |
+ * information and the other using propagated type information, return the error code that should |
+ * be reported, or {@code null} if no error should be reported. |
+ * @param staticError the error code computed using static type information |
+ * @param propagatedError the error code computed using propagated type information |
+ * @return the error code that should be reported |
+ */ |
+ ErrorCode select(ErrorCode staticError, ErrorCode propagatedError) { |
+ if (staticError == null || propagatedError == null) { |
+ return null; |
+ } |
+ return propagatedError; |
+ } |
+ |
+ /** |
+ * Return the propagated element if it is not {@code null}, or the static element if it is. |
+ * @param staticElement the element computed using static type information |
+ * @param propagatedElement the element computed using propagated type information |
+ * @return the more specific of the two elements |
+ */ |
+ ExecutableElement select2(ExecutableElement staticElement, ExecutableElement propagatedElement) => propagatedElement != null ? propagatedElement : staticElement; |
+ |
+ /** |
+ * Return the propagated method if it is not {@code null}, or the static method if it is. |
+ * @param staticMethod the method computed using static type information |
+ * @param propagatedMethod the method computed using propagated type information |
+ * @return the more specific of the two methods |
+ */ |
+ MethodElement select3(MethodElement staticMethod, MethodElement propagatedMethod) => propagatedMethod != null ? propagatedMethod : staticMethod; |
+ |
+ /** |
+ * Given a node that can have annotations associated with it and the element to which that node |
+ * has been resolved, create the annotations in the element model representing the annotations on |
+ * the node. |
+ * @param element the element to which the node has been resolved |
+ * @param node the node that can have annotations associated with it |
+ */ |
+ void setMetadata(Element element, AnnotatedNode node) { |
+ if (element is! ElementImpl) { |
+ return; |
+ } |
+ List<AnnotationImpl> annotationList = new List<AnnotationImpl>(); |
+ addAnnotations(annotationList, node.metadata); |
+ if (node is VariableDeclaration && node.parent is VariableDeclarationList) { |
+ VariableDeclarationList list = node.parent as VariableDeclarationList; |
+ addAnnotations(annotationList, list.metadata); |
+ if (list.parent is FieldDeclaration) { |
+ FieldDeclaration fieldDeclaration = list.parent as FieldDeclaration; |
+ addAnnotations(annotationList, fieldDeclaration.metadata); |
+ } else if (list.parent is TopLevelVariableDeclaration) { |
+ TopLevelVariableDeclaration variableDeclaration = list.parent as TopLevelVariableDeclaration; |
+ addAnnotations(annotationList, variableDeclaration.metadata); |
+ } |
+ } |
+ if (!annotationList.isEmpty) { |
+ ((element as ElementImpl)).metadata = new List.from(annotationList); |
+ } |
+ } |
+ |
+ /** |
+ * Return {@code true} if we should report an error as a result of looking up a member in the |
+ * given type and not finding any member. |
+ * @param type the type in which we attempted to perform the look-up |
+ * @param member the result of the look-up |
+ * @return {@code true} if we should report an error |
+ */ |
+ bool shouldReportMissingMember(Type2 type, ExecutableElement member) { |
+ if (member != null || type == null || type.isDynamic()) { |
+ return false; |
+ } |
+ if (type is InterfaceType) { |
+ return !classDeclaresNoSuchMethod(((type as InterfaceType)).element); |
+ } |
+ return true; |
+ } |
} |
-class Identifier_8 extends Identifier { |
- String name3; |
- Identifier_8(this.name3) : super(); |
+ |
+/** |
+ * Instances of the class {@code SyntheticIdentifier} implement an identifier that can be used to |
+ * look up names in the lexical scope when there is no identifier in the AST structure. There is |
+ * no identifier in the AST when the parser could not distinguish between a method invocation and |
+ * an invocation of a top-level function imported with a prefix. |
+ */ |
+class ElementResolver_SyntheticIdentifier extends Identifier { |
+ |
+ /** |
+ * The name of the synthetic identifier. |
+ */ |
+ String _name; |
+ |
+ /** |
+ * Initialize a newly created synthetic identifier to have the given name. |
+ * @param name the name of the synthetic identifier |
+ */ |
+ ElementResolver_SyntheticIdentifier(String name) { |
+ this._name = name; |
+ } |
accept(ASTVisitor visitor) => null; |
sc.Token get beginToken => null; |
Element get element => null; |
sc.Token get endToken => null; |
- String get name => name3; |
+ String get name => _name; |
+ Element get staticElement => null; |
void visitChildren(ASTVisitor<Object> visitor) { |
} |
} |
+ |
/** |
+ * Instances of the class {@code InheritanceManager} manage the knowledge of where class members |
+ * (methods, getters & setters) are inherited from. |
+ * @coverage dart.engine.resolver |
+ */ |
+class InheritanceManager { |
+ |
+ /** |
+ * The {@link LibraryElement} that is managed by this manager. |
+ */ |
+ LibraryElement _library; |
+ |
+ /** |
+ * This is a mapping between each {@link ClassElement} and a map between the {@link String} member |
+ * names and the associated {@link ExecutableElement} in the mixin and superclass chain. |
+ */ |
+ Map<ClassElement, Map<String, ExecutableElement>> _classLookup; |
+ |
+ /** |
+ * This is a mapping between each {@link ClassElement} and a map between the {@link String} member |
+ * names and the associated {@link ExecutableElement} in the interface set. |
+ */ |
+ Map<ClassElement, Map<String, ExecutableElement>> _interfaceLookup; |
+ |
+ /** |
+ * A map between each visited {@link ClassElement} and the set of {@link AnalysisError}s found on |
+ * the class element. |
+ */ |
+ Map<ClassElement, Set<AnalysisError>> _errorsInClassElement = new Map<ClassElement, Set<AnalysisError>>(); |
+ |
+ /** |
+ * Initialize a newly created inheritance manager. |
+ * @param library the library element context that the inheritance mappings are being generated |
+ */ |
+ InheritanceManager(LibraryElement library) { |
+ this._library = library; |
+ _classLookup = new Map<ClassElement, Map<String, ExecutableElement>>(); |
+ _interfaceLookup = new Map<ClassElement, Map<String, ExecutableElement>>(); |
+ } |
+ |
+ /** |
+ * Return the set of {@link AnalysisError}s found on the passed {@link ClassElement}, or{@code null} if there are none. |
+ * @param classElt the class element to query |
+ * @return the set of {@link AnalysisError}s found on the passed {@link ClassElement}, or{@code null} if there are none |
+ */ |
+ Set<AnalysisError> getErrors(ClassElement classElt) => _errorsInClassElement[classElt]; |
+ |
+ /** |
+ * Get and return a mapping between the set of all string names of the members inherited from the |
+ * passed {@link ClassElement} superclass hierarchy, and the associated {@link ExecutableElement}. |
+ * @param classElt the class element to query |
+ * @return a mapping between the set of all members inherited from the passed {@link ClassElement}superclass hierarchy, and the associated {@link ExecutableElement} |
+ */ |
+ Map<String, ExecutableElement> getMapOfMembersInheritedFromClasses(ClassElement classElt) => computeClassChainLookupMap(classElt, new Set<ClassElement>()); |
+ |
+ /** |
+ * Get and return a mapping between the set of all string names of the members inherited from the |
+ * passed {@link ClassElement} interface hierarchy, and the associated {@link ExecutableElement}. |
+ * @param classElt the class element to query |
+ * @return a mapping between the set of all string names of the members inherited from the passed{@link ClassElement} interface hierarchy, and the associated {@link ExecutableElement}. |
+ */ |
+ Map<String, ExecutableElement> getMapOfMembersInheritedFromInterfaces(ClassElement classElt) => computeInterfaceLookupMap(classElt, new Set<ClassElement>()); |
+ |
+ /** |
+ * Given some {@link ClassElement class element} and some member name, this returns the{@link ExecutableElement executable element} that the class inherits from the mixins, |
+ * superclasses or interfaces, that has the member name, if no member is inherited {@code null} is |
+ * returned. |
+ * @param classElt the class element to query |
+ * @param memberName the name of the executable element to find and return |
+ * @return the inherited executable element with the member name, or {@code null} if no such |
+ * member exists |
+ */ |
+ ExecutableElement lookupInheritance(ClassElement classElt, String memberName) { |
+ if (memberName == null || memberName.isEmpty) { |
+ return null; |
+ } |
+ ExecutableElement executable = computeClassChainLookupMap(classElt, new Set<ClassElement>())[memberName]; |
+ if (executable == null) { |
+ return computeInterfaceLookupMap(classElt, new Set<ClassElement>())[memberName]; |
+ } |
+ return executable; |
+ } |
+ |
+ /** |
+ * Given some {@link ClassElement class element} and some member name, this returns the{@link ExecutableElement executable element} that the class either declares itself, or |
+ * inherits, that has the member name, if no member is inherited {@code null} is returned. |
+ * @param classElt the class element to query |
+ * @param memberName the name of the executable element to find and return |
+ * @return the inherited executable element with the member name, or {@code null} if no such |
+ * member exists |
+ */ |
+ ExecutableElement lookupMember(ClassElement classElt, String memberName) { |
+ ExecutableElement element = lookupMemberInClass(classElt, memberName); |
+ if (element != null) { |
+ return element; |
+ } |
+ return lookupInheritance(classElt, memberName); |
+ } |
+ |
+ /** |
+ * Set the new library element context. |
+ * @param library the new library element |
+ */ |
+ void set libraryElement(LibraryElement library2) { |
+ this._library = library2; |
+ } |
+ |
+ /** |
+ * This method takes some inherited {@link FunctionType}, and resolves all the parameterized types |
+ * in the function type, dependent on the class in which it is being overridden. |
+ * @param baseFunctionType the function type that is being overridden |
+ * @param memberName the name of the member, this is used to lookup the inheritance path of the |
+ * override |
+ * @param definingType the type that is overriding the member |
+ * @return the passed function type with any parameterized types substituted |
+ */ |
+ FunctionType substituteTypeArgumentsInMemberFromInheritance(FunctionType baseFunctionType, String memberName, InterfaceType definingType) { |
+ if (baseFunctionType == null) { |
+ return baseFunctionType; |
+ } |
+ Queue<InterfaceType> inheritancePath = new Queue<InterfaceType>(); |
+ computeInheritancePath(inheritancePath, definingType, memberName); |
+ if (inheritancePath == null || inheritancePath.length < 2) { |
+ return baseFunctionType; |
+ } |
+ FunctionType functionTypeToReturn = baseFunctionType; |
+ InterfaceType lastType = inheritancePath.removeLast(); |
+ while (inheritancePath.length > 0) { |
+ List<Type2> paramTypes = TypeVariableTypeImpl.getTypes(lastType.element.typeVariables); |
+ List<Type2> argTypes = lastType.typeArguments; |
+ functionTypeToReturn = functionTypeToReturn.substitute2(argTypes, paramTypes); |
+ lastType = inheritancePath.removeLast(); |
+ } |
+ return functionTypeToReturn; |
+ } |
+ |
+ /** |
+ * Compute and return a mapping between the set of all string names of the members inherited from |
+ * the passed {@link ClassElement} superclass hierarchy, and the associated{@link ExecutableElement}. |
+ * @param classElt the class element to query |
+ * @param visitedClasses a set of visited classes passed back into this method when it calls |
+ * itself recursively |
+ * @return a mapping between the set of all string names of the members inherited from the passed{@link ClassElement} superclass hierarchy, and the associated {@link ExecutableElement} |
+ */ |
+ Map<String, ExecutableElement> computeClassChainLookupMap(ClassElement classElt, Set<ClassElement> visitedClasses) { |
+ Map<String, ExecutableElement> resultMap = _classLookup[classElt]; |
+ if (resultMap != null) { |
+ return resultMap; |
+ } else { |
+ resultMap = new Map<String, ExecutableElement>(); |
+ } |
+ ClassElement superclassElt = null; |
+ InterfaceType supertype2 = classElt.supertype; |
+ if (supertype2 != null) { |
+ superclassElt = supertype2.element; |
+ } else { |
+ _classLookup[classElt] = resultMap; |
+ return resultMap; |
+ } |
+ if (superclassElt != null) { |
+ if (!visitedClasses.contains(superclassElt)) { |
+ javaSetAdd(visitedClasses, classElt); |
+ resultMap = new Map<String, ExecutableElement>.from(computeClassChainLookupMap(superclassElt, visitedClasses)); |
+ } else { |
+ _classLookup[superclassElt] = resultMap; |
+ return resultMap; |
+ } |
+ recordMapWithClassMembers(resultMap, superclassElt); |
+ } |
+ List<InterfaceType> mixins2 = classElt.mixins; |
+ for (int i = mixins2.length - 1; i >= 0; i--) { |
+ ClassElement mixinElement = mixins2[i].element; |
+ if (mixinElement != null) { |
+ recordMapWithClassMembers(resultMap, mixinElement); |
+ } |
+ } |
+ _classLookup[classElt] = resultMap; |
+ return resultMap; |
+ } |
+ |
+ /** |
+ * Compute and return the inheritance path given the context of a type and a member that is |
+ * overridden in the inheritance path (for which the type is in the path). |
+ * @param chain the inheritance path that is built up as this method calls itself recursively, |
+ * when this method is called an empty {@link LinkedList} should be provided |
+ * @param currentType the current type in the inheritance path |
+ * @param memberName the name of the member that is being looked up the inheritance path |
+ */ |
+ void computeInheritancePath(Queue<InterfaceType> chain, InterfaceType currentType, String memberName) { |
+ chain.add(currentType); |
+ ClassElement classElt = currentType.element; |
+ InterfaceType supertype2 = classElt.supertype; |
+ if (supertype2 == null) { |
+ return; |
+ } |
+ if (chain.length != 1) { |
+ if (lookupMemberInClass(classElt, memberName) != null) { |
+ return; |
+ } |
+ } |
+ List<InterfaceType> mixins2 = classElt.mixins; |
+ for (int i = mixins2.length - 1; i >= 0; i--) { |
+ ClassElement mixinElement = mixins2[i].element; |
+ if (mixinElement != null) { |
+ ExecutableElement elt = lookupMemberInClass(mixinElement, memberName); |
+ if (elt != null) { |
+ chain.add(mixins2[i]); |
+ return; |
+ } |
+ } |
+ } |
+ ClassElement superclassElt = supertype2.element; |
+ if (lookupMember(superclassElt, memberName) != null) { |
+ computeInheritancePath(chain, supertype2, memberName); |
+ return; |
+ } |
+ List<InterfaceType> interfaces2 = classElt.interfaces; |
+ for (InterfaceType interfaceType in interfaces2) { |
+ ClassElement interfaceElement = interfaceType.element; |
+ if (interfaceElement != null && lookupMember(interfaceElement, memberName) != null) { |
+ computeInheritancePath(chain, interfaceType, memberName); |
+ return; |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Compute and return a mapping between the set of all string names of the members inherited from |
+ * the passed {@link ClassElement} interface hierarchy, and the associated{@link ExecutableElement}. |
+ * @param classElt the class element to query |
+ * @param visitedInterfaces a set of visited classes passed back into this method when it calls |
+ * itself recursively |
+ * @return a mapping between the set of all string names of the members inherited from the passed{@link ClassElement} interface hierarchy, and the associated {@link ExecutableElement} |
+ */ |
+ Map<String, ExecutableElement> computeInterfaceLookupMap(ClassElement classElt, Set<ClassElement> visitedInterfaces) { |
+ Map<String, ExecutableElement> resultMap = _interfaceLookup[classElt]; |
+ if (resultMap != null) { |
+ return resultMap; |
+ } else { |
+ resultMap = new Map<String, ExecutableElement>(); |
+ } |
+ List<InterfaceType> interfaces2 = classElt.interfaces; |
+ if (interfaces2.length == 0) { |
+ _interfaceLookup[classElt] = resultMap; |
+ return resultMap; |
+ } |
+ List<Map<String, ExecutableElement>> lookupMaps = new List<Map<String, ExecutableElement>>(); |
+ for (InterfaceType interfaceType in interfaces2) { |
+ ClassElement interfaceElement = interfaceType.element; |
+ if (interfaceElement != null) { |
+ if (!visitedInterfaces.contains(interfaceElement)) { |
+ javaSetAdd(visitedInterfaces, interfaceElement); |
+ lookupMaps.add(computeInterfaceLookupMap(interfaceElement, visitedInterfaces)); |
+ } else { |
+ Map<String, ExecutableElement> map = _interfaceLookup[classElt]; |
+ if (map != null) { |
+ lookupMaps.add(map); |
+ } else { |
+ _interfaceLookup[interfaceElement] = resultMap; |
+ return resultMap; |
+ } |
+ } |
+ } |
+ } |
+ if (lookupMaps.length == 0) { |
+ _interfaceLookup[classElt] = resultMap; |
+ return resultMap; |
+ } |
+ Map<String, Set<ExecutableElement>> unionMap = new Map<String, Set<ExecutableElement>>(); |
+ for (Map<String, ExecutableElement> lookupMap in lookupMaps) { |
+ for (MapEntry<String, ExecutableElement> entry in getMapEntrySet(lookupMap)) { |
+ String key = entry.getKey(); |
+ if (!unionMap.containsKey(key)) { |
+ Set<ExecutableElement> set = new Set<ExecutableElement>(); |
+ javaSetAdd(set, entry.getValue()); |
+ unionMap[key] = set; |
+ } else { |
+ javaSetAdd(unionMap[key], entry.getValue()); |
+ } |
+ } |
+ } |
+ for (InterfaceType interfaceType in interfaces2) { |
+ ClassElement interfaceElement = interfaceType.element; |
+ if (interfaceElement != null) { |
+ List<MethodElement> methods2 = interfaceElement.methods; |
+ for (MethodElement method in methods2) { |
+ if (method.isAccessibleIn(_library) && !method.isStatic()) { |
+ String key = method.name; |
+ if (!unionMap.containsKey(key)) { |
+ Set<ExecutableElement> set = new Set<ExecutableElement>(); |
+ javaSetAdd(set, method); |
+ unionMap[key] = set; |
+ } else { |
+ javaSetAdd(unionMap[key], method); |
+ } |
+ } |
+ } |
+ List<PropertyAccessorElement> accessors2 = interfaceElement.accessors; |
+ for (PropertyAccessorElement accessor in accessors2) { |
+ if (accessor.isAccessibleIn(_library) && !accessor.isStatic()) { |
+ String key = accessor.name; |
+ if (!unionMap.containsKey(key)) { |
+ Set<ExecutableElement> set = new Set<ExecutableElement>(); |
+ javaSetAdd(set, accessor); |
+ unionMap[key] = set; |
+ } else { |
+ javaSetAdd(unionMap[key], accessor); |
+ } |
+ } |
+ } |
+ } |
+ } |
+ for (MapEntry<String, Set<ExecutableElement>> entry in getMapEntrySet(unionMap)) { |
+ String key = entry.getKey(); |
+ Set<ExecutableElement> set = entry.getValue(); |
+ int numOfEltsWithMatchingNames = set.length; |
+ if (numOfEltsWithMatchingNames == 1) { |
+ resultMap[key] = new JavaIterator(set).next(); |
+ } else { |
+ bool allMethods = true; |
+ bool allSetters = true; |
+ bool allGetters = true; |
+ for (ExecutableElement executableElement in set) { |
+ if (executableElement is PropertyAccessorElement) { |
+ allMethods = false; |
+ if (((executableElement as PropertyAccessorElement)).isSetter()) { |
+ allGetters = false; |
+ } else { |
+ allSetters = false; |
+ } |
+ } else { |
+ allGetters = false; |
+ allSetters = false; |
+ } |
+ } |
+ if (allMethods || allGetters || allSetters) { |
+ List<ExecutableElement> elements = new List.from(set); |
+ List<FunctionType> executableElementTypes = new List<FunctionType>(numOfEltsWithMatchingNames); |
+ for (int i = 0; i < numOfEltsWithMatchingNames; i++) { |
+ executableElementTypes[i] = elements[i].type; |
+ } |
+ bool foundSubtypeOfAllTypes = true; |
+ for (int i = 0; i < numOfEltsWithMatchingNames; i++) { |
+ FunctionType subtype = executableElementTypes[i]; |
+ if (subtype == null) { |
+ continue; |
+ } |
+ bool subtypeOfAllTypes = true; |
+ for (int j = 0; j < numOfEltsWithMatchingNames && subtypeOfAllTypes; j++) { |
+ if (i != j) { |
+ if (!subtype.isSubtypeOf(executableElementTypes[j])) { |
+ subtypeOfAllTypes = false; |
+ foundSubtypeOfAllTypes = false; |
+ break; |
+ } |
+ } |
+ } |
+ if (subtypeOfAllTypes) { |
+ resultMap[key] = elements[i]; |
+ break; |
+ } |
+ } |
+ if (!foundSubtypeOfAllTypes) { |
+ reportError(classElt, classElt.nameOffset, classElt.displayName.length, StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE, [key]); |
+ } |
+ } else { |
+ if (!allMethods && !allGetters) { |
+ reportError(classElt, classElt.nameOffset, classElt.displayName.length, StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD, [key]); |
+ } |
+ resultMap.remove(entry.getKey()); |
+ } |
+ } |
+ } |
+ _interfaceLookup[classElt] = resultMap; |
+ return resultMap; |
+ } |
+ |
+ /** |
+ * Given some {@link ClassElement}, this method finds and returns the {@link ExecutableElement} of |
+ * the passed name in the class element. Static members, members in super types and members not |
+ * accessible from the current library are not considered. |
+ * @param classElt the class element to query |
+ * @param memberName the name of the member to lookup in the class |
+ * @return the found {@link ExecutableElement}, or {@code null} if no such member was found |
+ */ |
+ ExecutableElement lookupMemberInClass(ClassElement classElt, String memberName) { |
+ List<MethodElement> methods2 = classElt.methods; |
+ for (MethodElement method in methods2) { |
+ if (memberName == method.name && method.isAccessibleIn(_library) && !method.isStatic()) { |
+ return method; |
+ } |
+ } |
+ List<PropertyAccessorElement> accessors2 = classElt.accessors; |
+ for (PropertyAccessorElement accessor in accessors2) { |
+ if (memberName == accessor.name && accessor.isAccessibleIn(_library) && !accessor.isStatic()) { |
+ return accessor; |
+ } |
+ } |
+ return null; |
+ } |
+ |
+ /** |
+ * Record the passed map with the set of all members (methods, getters and setters) in the class |
+ * into the passed map. |
+ * @param map some non-{@code null} |
+ * @param classElt the class element that will be recorded into the passed map |
+ */ |
+ void recordMapWithClassMembers(Map<String, ExecutableElement> map, ClassElement classElt) { |
+ List<MethodElement> methods2 = classElt.methods; |
+ for (MethodElement method in methods2) { |
+ if (method.isAccessibleIn(_library) && !method.isStatic()) { |
+ map[method.name] = method; |
+ } |
+ } |
+ List<PropertyAccessorElement> accessors2 = classElt.accessors; |
+ for (PropertyAccessorElement accessor in accessors2) { |
+ if (accessor.isAccessibleIn(_library) && !accessor.isStatic()) { |
+ map[accessor.name] = accessor; |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * This method is used to report errors on when they are found computing inheritance information. |
+ * See {@link ErrorVerifier#checkForInconsistentMethodInheritance()} to see where these generated |
+ * error codes are reported back into the analysis engine. |
+ * @param classElt the location of the source for which the exception occurred |
+ * @param offset the offset of the location of the error |
+ * @param length the length of the location of the error |
+ * @param errorCode the error code to be associated with this error |
+ * @param arguments the arguments used to build the error message |
+ */ |
+ void reportError(ClassElement classElt, int offset, int length, ErrorCode errorCode, List<Object> arguments) { |
+ Set<AnalysisError> errorSet = _errorsInClassElement[classElt]; |
+ if (errorSet == null) { |
+ errorSet = new Set<AnalysisError>(); |
+ _errorsInClassElement[classElt] = errorSet; |
+ } |
+ javaSetAdd(errorSet, new AnalysisError.con2(classElt.source, offset, length, errorCode, arguments)); |
+ } |
+} |
+ |
+/** |
* Instances of the class {@code Library} represent the data about a single library during the |
* resolution of some (possibly different) library. They are not intended to be used except during |
* the resolution process. |
* @coverage dart.engine.resolver |
*/ |
class Library { |
+ |
/** |
* The analysis context in which this library is being analyzed. |
*/ |
InternalAnalysisContext _analysisContext; |
+ |
/** |
+ * The inheritance manager which is used for this member lookups in this library. |
+ */ |
+ InheritanceManager _inheritanceManager; |
+ |
+ /** |
* The listener to which analysis errors will be reported. |
*/ |
AnalysisErrorListener _errorListener; |
+ |
/** |
* The source specifying the defining compilation unit of this library. |
*/ |
Source _librarySource; |
+ |
/** |
* The library element representing this library. |
*/ |
LibraryElementImpl _libraryElement; |
+ |
/** |
* A list containing all of the libraries that are imported into this library. |
*/ |
Map<ImportDirective, Library> _importedLibraries = new Map<ImportDirective, Library>(); |
+ |
/** |
+ * A table mapping URI-based directive to the actual URI value. |
+ */ |
+ Map<UriBasedDirective, String> _directiveUris = new Map<UriBasedDirective, String>(); |
+ |
+ /** |
* A flag indicating whether this library explicitly imports core. |
*/ |
bool _explicitlyImportsCore = false; |
+ |
/** |
* A list containing all of the libraries that are exported from this library. |
*/ |
Map<ExportDirective, Library> _exportedLibraries = new Map<ExportDirective, Library>(); |
+ |
/** |
* A table mapping the sources for the compilation units in this library to their corresponding |
* AST structures. |
*/ |
Map<Source, CompilationUnit> _astMap = new Map<Source, CompilationUnit>(); |
+ |
/** |
* The library scope used when resolving elements within this library's compilation units. |
*/ |
LibraryScope _libraryScope; |
+ |
/** |
* Initialize a newly created data holder that can maintain the data associated with a library. |
* @param analysisContext the analysis context in which this library is being analyzed |
@@ -2614,6 +3741,7 @@ |
this._librarySource = librarySource; |
this._libraryElement = analysisContext.getLibraryElement(librarySource) as LibraryElementImpl; |
} |
+ |
/** |
* Record that the given library is exported from this library. |
* @param importLibrary the library that is exported from this library |
@@ -2621,6 +3749,7 @@ |
void addExport(ExportDirective directive, Library exportLibrary) { |
_exportedLibraries[directive] = exportLibrary; |
} |
+ |
/** |
* Record that the given library is imported into this library. |
* @param importLibrary the library that is imported into this library |
@@ -2628,6 +3757,7 @@ |
void addImport(ImportDirective directive, Library importLibrary) { |
_importedLibraries[directive] = importLibrary; |
} |
+ |
/** |
* Return the AST structure associated with the given source. |
* @param source the source representing the compilation unit whose AST is to be returned |
@@ -2642,12 +3772,14 @@ |
} |
return unit; |
} |
+ |
/** |
* Return a collection containing the sources for the compilation units in this library, including |
* the defining compilation unit. |
* @return the sources for the compilation units in this library |
*/ |
Set<Source> get compilationUnitSources => _astMap.keys.toSet(); |
+ |
/** |
* Return the AST structure associated with the defining compilation unit for this library. |
* @return the AST structure associated with the defining compilation unit for this library |
@@ -2655,17 +3787,20 @@ |
* unit |
*/ |
CompilationUnit get definingCompilationUnit => getAST(librarySource); |
+ |
/** |
* Return {@code true} if this library explicitly imports core. |
* @return {@code true} if this library explicitly imports core |
*/ |
bool get explicitlyImportsCore => _explicitlyImportsCore; |
+ |
/** |
* Return the library exported by the given directive. |
* @param directive the directive that exports the library to be returned |
* @return the library exported by the given directive |
*/ |
Library getExport(ExportDirective directive) => _exportedLibraries[directive]; |
+ |
/** |
* Return an array containing the libraries that are exported from this library. |
* @return an array containing the libraries that are exported from this library |
@@ -2675,12 +3810,14 @@ |
libraries.addAll(_exportedLibraries.values); |
return new List.from(libraries); |
} |
+ |
/** |
* Return the library imported by the given directive. |
* @param directive the directive that imports the library to be returned |
* @return the library imported by the given directive |
*/ |
Library getImport(ImportDirective directive) => _importedLibraries[directive]; |
+ |
/** |
* Return an array containing the libraries that are imported into this library. |
* @return an array containing the libraries that are imported into this library |
@@ -2690,6 +3827,7 @@ |
libraries.addAll(_importedLibraries.values); |
return new List.from(libraries); |
} |
+ |
/** |
* Return an array containing the libraries that are either imported or exported from this |
* library. |
@@ -2701,7 +3839,19 @@ |
libraries.addAll(_exportedLibraries.values); |
return new List.from(libraries); |
} |
+ |
/** |
+ * Return the inheritance manager for this library. |
+ * @return the inheritance manager for this library |
+ */ |
+ InheritanceManager get inheritanceManager { |
+ if (_inheritanceManager == null) { |
+ return _inheritanceManager = new InheritanceManager(_libraryElement); |
+ } |
+ return _inheritanceManager; |
+ } |
+ |
+ /** |
* Return the library element representing this library, creating it if necessary. |
* @return the library element representing this library |
*/ |
@@ -2715,6 +3865,7 @@ |
} |
return _libraryElement; |
} |
+ |
/** |
* Return the library scope used when resolving elements within this library's compilation units. |
* @return the library scope used when resolving elements within this library's compilation units |
@@ -2725,62 +3876,75 @@ |
} |
return _libraryScope; |
} |
+ |
/** |
* Return the source specifying the defining compilation unit of this library. |
* @return the source specifying the defining compilation unit of this library |
*/ |
Source get librarySource => _librarySource; |
+ |
/** |
- * Return the result of resolving the given URI against the URI of the library, or {@code null} if |
- * the URI is not valid. If the URI is not valid, report the error. |
- * @param uriLiteral the string literal specifying the URI to be resolved |
- * @return the result of resolving the given URI against the URI of the library |
+ * Return the result of resolving the URI of the given URI-based directive against the URI of the |
+ * library, or {@code null} if the URI is not valid. If the URI is not valid, report the error. |
+ * @param directive the directive which URI should be resolved |
+ * @return the result of resolving the URI against the URI of the library |
*/ |
- Source getSource(StringLiteral uriLiteral) { |
+ Source getSource(UriBasedDirective directive) { |
+ StringLiteral uriLiteral = directive.uri; |
if (uriLiteral is StringInterpolation) { |
_errorListener.onError(new AnalysisError.con2(_librarySource, uriLiteral.offset, uriLiteral.length, CompileTimeErrorCode.URI_WITH_INTERPOLATION, [])); |
return null; |
} |
- Source source = getSource2(getStringValue(uriLiteral)); |
- if (source == null || !source.exists()) { |
- _errorListener.onError(new AnalysisError.con2(_librarySource, uriLiteral.offset, uriLiteral.length, CompileTimeErrorCode.INVALID_URI, [uriLiteral.toSource()])); |
+ String uriContent = uriLiteral.stringValue.trim(); |
+ _directiveUris[directive] = uriContent; |
+ try { |
+ parseUriWithException(uriContent); |
+ Source source = getSource2(uriContent); |
+ if (source == null || !source.exists()) { |
+ _errorListener.onError(new AnalysisError.con2(_librarySource, uriLiteral.offset, uriLiteral.length, CompileTimeErrorCode.URI_DOES_NOT_EXIST, [uriContent])); |
+ } |
+ return source; |
+ } on URISyntaxException catch (exception) { |
+ _errorListener.onError(new AnalysisError.con2(_librarySource, uriLiteral.offset, uriLiteral.length, CompileTimeErrorCode.INVALID_URI, [uriContent])); |
} |
- return source; |
+ return null; |
} |
+ |
/** |
+ * Returns the URI value of the given directive. |
+ */ |
+ String getUri(UriBasedDirective directive) => _directiveUris[directive]; |
+ |
+ /** |
+ * Set the AST structure associated with the defining compilation unit for this library to the |
+ * given AST structure. |
+ * @param unit the AST structure associated with the defining compilation unit for this library |
+ */ |
+ void set definingCompilationUnit(CompilationUnit unit) { |
+ _astMap[librarySource] = unit; |
+ } |
+ |
+ /** |
* Set whether this library explicitly imports core to match the given value. |
* @param explicitlyImportsCore {@code true} if this library explicitly imports core |
*/ |
void set explicitlyImportsCore(bool explicitlyImportsCore2) { |
this._explicitlyImportsCore = explicitlyImportsCore2; |
} |
+ |
/** |
* Set the library element representing this library to the given library element. |
* @param libraryElement the library element representing this library |
*/ |
void set libraryElement(LibraryElementImpl libraryElement2) { |
this._libraryElement = libraryElement2; |
+ if (_inheritanceManager != null) { |
+ _inheritanceManager.libraryElement = libraryElement2; |
+ } |
} |
String toString() => _librarySource.shortName; |
+ |
/** |
- * Append the value of the given string literal to the given string builder. |
- * @param builder the builder to which the string's value is to be appended |
- * @param literal the string literal whose value is to be appended to the builder |
- * @throws IllegalArgumentException if the string is not a constant string without any string |
- * interpolation |
- */ |
- void appendStringValue(JavaStringBuilder builder, StringLiteral literal) { |
- if (literal is SimpleStringLiteral) { |
- builder.append(((literal as SimpleStringLiteral)).value); |
- } else if (literal is AdjacentStrings) { |
- for (StringLiteral stringLiteral in ((literal as AdjacentStrings)).strings) { |
- appendStringValue(builder, stringLiteral); |
- } |
- } else { |
- throw new IllegalArgumentException(); |
- } |
- } |
- /** |
* Return the result of resolving the given URI against the URI of the library, or {@code null} if |
* the URI is not valid. |
* @param uri the URI to be resolved |
@@ -2792,39 +3956,29 @@ |
} |
return _analysisContext.sourceFactory.resolveUri(_librarySource, uri); |
} |
- /** |
- * Return the value of the given string literal, or {@code null} if the string is not a constant |
- * string without any string interpolation. |
- * @param literal the string literal whose value is to be returned |
- * @return the value of the given string literal |
- */ |
- String getStringValue(StringLiteral literal) { |
- JavaStringBuilder builder = new JavaStringBuilder(); |
- try { |
- appendStringValue(builder, literal); |
- } on IllegalArgumentException catch (exception) { |
- return null; |
- } |
- return builder.toString().trim(); |
- } |
} |
+ |
/** |
* Instances of the class {@code LibraryElementBuilder} build an element model for a single library. |
* @coverage dart.engine.resolver |
*/ |
class LibraryElementBuilder { |
+ |
/** |
* The analysis context in which the element model will be built. |
*/ |
InternalAnalysisContext _analysisContext; |
+ |
/** |
* The listener to which errors will be reported. |
*/ |
AnalysisErrorListener _errorListener; |
+ |
/** |
* The name of the function used as an entry point. |
*/ |
static String _ENTRY_POINT_NAME = "main"; |
+ |
/** |
* Initialize a newly created library element builder. |
* @param resolver the resolver for which the element model is being built |
@@ -2833,6 +3987,7 @@ |
this._analysisContext = resolver.analysisContext; |
this._errorListener = resolver.errorListener; |
} |
+ |
/** |
* Build the library element for the given library. |
* @param library the library for which an element model is to be built |
@@ -2857,11 +4012,13 @@ |
directivesToResolve.add(directive); |
} |
} else if (directive is PartDirective) { |
- hasPartDirective = true; |
- StringLiteral partUri = ((directive as PartDirective)).uri; |
- Source partSource = library.getSource(partUri); |
+ PartDirective partDirective = directive as PartDirective; |
+ StringLiteral partUri = partDirective.uri; |
+ Source partSource = library.getSource(partDirective); |
if (partSource != null && partSource.exists()) { |
+ hasPartDirective = true; |
CompilationUnitElementImpl part = builder.buildCompilationUnit(partSource, library.getAST(partSource)); |
+ part.uri = library.getUri(partDirective); |
String partLibraryName = getPartLibraryName(library, partSource, directivesToResolve); |
if (partLibraryName == null) { |
_errorListener.onError(new AnalysisError.con2(librarySource2, partUri.offset, partUri.length, CompileTimeErrorCode.PART_OF_NON_PART, [partUri.toSource()])); |
@@ -2885,14 +4042,40 @@ |
if (entryPoint != null) { |
libraryElement.entryPoint = entryPoint; |
} |
+ int sourcedUnitCount = sourcedCompilationUnits.length; |
libraryElement.parts = new List.from(sourcedCompilationUnits); |
for (Directive directive in directivesToResolve) { |
directive.element = libraryElement; |
} |
library.libraryElement = libraryElement; |
+ if (sourcedUnitCount > 0) { |
+ patchTopLevelAccessors(libraryElement); |
+ } |
return libraryElement; |
} |
+ |
/** |
+ * Add all of the non-synthetic getters and setters defined in the given compilation unit that |
+ * have no corresponding accessor to one of the given collections. |
+ * @param getters the map to which getters are to be added |
+ * @param setters the list to which setters are to be added |
+ * @param unit the compilation unit defining the accessors that are potentially being added |
+ */ |
+ void collectAccessors(Map<String, PropertyAccessorElement> getters, List<PropertyAccessorElement> setters, CompilationUnitElement unit) { |
+ for (PropertyAccessorElement accessor in unit.accessors) { |
+ if (accessor.isGetter()) { |
+ if (!accessor.isSynthetic() && accessor.correspondingSetter == null) { |
+ getters[accessor.displayName] = accessor; |
+ } |
+ } else { |
+ if (!accessor.isSynthetic() && accessor.correspondingGetter == null) { |
+ setters.add(accessor); |
+ } |
+ } |
+ } |
+ } |
+ |
+ /** |
* Search the top-level functions defined in the given compilation unit for the entry point. |
* @param element the compilation unit to be searched |
* @return the entry point that was found, or {@code null} if the compilation unit does not define |
@@ -2906,6 +4089,7 @@ |
} |
return null; |
} |
+ |
/** |
* Return the name of the library that the given part is declared to be a part of, or {@code null}if the part does not contain a part-of directive. |
* @param library the library containing the part |
@@ -2930,87 +4114,151 @@ |
} |
return null; |
} |
+ |
+ /** |
+ * Look through all of the compilation units defined for the given library, looking for getters |
+ * and setters that are defined in different compilation units but that have the same names. If |
+ * any are found, make sure that they have the same variable element. |
+ * @param libraryElement the library defining the compilation units to be processed |
+ */ |
+ void patchTopLevelAccessors(LibraryElementImpl libraryElement) { |
+ Map<String, PropertyAccessorElement> getters = new Map<String, PropertyAccessorElement>(); |
+ List<PropertyAccessorElement> setters = new List<PropertyAccessorElement>(); |
+ collectAccessors(getters, setters, libraryElement.definingCompilationUnit); |
+ for (CompilationUnitElement unit in libraryElement.parts) { |
+ collectAccessors(getters, setters, unit); |
+ } |
+ for (PropertyAccessorElement setter in setters) { |
+ PropertyAccessorElement getter = getters[setter.displayName]; |
+ if (getter != null) { |
+ PropertyInducingElementImpl variable2 = getter.variable as PropertyInducingElementImpl; |
+ variable2.setter = setter; |
+ ((setter as PropertyAccessorElementImpl)).variable = variable2; |
+ } |
+ } |
+ } |
} |
+ |
/** |
* Instances of the class {@code LibraryResolver} are used to resolve one or more mutually dependent |
* libraries within a single context. |
* @coverage dart.engine.resolver |
*/ |
class LibraryResolver { |
+ |
/** |
* The analysis context in which the libraries are being analyzed. |
*/ |
InternalAnalysisContext _analysisContext; |
+ |
/** |
* The listener to which analysis errors will be reported, this error listener is either |
* references {@link #recordingErrorListener}, or it unions the passed{@link AnalysisErrorListener} with the {@link #recordingErrorListener}. |
*/ |
- AnalysisErrorListener _errorListener; |
+ RecordingErrorListener _errorListener; |
+ |
/** |
- * This error listener is used by the resolver to be able to call the listener and get back the |
- * set of errors for each {@link Source}. |
- * @see #recordResults() |
- */ |
- RecordingErrorListener _recordingErrorListener; |
- /** |
* A source object representing the core library (dart:core). |
*/ |
Source _coreLibrarySource; |
+ |
/** |
* The object representing the core library. |
*/ |
Library _coreLibrary; |
+ |
/** |
* The object used to access the types from the core library. |
*/ |
TypeProvider _typeProvider; |
+ |
/** |
* A table mapping library sources to the information being maintained for those libraries. |
*/ |
Map<Source, Library> _libraryMap = new Map<Source, Library>(); |
+ |
/** |
* A collection containing the libraries that are being resolved together. |
*/ |
Set<Library> _librariesInCycles; |
+ |
/** |
* Initialize a newly created library resolver to resolve libraries within the given context. |
* @param analysisContext the analysis context in which the library is being analyzed |
*/ |
- LibraryResolver.con1(InternalAnalysisContext analysisContext) { |
- _jtd_constructor_264_impl(analysisContext); |
+ LibraryResolver(InternalAnalysisContext analysisContext) { |
+ this._analysisContext = analysisContext; |
+ this._errorListener = new RecordingErrorListener(); |
+ _coreLibrarySource = analysisContext.sourceFactory.forUri(DartSdk.DART_CORE); |
} |
- _jtd_constructor_264_impl(InternalAnalysisContext analysisContext) { |
- _jtd_constructor_265_impl(analysisContext, null); |
- } |
+ |
/** |
- * Initialize a newly created library resolver to resolve libraries within the given context. |
- * @param analysisContext the analysis context in which the library is being analyzed |
- * @param errorListener the listener to which analysis errors will be reported |
- */ |
- LibraryResolver.con2(InternalAnalysisContext analysisContext2, AnalysisErrorListener additionalAnalysisErrorListener) { |
- _jtd_constructor_265_impl(analysisContext2, additionalAnalysisErrorListener); |
- } |
- _jtd_constructor_265_impl(InternalAnalysisContext analysisContext2, AnalysisErrorListener additionalAnalysisErrorListener) { |
- this._analysisContext = analysisContext2; |
- this._recordingErrorListener = new RecordingErrorListener(); |
- if (additionalAnalysisErrorListener == null) { |
- this._errorListener = _recordingErrorListener; |
- } else { |
- this._errorListener = new AnalysisErrorListener_9(this, additionalAnalysisErrorListener); |
- } |
- _coreLibrarySource = analysisContext2.sourceFactory.forUri(DartSdk.DART_CORE); |
- } |
- /** |
* Return the analysis context in which the libraries are being analyzed. |
* @return the analysis context in which the libraries are being analyzed |
*/ |
InternalAnalysisContext get analysisContext => _analysisContext; |
+ |
/** |
* Return the listener to which analysis errors will be reported. |
* @return the listener to which analysis errors will be reported |
*/ |
- AnalysisErrorListener get errorListener => _errorListener; |
+ RecordingErrorListener get errorListener => _errorListener; |
+ |
/** |
+ * Return an array containing information about all of the libraries that were resolved. |
+ * @return an array containing the libraries that were resolved |
+ */ |
+ Set<Library> get resolvedLibraries => _librariesInCycles; |
+ |
+ /** |
+ * Resolve the library specified by the given source in the given context. The library is assumed |
+ * to be embedded in the given source. |
+ * @param librarySource the source specifying the defining compilation unit of the library to be |
+ * resolved |
+ * @param unit the compilation unit representing the embedded library |
+ * @param fullAnalysis {@code true} if a full analysis should be performed |
+ * @return the element representing the resolved library |
+ * @throws AnalysisException if the library could not be resolved for some reason |
+ */ |
+ LibraryElement resolveEmbeddedLibrary(Source librarySource, CompilationUnit unit, bool fullAnalysis) { |
+ InstrumentationBuilder instrumentation = Instrumentation.builder2("dart.engine.LibraryResolver.resolveEmbeddedLibrary"); |
+ try { |
+ instrumentation.metric("fullAnalysis", fullAnalysis); |
+ instrumentation.data3("fullName", librarySource.fullName); |
+ Library targetLibrary = createLibrary2(librarySource, unit); |
+ _coreLibrary = _libraryMap[_coreLibrarySource]; |
+ if (_coreLibrary == null) { |
+ _coreLibrary = createLibrary(_coreLibrarySource); |
+ } |
+ instrumentation.metric3("createLibrary", "complete"); |
+ computeLibraryDependencies(targetLibrary); |
+ _librariesInCycles = computeLibrariesInCycles(targetLibrary); |
+ buildElementModels(); |
+ instrumentation.metric3("buildElementModels", "complete"); |
+ LibraryElement coreElement = _coreLibrary.libraryElement; |
+ if (coreElement == null) { |
+ throw new AnalysisException.con1("Could not resolve dart:core"); |
+ } |
+ buildDirectiveModels(); |
+ instrumentation.metric3("buildDirectiveModels", "complete"); |
+ _typeProvider = new TypeProviderImpl(coreElement); |
+ buildTypeHierarchies(); |
+ instrumentation.metric3("buildTypeHierarchies", "complete"); |
+ resolveReferencesAndTypes(); |
+ instrumentation.metric3("resolveReferencesAndTypes", "complete"); |
+ performConstantEvaluation(); |
+ instrumentation.metric3("performConstantEvaluation", "complete"); |
+ if (fullAnalysis) { |
+ runAdditionalAnalyses(); |
+ instrumentation.metric3("runAdditionalAnalyses", "complete"); |
+ } |
+ return targetLibrary.libraryElement; |
+ } finally { |
+ instrumentation.log(); |
+ } |
+ } |
+ |
+ /** |
* Resolve the library specified by the given source in the given context. |
* <p> |
* Note that because Dart allows circular imports between libraries, it is possible that more than |
@@ -3054,8 +4302,6 @@ |
runAdditionalAnalyses(); |
instrumentation.metric3("runAdditionalAnalyses", "complete"); |
} |
- recordResults(); |
- instrumentation.metric3("recordResults", "complete"); |
instrumentation.metric2("librariesInCycles", _librariesInCycles.length); |
for (Library lib in _librariesInCycles) { |
instrumentation.metric2("librariesInCycles-CompilationUnitSources-Size", lib.compilationUnitSources.length); |
@@ -3065,6 +4311,7 @@ |
instrumentation.log(); |
} |
} |
+ |
/** |
* Add a dependency to the given map from the referencing library to the referenced library. |
* @param dependencyMap the map to which the dependency is to be added |
@@ -3079,6 +4326,7 @@ |
} |
dependentLibraries.add(referencingLibrary); |
} |
+ |
/** |
* Given a library that is part of a cycle that includes the root library, add to the given set of |
* libraries all of the libraries reachable from the root library that are also included in the |
@@ -3098,6 +4346,7 @@ |
} |
} |
} |
+ |
/** |
* Add the given library, and all libraries reachable from it that have not already been visited, |
* to the given dependency map. |
@@ -3117,6 +4366,7 @@ |
} |
} |
} |
+ |
/** |
* Build the element model representing the combinators declared by the given directive. |
* @param directive the directive that declares the combinators |
@@ -3137,6 +4387,7 @@ |
} |
return new List.from(combinators); |
} |
+ |
/** |
* Every library now has a corresponding {@link LibraryElement}, so it is now possible to resolve |
* the import and export directives. |
@@ -3154,6 +4405,7 @@ |
Library importedLibrary = library.getImport(importDirective); |
if (importedLibrary != null) { |
ImportElementImpl importElement = new ImportElementImpl(); |
+ importElement.uri = library.getUri(importDirective); |
importElement.combinators = buildCombinators(importDirective); |
LibraryElement importedLibraryElement = importedLibrary.libraryElement; |
if (importedLibraryElement != null) { |
@@ -3175,6 +4427,7 @@ |
} else if (directive is ExportDirective) { |
ExportDirective exportDirective = directive as ExportDirective; |
ExportElementImpl exportElement = new ExportElementImpl(); |
+ exportElement.uri = library.getUri(exportDirective); |
exportElement.combinators = buildCombinators(exportDirective); |
Library exportedLibrary = library.getExport(exportDirective); |
if (exportedLibrary != null) { |
@@ -3199,6 +4452,7 @@ |
libraryElement2.exports = new List.from(exports); |
} |
} |
+ |
/** |
* Build element models for all of the libraries in the current cycle. |
* @throws AnalysisException if any of the element models cannot be built |
@@ -3210,6 +4464,7 @@ |
library.libraryElement = libraryElement; |
} |
} |
+ |
/** |
* Resolve the type hierarchy across all of the types declared in the libraries in the current |
* cycle. |
@@ -3223,6 +4478,7 @@ |
} |
} |
} |
+ |
/** |
* Compute a dependency map of libraries reachable from the given library. A dependency map is a |
* table that maps individual libraries to a list of the libraries that either import or export |
@@ -3238,6 +4494,7 @@ |
addToDependencyMap(library, dependencyMap, new Set<Library>()); |
return dependencyMap; |
} |
+ |
/** |
* Return a collection containing all of the libraries reachable from the given library that are |
* contained in a cycle that includes the given library. |
@@ -3251,6 +4508,7 @@ |
addLibrariesInCycle(library, librariesInCycle, dependencyMap); |
return librariesInCycle; |
} |
+ |
/** |
* Recursively traverse the libraries reachable from the given library, creating instances of the |
* class {@link Library} to represent them, and record the references in the library objects. |
@@ -3263,7 +4521,7 @@ |
for (Directive directive in unit.directives) { |
if (directive is ImportDirective) { |
ImportDirective importDirective = directive as ImportDirective; |
- Source importedSource = library.getSource(importDirective.uri); |
+ Source importedSource = library.getSource(importDirective); |
if (importedSource != null) { |
if (importedSource == _coreLibrarySource) { |
explicitlyImportsCore = true; |
@@ -3285,7 +4543,7 @@ |
} |
} else if (directive is ExportDirective) { |
ExportDirective exportDirective = directive as ExportDirective; |
- Source exportedSource = library.getSource(exportDirective.uri); |
+ Source exportedSource = library.getSource(exportDirective); |
if (exportedSource != null) { |
Library exportedLibrary = _libraryMap[exportedSource]; |
if (exportedLibrary == null) { |
@@ -3315,6 +4573,7 @@ |
} |
} |
} |
+ |
/** |
* Create an object to represent the information about the library defined by the compilation unit |
* with the given source. |
@@ -3328,8 +4587,23 @@ |
_libraryMap[librarySource] = library; |
return library; |
} |
+ |
/** |
* Create an object to represent the information about the library defined by the compilation unit |
+ * with the given source. |
+ * @param librarySource the source of the library's defining compilation unit |
+ * @return the library object that was created |
+ * @throws AnalysisException if the library source is not valid |
+ */ |
+ Library createLibrary2(Source librarySource, CompilationUnit unit) { |
+ Library library = new Library(_analysisContext, _errorListener, librarySource); |
+ library.definingCompilationUnit = unit; |
+ _libraryMap[librarySource] = library; |
+ return library; |
+ } |
+ |
+ /** |
+ * Create an object to represent the information about the library defined by the compilation unit |
* with the given source. Return the library object that was created, or {@code null} if the |
* source is not valid. |
* @param librarySource the source of the library's defining compilation unit |
@@ -3348,6 +4622,7 @@ |
_libraryMap[librarySource] = library; |
return library; |
} |
+ |
/** |
* Return {@code true} if and only if the passed {@link CompilationUnit} has a part-of directive. |
* @param node the {@link CompilationUnit} to test |
@@ -3362,6 +4637,7 @@ |
} |
return false; |
} |
+ |
/** |
* Return an array containing the lexical identifiers associated with the nodes in the given list. |
* @param names the AST nodes representing the identifiers |
@@ -3375,6 +4651,7 @@ |
} |
return identifiers; |
} |
+ |
/** |
* Compute a value for all of the constants in the libraries being analyzed. |
*/ |
@@ -3394,33 +4671,8 @@ |
} |
computer.computeValues(); |
} |
+ |
/** |
- * Record the results of resolution with the analysis context. This includes recording |
- * <ul> |
- * <li>the resolved AST associated with each compilation unit,</li> |
- * <li>the set of resolution errors produced for each compilation unit, and</li> |
- * <li>the element models produced for each library.</li> |
- * </ul> |
- */ |
- void recordResults() { |
- Map<Source, LibraryElement> elementMap = new Map<Source, LibraryElement>(); |
- for (Library library in _librariesInCycles) { |
- Source librarySource2 = library.librarySource; |
- recordResults2(librarySource2, librarySource2, library.definingCompilationUnit); |
- for (Source source in library.compilationUnitSources) { |
- recordResults2(source, librarySource2, library.getAST(source)); |
- } |
- elementMap[library.librarySource] = library.libraryElement; |
- } |
- _analysisContext.recordLibraryElements(elementMap); |
- } |
- void recordResults2(Source source, Source librarySource, CompilationUnit unit) { |
- List<AnalysisError> errors = _recordingErrorListener.getErrors2(source); |
- unit.resolutionErrors = errors; |
- _analysisContext.recordResolvedCompilationUnit(source, librarySource, unit); |
- _analysisContext.recordResolutionErrors(source, librarySource, errors, unit.lineInfo); |
- } |
- /** |
* Resolve the identifiers and perform type analysis in the libraries in the current cycle. |
* @throws AnalysisException if any of the identifiers could not be resolved or if any of the |
* libraries could not have their types analyzed |
@@ -3430,6 +4682,7 @@ |
resolveReferencesAndTypes2(library); |
} |
} |
+ |
/** |
* Resolve the identifiers and perform type analysis in the given library. |
* @param library the library to be resolved |
@@ -3442,6 +4695,7 @@ |
library.getAST(source).accept(visitor); |
} |
} |
+ |
/** |
* Run additional analyses, such as the {@link ConstantVerifier} and {@link ErrorVerifier}analysis in the current cycle. |
* @throws AnalysisException if any of the identifiers could not be resolved or if the types in |
@@ -3452,6 +4706,7 @@ |
runAdditionalAnalyses2(library); |
} |
} |
+ |
/** |
* Run additional analyses, such as the {@link ConstantVerifier} and {@link ErrorVerifier}analysis in the given library. |
* @param library the library to have the extra analyses processes run |
@@ -3462,50 +4717,48 @@ |
for (Source source in library.compilationUnitSources) { |
ErrorReporter errorReporter = new ErrorReporter(_errorListener, source); |
CompilationUnit unit = library.getAST(source); |
- ErrorVerifier errorVerifier = new ErrorVerifier(errorReporter, library.libraryElement, _typeProvider); |
+ ErrorVerifier errorVerifier = new ErrorVerifier(errorReporter, library.libraryElement, _typeProvider, library.inheritanceManager); |
unit.accept(errorVerifier); |
- ConstantVerifier constantVerifier = new ConstantVerifier(errorReporter); |
+ ConstantVerifier constantVerifier = new ConstantVerifier(errorReporter, _typeProvider); |
unit.accept(constantVerifier); |
} |
} |
} |
-class AnalysisErrorListener_9 implements AnalysisErrorListener { |
- final LibraryResolver LibraryResolver_this; |
- AnalysisErrorListener additionalAnalysisErrorListener; |
- AnalysisErrorListener_9(this.LibraryResolver_this, this.additionalAnalysisErrorListener); |
- void onError(AnalysisError error) { |
- additionalAnalysisErrorListener.onError(error); |
- LibraryResolver_this._recordingErrorListener.onError(error); |
- } |
-} |
+ |
/** |
* Instances of the class {@code ResolverVisitor} are used to resolve the nodes within a single |
* compilation unit. |
* @coverage dart.engine.resolver |
*/ |
class ResolverVisitor extends ScopedVisitor { |
+ |
/** |
* The object used to resolve the element associated with the current node. |
*/ |
ElementResolver _elementResolver; |
+ |
/** |
* The object used to compute the type associated with the current node. |
*/ |
StaticTypeAnalyzer _typeAnalyzer; |
+ |
/** |
* The class element representing the class containing the current node, or {@code null} if the |
* current node is not contained in a class. |
*/ |
ClassElement _enclosingClass = null; |
+ |
/** |
* The element representing the function containing the current node, or {@code null} if the |
* current node is not contained in a function. |
*/ |
ExecutableElement _enclosingFunction = null; |
+ |
/** |
* The object keeping track of which elements have had their types overridden. |
*/ |
TypeOverrideManager _overrideManager = new TypeOverrideManager(); |
+ |
/** |
* Initialize a newly created visitor to resolve the nodes in a compilation unit. |
* @param library the library containing the compilation unit being resolved |
@@ -3513,12 +4766,13 @@ |
* @param typeProvider the object used to access the types from the core library |
*/ |
ResolverVisitor.con1(Library library, Source source, TypeProvider typeProvider) : super.con1(library, source, typeProvider) { |
- _jtd_constructor_266_impl(library, source, typeProvider); |
+ _jtd_constructor_272_impl(library, source, typeProvider); |
} |
- _jtd_constructor_266_impl(Library library, Source source, TypeProvider typeProvider) { |
+ _jtd_constructor_272_impl(Library library, Source source, TypeProvider typeProvider) { |
this._elementResolver = new ElementResolver(this); |
this._typeAnalyzer = new StaticTypeAnalyzer(this); |
} |
+ |
/** |
* Initialize a newly created visitor to resolve the nodes in a compilation unit. |
* @param definingLibrary the element for the library containing the compilation unit being |
@@ -3529,12 +4783,13 @@ |
* during resolution |
*/ |
ResolverVisitor.con2(LibraryElement definingLibrary, Source source, TypeProvider typeProvider, AnalysisErrorListener errorListener) : super.con2(definingLibrary, source, typeProvider, errorListener) { |
- _jtd_constructor_267_impl(definingLibrary, source, typeProvider, errorListener); |
+ _jtd_constructor_273_impl(definingLibrary, source, typeProvider, errorListener); |
} |
- _jtd_constructor_267_impl(LibraryElement definingLibrary, Source source, TypeProvider typeProvider, AnalysisErrorListener errorListener) { |
+ _jtd_constructor_273_impl(LibraryElement definingLibrary, Source source, TypeProvider typeProvider, AnalysisErrorListener errorListener) { |
this._elementResolver = new ElementResolver(this); |
this._typeAnalyzer = new StaticTypeAnalyzer(this); |
} |
+ |
/** |
* Return the object keeping track of which elements have had their types overridden. |
* @return the object keeping track of which elements have had their types overridden |
@@ -3542,62 +4797,49 @@ |
TypeOverrideManager get overrideManager => _overrideManager; |
Object visitAsExpression(AsExpression node) { |
super.visitAsExpression(node); |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- VariableElement element = getOverridableElement(node.expression); |
- if (element != null) { |
- Type2 type2 = node.type.type; |
- if (type2 != null) { |
- override(element, getType(element), type2); |
- } |
- } |
+ VariableElement element = getOverridableElement(node.expression); |
+ if (element != null) { |
+ override(element, node.type.type); |
} |
return null; |
} |
Object visitAssertStatement(AssertStatement node) { |
super.visitAssertStatement(node); |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- propagateTrueState(node.condition); |
- } |
+ propagateTrueState(node.condition); |
return null; |
} |
Object visitBinaryExpression(BinaryExpression node) { |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- sc.TokenType operatorType = node.operator.type; |
- if (identical(operatorType, sc.TokenType.AMPERSAND_AMPERSAND)) { |
- Expression leftOperand2 = node.leftOperand; |
- leftOperand2.accept(this); |
- Expression rightOperand2 = node.rightOperand; |
- if (rightOperand2 != null) { |
- try { |
- _overrideManager.enterScope(); |
- propagateTrueState(leftOperand2); |
- rightOperand2.accept(this); |
- } finally { |
- _overrideManager.exitScope(); |
- } |
+ sc.TokenType operatorType = node.operator.type; |
+ Expression leftOperand2 = node.leftOperand; |
+ Expression rightOperand2 = node.rightOperand; |
+ if (identical(operatorType, sc.TokenType.AMPERSAND_AMPERSAND)) { |
+ safelyVisit(leftOperand2); |
+ if (rightOperand2 != null) { |
+ try { |
+ _overrideManager.enterScope(); |
+ propagateTrueState(leftOperand2); |
+ rightOperand2.accept(this); |
+ } finally { |
+ _overrideManager.exitScope(); |
} |
- } else if (identical(operatorType, sc.TokenType.BAR_BAR)) { |
- Expression leftOperand3 = node.leftOperand; |
- leftOperand3.accept(this); |
- Expression rightOperand3 = node.rightOperand; |
- if (rightOperand3 != null) { |
- try { |
- _overrideManager.enterScope(); |
- propagateFalseState(leftOperand3); |
- rightOperand3.accept(this); |
- } finally { |
- _overrideManager.exitScope(); |
- } |
+ } |
+ } else if (identical(operatorType, sc.TokenType.BAR_BAR)) { |
+ safelyVisit(leftOperand2); |
+ if (rightOperand2 != null) { |
+ try { |
+ _overrideManager.enterScope(); |
+ propagateFalseState(leftOperand2); |
+ rightOperand2.accept(this); |
+ } finally { |
+ _overrideManager.exitScope(); |
} |
- } else { |
- node.leftOperand.accept(this); |
- node.rightOperand.accept(this); |
} |
- node.accept(_elementResolver); |
- node.accept(_typeAnalyzer); |
} else { |
- super.visitBinaryExpression(node); |
+ safelyVisit(leftOperand2); |
+ safelyVisit(rightOperand2); |
} |
+ node.accept(_elementResolver); |
+ node.accept(_typeAnalyzer); |
return null; |
} |
Object visitBreakStatement(BreakStatement node) { |
@@ -3623,69 +4865,63 @@ |
return null; |
} |
Object visitCompilationUnit(CompilationUnit node) { |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- try { |
- _overrideManager.enterScope(); |
- for (Directive directive in node.directives) { |
- directive.accept(this); |
- } |
- List<CompilationUnitMember> classes = new List<CompilationUnitMember>(); |
- for (CompilationUnitMember declaration in node.declarations) { |
- if (declaration is ClassDeclaration) { |
- classes.add(declaration); |
- } else { |
- declaration.accept(this); |
- } |
- } |
- for (CompilationUnitMember declaration in classes) { |
+ try { |
+ _overrideManager.enterScope(); |
+ for (Directive directive in node.directives) { |
+ directive.accept(this); |
+ } |
+ List<CompilationUnitMember> classes = new List<CompilationUnitMember>(); |
+ for (CompilationUnitMember declaration in node.declarations) { |
+ if (declaration is ClassDeclaration) { |
+ classes.add(declaration); |
+ } else { |
declaration.accept(this); |
} |
- } finally { |
- _overrideManager.exitScope(); |
} |
- node.accept(_elementResolver); |
- node.accept(_typeAnalyzer); |
- } else { |
- super.visitCompilationUnit(node); |
+ for (CompilationUnitMember declaration in classes) { |
+ declaration.accept(this); |
+ } |
+ } finally { |
+ _overrideManager.exitScope(); |
} |
+ node.accept(_elementResolver); |
+ node.accept(_typeAnalyzer); |
return null; |
} |
Object visitConditionalExpression(ConditionalExpression node) { |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- Expression condition2 = node.condition; |
- condition2.accept(this); |
- Expression thenExpression2 = node.thenExpression; |
- if (thenExpression2 != null) { |
- try { |
- _overrideManager.enterScope(); |
- propagateTrueState(condition2); |
- thenExpression2.accept(this); |
- } finally { |
- _overrideManager.exitScope(); |
- } |
+ Expression condition2 = node.condition; |
+ safelyVisit(condition2); |
+ Expression thenExpression2 = node.thenExpression; |
+ if (thenExpression2 != null) { |
+ try { |
+ _overrideManager.enterScope(); |
+ propagateTrueState(condition2); |
+ thenExpression2.accept(this); |
+ } finally { |
+ _overrideManager.exitScope(); |
} |
- Expression elseExpression2 = node.elseExpression; |
- if (elseExpression2 != null) { |
- try { |
- _overrideManager.enterScope(); |
- propagateFalseState(condition2); |
- elseExpression2.accept(this); |
- } finally { |
- _overrideManager.exitScope(); |
- } |
- } |
- node.accept(_elementResolver); |
- node.accept(_typeAnalyzer); |
- bool thenIsAbrupt = thenExpression2 != null && isAbruptTermination(thenExpression2); |
- bool elseIsAbrupt = elseExpression2 != null && isAbruptTermination(elseExpression2); |
- if (elseIsAbrupt && !thenIsAbrupt) { |
- propagateTrueState(condition2); |
- } else if (thenIsAbrupt && !elseIsAbrupt) { |
+ } |
+ Expression elseExpression2 = node.elseExpression; |
+ if (elseExpression2 != null) { |
+ try { |
+ _overrideManager.enterScope(); |
propagateFalseState(condition2); |
+ elseExpression2.accept(this); |
+ } finally { |
+ _overrideManager.exitScope(); |
} |
- } else { |
- super.visitConditionalExpression(node); |
} |
+ node.accept(_elementResolver); |
+ node.accept(_typeAnalyzer); |
+ bool thenIsAbrupt = isAbruptTermination(thenExpression2); |
+ bool elseIsAbrupt = isAbruptTermination(elseExpression2); |
+ if (elseIsAbrupt && !thenIsAbrupt) { |
+ propagateTrueState(condition2); |
+ propagateState(thenExpression2); |
+ } else if (thenIsAbrupt && !elseIsAbrupt) { |
+ propagateFalseState(condition2); |
+ propagateState(elseExpression2); |
+ } |
return null; |
} |
Object visitConstructorDeclaration(ConstructorDeclaration node) { |
@@ -3714,57 +4950,50 @@ |
node.accept(_typeAnalyzer); |
return null; |
} |
+ Object visitDoStatement(DoStatement node) { |
+ try { |
+ _overrideManager.enterScope(); |
+ super.visitDoStatement(node); |
+ } finally { |
+ _overrideManager.exitScope(); |
+ } |
+ return null; |
+ } |
Object visitFieldDeclaration(FieldDeclaration node) { |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- try { |
- _overrideManager.enterScope(); |
- super.visitFieldDeclaration(node); |
- } finally { |
- Map<Element, Type2> overrides = captureOverrides(node.fields); |
- _overrideManager.exitScope(); |
- applyOverrides(overrides); |
- } |
- } else { |
+ try { |
+ _overrideManager.enterScope(); |
super.visitFieldDeclaration(node); |
+ } finally { |
+ Map<Element, Type2> overrides = _overrideManager.captureOverrides(node.fields); |
+ _overrideManager.exitScope(); |
+ _overrideManager.applyOverrides(overrides); |
} |
return null; |
} |
Object visitForEachStatement(ForEachStatement node) { |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- try { |
- _overrideManager.enterScope(); |
- super.visitForEachStatement(node); |
- } finally { |
- _overrideManager.exitScope(); |
- } |
- } else { |
+ try { |
+ _overrideManager.enterScope(); |
super.visitForEachStatement(node); |
+ } finally { |
+ _overrideManager.exitScope(); |
} |
return null; |
} |
Object visitForStatement(ForStatement node) { |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- try { |
- _overrideManager.enterScope(); |
- super.visitForStatement(node); |
- } finally { |
- _overrideManager.exitScope(); |
- } |
- } else { |
+ try { |
+ _overrideManager.enterScope(); |
super.visitForStatement(node); |
+ } finally { |
+ _overrideManager.exitScope(); |
} |
return null; |
} |
Object visitFunctionBody(FunctionBody node) { |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- try { |
- _overrideManager.enterScope(); |
- super.visitFunctionBody(node); |
- } finally { |
- _overrideManager.exitScope(); |
- } |
- } else { |
+ try { |
+ _overrideManager.enterScope(); |
super.visitFunctionBody(node); |
+ } finally { |
+ _overrideManager.exitScope(); |
} |
return null; |
} |
@@ -3783,55 +5012,57 @@ |
ExecutableElement outerFunction = _enclosingFunction; |
try { |
_enclosingFunction = node.element; |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- _overrideManager.enterScope(); |
- } |
+ _overrideManager.enterScope(); |
super.visitFunctionExpression(node); |
} finally { |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- _overrideManager.exitScope(); |
- } |
+ _overrideManager.exitScope(); |
_enclosingFunction = outerFunction; |
} |
return null; |
} |
Object visitHideCombinator(HideCombinator node) => null; |
Object visitIfStatement(IfStatement node) { |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- Expression condition2 = node.condition; |
- condition2.accept(this); |
- Statement thenStatement2 = node.thenStatement; |
- if (thenStatement2 != null) { |
- try { |
- _overrideManager.enterScope(); |
- propagateTrueState(condition2); |
- thenStatement2.accept(this); |
- } finally { |
- _overrideManager.exitScope(); |
- } |
+ Expression condition2 = node.condition; |
+ safelyVisit(condition2); |
+ Map<Element, Type2> thenOverrides = null; |
+ Statement thenStatement2 = node.thenStatement; |
+ if (thenStatement2 != null) { |
+ try { |
+ _overrideManager.enterScope(); |
+ propagateTrueState(condition2); |
+ thenStatement2.accept(this); |
+ } finally { |
+ thenOverrides = _overrideManager.captureLocalOverrides(); |
+ _overrideManager.exitScope(); |
} |
- Statement elseStatement2 = node.elseStatement; |
- if (elseStatement2 != null) { |
- try { |
- _overrideManager.enterScope(); |
- propagateFalseState(condition2); |
- elseStatement2.accept(this); |
- } finally { |
- _overrideManager.exitScope(); |
- } |
- } |
- node.accept(_elementResolver); |
- node.accept(_typeAnalyzer); |
- bool thenIsAbrupt = thenStatement2 != null && isAbruptTermination2(thenStatement2); |
- bool elseIsAbrupt = elseStatement2 != null && isAbruptTermination2(elseStatement2); |
- if (elseIsAbrupt && !thenIsAbrupt) { |
- propagateTrueState(condition2); |
- } else if (thenIsAbrupt && !elseIsAbrupt) { |
+ } |
+ Map<Element, Type2> elseOverrides = null; |
+ Statement elseStatement2 = node.elseStatement; |
+ if (elseStatement2 != null) { |
+ try { |
+ _overrideManager.enterScope(); |
propagateFalseState(condition2); |
+ elseStatement2.accept(this); |
+ } finally { |
+ elseOverrides = _overrideManager.captureLocalOverrides(); |
+ _overrideManager.exitScope(); |
} |
- } else { |
- super.visitIfStatement(node); |
} |
+ node.accept(_elementResolver); |
+ node.accept(_typeAnalyzer); |
+ bool thenIsAbrupt = isAbruptTermination2(thenStatement2); |
+ bool elseIsAbrupt = isAbruptTermination2(elseStatement2); |
+ if (elseIsAbrupt && !thenIsAbrupt) { |
+ propagateTrueState(condition2); |
+ if (thenOverrides != null) { |
+ _overrideManager.applyOverrides(thenOverrides); |
+ } |
+ } else if (thenIsAbrupt && !elseIsAbrupt) { |
+ propagateFalseState(condition2); |
+ if (elseOverrides != null) { |
+ _overrideManager.applyOverrides(elseOverrides); |
+ } |
+ } |
return null; |
} |
Object visitLabel(Label node) => null; |
@@ -3885,145 +5116,164 @@ |
return null; |
} |
Object visitSwitchCase(SwitchCase node) { |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- try { |
- _overrideManager.enterScope(); |
- super.visitSwitchCase(node); |
- } finally { |
- _overrideManager.exitScope(); |
- } |
- } else { |
+ try { |
+ _overrideManager.enterScope(); |
super.visitSwitchCase(node); |
+ } finally { |
+ _overrideManager.exitScope(); |
} |
return null; |
} |
Object visitSwitchDefault(SwitchDefault node) { |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- try { |
- _overrideManager.enterScope(); |
- super.visitSwitchDefault(node); |
- } finally { |
- _overrideManager.exitScope(); |
- } |
- } else { |
+ try { |
+ _overrideManager.enterScope(); |
super.visitSwitchDefault(node); |
+ } finally { |
+ _overrideManager.exitScope(); |
} |
return null; |
} |
Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- try { |
- _overrideManager.enterScope(); |
- super.visitTopLevelVariableDeclaration(node); |
- } finally { |
- Map<Element, Type2> overrides = captureOverrides(node.variables); |
- _overrideManager.exitScope(); |
- applyOverrides(overrides); |
- } |
- } else { |
+ try { |
+ _overrideManager.enterScope(); |
super.visitTopLevelVariableDeclaration(node); |
+ } finally { |
+ Map<Element, Type2> overrides = _overrideManager.captureOverrides(node.variables); |
+ _overrideManager.exitScope(); |
+ _overrideManager.applyOverrides(overrides); |
} |
return null; |
} |
Object visitTypeName(TypeName node) => null; |
Object visitWhileStatement(WhileStatement node) { |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- Expression condition2 = node.condition; |
- condition2.accept(this); |
- Statement body2 = node.body; |
- if (body2 != null) { |
- try { |
- _overrideManager.enterScope(); |
- propagateTrueState(condition2); |
- body2.accept(this); |
- } finally { |
- _overrideManager.exitScope(); |
- } |
+ Expression condition2 = node.condition; |
+ safelyVisit(condition2); |
+ Statement body2 = node.body; |
+ if (body2 != null) { |
+ try { |
+ _overrideManager.enterScope(); |
+ propagateTrueState(condition2); |
+ body2.accept(this); |
+ } finally { |
+ _overrideManager.exitScope(); |
} |
- node.accept(_elementResolver); |
- node.accept(_typeAnalyzer); |
- } else { |
- super.visitWhileStatement(node); |
} |
+ node.accept(_elementResolver); |
+ node.accept(_typeAnalyzer); |
return null; |
} |
+ |
/** |
* Return the class element representing the class containing the current node, or {@code null} if |
* the current node is not contained in a class. |
* @return the class element representing the class containing the current node |
*/ |
ClassElement get enclosingClass => _enclosingClass; |
+ |
/** |
* Return the element representing the function containing the current node, or {@code null} if |
* the current node is not contained in a function. |
* @return the element representing the function containing the current node |
*/ |
ExecutableElement get enclosingFunction => _enclosingFunction; |
+ |
/** |
* Return the element associated with the given expression whose type can be overridden, or{@code null} if there is no element whose type can be overridden. |
* @param expression the expression with which the element is associated |
* @return the element associated with the given expression |
*/ |
VariableElement getOverridableElement(Expression expression) { |
+ Element element = null; |
if (expression is SimpleIdentifier) { |
- Element element2 = ((expression as SimpleIdentifier)).element; |
- if (element2 is VariableElement) { |
- return element2 as VariableElement; |
- } |
+ element = ((expression as SimpleIdentifier)).element; |
+ } else if (expression is PrefixedIdentifier) { |
+ element = ((expression as PrefixedIdentifier)).element; |
+ } else if (expression is PropertyAccess) { |
+ element = ((expression as PropertyAccess)).propertyName.element; |
} |
+ if (element is VariableElement) { |
+ return element as VariableElement; |
+ } |
return null; |
} |
+ |
+ /** |
+ * If it is appropriate to do so, override the current type of the given element with the given |
+ * type. Generally speaking, it is appropriate if the given type is more specific than the current |
+ * type. |
+ * @param element the element whose type might be overridden |
+ * @param potentialType the potential type of the element |
+ */ |
+ void override(VariableElement element, Type2 potentialType) { |
+ if (potentialType == null || identical(potentialType, BottomTypeImpl.instance)) { |
+ return; |
+ } |
+ if (element is PropertyInducingElement) { |
+ PropertyInducingElement variable = element as PropertyInducingElement; |
+ if (!variable.isConst() && !variable.isFinal()) { |
+ return; |
+ } |
+ } |
+ Type2 currentType = getBestType(element); |
+ if (currentType == null || !currentType.isMoreSpecificThan(potentialType)) { |
+ _overrideManager.setType(element, potentialType); |
+ } |
+ } |
void visitForEachStatementInScope(ForEachStatement node) { |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- DeclaredIdentifier loopVariable2 = node.loopVariable; |
- safelyVisit(loopVariable2); |
- Expression iterator2 = node.iterator; |
- if (iterator2 != null) { |
- iterator2.accept(this); |
- if (loopVariable2 != null) { |
+ Expression iterator2 = node.iterator; |
+ safelyVisit(iterator2); |
+ DeclaredIdentifier loopVariable2 = node.loopVariable; |
+ safelyVisit(loopVariable2); |
+ Statement body2 = node.body; |
+ if (body2 != null) { |
+ try { |
+ _overrideManager.enterScope(); |
+ if (loopVariable2 != null && iterator2 != null) { |
LocalVariableElement loopElement = loopVariable2.element; |
- override(loopElement, loopElement.type, getIteratorElementType(iterator2)); |
+ if (loopElement != null) { |
+ override(loopElement, getIteratorElementType(iterator2)); |
+ } |
} |
+ body2.accept(this); |
+ } finally { |
+ _overrideManager.exitScope(); |
} |
- safelyVisit(node.body); |
- node.accept(_elementResolver); |
- node.accept(_typeAnalyzer); |
- } else { |
- super.visitForEachStatementInScope(node); |
} |
+ node.accept(_elementResolver); |
+ node.accept(_typeAnalyzer); |
} |
- /** |
- * Apply a set of overrides that were previously captured. |
- * @param overrides the overrides to be applied |
- */ |
- void applyOverrides(Map<Element, Type2> overrides) { |
- for (MapEntry<Element, Type2> entry in getMapEntrySet(overrides)) { |
- _overrideManager.setType(entry.getKey(), entry.getValue()); |
+ void visitForStatementInScope(ForStatement node) { |
+ safelyVisit(node.variables); |
+ safelyVisit(node.initialization); |
+ safelyVisit(node.condition); |
+ _overrideManager.enterScope(); |
+ try { |
+ propagateTrueState(node.condition); |
+ safelyVisit(node.body); |
+ node.updaters.accept(this); |
+ } finally { |
+ _overrideManager.exitScope(); |
} |
} |
+ |
/** |
- * Return a map from the elements for the variables in the given list that have their types |
- * overridden to the overriding type. |
- * @param variableList the list of variables whose overriding types are to be captured |
- * @return a table mapping elements to their overriding types |
+ * Return the best type information available for the given element. If the type of the element |
+ * has been overridden, then return the overriding type. Otherwise, return the static type. |
+ * @param element the element for which type information is to be returned |
+ * @return the best type information available for the given element |
*/ |
- Map<Element, Type2> captureOverrides(VariableDeclarationList variableList) { |
- Map<Element, Type2> overrides = new Map<Element, Type2>(); |
- if (StaticTypeAnalyzer.USE_TYPE_PROPAGATION) { |
- if (variableList.isConst() || variableList.isFinal()) { |
- for (VariableDeclaration variable in variableList.variables) { |
- Element element2 = variable.element; |
- if (element2 != null) { |
- Type2 type = _overrideManager.getType(element2); |
- if (type != null) { |
- overrides[element2] = type; |
- } |
- } |
- } |
+ Type2 getBestType(Element element) { |
+ Type2 bestType = _overrideManager.getType(element); |
+ if (bestType == null) { |
+ if (element is LocalVariableElement) { |
+ bestType = ((element as LocalVariableElement)).type; |
+ } else if (element is ParameterElement) { |
+ bestType = ((element as ParameterElement)).type; |
} |
} |
- return overrides; |
+ return bestType; |
} |
+ |
/** |
* The given expression is the expression used to compute the iterator for a for-each statement. |
* Attempt to compute the type of objects that will be assigned to the loop variable and return |
@@ -4049,20 +5299,8 @@ |
} |
return null; |
} |
+ |
/** |
- * Return the type of the given (overridable) element. |
- * @param element the element whose type is to be returned |
- * @return the type of the given element |
- */ |
- Type2 getType(Element element) { |
- if (element is LocalVariableElement) { |
- return ((element as LocalVariableElement)).type; |
- } else if (element is ParameterElement) { |
- return ((element as ParameterElement)).type; |
- } |
- return null; |
- } |
- /** |
* Return {@code true} if the given expression terminates abruptly (that is, if any expression |
* following the given expression will not be reached). |
* @param expression the expression being tested |
@@ -4074,6 +5312,7 @@ |
} |
return expression2 is ThrowExpression || expression2 is RethrowExpression; |
} |
+ |
/** |
* Return {@code true} if the given statement terminates abruptly (that is, if any statement |
* following the given statement will not be reached). |
@@ -4081,7 +5320,7 @@ |
* @return {@code true} if the given statement terminates abruptly |
*/ |
bool isAbruptTermination2(Statement statement) { |
- if (statement is ReturnStatement) { |
+ if (statement is ReturnStatement || statement is BreakStatement || statement is ContinueStatement) { |
return true; |
} else if (statement is ExpressionStatement) { |
return isAbruptTermination(((statement as ExpressionStatement)).expression); |
@@ -4095,92 +5334,74 @@ |
} |
return false; |
} |
+ |
/** |
- * If it is appropriate to do so, override the type of the given element. Use the static type and |
- * inferred type of the element to determine whether or not it is appropriate. |
- * @param element the element whose type might be overridden |
- * @param staticType the static type of the element |
- * @param inferredType the inferred type of the element |
- */ |
- void override(VariableElement element, Type2 staticType, Type2 inferredType) { |
- if (identical(inferredType, BottomTypeImpl.instance)) { |
- return; |
- } |
- if (element is PropertyInducingElement) { |
- PropertyInducingElement variable = element as PropertyInducingElement; |
- if (!variable.isConst() && !variable.isFinal()) { |
- return; |
- } |
- } |
- if (staticType == null || (inferredType != null && inferredType.isMoreSpecificThan(staticType))) { |
- _overrideManager.setType(element, inferredType); |
- } |
- } |
- /** |
* Propagate any type information that results from knowing that the given condition will have |
- * evaluated to 'false'. |
+ * been evaluated to 'false'. |
* @param condition the condition that will have evaluated to 'false' |
*/ |
void propagateFalseState(Expression condition) { |
- while (condition is ParenthesizedExpression) { |
- condition = ((condition as ParenthesizedExpression)).expression; |
- } |
- if (condition is IsExpression) { |
+ if (condition is BinaryExpression) { |
+ BinaryExpression binary = condition as BinaryExpression; |
+ if (identical(binary.operator.type, sc.TokenType.BAR_BAR)) { |
+ propagateFalseState(binary.leftOperand); |
+ propagateFalseState(binary.rightOperand); |
+ } |
+ } else if (condition is IsExpression) { |
IsExpression is2 = condition as IsExpression; |
if (is2.notOperator != null) { |
VariableElement element = getOverridableElement(is2.expression); |
if (element != null) { |
- Type2 type2 = is2.type.type; |
- if (type2 != null) { |
- override(element, getType(element), type2); |
- } |
+ override(element, is2.type.type); |
} |
} |
- } else if (condition is BinaryExpression) { |
- BinaryExpression binary = condition as BinaryExpression; |
- if (identical(binary.operator.type, sc.TokenType.BAR_BAR)) { |
- propagateFalseState(binary.leftOperand); |
- propagateFalseState(binary.rightOperand); |
+ } else if (condition is PrefixExpression) { |
+ PrefixExpression prefix = condition as PrefixExpression; |
+ if (identical(prefix.operator.type, sc.TokenType.BANG)) { |
+ propagateTrueState(prefix.operand); |
} |
+ } else if (condition is ParenthesizedExpression) { |
+ propagateFalseState(((condition as ParenthesizedExpression)).expression); |
} |
} |
+ |
/** |
+ * Propagate any type information that results from knowing that the given expression will have |
+ * been evaluated without altering the flow of execution. |
+ * @param expression the expression that will have been evaluated |
+ */ |
+ void propagateState(Expression expression) { |
+ } |
+ |
+ /** |
* Propagate any type information that results from knowing that the given condition will have |
- * evaluated to 'true'. |
+ * been evaluated to 'true'. |
* @param condition the condition that will have evaluated to 'true' |
*/ |
void propagateTrueState(Expression condition) { |
- while (condition is ParenthesizedExpression) { |
- condition = ((condition as ParenthesizedExpression)).expression; |
- } |
- if (condition is IsExpression) { |
+ if (condition is BinaryExpression) { |
+ BinaryExpression binary = condition as BinaryExpression; |
+ if (identical(binary.operator.type, sc.TokenType.AMPERSAND_AMPERSAND)) { |
+ propagateTrueState(binary.leftOperand); |
+ propagateTrueState(binary.rightOperand); |
+ } |
+ } else if (condition is IsExpression) { |
IsExpression is2 = condition as IsExpression; |
if (is2.notOperator == null) { |
VariableElement element = getOverridableElement(is2.expression); |
if (element != null) { |
- Type2 type2 = is2.type.type; |
- if (type2 != null) { |
- override(element, getType(element), type2); |
- } |
+ override(element, is2.type.type); |
} |
} |
- } else if (condition is BinaryExpression) { |
- BinaryExpression binary = condition as BinaryExpression; |
- if (identical(binary.operator.type, sc.TokenType.AMPERSAND_AMPERSAND)) { |
- propagateTrueState(binary.leftOperand); |
- propagateTrueState(binary.rightOperand); |
+ } else if (condition is PrefixExpression) { |
+ PrefixExpression prefix = condition as PrefixExpression; |
+ if (identical(prefix.operator.type, sc.TokenType.BANG)) { |
+ propagateFalseState(prefix.operand); |
} |
+ } else if (condition is ParenthesizedExpression) { |
+ propagateTrueState(((condition as ParenthesizedExpression)).expression); |
} |
} |
- /** |
- * Visit the given AST node if it is not null. |
- * @param node the node to be visited |
- */ |
- void safelyVisit(ASTNode node) { |
- if (node != null) { |
- node.accept(this); |
- } |
- } |
get elementResolver_J2DAccessor => _elementResolver; |
set elementResolver_J2DAccessor(__v) => _elementResolver = __v; |
get labelScope_J2DAccessor => _labelScope; |
@@ -4192,36 +5413,44 @@ |
get enclosingClass_J2DAccessor => _enclosingClass; |
set enclosingClass_J2DAccessor(__v) => _enclosingClass = __v; |
} |
+ |
/** |
* The abstract class {@code ScopedVisitor} maintains name and label scopes as an AST structure is |
* being visited. |
* @coverage dart.engine.resolver |
*/ |
abstract class ScopedVisitor extends GeneralizingASTVisitor<Object> { |
+ |
/** |
* The element for the library containing the compilation unit being visited. |
*/ |
LibraryElement _definingLibrary; |
+ |
/** |
* The source representing the compilation unit being visited. |
*/ |
Source _source; |
+ |
/** |
* The error listener that will be informed of any errors that are found during resolution. |
*/ |
AnalysisErrorListener _errorListener; |
+ |
/** |
* The scope used to resolve identifiers. |
*/ |
Scope _nameScope; |
+ |
/** |
* The object used to access the types from the core library. |
*/ |
TypeProvider _typeProvider; |
+ |
/** |
* The scope used to resolve labels for {@code break} and {@code continue} statements, or{@code null} if no labels have been defined in the current context. |
*/ |
LabelScope _labelScope; |
+ |
/** |
* Initialize a newly created visitor to resolve the nodes in a compilation unit. |
* @param library the library containing the compilation unit being resolved |
@@ -4229,9 +5458,9 @@ |
* @param typeProvider the object used to access the types from the core library |
*/ |
ScopedVisitor.con1(Library library, Source source2, TypeProvider typeProvider2) { |
- _jtd_constructor_268_impl(library, source2, typeProvider2); |
+ _jtd_constructor_274_impl(library, source2, typeProvider2); |
} |
- _jtd_constructor_268_impl(Library library, Source source2, TypeProvider typeProvider2) { |
+ _jtd_constructor_274_impl(Library library, Source source2, TypeProvider typeProvider2) { |
this._definingLibrary = library.libraryElement; |
this._source = source2; |
LibraryScope libraryScope2 = library.libraryScope; |
@@ -4239,6 +5468,7 @@ |
this._nameScope = libraryScope2; |
this._typeProvider = typeProvider2; |
} |
+ |
/** |
* Initialize a newly created visitor to resolve the nodes in a compilation unit. |
* @param definingLibrary the element for the library containing the compilation unit being |
@@ -4249,20 +5479,22 @@ |
* during resolution |
*/ |
ScopedVisitor.con2(LibraryElement definingLibrary2, Source source2, TypeProvider typeProvider2, AnalysisErrorListener errorListener2) { |
- _jtd_constructor_269_impl(definingLibrary2, source2, typeProvider2, errorListener2); |
+ _jtd_constructor_275_impl(definingLibrary2, source2, typeProvider2, errorListener2); |
} |
- _jtd_constructor_269_impl(LibraryElement definingLibrary2, Source source2, TypeProvider typeProvider2, AnalysisErrorListener errorListener2) { |
+ _jtd_constructor_275_impl(LibraryElement definingLibrary2, Source source2, TypeProvider typeProvider2, AnalysisErrorListener errorListener2) { |
this._definingLibrary = definingLibrary2; |
this._source = source2; |
this._errorListener = errorListener2; |
this._nameScope = new LibraryScope(definingLibrary2, errorListener2); |
this._typeProvider = typeProvider2; |
} |
+ |
/** |
* Return the library element for the library containing the compilation unit being resolved. |
* @return the library element for the library containing the compilation unit being resolved |
*/ |
LibraryElement get definingLibrary => _definingLibrary; |
+ |
/** |
* Return the object used to access the types from the core library. |
* @return the object used to access the types from the core library |
@@ -4363,7 +5595,7 @@ |
Scope outerNameScope = _nameScope; |
_nameScope = new EnclosedScope(_nameScope); |
try { |
- super.visitForStatement(node); |
+ visitForStatementInScope(node); |
} finally { |
_nameScope = outerNameScope; |
_labelScope = outerLabelScope; |
@@ -4385,16 +5617,20 @@ |
return null; |
} |
Object visitFunctionExpression(FunctionExpression node) { |
- Scope outerScope = _nameScope; |
- try { |
- ExecutableElement functionElement = node.element; |
- if (functionElement == null) { |
- } else { |
- _nameScope = new FunctionScope(_nameScope, functionElement); |
+ if (node.parent is FunctionDeclaration) { |
+ super.visitFunctionExpression(node); |
+ } else { |
+ Scope outerScope = _nameScope; |
+ try { |
+ ExecutableElement functionElement = node.element; |
+ if (functionElement == null) { |
+ } else { |
+ _nameScope = new FunctionScope(_nameScope, functionElement); |
+ } |
+ super.visitFunctionExpression(node); |
+ } finally { |
+ _nameScope = outerScope; |
} |
- super.visitFunctionExpression(node); |
- } finally { |
- _nameScope = outerScope; |
} |
return null; |
} |
@@ -4429,26 +5665,22 @@ |
} |
Object visitSwitchCase(SwitchCase node) { |
node.expression.accept(this); |
- LabelScope outerLabelScope = addScopesFor(node.labels); |
Scope outerNameScope = _nameScope; |
_nameScope = new EnclosedScope(_nameScope); |
try { |
node.statements.accept(this); |
} finally { |
_nameScope = outerNameScope; |
- _labelScope = outerLabelScope; |
} |
return null; |
} |
Object visitSwitchDefault(SwitchDefault node) { |
- LabelScope outerLabelScope = addScopesFor(node.labels); |
Scope outerNameScope = _nameScope; |
_nameScope = new EnclosedScope(_nameScope); |
try { |
node.statements.accept(this); |
} finally { |
_nameScope = outerNameScope; |
- _labelScope = outerLabelScope; |
} |
return null; |
} |
@@ -4459,7 +5691,7 @@ |
for (Label label in member.labels) { |
SimpleIdentifier labelName = label.label; |
LabelElement labelElement = labelName.element as LabelElement; |
- _labelScope = new LabelScope.con2(outerScope, labelName.name, labelElement); |
+ _labelScope = new LabelScope.con2(_labelScope, labelName.name, labelElement); |
} |
} |
try { |
@@ -4489,16 +5721,19 @@ |
} |
return null; |
} |
+ |
/** |
* Return the label scope in which the current node is being resolved. |
* @return the label scope in which the current node is being resolved |
*/ |
LabelScope get labelScope => _labelScope; |
+ |
/** |
* Return the name scope in which the current node is being resolved. |
* @return the name scope in which the current node is being resolved |
*/ |
Scope get nameScope => _nameScope; |
+ |
/** |
* Report an error with the given error code and arguments. |
* @param errorCode the error code of the error to be reported |
@@ -4508,25 +5743,61 @@ |
void reportError(ErrorCode errorCode, ASTNode node, List<Object> arguments) { |
_errorListener.onError(new AnalysisError.con2(_source, node.offset, node.length, errorCode, arguments)); |
} |
+ |
/** |
* Report an error with the given error code and arguments. |
* @param errorCode the error code of the error to be reported |
+ * @param offset the offset of the location of the error |
+ * @param length the length of the location of the error |
+ * @param arguments the arguments to the error, used to compose the error message |
+ */ |
+ void reportError5(ErrorCode errorCode, int offset, int length, List<Object> arguments) { |
+ _errorListener.onError(new AnalysisError.con2(_source, offset, length, errorCode, arguments)); |
+ } |
+ |
+ /** |
+ * Report an error with the given error code and arguments. |
+ * @param errorCode the error code of the error to be reported |
* @param token the token specifying the location of the error |
* @param arguments the arguments to the error, used to compose the error message |
*/ |
- void reportError3(ErrorCode errorCode, sc.Token token, List<Object> arguments) { |
+ void reportError6(ErrorCode errorCode, sc.Token token, List<Object> arguments) { |
_errorListener.onError(new AnalysisError.con2(_source, token.offset, token.length, errorCode, arguments)); |
} |
+ |
/** |
+ * Visit the given AST node if it is not null. |
+ * @param node the node to be visited |
+ */ |
+ void safelyVisit(ASTNode node) { |
+ if (node != null) { |
+ node.accept(this); |
+ } |
+ } |
+ |
+ /** |
* Visit the given statement after it's scope has been created. This replaces the normal call to |
* the inherited visit method so that ResolverVisitor can intervene when type propagation is |
* enabled. |
* @param node the statement to be visited |
*/ |
void visitForEachStatementInScope(ForEachStatement node) { |
- super.visitForEachStatement(node); |
+ safelyVisit(node.iterator); |
+ safelyVisit(node.loopVariable); |
+ safelyVisit(node.body); |
} |
+ |
/** |
+ * Visit the given statement after it's scope has been created. This replaces the normal call to |
+ * the inherited visit method so that ResolverVisitor can intervene when type propagation is |
+ * enabled. |
+ * @param node the statement to be visited |
+ */ |
+ void visitForStatementInScope(ForStatement node) { |
+ super.visitForStatement(node); |
+ } |
+ |
+ /** |
* Add scopes for each of the given labels. |
* @param labels the labels for which new scopes are to be added |
* @return the scope that was in effect before the new scopes were added |
@@ -4542,6 +5813,7 @@ |
return outerScope; |
} |
} |
+ |
/** |
* Instances of the class {@code StaticTypeAnalyzer} perform two type-related tasks. First, they |
* compute the static type of every expression. Second, they look for any static type errors or |
@@ -4553,6 +5825,7 @@ |
* @coverage dart.engine.resolver |
*/ |
class StaticTypeAnalyzer extends SimpleASTVisitor<Object> { |
+ |
/** |
* Create a table mapping HTML tag names to the names of the classes (in 'dart:html') that |
* implement those tags. |
@@ -4620,36 +5893,39 @@ |
map["video"] = "VideoElement"; |
return map; |
} |
+ |
/** |
* The resolver driving the resolution and type analysis. |
*/ |
ResolverVisitor _resolver; |
+ |
/** |
* The object providing access to the types defined by the language. |
*/ |
TypeProvider _typeProvider; |
+ |
/** |
* The type representing the type 'dynamic'. |
*/ |
Type2 _dynamicType; |
+ |
/** |
* The type representing the class containing the nodes being analyzed, or {@code null} if the |
* nodes are not within a class. |
*/ |
InterfaceType _thisType; |
+ |
/** |
* The object keeping track of which elements have had their types overridden. |
*/ |
TypeOverrideManager _overrideManager; |
+ |
/** |
- * A flag indicating whether type propagation should be enabled. |
- */ |
- static bool USE_TYPE_PROPAGATION = true; |
- /** |
* A table mapping HTML tag names to the names of the classes (in 'dart:html') that implement |
* those tags. |
*/ |
static Map<String, String> _HTML_ELEMENT_TO_CLASS_MAP = createHtmlTagToClassMap(); |
+ |
/** |
* Initialize a newly created type analyzer. |
* @param resolver the resolver driving this participant |
@@ -4660,6 +5936,7 @@ |
_dynamicType = _typeProvider.dynamicType; |
_overrideManager = resolver.overrideManager; |
} |
+ |
/** |
* Set the type of the class being analyzed to the given type. |
* @param thisType the type representing the class containing the nodes being analyzed |
@@ -4667,15 +5944,24 @@ |
void set thisType(InterfaceType thisType2) { |
this._thisType = thisType2; |
} |
+ |
/** |
* The Dart Language Specification, 12.5: <blockquote>The static type of a string literal is{@code String}.</blockquote> |
*/ |
- Object visitAdjacentStrings(AdjacentStrings node) => recordType(node, _typeProvider.stringType); |
+ Object visitAdjacentStrings(AdjacentStrings node) { |
+ recordStaticType(node, _typeProvider.stringType); |
+ return null; |
+ } |
+ |
/** |
* The Dart Language Specification, 12.33: <blockquote>The static type of an argument definition |
* test is {@code bool}.</blockquote> |
*/ |
- Object visitArgumentDefinitionTest(ArgumentDefinitionTest node) => recordType(node, _typeProvider.boolType); |
+ Object visitArgumentDefinitionTest(ArgumentDefinitionTest node) { |
+ recordStaticType(node, _typeProvider.boolType); |
+ return null; |
+ } |
+ |
/** |
* The Dart Language Specification, 12.32: <blockquote>... the cast expression <i>e as T</i> ... |
* <p> |
@@ -4684,10 +5970,14 @@ |
* <p> |
* The static type of a cast expression <i>e as T</i> is <i>T</i>.</blockquote> |
*/ |
- Object visitAsExpression(AsExpression node) => recordType(node, getType4(node.type)); |
+ Object visitAsExpression(AsExpression node) { |
+ recordStaticType(node, getType2(node.type)); |
+ return null; |
+ } |
+ |
/** |
- * The Dart Language Specification, 12.18: <blockquote> ... an assignment <i>a</i> of the form |
- * <i>v = e</i> ... |
+ * The Dart Language Specification, 12.18: <blockquote>... an assignment <i>a</i> of the form <i>v |
+ * = e</i> ... |
* <p> |
* It is a static type warning if the static type of <i>e</i> may not be assigned to the static |
* type of <i>v</i>. |
@@ -4722,22 +6012,41 @@ |
* <i>e<sub>2</sub></i>. A compound assignment of the form <i>e<sub>1</sub>\[e<sub>2</sub>\] op= |
* e<sub>3</sub></i> is equivalent to <i>((a, i) => a\[i\] = a\[i\] op e<sub>3</sub>)(e<sub>1</sub>, |
* e<sub>2</sub>)</i> where <i>a</i> and <i>i</i> are a variables that are not used in |
- * <i>e<sub>3</sub></i>. </blockquote> |
+ * <i>e<sub>3</sub></i>.</blockquote> |
*/ |
Object visitAssignmentExpression(AssignmentExpression node) { |
sc.TokenType operator2 = node.operator.type; |
- if (operator2 != sc.TokenType.EQ) { |
- return recordReturnType(node, node.element); |
- } |
- Type2 rightType = getType2(node.rightHandSide); |
- if (USE_TYPE_PROPAGATION) { |
+ if (identical(operator2, sc.TokenType.EQ)) { |
+ Expression rightHandSide2 = node.rightHandSide; |
+ Type2 staticType = getStaticType(rightHandSide2); |
+ recordStaticType(node, staticType); |
+ Type2 overrideType = staticType; |
+ Type2 propagatedType = getPropagatedType(rightHandSide2); |
+ if (propagatedType != null) { |
+ if (propagatedType.isMoreSpecificThan(staticType)) { |
+ recordPropagatedType(node, propagatedType); |
+ } |
+ overrideType = propagatedType; |
+ } |
VariableElement element = _resolver.getOverridableElement(node.leftHandSide); |
if (element != null) { |
- override(element, getType(element), rightType); |
+ _resolver.override(element, overrideType); |
} |
+ } else { |
+ ExecutableElement staticMethodElement = node.staticElement; |
+ Type2 staticType = computeReturnType(staticMethodElement); |
+ recordStaticType(node, staticType); |
+ MethodElement propagatedMethodElement = node.element; |
+ if (propagatedMethodElement != staticMethodElement) { |
+ Type2 propagatedType = computeReturnType(propagatedMethodElement); |
+ if (propagatedType != null && propagatedType.isMoreSpecificThan(staticType)) { |
+ recordPropagatedType(node, propagatedType); |
+ } |
+ } |
} |
- return recordType(node, rightType); |
+ return null; |
} |
+ |
/** |
* The Dart Language Specification, 12.20: <blockquote>The static type of a logical boolean |
* expression is {@code bool}.</blockquote> |
@@ -4776,35 +6085,40 @@ |
* <i>super.op(e<sub>2</sub>)</i>.</blockquote> |
*/ |
Object visitBinaryExpression(BinaryExpression node) { |
- sc.TokenType operator2 = node.operator.type; |
- while (true) { |
- if (operator2 == sc.TokenType.AMPERSAND_AMPERSAND || operator2 == sc.TokenType.BAR_BAR || operator2 == sc.TokenType.EQ_EQ || operator2 == sc.TokenType.BANG_EQ) { |
- return recordType(node, _typeProvider.boolType); |
- } else if (operator2 == sc.TokenType.MINUS || operator2 == sc.TokenType.PERCENT || operator2 == sc.TokenType.PLUS || operator2 == sc.TokenType.STAR || operator2 == sc.TokenType.TILDE_SLASH) { |
- Type2 intType2 = _typeProvider.intType; |
- if (identical(getType2(node.leftOperand), intType2) && identical(getType2(node.rightOperand), intType2)) { |
- return recordType(node, intType2); |
- } |
- } else if (operator2 == sc.TokenType.SLASH) { |
- Type2 doubleType2 = _typeProvider.doubleType; |
- if (identical(getType2(node.leftOperand), doubleType2) || identical(getType2(node.rightOperand), doubleType2)) { |
- return recordType(node, doubleType2); |
- } |
+ ExecutableElement staticMethodElement = node.staticElement; |
+ Type2 staticType = computeReturnType(staticMethodElement); |
+ staticType = refineBinaryExpressionType(node, staticType); |
+ recordStaticType(node, staticType); |
+ MethodElement propagatedMethodElement = node.element; |
+ if (propagatedMethodElement != staticMethodElement) { |
+ Type2 propagatedType = computeReturnType(propagatedMethodElement); |
+ if (propagatedType != null && propagatedType.isMoreSpecificThan(staticType)) { |
+ recordPropagatedType(node, propagatedType); |
} |
- break; |
} |
- return recordReturnType(node, node.element); |
+ return null; |
} |
+ |
/** |
- * The Dart Language Specification, 12.4: <blockquote>The static type of a boolean literal is{@code bool}.</blockquote> |
+ * The Dart Language Specification, 12.4: <blockquote>The static type of a boolean literal is |
+ * bool.</blockquote> |
*/ |
- Object visitBooleanLiteral(BooleanLiteral node) => recordType(node, _typeProvider.boolType); |
+ Object visitBooleanLiteral(BooleanLiteral node) { |
+ recordStaticType(node, _typeProvider.boolType); |
+ return null; |
+ } |
+ |
/** |
* The Dart Language Specification, 12.15.2: <blockquote>A cascaded method invocation expression |
* of the form <i>e..suffix</i> is equivalent to the expression <i>(t) {t.suffix; return |
* t;}(e)</i>.</blockquote> |
*/ |
- Object visitCascadeExpression(CascadeExpression node) => recordType(node, getType2(node.target)); |
+ Object visitCascadeExpression(CascadeExpression node) { |
+ recordStaticType(node, getStaticType(node.target)); |
+ recordPropagatedType(node, getPropagatedType(node.target)); |
+ return null; |
+ } |
+ |
/** |
* The Dart Language Specification, 12.19: <blockquote> ... a conditional expression <i>c</i> of |
* the form <i>e<sub>1</sub> ? e<sub>2</sub> : e<sub>3</sub></i> ... |
@@ -4815,24 +6129,52 @@ |
* and the static type of <i>e<sub>3</sub></i>.</blockquote> |
*/ |
Object visitConditionalExpression(ConditionalExpression node) { |
- Type2 thenType = getType2(node.thenExpression); |
- Type2 elseType = getType2(node.elseExpression); |
- if (thenType == null) { |
- return recordType(node, _dynamicType); |
+ Type2 staticThenType = getStaticType(node.thenExpression); |
+ Type2 staticElseType = getStaticType(node.elseExpression); |
+ if (staticThenType == null) { |
+ staticThenType = _dynamicType; |
} |
- Type2 resultType = thenType.getLeastUpperBound(elseType); |
- return recordType(node, resultType); |
+ if (staticElseType == null) { |
+ staticElseType = _dynamicType; |
+ } |
+ Type2 staticType = staticThenType.getLeastUpperBound(staticElseType); |
+ if (staticType == null) { |
+ staticType = _dynamicType; |
+ } |
+ recordStaticType(node, staticType); |
+ Type2 propagatedThenType = getPropagatedType(node.thenExpression); |
+ Type2 propagatedElseType = getPropagatedType(node.elseExpression); |
+ if (propagatedThenType != null || propagatedElseType != null) { |
+ if (propagatedThenType == null) { |
+ propagatedThenType = staticThenType; |
+ } |
+ if (propagatedElseType == null) { |
+ propagatedElseType = staticElseType; |
+ } |
+ Type2 propagatedType = propagatedThenType.getLeastUpperBound(propagatedElseType); |
+ if (propagatedType != null && propagatedType.isMoreSpecificThan(staticType)) { |
+ recordPropagatedType(node, propagatedType); |
+ } |
+ } |
+ return null; |
} |
+ |
/** |
- * The Dart Language Specification, 12.3: <blockquote>The static type of a literal double is{@code double}.</blockquote> |
+ * The Dart Language Specification, 12.3: <blockquote>The static type of a literal double is |
+ * double.</blockquote> |
*/ |
- Object visitDoubleLiteral(DoubleLiteral node) => recordType(node, _typeProvider.doubleType); |
+ Object visitDoubleLiteral(DoubleLiteral node) { |
+ recordStaticType(node, _typeProvider.doubleType); |
+ return null; |
+ } |
Object visitFunctionDeclaration(FunctionDeclaration node) { |
FunctionExpression function = node.functionExpression; |
FunctionTypeImpl functionType = node.element.type as FunctionTypeImpl; |
- setTypeInformation(functionType, computeReturnType(node), function.parameters); |
- return recordType(function, functionType); |
+ setTypeInformation(functionType, computeReturnType2(node), function.parameters); |
+ recordStaticType(function, functionType); |
+ return null; |
} |
+ |
/** |
* The Dart Language Specification, 12.9: <blockquote>The static type of a function literal of the |
* form <i>(T<sub>1</sub> a<sub>1</sub>, …, T<sub>n</sub> a<sub>n</sub>, \[T<sub>n+1</sub> |
@@ -4868,9 +6210,11 @@ |
return null; |
} |
FunctionTypeImpl functionType = node.element.type as FunctionTypeImpl; |
- setTypeInformation(functionType, computeReturnType2(node), node.parameters); |
- return recordType(node, functionType); |
+ setTypeInformation(functionType, computeReturnType3(node), node.parameters); |
+ recordStaticType(node, functionType); |
+ return null; |
} |
+ |
/** |
* The Dart Language Specification, 12.14.4: <blockquote>A function expression invocation <i>i</i> |
* has the form <i>e<sub>f</sub>(a<sub>1</sub>, …, a<sub>n</sub>, x<sub>n+1</sub>: |
@@ -4883,7 +6227,20 @@ |
* If <i>F</i> is not a function type, the static type of <i>i</i> is dynamic. Otherwise the |
* static type of <i>i</i> is the declared return type of <i>F</i>.</blockquote> |
*/ |
- Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) => recordReturnType(node, node.element); |
+ Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
+ ExecutableElement staticMethodElement = node.staticElement; |
+ Type2 staticType = computeReturnType(staticMethodElement); |
+ recordStaticType(node, staticType); |
+ ExecutableElement propagatedMethodElement = node.element; |
+ Type2 propagatedType = computeReturnType(propagatedMethodElement); |
+ if (staticType == null) { |
+ recordStaticType(node, propagatedType); |
+ } else if (propagatedType != null && propagatedType.isMoreSpecificThan(staticType)) { |
+ recordPropagatedType(node, propagatedType); |
+ } |
+ return null; |
+ } |
+ |
/** |
* The Dart Language Specification, 12.29: <blockquote>An assignable expression of the form |
* <i>e<sub>1</sub>\[e<sub>2</sub>\]</i> is evaluated as a method invocation of the operator method |
@@ -4891,10 +6248,31 @@ |
*/ |
Object visitIndexExpression(IndexExpression node) { |
if (node.inSetterContext()) { |
- return recordArgumentType(node, node.element); |
+ ExecutableElement staticMethodElement = node.staticElement; |
+ Type2 staticType = computeArgumentType(staticMethodElement); |
+ recordStaticType(node, staticType); |
+ MethodElement propagatedMethodElement = node.element; |
+ if (propagatedMethodElement != staticMethodElement) { |
+ Type2 propagatedType = computeArgumentType(propagatedMethodElement); |
+ if (propagatedType != null && propagatedType.isMoreSpecificThan(staticType)) { |
+ recordPropagatedType(node, propagatedType); |
+ } |
+ } |
+ } else { |
+ ExecutableElement staticMethodElement = node.staticElement; |
+ Type2 staticType = computeReturnType(staticMethodElement); |
+ recordStaticType(node, staticType); |
+ MethodElement propagatedMethodElement = node.element; |
+ if (propagatedMethodElement != staticMethodElement) { |
+ Type2 propagatedType = computeReturnType(propagatedMethodElement); |
+ if (propagatedType != null && propagatedType.isMoreSpecificThan(staticType)) { |
+ recordPropagatedType(node, propagatedType); |
+ } |
+ } |
} |
- return recordReturnType(node, node.element); |
+ return null; |
} |
+ |
/** |
* The Dart Language Specification, 12.11.1: <blockquote>The static type of a new expression of |
* either the form <i>new T.id(a<sub>1</sub>, …, a<sub>n</sub>)</i> or the form <i>new |
@@ -4905,31 +6283,39 @@ |
* form <i>const T(a<sub>1</sub>, …, a<sub>n</sub>)</i> is <i>T</i>. </blockquote> |
*/ |
Object visitInstanceCreationExpression(InstanceCreationExpression node) { |
- if (USE_TYPE_PROPAGATION) { |
- ConstructorElement element2 = node.element; |
- if (element2 != null && "Element" == element2.enclosingElement.name && "tag" == element2.name) { |
- LibraryElement library2 = element2.library; |
- if (isHtmlLibrary(library2)) { |
- Type2 returnType = getFirstArgumentAsType2(library2, node.argumentList, _HTML_ELEMENT_TO_CLASS_MAP); |
- if (returnType != null) { |
- return recordType(node, returnType); |
- } |
+ recordStaticType(node, node.constructorName.type.type); |
+ ConstructorElement element2 = node.element; |
+ if (element2 != null && "Element" == element2.enclosingElement.name && "tag" == element2.name) { |
+ LibraryElement library2 = element2.library; |
+ if (isHtmlLibrary(library2)) { |
+ Type2 returnType = getFirstArgumentAsType2(library2, node.argumentList, _HTML_ELEMENT_TO_CLASS_MAP); |
+ if (returnType != null) { |
+ recordPropagatedType(node, returnType); |
} |
} |
} |
- return recordType(node, node.constructorName.type.type); |
+ return null; |
} |
+ |
/** |
* The Dart Language Specification, 12.3: <blockquote>The static type of an integer literal is{@code int}.</blockquote> |
*/ |
- Object visitIntegerLiteral(IntegerLiteral node) => recordType(node, _typeProvider.intType); |
+ Object visitIntegerLiteral(IntegerLiteral node) { |
+ recordStaticType(node, _typeProvider.intType); |
+ return null; |
+ } |
+ |
/** |
* The Dart Language Specification, 12.31: <blockquote>It is a static warning if <i>T</i> does not |
* denote a type available in the current lexical scope. |
* <p> |
* The static type of an is-expression is {@code bool}.</blockquote> |
*/ |
- Object visitIsExpression(IsExpression node) => recordType(node, _typeProvider.boolType); |
+ Object visitIsExpression(IsExpression node) { |
+ recordStaticType(node, _typeProvider.boolType); |
+ return null; |
+ } |
+ |
/** |
* The Dart Language Specification, 12.6: <blockquote>The static type of a list literal of the |
* form <i><b>const</b> <E>\[e<sub>1</sub>, …, e<sub>n</sub>\]</i> or the form |
@@ -4938,16 +6324,41 @@ |
* the form <i>\[e<sub>1</sub>, …, e<sub>n</sub>\]</i> is {@code List<dynamic>}.</blockquote> |
*/ |
Object visitListLiteral(ListLiteral node) { |
+ Type2 staticType = _dynamicType; |
TypeArgumentList typeArguments2 = node.typeArguments; |
if (typeArguments2 != null) { |
NodeList<TypeName> arguments2 = typeArguments2.arguments; |
if (arguments2 != null && arguments2.length == 1) { |
- TypeName argumentType = arguments2[0]; |
- return recordType(node, _typeProvider.listType.substitute5(<Type2> [getType4(argumentType)])); |
+ TypeName argumentTypeName = arguments2[0]; |
+ Type2 argumentType = getType2(argumentTypeName); |
+ if (argumentType != null) { |
+ staticType = argumentType; |
+ } |
} |
} |
- return recordType(node, _typeProvider.listType.substitute5(<Type2> [_dynamicType])); |
+ recordStaticType(node, _typeProvider.listType.substitute5(<Type2> [staticType])); |
+ NodeList<Expression> elements2 = node.elements; |
+ int count = elements2.length; |
+ if (count > 0) { |
+ Type2 propagatedType = getBestType(elements2[0]); |
+ for (int i = 1; i < count; i++) { |
+ Type2 elementType = getBestType(elements2[i]); |
+ if (propagatedType != elementType) { |
+ propagatedType = _dynamicType; |
+ } else { |
+ propagatedType = propagatedType.getLeastUpperBound(elementType); |
+ if (propagatedType == null) { |
+ propagatedType = _dynamicType; |
+ } |
+ } |
+ } |
+ if (propagatedType.isMoreSpecificThan(staticType)) { |
+ recordPropagatedType(node, _typeProvider.listType.substitute5(<Type2> [propagatedType])); |
+ } |
+ } |
+ return null; |
} |
+ |
/** |
* The Dart Language Specification, 12.7: <blockquote>The static type of a map literal of the form |
* <i><b>const</b> <String, V> {k<sub>1</sub>:e<sub>1</sub>, …, |
@@ -4961,19 +6372,67 @@ |
* <i>String</i>.</blockquote> |
*/ |
Object visitMapLiteral(MapLiteral node) { |
+ Type2 staticKeyType = _dynamicType; |
+ Type2 staticValueType = _dynamicType; |
TypeArgumentList typeArguments2 = node.typeArguments; |
if (typeArguments2 != null) { |
NodeList<TypeName> arguments2 = typeArguments2.arguments; |
if (arguments2 != null && arguments2.length == 2) { |
- TypeName keyType = arguments2[0]; |
- if (keyType != _typeProvider.stringType) { |
+ TypeName entryKeyTypeName = arguments2[0]; |
+ Type2 entryKeyType = getType2(entryKeyTypeName); |
+ if (entryKeyType != null) { |
+ staticKeyType = entryKeyType; |
} |
- TypeName valueType = arguments2[1]; |
- return recordType(node, _typeProvider.mapType.substitute5(<Type2> [_typeProvider.stringType, getType4(valueType)])); |
+ TypeName entryValueTypeName = arguments2[1]; |
+ Type2 entryValueType = getType2(entryValueTypeName); |
+ if (entryValueType != null) { |
+ staticValueType = entryValueType; |
+ } |
} |
} |
- return recordType(node, _typeProvider.mapType.substitute5(<Type2> [_typeProvider.stringType, _dynamicType])); |
+ recordStaticType(node, _typeProvider.mapType.substitute5(<Type2> [staticKeyType, staticValueType])); |
+ NodeList<MapLiteralEntry> entries2 = node.entries; |
+ int count = entries2.length; |
+ if (count > 0) { |
+ MapLiteralEntry entry = entries2[0]; |
+ Type2 propagatedKeyType = getBestType(entry.key); |
+ Type2 propagatedValueType = getBestType(entry.value); |
+ for (int i = 1; i < count; i++) { |
+ entry = entries2[i]; |
+ Type2 elementKeyType = getBestType(entry.key); |
+ if (propagatedKeyType != elementKeyType) { |
+ propagatedKeyType = _dynamicType; |
+ } else { |
+ propagatedKeyType = propagatedKeyType.getLeastUpperBound(elementKeyType); |
+ if (propagatedKeyType == null) { |
+ propagatedKeyType = _dynamicType; |
+ } |
+ } |
+ Type2 elementValueType = getBestType(entry.value); |
+ if (propagatedValueType != elementValueType) { |
+ propagatedValueType = _dynamicType; |
+ } else { |
+ propagatedValueType = propagatedValueType.getLeastUpperBound(elementValueType); |
+ if (propagatedValueType == null) { |
+ propagatedValueType = _dynamicType; |
+ } |
+ } |
+ } |
+ bool betterKey = propagatedKeyType != null && propagatedKeyType.isMoreSpecificThan(staticKeyType); |
+ bool betterValue = propagatedValueType != null && propagatedValueType.isMoreSpecificThan(staticValueType); |
+ if (betterKey || betterValue) { |
+ if (!betterKey) { |
+ propagatedKeyType = staticKeyType; |
+ } |
+ if (!betterValue) { |
+ propagatedValueType = staticValueType; |
+ } |
+ recordPropagatedType(node, _typeProvider.mapType.substitute5(<Type2> [propagatedKeyType, propagatedValueType])); |
+ } |
+ } |
+ return null; |
} |
+ |
/** |
* The Dart Language Specification, 12.15.1: <blockquote>An ordinary method invocation <i>i</i> |
* has the form <i>o.m(a<sub>1</sub>, …, a<sub>n</sub>, x<sub>n+1</sub>: a<sub>n+1</sub>, |
@@ -5011,63 +6470,103 @@ |
* <i>F</i>.</blockquote> |
*/ |
Object visitMethodInvocation(MethodInvocation node) { |
- if (USE_TYPE_PROPAGATION) { |
- String methodName2 = node.methodName.name; |
- if (methodName2 == "\$dom_createEvent") { |
- Expression target = node.realTarget; |
- if (target != null) { |
- Type2 targetType = getType2(target); |
- if (targetType is InterfaceType && (targetType.name == "HtmlDocument" || targetType.name == "Document")) { |
- LibraryElement library2 = targetType.element.library; |
- if (isHtmlLibrary(library2)) { |
- Type2 returnType = getFirstArgumentAsType(library2, node.argumentList); |
- if (returnType != null) { |
- return recordType(node, returnType); |
- } |
+ SimpleIdentifier methodNameNode = node.methodName; |
+ Element staticMethodElement = methodNameNode.staticElement; |
+ if (staticMethodElement == null) { |
+ staticMethodElement = methodNameNode.element; |
+ } |
+ Type2 staticType = computeReturnType(staticMethodElement); |
+ recordStaticType(node, staticType); |
+ String methodName = methodNameNode.name; |
+ if (methodName == "\$dom_createEvent") { |
+ Expression target = node.realTarget; |
+ if (target != null) { |
+ Type2 targetType = getBestType(target); |
+ if (targetType is InterfaceType && (targetType.name == "HtmlDocument" || targetType.name == "Document")) { |
+ LibraryElement library2 = targetType.element.library; |
+ if (isHtmlLibrary(library2)) { |
+ Type2 returnType = getFirstArgumentAsType(library2, node.argumentList); |
+ if (returnType != null) { |
+ recordPropagatedType(node, returnType); |
} |
} |
} |
- } else if (methodName2 == "query") { |
- Expression target = node.realTarget; |
- if (target == null) { |
- Element methodElement = node.methodName.element; |
- if (methodElement != null) { |
- LibraryElement library3 = methodElement.library; |
- if (isHtmlLibrary(library3)) { |
- Type2 returnType = getFirstArgumentAsQuery(library3, node.argumentList); |
- if (returnType != null) { |
- return recordType(node, returnType); |
- } |
+ } |
+ } else if (methodName == "query") { |
+ Expression target = node.realTarget; |
+ if (target == null) { |
+ Element methodElement = methodNameNode.element; |
+ if (methodElement != null) { |
+ LibraryElement library3 = methodElement.library; |
+ if (isHtmlLibrary(library3)) { |
+ Type2 returnType = getFirstArgumentAsQuery(library3, node.argumentList); |
+ if (returnType != null) { |
+ recordPropagatedType(node, returnType); |
} |
} |
- } else { |
- Type2 targetType = getType2(target); |
- if (targetType is InterfaceType && (targetType.name == "HtmlDocument" || targetType.name == "Document")) { |
- LibraryElement library4 = targetType.element.library; |
- if (isHtmlLibrary(library4)) { |
- Type2 returnType = getFirstArgumentAsQuery(library4, node.argumentList); |
- if (returnType != null) { |
- return recordType(node, returnType); |
- } |
+ } |
+ } else { |
+ Type2 targetType = getBestType(target); |
+ if (targetType is InterfaceType && (targetType.name == "HtmlDocument" || targetType.name == "Document")) { |
+ LibraryElement library4 = targetType.element.library; |
+ if (isHtmlLibrary(library4)) { |
+ Type2 returnType = getFirstArgumentAsQuery(library4, node.argumentList); |
+ if (returnType != null) { |
+ recordPropagatedType(node, returnType); |
} |
} |
} |
- } else if (methodName2 == "JS") { |
- Type2 returnType = getFirstArgumentAsType(_typeProvider.objectType.element.library, node.argumentList); |
- if (returnType != null) { |
- return recordType(node, returnType); |
+ } |
+ } else if (methodName == "\$dom_createElement") { |
+ Expression target = node.realTarget; |
+ Type2 targetType = getBestType(target); |
+ if (targetType is InterfaceType && (targetType.name == "HtmlDocument" || targetType.name == "Document")) { |
+ LibraryElement library5 = targetType.element.library; |
+ if (isHtmlLibrary(library5)) { |
+ Type2 returnType = getFirstArgumentAsQuery(library5, node.argumentList); |
+ if (returnType != null) { |
+ recordPropagatedType(node, returnType); |
+ } |
} |
} |
+ } else if (methodName == "JS") { |
+ Type2 returnType = getFirstArgumentAsType(_typeProvider.objectType.element.library, node.argumentList); |
+ if (returnType != null) { |
+ recordPropagatedType(node, returnType); |
+ } |
+ } else { |
+ Element propagatedElement = methodNameNode.element; |
+ if (propagatedElement != staticMethodElement) { |
+ Type2 propagatedType = computeReturnType(propagatedElement); |
+ if (propagatedType != null && propagatedType.isMoreSpecificThan(staticType)) { |
+ recordPropagatedType(node, propagatedType); |
+ } |
+ } |
} |
- return recordReturnType(node, node.methodName.element); |
+ return null; |
} |
- Object visitNamedExpression(NamedExpression node) => recordType(node, getType2(node.expression)); |
+ Object visitNamedExpression(NamedExpression node) { |
+ Expression expression2 = node.expression; |
+ recordStaticType(node, getStaticType(expression2)); |
+ recordPropagatedType(node, getPropagatedType(expression2)); |
+ return null; |
+ } |
+ |
/** |
* The Dart Language Specification, 12.2: <blockquote>The static type of {@code null} is bottom. |
* </blockquote> |
*/ |
- Object visitNullLiteral(NullLiteral node) => recordType(node, _typeProvider.bottomType); |
- Object visitParenthesizedExpression(ParenthesizedExpression node) => recordType(node, getType2(node.expression)); |
+ Object visitNullLiteral(NullLiteral node) { |
+ recordStaticType(node, _typeProvider.bottomType); |
+ return null; |
+ } |
+ Object visitParenthesizedExpression(ParenthesizedExpression node) { |
+ Expression expression2 = node.expression; |
+ recordStaticType(node, getStaticType(expression2)); |
+ recordPropagatedType(node, getPropagatedType(expression2)); |
+ return null; |
+ } |
+ |
/** |
* The Dart Language Specification, 12.28: <blockquote>A postfix expression of the form |
* <i>v++</i>, where <i>v</i> is an identifier, is equivalent to <i>(){var r = v; v = r + 1; |
@@ -5094,47 +6593,57 @@ |
* A postfix expression of the form <i>e1\[e2\]--</i> is equivalent to <i>(a, i){var r = a\[i\]; a\[i\] |
* = r - 1; return r}(e1, e2)</i></blockquote> |
*/ |
- Object visitPostfixExpression(PostfixExpression node) => recordType(node, getType2(node.operand)); |
+ Object visitPostfixExpression(PostfixExpression node) { |
+ Expression operand2 = node.operand; |
+ Type2 staticType = getStaticType(operand2); |
+ sc.TokenType operator2 = node.operator.type; |
+ if (identical(operator2, sc.TokenType.MINUS_MINUS) || identical(operator2, sc.TokenType.PLUS_PLUS)) { |
+ Type2 intType2 = _typeProvider.intType; |
+ if (identical(getStaticType(node.operand), intType2)) { |
+ staticType = intType2; |
+ } |
+ } |
+ recordStaticType(node, staticType); |
+ recordPropagatedType(node, getPropagatedType(operand2)); |
+ return null; |
+ } |
+ |
/** |
* See {@link #visitSimpleIdentifier(SimpleIdentifier)}. |
*/ |
Object visitPrefixedIdentifier(PrefixedIdentifier node) { |
SimpleIdentifier prefixedIdentifier = node.identifier; |
Element element2 = prefixedIdentifier.element; |
- if (element2 == null) { |
- return recordType(node, _dynamicType); |
- } |
- if (USE_TYPE_PROPAGATION) { |
- Type2 type = _overrideManager.getType(element2); |
- if (type != null) { |
- return recordType(node, type); |
- } |
- } |
- Type2 type; |
+ Type2 staticType = _dynamicType; |
if (element2 is ClassElement) { |
if (isNotTypeLiteral(node)) { |
- type = ((element2 as ClassElement)).type; |
+ staticType = ((element2 as ClassElement)).type; |
} else { |
- type = _typeProvider.typeType; |
+ staticType = _typeProvider.typeType; |
} |
} else if (element2 is FunctionTypeAliasElement) { |
- type = ((element2 as FunctionTypeAliasElement)).type; |
+ staticType = ((element2 as FunctionTypeAliasElement)).type; |
} else if (element2 is MethodElement) { |
- type = ((element2 as MethodElement)).type; |
+ staticType = ((element2 as MethodElement)).type; |
} else if (element2 is PropertyAccessorElement) { |
- type = getType3((element2 as PropertyAccessorElement), node.prefix.staticType); |
+ staticType = getType((element2 as PropertyAccessorElement), node.prefix.staticType); |
} else if (element2 is ExecutableElement) { |
- type = ((element2 as ExecutableElement)).type; |
+ staticType = ((element2 as ExecutableElement)).type; |
} else if (element2 is TypeVariableElement) { |
- type = ((element2 as TypeVariableElement)).type; |
+ staticType = ((element2 as TypeVariableElement)).type; |
} else if (element2 is VariableElement) { |
- type = ((element2 as VariableElement)).type; |
- } else { |
- type = _dynamicType; |
+ staticType = ((element2 as VariableElement)).type; |
} |
- recordType(prefixedIdentifier, type); |
- return recordType(node, type); |
+ recordStaticType(prefixedIdentifier, staticType); |
+ recordStaticType(node, staticType); |
+ Type2 propagatedType = _overrideManager.getType(element2); |
+ if (propagatedType != null && propagatedType.isMoreSpecificThan(staticType)) { |
+ recordPropagatedType(prefixedIdentifier, propagatedType); |
+ recordPropagatedType(node, propagatedType); |
+ } |
+ return null; |
} |
+ |
/** |
* The Dart Language Specification, 12.27: <blockquote>A unary expression <i>u</i> of the form |
* <i>op e</i> is equivalent to a method invocation <i>expression e.op()</i>. An expression of the |
@@ -5143,10 +6652,28 @@ |
Object visitPrefixExpression(PrefixExpression node) { |
sc.TokenType operator2 = node.operator.type; |
if (identical(operator2, sc.TokenType.BANG)) { |
- return recordType(node, _typeProvider.boolType); |
+ recordStaticType(node, _typeProvider.boolType); |
+ } else { |
+ ExecutableElement staticMethodElement = node.staticElement; |
+ Type2 staticType = computeReturnType(staticMethodElement); |
+ if (identical(operator2, sc.TokenType.MINUS_MINUS) || identical(operator2, sc.TokenType.PLUS_PLUS)) { |
+ Type2 intType2 = _typeProvider.intType; |
+ if (identical(getStaticType(node.operand), intType2)) { |
+ staticType = intType2; |
+ } |
+ } |
+ recordStaticType(node, staticType); |
+ MethodElement propagatedMethodElement = node.element; |
+ if (propagatedMethodElement != staticMethodElement) { |
+ Type2 propagatedType = computeReturnType(propagatedMethodElement); |
+ if (propagatedType != null && propagatedType.isMoreSpecificThan(staticType)) { |
+ recordPropagatedType(node, propagatedType); |
+ } |
+ } |
} |
- return recordReturnType(node, node.element); |
+ return null; |
} |
+ |
/** |
* The Dart Language Specification, 12.13: <blockquote> Property extraction allows for a member of |
* an object to be concisely extracted from the object. If <i>o</i> is an object, and if <i>m</i> |
@@ -5193,30 +6720,31 @@ |
Object visitPropertyAccess(PropertyAccess node) { |
SimpleIdentifier propertyName2 = node.propertyName; |
Element element2 = propertyName2.element; |
- if (USE_TYPE_PROPAGATION) { |
- Type2 type = _overrideManager.getType(element2); |
- if (type != null) { |
- return recordType(node, type); |
- } |
- } |
+ Type2 staticType = _dynamicType; |
if (element2 is MethodElement) { |
- FunctionType type2 = ((element2 as MethodElement)).type; |
- recordType(propertyName2, type2); |
- return recordType(node, type2); |
+ staticType = ((element2 as MethodElement)).type; |
} else if (element2 is PropertyAccessorElement) { |
- Type2 propertyType = getType3((element2 as PropertyAccessorElement), node.target != null ? node.target.staticType : null); |
- recordType(propertyName2, propertyType); |
- return recordType(node, propertyType); |
+ staticType = getType((element2 as PropertyAccessorElement), node.target != null ? getStaticType(node.target) : null); |
} else { |
} |
- recordType(propertyName2, _dynamicType); |
- return recordType(node, _dynamicType); |
+ recordStaticType(propertyName2, staticType); |
+ recordStaticType(node, staticType); |
+ Type2 propagatedType = _overrideManager.getType(element2); |
+ if (propagatedType != null && propagatedType.isMoreSpecificThan(staticType)) { |
+ recordPropagatedType(node, propagatedType); |
+ } |
+ return null; |
} |
+ |
/** |
* The Dart Language Specification, 12.9: <blockquote>The static type of a rethrow expression is |
* bottom.</blockquote> |
*/ |
- Object visitRethrowExpression(RethrowExpression node) => recordType(node, _typeProvider.bottomType); |
+ Object visitRethrowExpression(RethrowExpression node) { |
+ recordStaticType(node, _typeProvider.bottomType); |
+ return null; |
+ } |
+ |
/** |
* The Dart Language Specification, 12.30: <blockquote>Evaluation of an identifier expression |
* <i>e</i> of the form <i>id</i> proceeds as follows: |
@@ -5261,99 +6789,169 @@ |
*/ |
Object visitSimpleIdentifier(SimpleIdentifier node) { |
Element element2 = node.element; |
- if (element2 == null) { |
- return recordType(node, _dynamicType); |
- } |
- if (USE_TYPE_PROPAGATION) { |
- Type2 type = _overrideManager.getType(element2); |
- if (type != null) { |
- return recordType(node, type); |
- } |
- } |
- Type2 type; |
+ Type2 staticType = _dynamicType; |
if (element2 is ClassElement) { |
if (isNotTypeLiteral(node)) { |
- type = ((element2 as ClassElement)).type; |
+ staticType = ((element2 as ClassElement)).type; |
} else { |
- type = _typeProvider.typeType; |
+ staticType = _typeProvider.typeType; |
} |
} else if (element2 is FunctionTypeAliasElement) { |
- type = ((element2 as FunctionTypeAliasElement)).type; |
+ staticType = ((element2 as FunctionTypeAliasElement)).type; |
} else if (element2 is MethodElement) { |
- type = ((element2 as MethodElement)).type; |
+ staticType = ((element2 as MethodElement)).type; |
} else if (element2 is PropertyAccessorElement) { |
- type = getType3((element2 as PropertyAccessorElement), null); |
+ staticType = getType((element2 as PropertyAccessorElement), null); |
} else if (element2 is ExecutableElement) { |
- type = ((element2 as ExecutableElement)).type; |
+ staticType = ((element2 as ExecutableElement)).type; |
} else if (element2 is TypeVariableElement) { |
- type = ((element2 as TypeVariableElement)).type; |
+ staticType = ((element2 as TypeVariableElement)).type; |
} else if (element2 is VariableElement) { |
- type = ((element2 as VariableElement)).type; |
+ staticType = ((element2 as VariableElement)).type; |
} else if (element2 is PrefixElement) { |
return null; |
} else { |
- type = _dynamicType; |
+ staticType = _dynamicType; |
} |
- return recordType(node, type); |
+ recordStaticType(node, staticType); |
+ Type2 propagatedType = _overrideManager.getType(element2); |
+ if (propagatedType != null && propagatedType.isMoreSpecificThan(staticType)) { |
+ recordPropagatedType(node, propagatedType); |
+ } |
+ return null; |
} |
+ |
/** |
* The Dart Language Specification, 12.5: <blockquote>The static type of a string literal is{@code String}.</blockquote> |
*/ |
- Object visitSimpleStringLiteral(SimpleStringLiteral node) => recordType(node, _typeProvider.stringType); |
+ Object visitSimpleStringLiteral(SimpleStringLiteral node) { |
+ recordStaticType(node, _typeProvider.stringType); |
+ return null; |
+ } |
+ |
/** |
* The Dart Language Specification, 12.5: <blockquote>The static type of a string literal is{@code String}.</blockquote> |
*/ |
- Object visitStringInterpolation(StringInterpolation node) => recordType(node, _typeProvider.stringType); |
+ Object visitStringInterpolation(StringInterpolation node) { |
+ recordStaticType(node, _typeProvider.stringType); |
+ return null; |
+ } |
Object visitSuperExpression(SuperExpression node) { |
if (_thisType == null) { |
- return recordType(node, _dynamicType); |
+ recordStaticType(node, _dynamicType); |
} else { |
- return recordType(node, _thisType.superclass); |
+ recordStaticType(node, _thisType); |
} |
+ return null; |
} |
+ |
/** |
* The Dart Language Specification, 12.10: <blockquote>The static type of {@code this} is the |
* interface of the immediately enclosing class.</blockquote> |
*/ |
Object visitThisExpression(ThisExpression node) { |
if (_thisType == null) { |
- return recordType(node, _dynamicType); |
+ recordStaticType(node, _dynamicType); |
} else { |
- return recordType(node, _thisType); |
+ recordStaticType(node, _thisType); |
} |
+ return null; |
} |
+ |
/** |
* The Dart Language Specification, 12.8: <blockquote>The static type of a throw expression is |
* bottom.</blockquote> |
*/ |
- Object visitThrowExpression(ThrowExpression node) => recordType(node, _typeProvider.bottomType); |
+ Object visitThrowExpression(ThrowExpression node) { |
+ recordStaticType(node, _typeProvider.bottomType); |
+ return null; |
+ } |
Object visitVariableDeclaration(VariableDeclaration node) { |
- if (USE_TYPE_PROPAGATION) { |
- Expression initializer2 = node.initializer; |
- if (initializer2 != null) { |
- Type2 rightType = getType2(initializer2); |
- VariableElement element2 = node.name.element as VariableElement; |
- if (element2 != null) { |
- override(element2, getType(element2), rightType); |
- } |
+ Expression initializer2 = node.initializer; |
+ if (initializer2 != null) { |
+ Type2 rightType = getBestType(initializer2); |
+ VariableElement element2 = node.name.element as VariableElement; |
+ if (element2 != null) { |
+ _resolver.override(element2, rightType); |
} |
} |
return null; |
} |
+ |
/** |
+ * Record that the static type of the given node is the type of the second argument to the method |
+ * represented by the given element. |
+ * @param element the element representing the method invoked by the given node |
+ */ |
+ Type2 computeArgumentType(ExecutableElement element) { |
+ if (element != null) { |
+ List<ParameterElement> parameters2 = element.parameters; |
+ if (parameters2 != null && parameters2.length == 2) { |
+ return parameters2[1].type; |
+ } |
+ } |
+ return _dynamicType; |
+ } |
+ |
+ /** |
+ * Compute the return type of the method or function represented by the given element. |
+ * @param element the element representing the method or function invoked by the given node |
+ * @return the return type that was computed |
+ */ |
+ Type2 computeReturnType(Element element) { |
+ if (element is PropertyAccessorElement) { |
+ FunctionType propertyType = ((element as PropertyAccessorElement)).type; |
+ if (propertyType != null) { |
+ Type2 returnType2 = propertyType.returnType; |
+ if (returnType2 is InterfaceType) { |
+ if (identical(returnType2, _typeProvider.functionType)) { |
+ return _dynamicType; |
+ } |
+ MethodElement callMethod = ((returnType2 as InterfaceType)).lookUpMethod(ElementResolver.CALL_METHOD_NAME, _resolver.definingLibrary); |
+ if (callMethod != null) { |
+ return callMethod.type.returnType; |
+ } |
+ } else if (returnType2 is FunctionType) { |
+ Type2 innerReturnType = ((returnType2 as FunctionType)).returnType; |
+ if (innerReturnType != null) { |
+ return innerReturnType; |
+ } |
+ } else if (returnType2.isDartCoreFunction()) { |
+ return _dynamicType; |
+ } |
+ if (returnType2 != null) { |
+ return returnType2; |
+ } |
+ } |
+ } else if (element is ExecutableElement) { |
+ FunctionType type2 = ((element as ExecutableElement)).type; |
+ if (type2 != null) { |
+ return type2.returnType; |
+ } |
+ } else if (element is VariableElement) { |
+ Type2 variableType = ((element as VariableElement)).type; |
+ if (variableType is FunctionType) { |
+ return ((variableType as FunctionType)).returnType; |
+ } |
+ } |
+ return _dynamicType; |
+ } |
+ |
+ /** |
* Given a function declaration, compute the return type of the function. The return type of |
* functions with a block body is {@code dynamicType}, with an expression body it is the type of |
* the expression. |
* @param node the function expression whose return type is to be computed |
* @return the return type that was computed |
*/ |
- Type2 computeReturnType(FunctionDeclaration node) { |
+ Type2 computeReturnType2(FunctionDeclaration node) { |
TypeName returnType2 = node.returnType; |
if (returnType2 == null) { |
- return computeReturnType2(node.functionExpression); |
+ return _dynamicType; |
} |
return returnType2.type; |
} |
+ |
/** |
* Given a function expression, compute the return type of the function. The return type of |
* functions with a block body is {@code dynamicType}, with an expression body it is the type of |
@@ -5361,14 +6959,32 @@ |
* @param node the function expression whose return type is to be computed |
* @return the return type that was computed |
*/ |
- Type2 computeReturnType2(FunctionExpression node) { |
+ Type2 computeReturnType3(FunctionExpression node) { |
FunctionBody body2 = node.body; |
if (body2 is ExpressionFunctionBody) { |
- return getType2(((body2 as ExpressionFunctionBody)).expression); |
+ return getStaticType(((body2 as ExpressionFunctionBody)).expression); |
} |
return _dynamicType; |
} |
+ |
/** |
+ * Return the propagated type of the given expression if it is available, or the static type if |
+ * there is no propagated type. |
+ * @param expression the expression whose type is to be returned |
+ * @return the propagated or static type of the given expression |
+ */ |
+ Type2 getBestType(Expression expression) { |
+ Type2 type = expression.propagatedType; |
+ if (type == null) { |
+ type = expression.staticType; |
+ if (type == null) { |
+ return _dynamicType; |
+ } |
+ } |
+ return type; |
+ } |
+ |
+ /** |
* If the given argument list contains at least one argument, and if the argument is a simple |
* string literal, then parse that argument as a query string and return the type specified by the |
* argument. |
@@ -5395,6 +7011,7 @@ |
} |
return null; |
} |
+ |
/** |
* If the given argument list contains at least one argument, and if the argument is a simple |
* string literal, return the String value of the argument. |
@@ -5411,6 +7028,7 @@ |
} |
return null; |
} |
+ |
/** |
* If the given argument list contains at least one argument, and if the argument is a simple |
* string literal, and if the value of the argument is the name of a class defined within the |
@@ -5420,6 +7038,7 @@ |
* @return the type specified by the first argument in the argument list |
*/ |
Type2 getFirstArgumentAsType(LibraryElement library, ArgumentList argumentList) => getFirstArgumentAsType2(library, argumentList, null); |
+ |
/** |
* If the given argument list contains at least one argument, and if the argument is a simple |
* string literal, and if the value of the argument is the name of a class defined within the |
@@ -5441,31 +7060,30 @@ |
} |
return null; |
} |
+ |
/** |
- * Return the type of the given (overridable) element. |
- * @param element the element whose type is to be returned |
- * @return the type of the given element |
+ * Return the propagated type of the given expression. |
+ * @param expression the expression whose type is to be returned |
+ * @return the propagated type of the given expression |
*/ |
- Type2 getType(Element element) { |
- if (element is LocalVariableElement) { |
- return ((element as LocalVariableElement)).type; |
- } else if (element is ParameterElement) { |
- return ((element as ParameterElement)).type; |
- } |
- return null; |
+ Type2 getPropagatedType(Expression expression) { |
+ Type2 type = expression.propagatedType; |
+ return type; |
} |
+ |
/** |
- * Return the type of the given expression that is to be used for type analysis. |
+ * Return the static type of the given expression. |
* @param expression the expression whose type is to be returned |
- * @return the type of the given expression |
+ * @return the static type of the given expression |
*/ |
- Type2 getType2(Expression expression) { |
+ Type2 getStaticType(Expression expression) { |
Type2 type = expression.staticType; |
if (type == null) { |
return _dynamicType; |
} |
return type; |
} |
+ |
/** |
* Return the type that should be recorded for a node that resolved to the given accessor. |
* @param accessor the accessor that the node resolved to |
@@ -5474,7 +7092,7 @@ |
* specific type information |
* @return the type that should be recorded for a node that resolved to the given accessor |
*/ |
- Type2 getType3(PropertyAccessorElement accessor, Type2 context) { |
+ Type2 getType(PropertyAccessorElement accessor, Type2 context) { |
FunctionType functionType = accessor.type; |
if (functionType == null) { |
return _dynamicType; |
@@ -5508,24 +7126,27 @@ |
} |
return returnType2; |
} |
+ |
/** |
* Return the type represented by the given type name. |
* @param typeName the type name representing the type to be returned |
* @return the type represented by the type name |
*/ |
- Type2 getType4(TypeName typeName) { |
+ Type2 getType2(TypeName typeName) { |
Type2 type2 = typeName.type; |
if (type2 == null) { |
return _dynamicType; |
} |
return type2; |
} |
+ |
/** |
* Return {@code true} if the given library is the 'dart:html' library. |
* @param library the library being tested |
* @return {@code true} if the library is 'dart:html' |
*/ |
bool isHtmlLibrary(LibraryElement library) => library.name == "dart.dom.html"; |
+ |
/** |
* Return {@code true} if the given node is not a type literal. |
* @param node the node being tested |
@@ -5535,92 +7156,58 @@ |
ASTNode parent2 = node.parent; |
return parent2 is TypeName || (parent2 is PrefixedIdentifier && (parent2.parent is TypeName || identical(((parent2 as PrefixedIdentifier)).prefix, node))) || (parent2 is PropertyAccess && identical(((parent2 as PropertyAccess)).target, node)) || (parent2 is MethodInvocation && identical(node, ((parent2 as MethodInvocation)).target)); |
} |
+ |
/** |
- * If it is appropriate to do so, override the type of the given element. Use the static type and |
- * inferred type of the element to determine whether or not it is appropriate. |
- * @param element the element whose type might be overridden |
- * @param staticType the static type of the element |
- * @param inferredType the inferred type of the element |
- */ |
- void override(VariableElement element, Type2 staticType, Type2 inferredType) { |
- if (identical(inferredType, BottomTypeImpl.instance)) { |
- return; |
- } |
- if (element is PropertyInducingElement) { |
- PropertyInducingElement variable = element as PropertyInducingElement; |
- if (!variable.isConst() && !variable.isFinal()) { |
- return; |
- } |
- } |
- if (staticType == null || (inferredType != null && inferredType.isMoreSpecificThan(staticType))) { |
- _overrideManager.setType(element, inferredType); |
- } |
- } |
- /** |
- * Record that the static type of the given node is the type of the second argument to the method |
- * represented by the given element. |
+ * Record that the propagated type of the given node is the given type. |
* @param expression the node whose type is to be recorded |
- * @param element the element representing the method invoked by the given node |
+ * @param type the propagated type of the node |
*/ |
- Object recordArgumentType(IndexExpression expression, MethodElement element) { |
- if (element != null) { |
- List<ParameterElement> parameters2 = element.parameters; |
- if (parameters2 != null && parameters2.length == 2) { |
- return recordType(expression, parameters2[1].type); |
- } |
+ void recordPropagatedType(Expression expression, Type2 type) { |
+ if (type != null && !type.isDynamic()) { |
+ expression.propagatedType = type; |
} |
- return recordType(expression, _dynamicType); |
} |
+ |
/** |
- * Record that the static type of the given node is the return type of the method or function |
- * represented by the given element. |
- * @param expression the node whose type is to be recorded |
- * @param element the element representing the method or function invoked by the given node |
- */ |
- Object recordReturnType(Expression expression, Element element) { |
- if (element is PropertyAccessorElement) { |
- FunctionType propertyType = ((element as PropertyAccessorElement)).type; |
- if (propertyType != null) { |
- Type2 returnType2 = propertyType.returnType; |
- if (returnType2 is FunctionType) { |
- Type2 innerReturnType = ((returnType2 as FunctionType)).returnType; |
- if (innerReturnType != null) { |
- return recordType(expression, innerReturnType); |
- } |
- } else if (returnType2.isDartCoreFunction()) { |
- return recordType(expression, _dynamicType); |
- } |
- if (returnType2 != null) { |
- return recordType(expression, returnType2); |
- } |
- } |
- } else if (element is ExecutableElement) { |
- FunctionType type2 = ((element as ExecutableElement)).type; |
- if (type2 != null) { |
- return recordType(expression, type2.returnType); |
- } |
- } else if (element is VariableElement) { |
- Type2 variableType = ((element as VariableElement)).type; |
- if (variableType is FunctionType) { |
- return recordType(expression, ((variableType as FunctionType)).returnType); |
- } |
- } |
- return recordType(expression, _dynamicType); |
- } |
- /** |
* Record that the static type of the given node is the given type. |
* @param expression the node whose type is to be recorded |
* @param type the static type of the node |
*/ |
- Object recordType(Expression expression, Type2 type) { |
+ void recordStaticType(Expression expression, Type2 type) { |
if (type == null) { |
expression.staticType = _dynamicType; |
} else { |
expression.staticType = type; |
} |
- return null; |
} |
+ |
/** |
+ * Attempts to make a better guess for the static type of the given binary expression. |
+ * @param node the binary expression to analyze |
+ * @param staticType the static type of the expression as resolved |
+ * @return the better type guess, or the same static type as given |
+ */ |
+ Type2 refineBinaryExpressionType(BinaryExpression node, Type2 staticType) { |
+ sc.TokenType operator2 = node.operator.type; |
+ if (identical(operator2, sc.TokenType.AMPERSAND_AMPERSAND) || identical(operator2, sc.TokenType.BAR_BAR) || identical(operator2, sc.TokenType.EQ_EQ) || identical(operator2, sc.TokenType.BANG_EQ)) { |
+ return _typeProvider.boolType; |
+ } |
+ if (identical(operator2, sc.TokenType.MINUS) || identical(operator2, sc.TokenType.PERCENT) || identical(operator2, sc.TokenType.PLUS) || identical(operator2, sc.TokenType.STAR)) { |
+ Type2 doubleType2 = _typeProvider.doubleType; |
+ if (identical(getStaticType(node.leftOperand), doubleType2) || identical(getStaticType(node.rightOperand), doubleType2)) { |
+ return doubleType2; |
+ } |
+ } |
+ if (identical(operator2, sc.TokenType.MINUS) || identical(operator2, sc.TokenType.PERCENT) || identical(operator2, sc.TokenType.PLUS) || identical(operator2, sc.TokenType.STAR) || identical(operator2, sc.TokenType.TILDE_SLASH)) { |
+ Type2 intType2 = _typeProvider.intType; |
+ if (identical(getStaticType(node.leftOperand), intType2) && identical(getStaticType(node.rightOperand), intType2)) { |
+ staticType = intType2; |
+ } |
+ } |
+ return staticType; |
+ } |
+ |
+ /** |
* Set the return type and parameter type information for the given function type based on the |
* given return type and parameter elements. |
* @param functionType the function type to be filled in |
@@ -5653,26 +7240,61 @@ |
get thisType_J2DAccessor => _thisType; |
set thisType_J2DAccessor(__v) => _thisType = __v; |
} |
+ |
/** |
* Instances of the class {@code TypeOverrideManager} manage the ability to override the type of an |
* element within a given context. |
*/ |
class TypeOverrideManager { |
+ |
/** |
* The current override scope, or {@code null} if no scope has been entered. |
*/ |
TypeOverrideManager_TypeOverrideScope _currentScope; |
+ |
/** |
- * Initialize a newly created override manager to not be in any scope. |
+ * Apply a set of overrides that were previously captured. |
+ * @param overrides the overrides to be applied |
*/ |
- TypeOverrideManager() : super() { |
+ void applyOverrides(Map<Element, Type2> overrides) { |
+ if (_currentScope == null) { |
+ throw new IllegalStateException("Cannot apply overrides without a scope"); |
+ } |
+ _currentScope.applyOverrides(overrides); |
} |
+ |
/** |
+ * Return a table mapping the elements whose type is overridden in the current scope to the |
+ * overriding type. |
+ * @return the overrides in the current scope |
+ */ |
+ Map<Element, Type2> captureLocalOverrides() { |
+ if (_currentScope == null) { |
+ throw new IllegalStateException("Cannot capture local overrides without a scope"); |
+ } |
+ return _currentScope.captureLocalOverrides(); |
+ } |
+ |
+ /** |
+ * Return a map from the elements for the variables in the given list that have their types |
+ * overridden to the overriding type. |
+ * @param variableList the list of variables whose overriding types are to be captured |
+ * @return a table mapping elements to their overriding types |
+ */ |
+ Map<Element, Type2> captureOverrides(VariableDeclarationList variableList) { |
+ if (_currentScope == null) { |
+ throw new IllegalStateException("Cannot capture overrides without a scope"); |
+ } |
+ return _currentScope.captureOverrides(variableList); |
+ } |
+ |
+ /** |
* Enter a new override scope. |
*/ |
void enterScope() { |
_currentScope = new TypeOverrideManager_TypeOverrideScope(_currentScope); |
} |
+ |
/** |
* Exit the current override scope. |
*/ |
@@ -5682,6 +7304,7 @@ |
} |
_currentScope = _currentScope._outerScope; |
} |
+ |
/** |
* Return the overridden type of the given element, or {@code null} if the type of the element has |
* not been overridden. |
@@ -5694,6 +7317,7 @@ |
} |
return _currentScope.getType(element); |
} |
+ |
/** |
* Set the overridden type of the given element to the given type |
* @param element the element whose type might have been overridden |
@@ -5706,19 +7330,23 @@ |
_currentScope.setType(element, type); |
} |
} |
+ |
/** |
* Instances of the class {@code TypeOverrideScope} represent a scope in which the types of |
* elements can be overridden. |
*/ |
class TypeOverrideManager_TypeOverrideScope { |
+ |
/** |
* The outer scope in which types might be overridden. |
*/ |
TypeOverrideManager_TypeOverrideScope _outerScope; |
+ |
/** |
* A table mapping elements to the overridden type of that element. |
*/ |
Map<Element, Type2> _overridenTypes = new Map<Element, Type2>(); |
+ |
/** |
* Initialize a newly created scope to be an empty child of the given scope. |
* @param outerScope the outer scope in which types might be overridden |
@@ -5726,7 +7354,47 @@ |
TypeOverrideManager_TypeOverrideScope(TypeOverrideManager_TypeOverrideScope outerScope) { |
this._outerScope = outerScope; |
} |
+ |
/** |
+ * Apply a set of overrides that were previously captured. |
+ * @param overrides the overrides to be applied |
+ */ |
+ void applyOverrides(Map<Element, Type2> overrides) { |
+ for (MapEntry<Element, Type2> entry in getMapEntrySet(overrides)) { |
+ _overridenTypes[entry.getKey()] = entry.getValue(); |
+ } |
+ } |
+ |
+ /** |
+ * Return a table mapping the elements whose type is overridden in the current scope to the |
+ * overriding type. |
+ * @return the overrides in the current scope |
+ */ |
+ Map<Element, Type2> captureLocalOverrides() => _overridenTypes; |
+ |
+ /** |
+ * Return a map from the elements for the variables in the given list that have their types |
+ * overridden to the overriding type. |
+ * @param variableList the list of variables whose overriding types are to be captured |
+ * @return a table mapping elements to their overriding types |
+ */ |
+ Map<Element, Type2> captureOverrides(VariableDeclarationList variableList) { |
+ Map<Element, Type2> overrides = new Map<Element, Type2>(); |
+ if (variableList.isConst() || variableList.isFinal()) { |
+ for (VariableDeclaration variable in variableList.variables) { |
+ Element element2 = variable.element; |
+ if (element2 != null) { |
+ Type2 type = _overridenTypes[element2]; |
+ if (type != null) { |
+ overrides[element2] = type; |
+ } |
+ } |
+ } |
+ } |
+ return overrides; |
+ } |
+ |
+ /** |
* Return the overridden type of the given element, or {@code null} if the type of the element |
* has not been overridden. |
* @param element the element whose type might have been overridden |
@@ -5744,6 +7412,7 @@ |
} |
return null; |
} |
+ |
/** |
* Set the overridden type of the given element to the given type |
* @param element the element whose type might have been overridden |
@@ -5753,136 +7422,165 @@ |
_overridenTypes[element] = type; |
} |
} |
+ |
/** |
* The interface {@code TypeProvider} defines the behavior of objects that provide access to types |
* defined by the language. |
* @coverage dart.engine.resolver |
*/ |
abstract class TypeProvider { |
+ |
/** |
* Return the type representing the built-in type 'bool'. |
* @return the type representing the built-in type 'bool' |
*/ |
InterfaceType get boolType; |
+ |
/** |
* Return the type representing the type 'bottom'. |
* @return the type representing the type 'bottom' |
*/ |
Type2 get bottomType; |
+ |
/** |
* Return the type representing the built-in type 'double'. |
* @return the type representing the built-in type 'double' |
*/ |
InterfaceType get doubleType; |
+ |
/** |
* Return the type representing the built-in type 'dynamic'. |
* @return the type representing the built-in type 'dynamic' |
*/ |
Type2 get dynamicType; |
+ |
/** |
* Return the type representing the built-in type 'Function'. |
* @return the type representing the built-in type 'Function' |
*/ |
InterfaceType get functionType; |
+ |
/** |
* Return the type representing the built-in type 'int'. |
* @return the type representing the built-in type 'int' |
*/ |
InterfaceType get intType; |
+ |
/** |
* Return the type representing the built-in type 'List'. |
* @return the type representing the built-in type 'List' |
*/ |
InterfaceType get listType; |
+ |
/** |
* Return the type representing the built-in type 'Map'. |
* @return the type representing the built-in type 'Map' |
*/ |
InterfaceType get mapType; |
+ |
/** |
* Return the type representing the built-in type 'num'. |
* @return the type representing the built-in type 'num' |
*/ |
InterfaceType get numType; |
+ |
/** |
* Return the type representing the built-in type 'Object'. |
* @return the type representing the built-in type 'Object' |
*/ |
InterfaceType get objectType; |
+ |
/** |
* Return the type representing the built-in type 'StackTrace'. |
* @return the type representing the built-in type 'StackTrace' |
*/ |
InterfaceType get stackTraceType; |
+ |
/** |
* Return the type representing the built-in type 'String'. |
* @return the type representing the built-in type 'String' |
*/ |
InterfaceType get stringType; |
+ |
/** |
* Return the type representing the built-in type 'Type'. |
* @return the type representing the built-in type 'Type' |
*/ |
InterfaceType get typeType; |
} |
+ |
/** |
* Instances of the class {@code TypeProviderImpl} provide access to types defined by the language |
* by looking for those types in the element model for the core library. |
* @coverage dart.engine.resolver |
*/ |
class TypeProviderImpl implements TypeProvider { |
+ |
/** |
* The type representing the built-in type 'bool'. |
*/ |
InterfaceType _boolType; |
+ |
/** |
* The type representing the type 'bottom'. |
*/ |
Type2 _bottomType; |
+ |
/** |
* The type representing the built-in type 'double'. |
*/ |
InterfaceType _doubleType; |
+ |
/** |
* The type representing the built-in type 'dynamic'. |
*/ |
Type2 _dynamicType; |
+ |
/** |
* The type representing the built-in type 'Function'. |
*/ |
InterfaceType _functionType; |
+ |
/** |
* The type representing the built-in type 'int'. |
*/ |
InterfaceType _intType; |
+ |
/** |
* The type representing the built-in type 'List'. |
*/ |
InterfaceType _listType; |
+ |
/** |
* The type representing the built-in type 'Map'. |
*/ |
InterfaceType _mapType; |
+ |
/** |
* The type representing the built-in type 'num'. |
*/ |
InterfaceType _numType; |
+ |
/** |
* The type representing the built-in type 'Object'. |
*/ |
InterfaceType _objectType; |
+ |
/** |
* The type representing the built-in type 'StackTrace'. |
*/ |
InterfaceType _stackTraceType; |
+ |
/** |
* The type representing the built-in type 'String'. |
*/ |
InterfaceType _stringType; |
+ |
/** |
* The type representing the built-in type 'Type'. |
*/ |
InterfaceType _typeType; |
+ |
/** |
* Initialize a newly created type provider to provide the types defined in the given library. |
* @param coreLibrary the element representing the core library (dart:core). |
@@ -5903,6 +7601,7 @@ |
InterfaceType get stackTraceType => _stackTraceType; |
InterfaceType get stringType => _stringType; |
InterfaceType get typeType => _typeType; |
+ |
/** |
* Return the type with the given name from the given namespace, or {@code null} if there is no |
* class with the given name. |
@@ -5918,6 +7617,7 @@ |
} |
return ((element as ClassElement)).type; |
} |
+ |
/** |
* Initialize the types provided by this type provider from the given library. |
* @param library the library containing the definitions of the core types |
@@ -5939,6 +7639,7 @@ |
_typeType = getType(namespace, "Type"); |
} |
} |
+ |
/** |
* Instances of the class {@code TypeResolverVisitor} are used to resolve the types associated with |
* the elements in the element model. This includes the types of superclasses, mixins, interfaces, |
@@ -5947,22 +7648,30 @@ |
* @coverage dart.engine.resolver |
*/ |
class TypeResolverVisitor extends ScopedVisitor { |
+ |
/** |
* The type representing the type 'dynamic'. |
*/ |
Type2 _dynamicType; |
+ |
/** |
+ * The flag specifying if currently visited class references 'super' expression. |
+ */ |
+ bool _hasReferenceToSuper = false; |
+ |
+ /** |
* Initialize a newly created visitor to resolve the nodes in a compilation unit. |
* @param library the library containing the compilation unit being resolved |
* @param source the source representing the compilation unit being visited |
* @param typeProvider the object used to access the types from the core library |
*/ |
TypeResolverVisitor.con1(Library library, Source source, TypeProvider typeProvider) : super.con1(library, source, typeProvider) { |
- _jtd_constructor_274_impl(library, source, typeProvider); |
+ _jtd_constructor_280_impl(library, source, typeProvider); |
} |
- _jtd_constructor_274_impl(Library library, Source source, TypeProvider typeProvider) { |
+ _jtd_constructor_280_impl(Library library, Source source, TypeProvider typeProvider) { |
_dynamicType = typeProvider.dynamicType; |
} |
+ |
/** |
* Initialize a newly created visitor to resolve the nodes in a compilation unit. |
* @param definingLibrary the element for the library containing the compilation unit being |
@@ -5973,9 +7682,9 @@ |
* during resolution |
*/ |
TypeResolverVisitor.con2(LibraryElement definingLibrary, Source source, TypeProvider typeProvider, AnalysisErrorListener errorListener) : super.con2(definingLibrary, source, typeProvider, errorListener) { |
- _jtd_constructor_275_impl(definingLibrary, source, typeProvider, errorListener); |
+ _jtd_constructor_281_impl(definingLibrary, source, typeProvider, errorListener); |
} |
- _jtd_constructor_275_impl(LibraryElement definingLibrary, Source source, TypeProvider typeProvider, AnalysisErrorListener errorListener) { |
+ _jtd_constructor_281_impl(LibraryElement definingLibrary, Source source, TypeProvider typeProvider, AnalysisErrorListener errorListener) { |
_dynamicType = typeProvider.dynamicType; |
} |
Object visitCatchClause(CatchClause node) { |
@@ -5987,7 +7696,7 @@ |
if (exceptionTypeName == null) { |
exceptionType = typeProvider.objectType; |
} else { |
- exceptionType = getType5(exceptionTypeName); |
+ exceptionType = getType3(exceptionTypeName); |
} |
recordType(exception, exceptionType); |
Element element2 = exception.element; |
@@ -6003,12 +7712,14 @@ |
return null; |
} |
Object visitClassDeclaration(ClassDeclaration node) { |
+ _hasReferenceToSuper = false; |
super.visitClassDeclaration(node); |
ClassElementImpl classElement = getClassElement(node.name); |
InterfaceType superclassType = null; |
ExtendsClause extendsClause2 = node.extendsClause; |
if (extendsClause2 != null) { |
- superclassType = resolveType(extendsClause2.superclass, CompileTimeErrorCode.EXTENDS_NON_CLASS); |
+ ErrorCode errorCode = node.withClause == null ? CompileTimeErrorCode.EXTENDS_NON_CLASS : CompileTimeErrorCode.MIXIN_WITH_NON_CLASS_SUPERCLASS; |
+ superclassType = resolveType(extendsClause2.superclass, errorCode); |
if (superclassType != typeProvider.objectType) { |
classElement.validMixin = false; |
} |
@@ -6021,6 +7732,7 @@ |
} |
} |
classElement.supertype = superclassType; |
+ classElement.hasReferenceToSuper2 = _hasReferenceToSuper; |
} |
resolve(classElement, node.withClause, node.implementsClause); |
return null; |
@@ -6028,7 +7740,7 @@ |
Object visitClassTypeAlias(ClassTypeAlias node) { |
super.visitClassTypeAlias(node); |
ClassElementImpl classElement = getClassElement(node.name); |
- InterfaceType superclassType = resolveType(node.superclass, CompileTimeErrorCode.EXTENDS_NON_CLASS); |
+ InterfaceType superclassType = resolveType(node.superclass, CompileTimeErrorCode.MIXIN_WITH_NON_CLASS_SUPERCLASS); |
if (superclassType == null) { |
superclassType = typeProvider.objectType; |
} |
@@ -6054,7 +7766,7 @@ |
if (typeName == null) { |
declaredType = _dynamicType; |
} else { |
- declaredType = getType5(typeName); |
+ declaredType = getType3(typeName); |
} |
LocalVariableElementImpl element2 = node.element as LocalVariableElementImpl; |
element2.type = declaredType; |
@@ -6074,7 +7786,7 @@ |
if (typeName == null) { |
type = _dynamicType; |
} else { |
- type = getType5(typeName); |
+ type = getType3(typeName); |
} |
parameter.type = type; |
} else { |
@@ -6099,8 +7811,10 @@ |
Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { |
super.visitFunctionTypedFormalParameter(node); |
ParameterElementImpl element2 = node.identifier.element as ParameterElementImpl; |
- FunctionTypeImpl type = new FunctionTypeImpl.con1((null as ExecutableElement)); |
- setTypeInformation(type, node.returnType, getElements(node.parameters)); |
+ AnonymousFunctionTypeImpl type = new AnonymousFunctionTypeImpl(); |
+ List<ParameterElement> parameters2 = getElements(node.parameters); |
+ setTypeInformation(type, node.returnType, parameters2); |
+ type.baseParameters = parameters2; |
element2.type = type; |
return null; |
} |
@@ -6131,7 +7845,7 @@ |
if (typeName == null) { |
declaredType = _dynamicType; |
} else { |
- declaredType = getType5(typeName); |
+ declaredType = getType3(typeName); |
} |
Element element2 = node.identifier.element; |
if (element2 is ParameterElement) { |
@@ -6140,6 +7854,10 @@ |
} |
return null; |
} |
+ Object visitSuperExpression(SuperExpression node) { |
+ _hasReferenceToSuper = true; |
+ return super.visitSuperExpression(node); |
+ } |
Object visitTypeName(TypeName node) { |
super.visitTypeName(node); |
Identifier typeName = node.name; |
@@ -6179,18 +7897,36 @@ |
} |
} |
} |
- if (element == null) { |
- Identifier simpleIdentifier; |
- if (typeName is SimpleIdentifier) { |
- simpleIdentifier = typeName; |
+ bool elementValid = element is! MultiplyDefinedElement; |
+ if (elementValid && element is! ClassElement && isTypeNameInInstanceCreationExpression(node)) { |
+ SimpleIdentifier typeNameSimple = getTypeSimpleIdentifier(typeName); |
+ InstanceCreationExpression creation = node.parent.parent as InstanceCreationExpression; |
+ if (creation.isConst()) { |
+ if (element == null) { |
+ reportError(CompileTimeErrorCode.UNDEFINED_CLASS, typeNameSimple, [typeName]); |
+ } else { |
+ reportError(CompileTimeErrorCode.CONST_WITH_NON_TYPE, typeNameSimple, [typeName]); |
+ } |
+ elementValid = false; |
} else { |
- simpleIdentifier = ((typeName as PrefixedIdentifier)).prefix; |
+ if (element != null) { |
+ reportError(StaticWarningCode.NEW_WITH_NON_TYPE, typeNameSimple, [typeName]); |
+ elementValid = false; |
+ } |
} |
- if (simpleIdentifier.name == "boolean") { |
- reportError(StaticWarningCode.UNDEFINED_CLASS_BOOLEAN, simpleIdentifier, []); |
+ } |
+ if (elementValid && element == null) { |
+ SimpleIdentifier typeNameSimple = getTypeSimpleIdentifier(typeName); |
+ if (typeNameSimple.name == "boolean") { |
+ reportError(StaticWarningCode.UNDEFINED_CLASS_BOOLEAN, typeNameSimple, []); |
+ } else if (isTypeNameInCatchClause(node)) { |
+ reportError(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, node, [node]); |
} else { |
- reportError(StaticWarningCode.UNDEFINED_CLASS, simpleIdentifier, [simpleIdentifier.name]); |
+ reportError(StaticWarningCode.UNDEFINED_CLASS, typeNameSimple, [typeNameSimple.name]); |
} |
+ elementValid = false; |
+ } |
+ if (!elementValid) { |
setElement(typeName, _dynamicType.element); |
typeName.staticType = _dynamicType; |
node.type = _dynamicType; |
@@ -6215,6 +7951,9 @@ |
node.type = type; |
} |
} else { |
+ if (isTypeNameInCatchClause(node)) { |
+ reportError(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, node, [node]); |
+ } |
setElement(typeName, _dynamicType.element); |
typeName.staticType = _dynamicType; |
node.type = _dynamicType; |
@@ -6228,7 +7967,7 @@ |
int count = Math.min(argumentCount, parameterCount); |
List<Type2> typeArguments = new List<Type2>(); |
for (int i = 0; i < count; i++) { |
- Type2 argumentType = getType5(arguments2[i]); |
+ Type2 argumentType = getType3(arguments2[i]); |
if (argumentType != null) { |
typeArguments.add(argumentType); |
} |
@@ -6273,7 +8012,7 @@ |
if (typeName == null) { |
declaredType = _dynamicType; |
} else { |
- declaredType = getType5(typeName); |
+ declaredType = getType3(typeName); |
} |
Element element2 = node.name.element; |
if (element2 is VariableElement) { |
@@ -6296,6 +8035,7 @@ |
} |
return null; |
} |
+ |
/** |
* Return the class element that represents the class whose name was provided. |
* @param identifier the name from the declaration of a class |
@@ -6311,6 +8051,7 @@ |
} |
return element2 as ClassElementImpl; |
} |
+ |
/** |
* Return an array containing all of the elements associated with the parameters in the given |
* list. |
@@ -6327,6 +8068,7 @@ |
} |
return new List.from(elements); |
} |
+ |
/** |
* The number of type arguments in the given type name does not match the number of parameters in |
* the corresponding class element. Return the error code that should be used to report this |
@@ -6349,6 +8091,7 @@ |
} |
return StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS; |
} |
+ |
/** |
* Given the multiple elements to which a single name could potentially be resolved, return the |
* single interface type that should be used, or {@code null} if there is no clear choice. |
@@ -6367,18 +8110,20 @@ |
} |
return type; |
} |
+ |
/** |
* Return the type represented by the given type name. |
* @param typeName the type name representing the type to be returned |
* @return the type represented by the type name |
*/ |
- Type2 getType5(TypeName typeName) { |
+ Type2 getType3(TypeName typeName) { |
Type2 type2 = typeName.type; |
if (type2 == null) { |
return _dynamicType; |
} |
return type2; |
} |
+ |
/** |
* Return the type arguments associated with the given type. |
* @param type the type whole type arguments are to be returned |
@@ -6392,7 +8137,50 @@ |
} |
return TypeImpl.EMPTY_ARRAY; |
} |
+ |
/** |
+ * Returns the simple identifier of the given (may be qualified) type name. |
+ * @param typeName the (may be qualified) qualified type name |
+ * @return the simple identifier of the given (may be qualified) type name. |
+ */ |
+ SimpleIdentifier getTypeSimpleIdentifier(Identifier typeName) { |
+ if (typeName is SimpleIdentifier) { |
+ return typeName as SimpleIdentifier; |
+ } else { |
+ return ((typeName as PrefixedIdentifier)).identifier; |
+ } |
+ } |
+ |
+ /** |
+ * Checks if the given type name is used as the exception type in the catch clause. |
+ * @param typeName the type name to analyzer |
+ * @return {@code true} if the given type name is used as the exception type in the catch clause. |
+ */ |
+ bool isTypeNameInCatchClause(TypeName typeName) { |
+ ASTNode parent2 = typeName.parent; |
+ if (parent2 is CatchClause) { |
+ CatchClause catchClause = parent2 as CatchClause; |
+ return identical(catchClause.exceptionType, typeName); |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Checks if the given type name is used as the type in the instance creation expression. |
+ * @param typeName the type name to analyzer |
+ * @return {@code true} if the given type name is used as the type in the instance creation |
+ * expression |
+ */ |
+ bool isTypeNameInInstanceCreationExpression(TypeName typeName) { |
+ ASTNode parent2 = typeName.parent; |
+ if (parent2 is ConstructorName && parent2.parent is InstanceCreationExpression) { |
+ ConstructorName constructorName = parent2 as ConstructorName; |
+ return constructorName != null && identical(constructorName.type, typeName); |
+ } |
+ return false; |
+ } |
+ |
+ /** |
* Record that the static type of the given node is the given type. |
* @param expression the node whose type is to be recorded |
* @param type the static type of the node |
@@ -6405,6 +8193,7 @@ |
} |
return null; |
} |
+ |
/** |
* Resolve the types in the given with and implements clauses and associate those types with the |
* given class element. |
@@ -6434,20 +8223,15 @@ |
String name3 = typeName.name.name; |
if (name3 == dynamicKeyword) { |
reportError(CompileTimeErrorCode.IMPLEMENTS_DYNAMIC, typeName, []); |
- } else { |
- Element element3 = typeName.name.element; |
- if (element3 != null && element3 == classElement) { |
- reportError(CompileTimeErrorCode.IMPLEMENTS_SELF, typeName, [name3]); |
- } |
} |
if (!detectedRepeatOnIndex[i]) { |
for (int j = i + 1; j < typeNames.length; j++) { |
- Element element4 = typeName.name.element; |
+ Element element3 = typeName.name.element; |
TypeName typeName2 = typeNames[j]; |
Identifier identifier2 = typeName2.name; |
String name2 = identifier2.name; |
Element element2 = identifier2.element; |
- if (element4 != null && element4 == element2) { |
+ if (element3 != null && element3 == element2) { |
detectedRepeatOnIndex[j] = true; |
reportError(CompileTimeErrorCode.IMPLEMENTS_REPEATED, typeName2, [name2]); |
} |
@@ -6459,6 +8243,7 @@ |
} |
} |
} |
+ |
/** |
* Return the type specified by the given name. |
* @param typeName the type name specifying the type to be returned |
@@ -6477,6 +8262,7 @@ |
} |
return null; |
} |
+ |
/** |
* Resolve the types in the given list of type names. |
* @param typeNames the type names to be resolved |
@@ -6509,6 +8295,7 @@ |
} |
} |
} |
+ |
/** |
* Set the return type and parameter type information for the given function type based on the |
* given return type and parameter elements. |
@@ -6548,11 +8335,13 @@ |
} |
} |
} |
+ |
/** |
* Instances of the class {@code ClassScope} implement the scope defined by a class. |
* @coverage dart.engine.resolver |
*/ |
class ClassScope extends EnclosedScope { |
+ |
/** |
* Initialize a newly created scope enclosed within another scope. |
* @param enclosingScope the scope in which this scope is lexically enclosed |
@@ -6562,6 +8351,17 @@ |
defineTypeParameters(typeElement); |
defineMembers(typeElement); |
} |
+ AnalysisError getErrorForDuplicate(Element existing, Element duplicate) { |
+ if (existing is PropertyAccessorElement && duplicate is MethodElement) { |
+ if (existing.nameOffset < duplicate.nameOffset) { |
+ return new AnalysisError.con2(duplicate.source, duplicate.nameOffset, duplicate.displayName.length, CompileTimeErrorCode.METHOD_AND_GETTER_WITH_SAME_NAME, [existing.displayName]); |
+ } else { |
+ return new AnalysisError.con2(existing.source, existing.nameOffset, existing.displayName.length, CompileTimeErrorCode.GETTER_AND_METHOD_WITH_SAME_NAME, [existing.displayName]); |
+ } |
+ } |
+ return super.getErrorForDuplicate(existing, duplicate); |
+ } |
+ |
/** |
* Define the instance members defined by the class. |
* @param typeElement the element representing the type represented by this scope |
@@ -6574,6 +8374,7 @@ |
define(method); |
} |
} |
+ |
/** |
* Define the type parameters for the class. |
* @param typeElement the element representing the type represented by this scope |
@@ -6585,16 +8386,19 @@ |
} |
} |
} |
+ |
/** |
* Instances of the class {@code EnclosedScope} implement a scope that is lexically enclosed in |
* another scope. |
* @coverage dart.engine.resolver |
*/ |
class EnclosedScope extends Scope { |
+ |
/** |
* The scope in which this scope is lexically enclosed. |
*/ |
Scope _enclosingScope; |
+ |
/** |
* Initialize a newly created scope enclosed within another scope. |
* @param enclosingScope the scope in which this scope is lexically enclosed |
@@ -6604,24 +8408,27 @@ |
} |
LibraryElement get definingLibrary => _enclosingScope.definingLibrary; |
AnalysisErrorListener get errorListener => _enclosingScope.errorListener; |
+ |
/** |
* Return the scope in which this scope is lexically enclosed. |
* @return the scope in which this scope is lexically enclosed |
*/ |
Scope get enclosingScope => _enclosingScope; |
- Element lookup3(String name, LibraryElement referencingLibrary) { |
+ Element lookup3(Identifier identifier, String name, LibraryElement referencingLibrary) { |
Element element = localLookup(name, referencingLibrary); |
if (element != null) { |
return element; |
} |
- return _enclosingScope.lookup3(name, referencingLibrary); |
+ return _enclosingScope.lookup3(identifier, name, referencingLibrary); |
} |
} |
+ |
/** |
* Instances of the class {@code FunctionScope} implement the scope defined by a function. |
* @coverage dart.engine.resolver |
*/ |
class FunctionScope extends EnclosedScope { |
+ |
/** |
* Initialize a newly created scope enclosed within another scope. |
* @param enclosingScope the scope in which this scope is lexically enclosed |
@@ -6630,6 +8437,7 @@ |
FunctionScope(Scope enclosingScope, ExecutableElement functionElement) : super(new EnclosedScope(enclosingScope)) { |
defineParameters(functionElement); |
} |
+ |
/** |
* Define the parameters for the given function in the scope that encloses this function. |
* @param functionElement the element representing the function represented by this scope |
@@ -6649,12 +8457,14 @@ |
} |
} |
} |
+ |
/** |
* Instances of the class {@code FunctionTypeScope} implement the scope defined by a function type |
* alias. |
* @coverage dart.engine.resolver |
*/ |
class FunctionTypeScope extends EnclosedScope { |
+ |
/** |
* Initialize a newly created scope enclosed within another scope. |
* @param enclosingScope the scope in which this scope is lexically enclosed |
@@ -6664,6 +8474,7 @@ |
defineTypeVariables(typeElement); |
defineParameters(typeElement); |
} |
+ |
/** |
* Define the parameters for the function type alias. |
* @param typeElement the element representing the type represented by this scope |
@@ -6673,6 +8484,7 @@ |
define(parameter); |
} |
} |
+ |
/** |
* Define the type variables for the function type alias. |
* @param typeElement the element representing the type represented by this scope |
@@ -6684,31 +8496,38 @@ |
} |
} |
} |
+ |
/** |
* Instances of the class {@code LabelScope} represent a scope in which a single label is defined. |
* @coverage dart.engine.resolver |
*/ |
class LabelScope { |
+ |
/** |
* The label scope enclosing this label scope. |
*/ |
LabelScope _outerScope; |
+ |
/** |
* The label defined in this scope. |
*/ |
String _label; |
+ |
/** |
* The element to which the label resolves. |
*/ |
LabelElement _element; |
+ |
/** |
* The marker used to look up a label element for an unlabeled {@code break} or {@code continue}. |
*/ |
static String EMPTY_LABEL = ""; |
+ |
/** |
* The label element returned for scopes that can be the target of an unlabeled {@code break} or{@code continue}. |
*/ |
static SimpleIdentifier _EMPTY_LABEL_IDENTIFIER = new SimpleIdentifier.full(new sc.StringToken(sc.TokenType.IDENTIFIER, "", 0)); |
+ |
/** |
* Initialize a newly created scope to represent the potential target of an unlabeled{@code break} or {@code continue}. |
* @param outerScope the label scope enclosing the new label scope |
@@ -6716,11 +8535,12 @@ |
* @param onSwitchMember {@code true} if this label is associated with a {@code switch} member |
*/ |
LabelScope.con1(LabelScope outerScope, bool onSwitchStatement, bool onSwitchMember) { |
- _jtd_constructor_280_impl(outerScope, onSwitchStatement, onSwitchMember); |
+ _jtd_constructor_286_impl(outerScope, onSwitchStatement, onSwitchMember); |
} |
- _jtd_constructor_280_impl(LabelScope outerScope, bool onSwitchStatement, bool onSwitchMember) { |
- _jtd_constructor_281_impl(outerScope, EMPTY_LABEL, new LabelElementImpl(_EMPTY_LABEL_IDENTIFIER, onSwitchStatement, onSwitchMember)); |
+ _jtd_constructor_286_impl(LabelScope outerScope, bool onSwitchStatement, bool onSwitchMember) { |
+ _jtd_constructor_287_impl(outerScope, EMPTY_LABEL, new LabelElementImpl(_EMPTY_LABEL_IDENTIFIER, onSwitchStatement, onSwitchMember)); |
} |
+ |
/** |
* Initialize a newly created scope to represent the given label. |
* @param outerScope the label scope enclosing the new label scope |
@@ -6728,13 +8548,14 @@ |
* @param element the element to which the label resolves |
*/ |
LabelScope.con2(LabelScope outerScope2, String label2, LabelElement element2) { |
- _jtd_constructor_281_impl(outerScope2, label2, element2); |
+ _jtd_constructor_287_impl(outerScope2, label2, element2); |
} |
- _jtd_constructor_281_impl(LabelScope outerScope2, String label2, LabelElement element2) { |
+ _jtd_constructor_287_impl(LabelScope outerScope2, String label2, LabelElement element2) { |
this._outerScope = outerScope2; |
this._label = label2; |
this._element = element2; |
} |
+ |
/** |
* Return the label element corresponding to the given label, or {@code null} if the given label |
* is not defined in this scope. |
@@ -6742,6 +8563,7 @@ |
* @return the label element corresponding to the given label |
*/ |
LabelElement lookup(SimpleIdentifier targetLabel) => lookup2(targetLabel.name); |
+ |
/** |
* Return the label element corresponding to the given label, or {@code null} if the given label |
* is not defined in this scope. |
@@ -6758,25 +8580,75 @@ |
} |
} |
} |
+ |
/** |
* Instances of the class {@code LibraryImportScope} represent the scope containing all of the names |
* available from imported libraries. |
* @coverage dart.engine.resolver |
*/ |
class LibraryImportScope extends Scope { |
+ |
/** |
+ * @return {@code true} if the given {@link Identifier} is the part of type annotation. |
+ */ |
+ static bool isTypeAnnotation(Identifier identifier) { |
+ ASTNode parent4 = identifier.parent; |
+ if (parent4 is TypeName) { |
+ ASTNode parent2 = parent4.parent; |
+ if (parent2 is FunctionDeclaration) { |
+ FunctionDeclaration decl = parent2 as FunctionDeclaration; |
+ return identical(decl.returnType, parent4); |
+ } |
+ if (parent2 is FunctionTypeAlias) { |
+ FunctionTypeAlias decl = parent2 as FunctionTypeAlias; |
+ return identical(decl.returnType, parent4); |
+ } |
+ if (parent2 is MethodDeclaration) { |
+ MethodDeclaration decl = parent2 as MethodDeclaration; |
+ return identical(decl.returnType, parent4); |
+ } |
+ if (parent2 is VariableDeclarationList) { |
+ VariableDeclarationList decl = parent2 as VariableDeclarationList; |
+ return identical(decl.type, parent4); |
+ } |
+ if (parent2 is SimpleFormalParameter) { |
+ SimpleFormalParameter decl = parent2 as SimpleFormalParameter; |
+ return identical(decl.type, parent4); |
+ } |
+ if (parent2 is TypeParameter) { |
+ TypeParameter decl = parent2 as TypeParameter; |
+ return identical(decl.bound, parent4); |
+ } |
+ if (parent2 is TypeArgumentList) { |
+ ASTNode parent3 = parent2.parent; |
+ if (parent3 is TypeName) { |
+ TypeName typeName = parent3 as TypeName; |
+ if (identical((typeName).typeArguments, parent2)) { |
+ return isTypeAnnotation(typeName.name); |
+ } |
+ } |
+ } |
+ return false; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
* The element representing the library in which this scope is enclosed. |
*/ |
LibraryElement _definingLibrary; |
+ |
/** |
* The listener that is to be informed when an error is encountered. |
*/ |
AnalysisErrorListener _errorListener; |
+ |
/** |
* A list of the namespaces representing the names that are available in this scope from imported |
* libraries. |
*/ |
List<Namespace> _importedNamespaces = new List<Namespace>(); |
+ |
/** |
* Initialize a newly created scope representing the names imported into the given library. |
* @param definingLibrary the element representing the library that imports the names defined in |
@@ -6789,13 +8661,13 @@ |
createImportedNamespaces(definingLibrary); |
} |
void define(Element element) { |
- if (!Scope.isPrivateName(element.name)) { |
+ if (!Scope.isPrivateName(element.displayName)) { |
super.define(element); |
} |
} |
LibraryElement get definingLibrary => _definingLibrary; |
AnalysisErrorListener get errorListener => _errorListener; |
- Element lookup3(String name, LibraryElement referencingLibrary) { |
+ Element lookup3(Identifier identifier, String name, LibraryElement referencingLibrary) { |
Element foundElement = localLookup(name, referencingLibrary); |
if (foundElement != null) { |
return foundElement; |
@@ -6811,12 +8683,27 @@ |
} |
} |
if (foundElement is MultiplyDefinedElementImpl) { |
+ String foundEltName = foundElement.displayName; |
+ String libName1 = "", libName2 = ""; |
+ List<Element> conflictingMembers = ((foundElement as MultiplyDefinedElementImpl)).conflictingElements; |
+ LibraryElement enclosingLibrary = conflictingMembers[0].getAncestor(LibraryElement); |
+ if (enclosingLibrary != null) { |
+ libName1 = enclosingLibrary.definingCompilationUnit.displayName; |
+ } |
+ enclosingLibrary = conflictingMembers[1].getAncestor(LibraryElement); |
+ if (enclosingLibrary != null) { |
+ libName2 = enclosingLibrary.definingCompilationUnit.displayName; |
+ } |
+ ErrorCode errorCode = isTypeAnnotation(identifier) ? StaticWarningCode.AMBIGUOUS_IMPORT : CompileTimeErrorCode.AMBIGUOUS_IMPORT; |
+ _errorListener.onError(new AnalysisError.con2(source, identifier.offset, identifier.length, errorCode, [foundEltName, libName1, libName2])); |
+ return foundElement; |
} |
if (foundElement != null) { |
- defineWithoutChecking(foundElement); |
+ defineWithoutChecking2(name, foundElement); |
} |
return foundElement; |
} |
+ |
/** |
* Create all of the namespaces associated with the libraries imported into this library. The |
* names are not added to this scope, but are stored for later reference. |
@@ -6830,12 +8717,14 @@ |
} |
} |
} |
+ |
/** |
* Instances of the class {@code LibraryScope} implement a scope containing all of the names defined |
* in a given library. |
* @coverage dart.engine.resolver |
*/ |
class LibraryScope extends EnclosedScope { |
+ |
/** |
* Initialize a newly created scope representing the names defined in the given library. |
* @param definingLibrary the element representing the library represented by this scope |
@@ -6844,6 +8733,20 @@ |
LibraryScope(LibraryElement definingLibrary, AnalysisErrorListener errorListener) : super(new LibraryImportScope(definingLibrary, errorListener)) { |
defineTopLevelNames(definingLibrary); |
} |
+ AnalysisError getErrorForDuplicate(Element existing, Element duplicate) { |
+ if (existing is PrefixElement) { |
+ int offset = duplicate.nameOffset; |
+ if (duplicate is PropertyAccessorElement) { |
+ PropertyAccessorElement accessor = duplicate as PropertyAccessorElement; |
+ if (accessor.isSynthetic()) { |
+ offset = accessor.variable.nameOffset; |
+ } |
+ } |
+ return new AnalysisError.con2(source, offset, duplicate.displayName.length, CompileTimeErrorCode.PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER, [existing.displayName]); |
+ } |
+ return super.getErrorForDuplicate(existing, duplicate); |
+ } |
+ |
/** |
* Add to this scope all of the public top-level names that are defined in the given compilation |
* unit. |
@@ -6864,6 +8767,7 @@ |
define(element); |
} |
} |
+ |
/** |
* Add to this scope all of the names that are explicitly defined in the given library. |
* @param definingLibrary the element representing the library that defines the names in this |
@@ -6879,21 +8783,25 @@ |
} |
} |
} |
+ |
/** |
* Instances of the class {@code Namespace} implement a mapping of identifiers to the elements |
* represented by those identifiers. Namespaces are the building blocks for scopes. |
* @coverage dart.engine.resolver |
*/ |
class Namespace { |
+ |
/** |
* A table mapping names that are defined in this namespace to the element representing the thing |
* declared with that name. |
*/ |
Map<String, Element> _definedNames; |
+ |
/** |
* An empty namespace. |
*/ |
static Namespace EMPTY = new Namespace(new Map<String, Element>()); |
+ |
/** |
* Initialize a newly created namespace to have the given defined names. |
* @param definedNames the mapping from names that are defined in this namespace to the |
@@ -6902,6 +8810,7 @@ |
Namespace(Map<String, Element> definedNames) { |
this._definedNames = definedNames; |
} |
+ |
/** |
* Return the element in this namespace that is available to the containing scope using the given |
* name. |
@@ -6909,29 +8818,43 @@ |
* @return the element represented by the given identifier |
*/ |
Element get(String name) => _definedNames[name]; |
+ |
/** |
* Return a table containing the same mappings as those defined by this namespace. |
* @return a table containing the same mappings as those defined by this namespace |
*/ |
Map<String, Element> get definedNames => new Map<String, Element>.from(_definedNames); |
} |
+ |
/** |
* Instances of the class {@code NamespaceBuilder} are used to build a {@code Namespace}. Namespace |
* builders are thread-safe and re-usable. |
* @coverage dart.engine.resolver |
*/ |
class NamespaceBuilder { |
+ |
/** |
- * Initialize a newly created namespace builder. |
+ * Create a namespace representing the export namespace of the given {@link ExportElement}. |
+ * @param element the export element whose export namespace is to be created |
+ * @return the export namespace that was created |
*/ |
- NamespaceBuilder() : super() { |
+ Namespace createExportNamespace(ExportElement element) { |
+ LibraryElement exportedLibrary2 = element.exportedLibrary; |
+ if (exportedLibrary2 == null) { |
+ return Namespace.EMPTY; |
+ } |
+ Map<String, Element> definedNames = createExportMapping(exportedLibrary2, new Set<LibraryElement>()); |
+ definedNames = apply(definedNames, element.combinators); |
+ return new Namespace(definedNames); |
} |
+ |
/** |
* Create a namespace representing the export namespace of the given library. |
* @param library the library whose export namespace is to be created |
* @return the export namespace that was created |
*/ |
- Namespace createExportNamespace(LibraryElement library) => new Namespace(createExportMapping(library, new Set<LibraryElement>())); |
+ Namespace createExportNamespace2(LibraryElement library) => new Namespace(createExportMapping(library, new Set<LibraryElement>())); |
+ |
/** |
* Create a namespace representing the import namespace of the given library. |
* @param library the library whose import namespace is to be created |
@@ -6947,6 +8870,7 @@ |
definedNames = apply2(definedNames, element.prefix); |
return new Namespace(definedNames); |
} |
+ |
/** |
* Create a namespace representing the public namespace of the given library. |
* @param library the library whose public namespace is to be created |
@@ -6960,6 +8884,7 @@ |
} |
return new Namespace(definedNames); |
} |
+ |
/** |
* Add all of the names in the given namespace to the given mapping table. |
* @param definedNames the mapping table to which the names in the given namespace are to be added |
@@ -6970,6 +8895,7 @@ |
definedNames[entry.getKey()] = entry.getValue(); |
} |
} |
+ |
/** |
* Add all of the names in the given namespace to the given mapping table. |
* @param definedNames the mapping table to which the names in the given namespace are to be added |
@@ -6980,6 +8906,7 @@ |
addAll(definedNames2, namespace.definedNames); |
} |
} |
+ |
/** |
* Add the given element to the given mapping table if it has a publicly visible name. |
* @param definedNames the mapping table to which the public name is to be added |
@@ -6991,6 +8918,7 @@ |
definedNames[name2] = element; |
} |
} |
+ |
/** |
* Add to the given mapping table all of the public top-level names that are defined in the given |
* compilation unit. |
@@ -7011,10 +8939,8 @@ |
for (ClassElement element in compilationUnit.types) { |
addIfPublic(definedNames, element); |
} |
- for (VariableElement element in compilationUnit.topLevelVariables) { |
- addIfPublic(definedNames, element); |
- } |
} |
+ |
/** |
* Apply the given combinators to all of the names in the given mapping table. |
* @param definedNames the mapping table to which the namespace operations are to be applied |
@@ -7032,6 +8958,7 @@ |
} |
return definedNames; |
} |
+ |
/** |
* Apply the given prefix to all of the names in the table of defined names. |
* @param definedNames the names that were defined before this operation |
@@ -7049,6 +8976,7 @@ |
return definedNames; |
} |
} |
+ |
/** |
* Create a mapping table representing the export namespace of the given library. |
* @param library the library whose public namespace is to be created |
@@ -7075,6 +9003,7 @@ |
visitedElements.remove(library); |
} |
} |
+ |
/** |
* Hide all of the given names by removing them from the given collection of defined names. |
* @param definedNames the names that were defined before this operation |
@@ -7085,6 +9014,7 @@ |
definedNames.remove(name); |
} |
} |
+ |
/** |
* Show only the given names by removing all other names from the given collection of defined |
* names. |
@@ -7102,43 +9032,45 @@ |
return newNames; |
} |
} |
+ |
/** |
* The abstract class {@code Scope} defines the behavior common to name scopes used by the resolver |
* to determine which names are visible at any given point in the code. |
* @coverage dart.engine.resolver |
*/ |
abstract class Scope { |
+ |
/** |
* The prefix used to mark an identifier as being private to its library. |
*/ |
static String PRIVATE_NAME_PREFIX = "_"; |
+ |
/** |
* The suffix added to the declared name of a setter when looking up the setter. Used to |
* disambiguate between a getter and a setter that have the same name. |
*/ |
static String SETTER_SUFFIX = "="; |
+ |
/** |
* The name used to look up the method used to implement the unary minus operator. Used to |
* disambiguate between the unary and binary operators. |
*/ |
static String UNARY_MINUS = "unary-"; |
+ |
/** |
* Return {@code true} if the given name is a library-private name. |
* @param name the name being tested |
* @return {@code true} if the given name is a library-private name |
*/ |
static bool isPrivateName(String name) => name != null && name.startsWith(PRIVATE_NAME_PREFIX); |
+ |
/** |
* A table mapping names that are defined in this scope to the element representing the thing |
* declared with that name. |
*/ |
Map<String, Element> _definedNames = new Map<String, Element>(); |
+ |
/** |
- * Initialize a newly created scope to be empty. |
- */ |
- Scope() : super() { |
- } |
- /** |
* Add the given element to this scope. If there is already an element with the given name defined |
* in this scope, then an error will be generated and the original element will continue to be |
* mapped to the name. If there is an element with the given name in an enclosing scope, then a |
@@ -7155,6 +9087,7 @@ |
} |
} |
} |
+ |
/** |
* Return the element with which the given identifier is associated, or {@code null} if the name |
* is not defined within this scope. |
@@ -7163,7 +9096,8 @@ |
* implement library-level privacy |
* @return the element with which the given identifier is associated |
*/ |
- Element lookup(Identifier identifier, LibraryElement referencingLibrary) => lookup3(identifier.name, referencingLibrary); |
+ Element lookup(Identifier identifier, LibraryElement referencingLibrary) => lookup3(identifier, identifier.name, referencingLibrary); |
+ |
/** |
* Add the given element to this scope without checking for duplication or hiding. |
* @param element the element to be added to this scope |
@@ -7171,11 +9105,22 @@ |
void defineWithoutChecking(Element element) { |
_definedNames[getName(element)] = element; |
} |
+ |
/** |
+ * Add the given element to this scope without checking for duplication or hiding. |
+ * @param name the name of the element to be added |
+ * @param element the element to be added to this scope |
+ */ |
+ void defineWithoutChecking2(String name, Element element) { |
+ _definedNames[name] = element; |
+ } |
+ |
+ /** |
* Return the element representing the library in which this scope is enclosed. |
* @return the element representing the library in which this scope is enclosed |
*/ |
LibraryElement get definingLibrary; |
+ |
/** |
* Return the error code to be used when reporting that a name being defined locally conflicts |
* with another element of the same name in the local scope. |
@@ -7188,19 +9133,22 @@ |
if (source2 == null) { |
source2 = source; |
} |
- return new AnalysisError.con2(source2, duplicate.nameOffset, duplicate.name.length, CompileTimeErrorCode.DUPLICATE_DEFINITION, [existing.name]); |
+ return new AnalysisError.con2(source2, duplicate.nameOffset, duplicate.displayName.length, CompileTimeErrorCode.DUPLICATE_DEFINITION, [existing.displayName]); |
} |
+ |
/** |
* Return the listener that is to be informed when an error is encountered. |
* @return the listener that is to be informed when an error is encountered |
*/ |
AnalysisErrorListener get errorListener; |
+ |
/** |
* Return the source object representing the compilation unit with which errors related to this |
* scope should be associated. |
* @return the source object with which errors should be associated |
*/ |
Source get source => definingLibrary.definingCompilationUnit.source; |
+ |
/** |
* Return the element with which the given name is associated, or {@code null} if the name is not |
* defined within this scope. This method only returns elements that are directly defined within |
@@ -7211,15 +9159,19 @@ |
* @return the element with which the given name is associated |
*/ |
Element localLookup(String name, LibraryElement referencingLibrary) => _definedNames[name]; |
+ |
/** |
* Return the element with which the given name is associated, or {@code null} if the name is not |
* defined within this scope. |
+ * @param identifier the identifier node to lookup element for, used to report correct kind of a |
+ * problem and associate problem with |
* @param name the name associated with the element to be returned |
* @param referencingLibrary the library that contains the reference to the name, used to |
* implement library-level privacy |
* @return the element with which the given name is associated |
*/ |
- Element lookup3(String name, LibraryElement referencingLibrary); |
+ Element lookup3(Identifier identifier, String name, LibraryElement referencingLibrary); |
+ |
/** |
* Return the name that will be used to look up the given element. |
* @param element the element whose look-up name is to be returned |
@@ -7231,15 +9183,11 @@ |
if (method.name == "-" && method.parameters.length == 0) { |
return UNARY_MINUS; |
} |
- } else if (element is PropertyAccessorElement) { |
- PropertyAccessorElement accessor = element as PropertyAccessorElement; |
- if (accessor.isSetter()) { |
- return "${accessor.name}${SETTER_SUFFIX}"; |
- } |
} |
return element.name; |
} |
} |
+ |
/** |
* Instances of the class {@code ConstantVerifier} traverse an AST structure looking for additional |
* errors and warnings not covered by the parser and resolver. In particular, it looks for errors |
@@ -7247,22 +9195,58 @@ |
* @coverage dart.engine.resolver |
*/ |
class ConstantVerifier extends RecursiveASTVisitor<Object> { |
+ |
/** |
* The error reporter by which errors will be reported. |
*/ |
ErrorReporter _errorReporter; |
+ |
/** |
+ * The type representing the type 'bool'. |
+ */ |
+ InterfaceType _boolType; |
+ |
+ /** |
+ * The type representing the type 'int'. |
+ */ |
+ InterfaceType _intType; |
+ |
+ /** |
+ * The type representing the type 'num'. |
+ */ |
+ InterfaceType _numType; |
+ |
+ /** |
+ * The type representing the type 'string'. |
+ */ |
+ InterfaceType _stringType; |
+ |
+ /** |
* Initialize a newly created constant verifier. |
* @param errorReporter the error reporter by which errors will be reported |
*/ |
- ConstantVerifier(ErrorReporter errorReporter) { |
+ ConstantVerifier(ErrorReporter errorReporter, TypeProvider typeProvider) { |
this._errorReporter = errorReporter; |
+ this._boolType = typeProvider.boolType; |
+ this._intType = typeProvider.intType; |
+ this._numType = typeProvider.numType; |
+ this._stringType = typeProvider.stringType; |
} |
+ Object visitConstructorDeclaration(ConstructorDeclaration node) { |
+ if (node.constKeyword != null) { |
+ validateInitializers(node); |
+ } |
+ return super.visitConstructorDeclaration(node); |
+ } |
Object visitFunctionExpression(FunctionExpression node) { |
super.visitFunctionExpression(node); |
validateDefaultValues(node.parameters); |
return null; |
} |
+ Object visitInstanceCreationExpression(InstanceCreationExpression node) { |
+ validateConstantArguments(node); |
+ return super.visitInstanceCreationExpression(node); |
+ } |
Object visitListLiteral(ListLiteral node) { |
super.visitListLiteral(node); |
if (node.modifier != null) { |
@@ -7275,20 +9259,39 @@ |
Object visitMapLiteral(MapLiteral node) { |
super.visitMapLiteral(node); |
bool isConst = node.modifier != null; |
- Set<String> keys = new Set<String>(); |
+ bool reportEqualKeys = true; |
+ Set<Object> keys = new Set<Object>(); |
+ List<Expression> invalidKeys = new List<Expression>(); |
for (MapLiteralEntry entry in node.entries) { |
- StringLiteral key2 = entry.key; |
- EvaluationResultImpl result = validate(key2, CompileTimeErrorCode.NON_CONSTANT_MAP_KEY); |
- if (result is ValidResult && ((result as ValidResult)).value is String) { |
- String value2 = ((result as ValidResult)).value as String; |
- if (keys.contains(value2)) { |
- _errorReporter.reportError(StaticWarningCode.EQUAL_KEYS_IN_MAP, key2, []); |
+ Expression key2 = entry.key; |
+ if (isConst) { |
+ EvaluationResultImpl result = validate(key2, CompileTimeErrorCode.NON_CONSTANT_MAP_KEY); |
+ validate(entry.value, CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE); |
+ if (result is ValidResult) { |
+ Object value2 = ((result as ValidResult)).value; |
+ if (keys.contains(value2)) { |
+ invalidKeys.add(key2); |
+ } else { |
+ javaSetAdd(keys, value2); |
+ } |
+ } |
+ } else { |
+ EvaluationResultImpl result = key2.accept(new ConstantVisitor()); |
+ if (result is ValidResult) { |
+ Object value3 = ((result as ValidResult)).value; |
+ if (keys.contains(value3)) { |
+ invalidKeys.add(key2); |
+ } else { |
+ javaSetAdd(keys, value3); |
+ } |
} else { |
- javaSetAdd(keys, value2); |
+ reportEqualKeys = false; |
} |
} |
- if (isConst) { |
- validate(entry.value, CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE); |
+ } |
+ if (reportEqualKeys) { |
+ for (Expression key in invalidKeys) { |
+ _errorReporter.reportError2(StaticWarningCode.EQUAL_KEYS_IN_MAP, key, []); |
} |
} |
return null; |
@@ -7318,19 +9321,35 @@ |
} |
return null; |
} |
+ |
/** |
+ * Return {@code true} if the given value is the result of evaluating an expression whose value is |
+ * a valid key in a const map literal. Keys in const map literals must be either a string, number, |
+ * boolean, list, map, or null. |
+ * @param value |
+ * @return {@code true} if the given value is a valid key in a const map literal |
+ */ |
+ bool isValidConstMapKey(Object value) => true; |
+ |
+ /** |
* If the given result represents one or more errors, report those errors. Except for special |
* cases, use the given error code rather than the one reported in the error. |
* @param result the result containing any errors that need to be reported |
* @param errorCode the error code to be used if the result represents an error |
*/ |
- void reportErrors(EvaluationResultImpl result, ErrorCode errorCode) { |
+ void reportErrors(EvaluationResultImpl result, ErrorCode errorCode2) { |
if (result is ErrorResult) { |
for (ErrorResult_ErrorData data in ((result as ErrorResult)).errorData) { |
- _errorReporter.reportError(errorCode, data.node, []); |
+ ErrorCode dataErrorCode = data.errorCode; |
+ if (identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION) || identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING) || identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL) || identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_INT) || identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM)) { |
+ _errorReporter.reportError2(dataErrorCode, data.node, []); |
+ } else { |
+ _errorReporter.reportError2(errorCode2, data.node, []); |
+ } |
} |
} |
} |
+ |
/** |
* Validate that the given expression is a compile time constant. Return the value of the compile |
* time constant, or {@code null} if the expression is not a compile time constant. |
@@ -7343,7 +9362,29 @@ |
reportErrors(result, errorCode); |
return result; |
} |
+ |
/** |
+ * Validate that if the passed instance creation is 'const' then all its arguments are constant |
+ * expressions. |
+ * @param node the instance creation evaluate |
+ */ |
+ void validateConstantArguments(InstanceCreationExpression node) { |
+ if (!node.isConst()) { |
+ return; |
+ } |
+ ArgumentList argumentList2 = node.argumentList; |
+ if (argumentList2 == null) { |
+ return; |
+ } |
+ for (Expression argument in argumentList2.arguments) { |
+ if (argument is NamedExpression) { |
+ argument = ((argument as NamedExpression)).expression; |
+ } |
+ validate(argument, CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT); |
+ } |
+ } |
+ |
+ /** |
* Validate that the default value associated with each of the parameters in the given list is a |
* compile time constant. |
* @param parameters the list of parameters to be validated |
@@ -7366,53 +9407,169 @@ |
} |
} |
} |
+ |
+ /** |
+ * Validates that the given expression is a compile time constant. |
+ * @param parameterElements the elements of parameters of constant constructor, they are |
+ * considered as a valid potentially constant expressions |
+ * @param expression the expression to validate |
+ */ |
+ void validateInitializerExpression(List<ParameterElement> parameterElements, Expression expression) { |
+ EvaluationResultImpl result = expression.accept(new ConstantVisitor_10(this, parameterElements)); |
+ reportErrors(result, CompileTimeErrorCode.NON_CONSTANT_VALUE_IN_INITIALIZER); |
+ } |
+ |
+ /** |
+ * Validates that all of the arguments of a constructor initializer are compile time constants. |
+ * @param parameterElements the elements of parameters of constant constructor, they are |
+ * considered as a valid potentially constant expressions |
+ * @param argumentList the argument list to validate |
+ */ |
+ void validateInitializerInvocationArguments(List<ParameterElement> parameterElements, ArgumentList argumentList) { |
+ if (argumentList == null) { |
+ return; |
+ } |
+ for (Expression argument in argumentList.arguments) { |
+ validateInitializerExpression(parameterElements, argument); |
+ } |
+ } |
+ |
+ /** |
+ * Validates that the expressions of the given initializers (of a constant constructor) are all |
+ * compile time constants. |
+ * @param constructor the constant constructor declaration to validate |
+ */ |
+ void validateInitializers(ConstructorDeclaration constructor) { |
+ List<ParameterElement> parameterElements = constructor.parameters.elements; |
+ NodeList<ConstructorInitializer> initializers2 = constructor.initializers; |
+ for (ConstructorInitializer initializer in initializers2) { |
+ if (initializer is ConstructorFieldInitializer) { |
+ ConstructorFieldInitializer fieldInitializer = initializer as ConstructorFieldInitializer; |
+ validateInitializerExpression(parameterElements, fieldInitializer.expression); |
+ } |
+ if (initializer is RedirectingConstructorInvocation) { |
+ RedirectingConstructorInvocation invocation = initializer as RedirectingConstructorInvocation; |
+ validateInitializerInvocationArguments(parameterElements, invocation.argumentList); |
+ } |
+ if (initializer is SuperConstructorInvocation) { |
+ SuperConstructorInvocation invocation = initializer as SuperConstructorInvocation; |
+ validateInitializerInvocationArguments(parameterElements, invocation.argumentList); |
+ } |
+ } |
+ } |
} |
+class ConstantVisitor_10 extends ConstantVisitor { |
+ final ConstantVerifier ConstantVerifier_this; |
+ List<ParameterElement> parameterElements; |
+ ConstantVisitor_10(this.ConstantVerifier_this, this.parameterElements) : super(); |
+ EvaluationResultImpl visitSimpleIdentifier(SimpleIdentifier node) { |
+ Element element2 = node.element; |
+ for (ParameterElement parameterElement in parameterElements) { |
+ if (identical(parameterElement, element2) && parameterElement != null) { |
+ Type2 type2 = parameterElement.type; |
+ if (type2 != null) { |
+ if (type2.isDynamic()) { |
+ return ValidResult.RESULT_DYNAMIC; |
+ } |
+ if (type2.isSubtypeOf(ConstantVerifier_this._boolType)) { |
+ return ValidResult.RESULT_BOOL; |
+ } |
+ if (type2.isSubtypeOf(ConstantVerifier_this._intType)) { |
+ return ValidResult.RESULT_INT; |
+ } |
+ if (type2.isSubtypeOf(ConstantVerifier_this._numType)) { |
+ return ValidResult.RESULT_NUM; |
+ } |
+ if (type2.isSubtypeOf(ConstantVerifier_this._stringType)) { |
+ return ValidResult.RESULT_STRING; |
+ } |
+ } |
+ return ValidResult.RESULT_OBJECT; |
+ } |
+ } |
+ return super.visitSimpleIdentifier(node); |
+ } |
+} |
+ |
/** |
* Instances of the class {@code ErrorVerifier} traverse an AST structure looking for additional |
* errors and warnings not covered by the parser and resolver. |
* @coverage dart.engine.resolver |
*/ |
class ErrorVerifier extends RecursiveASTVisitor<Object> { |
+ |
/** |
+ * Checks if the given expression is the reference to the type. |
+ * @param expr the expression to evaluate |
+ * @return {@code true} if the given expression is the reference to the type |
+ */ |
+ static bool isTypeReference(Expression expr) { |
+ if (expr is Identifier) { |
+ Identifier identifier = expr as Identifier; |
+ return identifier.element is ClassElement; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
* The error reporter by which errors will be reported. |
*/ |
ErrorReporter _errorReporter; |
+ |
/** |
* The current library that is being analyzed. |
*/ |
LibraryElement _currentLibrary; |
+ |
/** |
* The type representing the type 'dynamic'. |
*/ |
Type2 _dynamicType; |
+ |
/** |
* The object providing access to the types defined by the language. |
*/ |
TypeProvider _typeProvider; |
+ |
/** |
+ * The manager for the inheritance mappings. |
+ */ |
+ InheritanceManager _inheritanceManager; |
+ |
+ /** |
* This is set to {@code true} iff the visitor is currently visiting children nodes of a{@link ConstructorDeclaration} and the constructor is 'const'. |
* @see #visitConstructorDeclaration(ConstructorDeclaration) |
*/ |
bool _isEnclosingConstructorConst = false; |
+ |
/** |
* This is set to {@code true} iff the visitor is currently visiting children nodes of a{@link CatchClause}. |
* @see #visitCatchClause(CatchClause) |
*/ |
bool _isInCatchClause = false; |
+ |
/** |
+ * This is set to {@code true} iff the visitor is currently visiting a{@link ConstructorInitializer}. |
+ */ |
+ bool _isInConstructorInitializer = false; |
+ |
+ /** |
* This is set to {@code true} iff the visitor is currently visiting code in the SDK. |
*/ |
bool _isInSystemLibrary = false; |
+ |
/** |
* The class containing the AST nodes being visited, or {@code null} if we are not in the scope of |
* a class. |
*/ |
ClassElement _enclosingClass; |
+ |
/** |
* The method or function that we are currently visiting, or {@code null} if we are not inside a |
* method or function. |
*/ |
ExecutableElement _enclosingFunction; |
+ |
/** |
* This map is initialized when visiting the contents of a class declaration. If the visitor is |
* not in an enclosing class declaration, then the map is set to {@code null}. |
@@ -7425,15 +9582,37 @@ |
* @see #checkForAllFinalInitializedErrorCodes(ConstructorDeclaration) |
*/ |
Map<FieldElement, INIT_STATE> _initialFieldElementsMap; |
+ |
/** |
+ * A table mapping name of the library to the export directive which export this library. |
+ */ |
+ Map<String, LibraryElement> _nameToExportElement = new Map<String, LibraryElement>(); |
+ |
+ /** |
+ * A table mapping name of the library to the import directive which import this library. |
+ */ |
+ Map<String, LibraryElement> _nameToImportElement = new Map<String, LibraryElement>(); |
+ |
+ /** |
+ * A table mapping names to the export elements exported them. |
+ */ |
+ Map<String, ExportElement> _exportedNames = new Map<String, ExportElement>(); |
+ |
+ /** |
+ * A set of the names of the variable initializers we are visiting now. |
+ */ |
+ Set<String> _namesForReferenceToDeclaredVariableInInitializer = new Set<String>(); |
+ |
+ /** |
* A list of types used by the {@link CompileTimeErrorCode#EXTENDS_DISALLOWED_CLASS} and{@link CompileTimeErrorCode#IMPLEMENTS_DISALLOWED_CLASS} error codes. |
*/ |
List<InterfaceType> _DISALLOWED_TYPES_TO_EXTEND_OR_IMPLEMENT; |
- ErrorVerifier(ErrorReporter errorReporter, LibraryElement currentLibrary, TypeProvider typeProvider) { |
+ ErrorVerifier(ErrorReporter errorReporter, LibraryElement currentLibrary, TypeProvider typeProvider, InheritanceManager inheritanceManager) { |
this._errorReporter = errorReporter; |
this._currentLibrary = currentLibrary; |
this._isInSystemLibrary = currentLibrary.source.isInSystemLibrary(); |
this._typeProvider = typeProvider; |
+ this._inheritanceManager = inheritanceManager; |
_isEnclosingConstructorConst = false; |
_isInCatchClause = false; |
_dynamicType = typeProvider.dynamicType; |
@@ -7443,14 +9622,29 @@ |
checkForArgumentDefinitionTestNonParameter(node); |
return super.visitArgumentDefinitionTest(node); |
} |
+ Object visitArgumentList(ArgumentList node) { |
+ checkForArgumentTypeNotAssignable(node); |
+ return super.visitArgumentList(node); |
+ } |
Object visitAssertStatement(AssertStatement node) { |
checkForNonBoolExpression(node); |
return super.visitAssertStatement(node); |
} |
Object visitAssignmentExpression(AssignmentExpression node) { |
- checkForInvalidAssignment(node); |
+ sc.Token operator2 = node.operator; |
+ sc.TokenType operatorType = operator2.type; |
+ if (identical(operatorType, sc.TokenType.EQ)) { |
+ checkForInvalidAssignment2(node.leftHandSide, node.rightHandSide); |
+ } else { |
+ checkForInvalidAssignment(node); |
+ } |
+ checkForAssignmentToFinal(node); |
return super.visitAssignmentExpression(node); |
} |
+ Object visitBinaryExpression(BinaryExpression node) { |
+ checkForArgumentTypeNotAssignable2(node.rightOperand); |
+ return super.visitBinaryExpression(node); |
+ } |
Object visitCatchClause(CatchClause node) { |
bool previousIsInCatchClause = _isInCatchClause; |
try { |
@@ -7464,7 +9658,19 @@ |
ClassElement outerClass = _enclosingClass; |
try { |
_enclosingClass = node.element; |
+ WithClause withClause2 = node.withClause; |
+ ImplementsClause implementsClause2 = node.implementsClause; |
+ ExtendsClause extendsClause2 = node.extendsClause; |
checkForBuiltInIdentifierAsName(node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME); |
+ checkForMemberWithClassName(); |
+ checkForAllMixinErrorCodes(withClause2); |
+ if (implementsClause2 != null || extendsClause2 != null) { |
+ if (!checkForImplementsDisallowedClass(implementsClause2) && !checkForExtendsDisallowedClass(extendsClause2)) { |
+ checkForNonAbstractClassInheritsAbstractMember(node); |
+ checkForInconsistentMethodInheritance(); |
+ checkForRecursiveInterfaceInheritance(_enclosingClass, new List<ClassElement>()); |
+ } |
+ } |
ClassElement classElement = node.element; |
if (classElement != null) { |
List<FieldElement> fieldElements = classElement.fields; |
@@ -7484,6 +9690,14 @@ |
} |
Object visitClassTypeAlias(ClassTypeAlias node) { |
checkForBuiltInIdentifierAsName(node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME); |
+ checkForAllMixinErrorCodes(node.withClause); |
+ ClassElement outerClassElement = _enclosingClass; |
+ try { |
+ _enclosingClass = node.element; |
+ checkForRecursiveInterfaceInheritance(node.element, new List<ClassElement>()); |
+ } finally { |
+ _enclosingClass = outerClassElement; |
+ } |
return super.visitClassTypeAlias(node); |
} |
Object visitConditionalExpression(ConditionalExpression node) { |
@@ -7498,12 +9712,28 @@ |
checkForConstConstructorWithNonFinalField(node); |
checkForConflictingConstructorNameAndMember(node); |
checkForAllFinalInitializedErrorCodes(node); |
+ checkForRedirectingConstructorErrorCodes(node); |
+ checkForMultipleSuperInitializers(node); |
+ checkForRecursiveConstructorRedirect(node); |
+ checkForRecursiveFactoryRedirect(node); |
+ checkForRedirectToInvalidFunction(node); |
+ checkForUndefinedConstructorInInitializerImplicit(node); |
+ checkForRedirectToNonConstConstructor(node); |
return super.visitConstructorDeclaration(node); |
} finally { |
_isEnclosingConstructorConst = false; |
_enclosingFunction = outerFunction; |
} |
} |
+ Object visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
+ _isInConstructorInitializer = true; |
+ try { |
+ checkForFieldInitializerNotAssignable(node); |
+ return super.visitConstructorFieldInitializer(node); |
+ } finally { |
+ _isInConstructorInitializer = false; |
+ } |
+ } |
Object visitDefaultFormalParameter(DefaultFormalParameter node) { |
checkForPrivateOptionalParameter(node); |
return super.visitDefaultFormalParameter(node); |
@@ -7512,19 +9742,37 @@ |
checkForNonBoolCondition(node.condition); |
return super.visitDoStatement(node); |
} |
- Object visitExtendsClause(ExtendsClause node) { |
- checkForExtendsDisallowedClass(node); |
- return super.visitExtendsClause(node); |
+ Object visitExportDirective(ExportDirective node) { |
+ checkForAmbiguousExport(node); |
+ checkForExportDuplicateLibraryName(node); |
+ checkForExportInternalLibrary(node); |
+ return super.visitExportDirective(node); |
} |
Object visitFieldFormalParameter(FieldFormalParameter node) { |
checkForConstFormalParameter(node); |
- checkForFieldInitializerOutsideConstructor(node); |
+ checkForFieldInitializingFormalRedirectingConstructor(node); |
return super.visitFieldFormalParameter(node); |
} |
Object visitFunctionDeclaration(FunctionDeclaration node) { |
ExecutableElement outerFunction = _enclosingFunction; |
try { |
+ SimpleIdentifier identifier = node.name; |
+ String methoName = ""; |
+ if (identifier != null) { |
+ methoName = identifier.name; |
+ } |
_enclosingFunction = node.element; |
+ if (node.isSetter() || node.isGetter()) { |
+ checkForMismatchedAccessorTypes(node, methoName); |
+ if (node.isSetter()) { |
+ FunctionExpression functionExpression2 = node.functionExpression; |
+ if (functionExpression2 != null) { |
+ checkForWrongNumberOfParametersForSetter(node.name, functionExpression2.parameters); |
+ } |
+ TypeName returnType2 = node.returnType; |
+ checkForNonVoidReturnTypeForSetter(returnType2); |
+ } |
+ } |
return super.visitFunctionDeclaration(node); |
} finally { |
_enclosingFunction = outerFunction; |
@@ -7548,18 +9796,29 @@ |
checkForNonBoolCondition(node.condition); |
return super.visitIfStatement(node); |
} |
- Object visitImplementsClause(ImplementsClause node) { |
- checkForImplementsDisallowedClass(node); |
- return super.visitImplementsClause(node); |
+ Object visitImportDirective(ImportDirective node) { |
+ checkForImportDuplicateLibraryName(node); |
+ checkForImportInternalLibrary(node); |
+ return super.visitImportDirective(node); |
} |
+ Object visitIndexExpression(IndexExpression node) { |
+ checkForArgumentTypeNotAssignable2(node.index); |
+ return super.visitIndexExpression(node); |
+ } |
Object visitInstanceCreationExpression(InstanceCreationExpression node) { |
ConstructorName constructorName2 = node.constructorName; |
TypeName typeName = constructorName2.type; |
Type2 type2 = typeName.type; |
if (type2 is InterfaceType) { |
InterfaceType interfaceType = type2 as InterfaceType; |
- checkForConstWithNonConst(node); |
checkForConstOrNewWithAbstractClass(node, typeName, interfaceType); |
+ if (node.isConst()) { |
+ checkForConstWithNonConst(node); |
+ checkForConstWithUndefinedConstructor(node); |
+ checkForConstWithTypeParameters(node); |
+ } else { |
+ checkForNewWithUndefinedConstructor(node); |
+ } |
checkForTypeArgumentNotMatchingBounds(node, constructorName2.element, typeName); |
} |
return super.visitInstanceCreationExpression(node); |
@@ -7587,27 +9846,74 @@ |
} |
} |
} |
+ checkForNonConstMapAsExpressionStatement(node); |
return super.visitMapLiteral(node); |
} |
Object visitMethodDeclaration(MethodDeclaration node) { |
ExecutableElement previousFunction = _enclosingFunction; |
try { |
_enclosingFunction = node.element; |
- if (node.isSetter()) { |
- checkForWrongNumberOfParametersForSetter(node); |
+ SimpleIdentifier identifier = node.name; |
+ String methoName = ""; |
+ if (identifier != null) { |
+ methoName = identifier.name; |
+ } |
+ if (node.isSetter() || node.isGetter()) { |
+ checkForMismatchedAccessorTypes(node, methoName); |
+ checkForConflictingInstanceGetterAndSuperclassMember(node); |
+ } |
+ if (node.isGetter()) { |
+ checkForConflictingStaticGetterAndInstanceSetter(node); |
+ } else if (node.isSetter()) { |
+ checkForWrongNumberOfParametersForSetter(node.name, node.parameters); |
+ checkForNonVoidReturnTypeForSetter(node.returnType); |
+ checkForConflictingStaticSetterAndInstanceMember(node); |
} else if (node.isOperator()) { |
checkForOptionalParameterInOperator(node); |
+ checkForWrongNumberOfParametersForOperator(node); |
+ checkForNonVoidReturnTypeForOperator(node); |
} |
checkForConcreteClassWithAbstractMember(node); |
+ checkForAllInvalidOverrideErrorCodes(node); |
return super.visitMethodDeclaration(node); |
} finally { |
_enclosingFunction = previousFunction; |
} |
} |
+ Object visitMethodInvocation(MethodInvocation node) { |
+ checkForStaticAccessToInstanceMember(node.target, node.methodName); |
+ return super.visitMethodInvocation(node); |
+ } |
Object visitNativeFunctionBody(NativeFunctionBody node) { |
checkForNativeFunctionBodyInNonSDKCode(node); |
return super.visitNativeFunctionBody(node); |
} |
+ Object visitPostfixExpression(PostfixExpression node) { |
+ checkForAssignmentToFinal2(node.operand); |
+ return super.visitPostfixExpression(node); |
+ } |
+ Object visitPrefixedIdentifier(PrefixedIdentifier node) { |
+ checkForStaticAccessToInstanceMember(node.prefix, node.identifier); |
+ return super.visitPrefixedIdentifier(node); |
+ } |
+ Object visitPrefixExpression(PrefixExpression node) { |
+ if (node.operator.type.isIncrementOperator()) { |
+ checkForAssignmentToFinal2(node.operand); |
+ } |
+ return super.visitPrefixExpression(node); |
+ } |
+ Object visitPropertyAccess(PropertyAccess node) { |
+ checkForStaticAccessToInstanceMember(node.target, node.propertyName); |
+ return super.visitPropertyAccess(node); |
+ } |
+ Object visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) { |
+ _isInConstructorInitializer = true; |
+ try { |
+ return super.visitRedirectingConstructorInvocation(node); |
+ } finally { |
+ _isInConstructorInitializer = false; |
+ } |
+ } |
Object visitRethrowExpression(RethrowExpression node) { |
checkForRethrowOutsideCatch(node); |
return super.visitRethrowExpression(node); |
@@ -7620,11 +9926,30 @@ |
checkForConstFormalParameter(node); |
return super.visitSimpleFormalParameter(node); |
} |
+ Object visitSimpleIdentifier(SimpleIdentifier node) { |
+ checkForReferenceToDeclaredVariableInInitializer(node); |
+ checkForImplicitThisReferenceInInitializer(node); |
+ return super.visitSimpleIdentifier(node); |
+ } |
+ Object visitSuperConstructorInvocation(SuperConstructorInvocation node) { |
+ _isInConstructorInitializer = true; |
+ try { |
+ return super.visitSuperConstructorInvocation(node); |
+ } finally { |
+ _isInConstructorInitializer = false; |
+ } |
+ } |
Object visitSwitchStatement(SwitchStatement node) { |
checkForCaseExpressionTypeImplementsEquals(node); |
checkForInconsistentCaseExpressionTypes(node); |
+ checkForSwitchExpressionNotAssignable(node); |
+ checkForCaseBlocksNotTerminated(node); |
return super.visitSwitchStatement(node); |
} |
+ Object visitThisExpression(ThisExpression node) { |
+ checkForInvalidReferenceToThis(node); |
+ return super.visitThisExpression(node); |
+ } |
Object visitThrowExpression(ThrowExpression node) { |
checkForConstEvalThrowsException(node); |
return super.visitThrowExpression(node); |
@@ -7637,6 +9962,22 @@ |
checkForBuiltInIdentifierAsName(node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_VARIABLE_NAME); |
return super.visitTypeParameter(node); |
} |
+ Object visitVariableDeclaration(VariableDeclaration node) { |
+ SimpleIdentifier nameNode = node.name; |
+ Expression initializerNode = node.initializer; |
+ checkForInvalidAssignment2(nameNode, initializerNode); |
+ nameNode.accept(this); |
+ String name2 = nameNode.name; |
+ javaSetAdd(_namesForReferenceToDeclaredVariableInInitializer, name2); |
+ try { |
+ if (initializerNode != null) { |
+ initializerNode.accept(this); |
+ } |
+ } finally { |
+ _namesForReferenceToDeclaredVariableInInitializer.remove(name2); |
+ } |
+ return null; |
+ } |
Object visitVariableDeclarationList(VariableDeclarationList node) { |
checkForBuiltInIdentifierAsName2(node); |
return super.visitVariableDeclarationList(node); |
@@ -7649,11 +9990,12 @@ |
checkForNonBoolCondition(node.condition); |
return super.visitWhileStatement(node); |
} |
+ |
/** |
* This verifies that the passed constructor declaration does not violate any of the error codes |
* relating to the initialization of fields in the enclosing class. |
* @param node the {@link ConstructorDeclaration} to evaluate |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see #initialFieldElementsMap |
* @see CompileTimeErrorCode#FINAL_INITIALIZED_IN_DECLARATION_AND_CONSTRUCTOR |
* @see CompileTimeErrorCode#FINAL_INITIALIZED_MULTIPLE_TIMES |
@@ -7677,12 +10019,12 @@ |
fieldElementsMap[fieldElement] = INIT_STATE.INIT_IN_FIELD_FORMAL; |
} else if (identical(state, INIT_STATE.INIT_IN_DECLARATION)) { |
if (fieldElement.isFinal() || fieldElement.isConst()) { |
- _errorReporter.reportError(CompileTimeErrorCode.FINAL_INITIALIZED_IN_DECLARATION_AND_CONSTRUCTOR, formalParameter.identifier, [fieldElement.name]); |
+ _errorReporter.reportError2(CompileTimeErrorCode.FINAL_INITIALIZED_IN_DECLARATION_AND_CONSTRUCTOR, formalParameter.identifier, [fieldElement.displayName]); |
foundError = true; |
} |
} else if (identical(state, INIT_STATE.INIT_IN_FIELD_FORMAL)) { |
if (fieldElement.isFinal() || fieldElement.isConst()) { |
- _errorReporter.reportError(CompileTimeErrorCode.FINAL_INITIALIZED_MULTIPLE_TIMES, formalParameter.identifier, [fieldElement.name]); |
+ _errorReporter.reportError2(CompileTimeErrorCode.FINAL_INITIALIZED_MULTIPLE_TIMES, formalParameter.identifier, [fieldElement.displayName]); |
foundError = true; |
} |
} |
@@ -7701,14 +10043,14 @@ |
fieldElementsMap[fieldElement] = INIT_STATE.INIT_IN_INITIALIZERS; |
} else if (identical(state, INIT_STATE.INIT_IN_DECLARATION)) { |
if (fieldElement.isFinal() || fieldElement.isConst()) { |
- _errorReporter.reportError(CompileTimeErrorCode.FIELD_INITIALIZED_IN_INITIALIZER_AND_DECLARATION, fieldName2, []); |
+ _errorReporter.reportError2(CompileTimeErrorCode.FIELD_INITIALIZED_IN_INITIALIZER_AND_DECLARATION, fieldName2, []); |
foundError = true; |
} |
} else if (identical(state, INIT_STATE.INIT_IN_FIELD_FORMAL)) { |
- _errorReporter.reportError(CompileTimeErrorCode.FIELD_INITIALIZED_IN_PARAMETER_AND_INITIALIZER, fieldName2, []); |
+ _errorReporter.reportError2(CompileTimeErrorCode.FIELD_INITIALIZED_IN_PARAMETER_AND_INITIALIZER, fieldName2, []); |
foundError = true; |
} else if (identical(state, INIT_STATE.INIT_IN_INITIALIZERS)) { |
- _errorReporter.reportError(CompileTimeErrorCode.FIELD_INITIALIZED_BY_MULTIPLE_INITIALIZERS, fieldName2, [fieldElement.name]); |
+ _errorReporter.reportError2(CompileTimeErrorCode.FIELD_INITIALIZED_BY_MULTIPLE_INITIALIZERS, fieldName2, [fieldElement.displayName]); |
foundError = true; |
} |
} |
@@ -7716,7 +10058,181 @@ |
} |
return foundError; |
} |
+ |
/** |
+ * This checks the passed method declaration against override-error codes. |
+ * @param node the {@link MethodDeclaration} to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC |
+ * @see CompileTimeErrorCode#INVALID_OVERRIDE_REQUIRED |
+ * @see CompileTimeErrorCode#INVALID_OVERRIDE_POSITIONAL |
+ * @see CompileTimeErrorCode#INVALID_OVERRIDE_NAMED |
+ * @see StaticWarningCode#INVALID_GETTER_OVERRIDE_RETURN_TYPE |
+ * @see StaticWarningCode#INVALID_METHOD_OVERRIDE_RETURN_TYPE |
+ * @see StaticWarningCode#INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE |
+ * @see StaticWarningCode#INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE |
+ * @see StaticWarningCode#INVALID_METHOD_OVERRIDE_OPTIONAL_PARAM_TYPE |
+ * @see StaticWarningCode#INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE |
+ */ |
+ bool checkForAllInvalidOverrideErrorCodes(MethodDeclaration node) { |
+ if (_enclosingClass == null || node.isStatic() || node.body is NativeFunctionBody) { |
+ return false; |
+ } |
+ ExecutableElement executableElement = node.element; |
+ if (executableElement == null) { |
+ return false; |
+ } |
+ SimpleIdentifier methodName = node.name; |
+ if (methodName.isSynthetic()) { |
+ return false; |
+ } |
+ String methodNameStr = methodName.name; |
+ ExecutableElement overriddenExecutable = _inheritanceManager.lookupInheritance(_enclosingClass, executableElement.name); |
+ if (overriddenExecutable == null) { |
+ if (!node.isGetter() && !node.isSetter() && !node.isOperator()) { |
+ Set<ClassElement> visitedClasses = new Set<ClassElement>(); |
+ InterfaceType superclassType = _enclosingClass.supertype; |
+ ClassElement superclassElement = superclassType == null ? null : superclassType.element; |
+ while (superclassElement != null && !visitedClasses.contains(superclassElement)) { |
+ javaSetAdd(visitedClasses, superclassElement); |
+ List<FieldElement> fieldElts = superclassElement.fields; |
+ for (FieldElement fieldElt in fieldElts) { |
+ if (fieldElt.name == methodNameStr && fieldElt.isStatic()) { |
+ _errorReporter.reportError2(StaticWarningCode.INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC, methodName, [methodNameStr, fieldElt.enclosingElement.displayName]); |
+ return true; |
+ } |
+ } |
+ List<PropertyAccessorElement> propertyAccessorElts = superclassElement.accessors; |
+ for (PropertyAccessorElement accessorElt in propertyAccessorElts) { |
+ if (accessorElt.name == methodNameStr && accessorElt.isStatic()) { |
+ _errorReporter.reportError2(StaticWarningCode.INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC, methodName, [methodNameStr, accessorElt.enclosingElement.displayName]); |
+ return true; |
+ } |
+ } |
+ List<MethodElement> methodElements = superclassElement.methods; |
+ for (MethodElement methodElement in methodElements) { |
+ if (methodElement.name == methodNameStr && methodElement.isStatic()) { |
+ _errorReporter.reportError2(StaticWarningCode.INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC, methodName, [methodNameStr, methodElement.enclosingElement.displayName]); |
+ return true; |
+ } |
+ } |
+ superclassType = superclassElement.supertype; |
+ superclassElement = superclassType == null ? null : superclassType.element; |
+ } |
+ } |
+ return false; |
+ } |
+ FunctionType overridingFT = executableElement.type; |
+ FunctionType overriddenFT = overriddenExecutable.type; |
+ InterfaceType enclosingType = _enclosingClass.type; |
+ overriddenFT = _inheritanceManager.substituteTypeArgumentsInMemberFromInheritance(overriddenFT, methodNameStr, enclosingType); |
+ if (overridingFT == null || overriddenFT == null) { |
+ return false; |
+ } |
+ Type2 overridingFTReturnType = overridingFT.returnType; |
+ Type2 overriddenFTReturnType = overriddenFT.returnType; |
+ List<Type2> overridingNormalPT = overridingFT.normalParameterTypes; |
+ List<Type2> overriddenNormalPT = overriddenFT.normalParameterTypes; |
+ List<Type2> overridingPositionalPT = overridingFT.optionalParameterTypes; |
+ List<Type2> overriddenPositionalPT = overriddenFT.optionalParameterTypes; |
+ Map<String, Type2> overridingNamedPT = overridingFT.namedParameterTypes; |
+ Map<String, Type2> overriddenNamedPT = overriddenFT.namedParameterTypes; |
+ if (overridingNormalPT.length != overriddenNormalPT.length) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.INVALID_OVERRIDE_REQUIRED, methodName, [overriddenNormalPT.length, overriddenExecutable.enclosingElement.displayName]); |
+ return true; |
+ } |
+ if (overridingPositionalPT.length < overriddenPositionalPT.length) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.INVALID_OVERRIDE_POSITIONAL, methodName, [overriddenPositionalPT.length, overriddenExecutable.enclosingElement.displayName]); |
+ return true; |
+ } |
+ 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)) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.INVALID_OVERRIDE_NAMED, methodName, [overriddenParamName, overriddenExecutable.enclosingElement.displayName]); |
+ return true; |
+ } |
+ } |
+ if (overriddenFTReturnType != VoidTypeImpl.instance && !overridingFTReturnType.isAssignableTo(overriddenFTReturnType)) { |
+ _errorReporter.reportError2(!node.isGetter() ? StaticWarningCode.INVALID_METHOD_OVERRIDE_RETURN_TYPE : StaticWarningCode.INVALID_GETTER_OVERRIDE_RETURN_TYPE, methodName, [overridingFTReturnType.displayName, overriddenFTReturnType.displayName, overriddenExecutable.enclosingElement.displayName]); |
+ return true; |
+ } |
+ FormalParameterList formalParameterList = node.parameters; |
+ if (formalParameterList == null) { |
+ return false; |
+ } |
+ NodeList<FormalParameter> parameterNodeList = formalParameterList.parameters; |
+ int parameterIndex = 0; |
+ for (int i = 0; i < overridingNormalPT.length; i++) { |
+ if (!overridingNormalPT[i].isAssignableTo(overriddenNormalPT[i])) { |
+ _errorReporter.reportError2(!node.isSetter() ? StaticWarningCode.INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE : StaticWarningCode.INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE, parameterNodeList[parameterIndex], [overridingNormalPT[i].displayName, overriddenNormalPT[i].displayName, overriddenExecutable.enclosingElement.displayName]); |
+ return true; |
+ } |
+ parameterIndex++; |
+ } |
+ for (int i = 0; i < overriddenPositionalPT.length; i++) { |
+ if (!overridingPositionalPT[i].isAssignableTo(overriddenPositionalPT[i])) { |
+ _errorReporter.reportError2(StaticWarningCode.INVALID_METHOD_OVERRIDE_OPTIONAL_PARAM_TYPE, parameterNodeList[parameterIndex], [overridingPositionalPT[i].displayName, overriddenPositionalPT[i].displayName, overriddenExecutable.enclosingElement.displayName]); |
+ return true; |
+ } |
+ parameterIndex++; |
+ } |
+ 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) { |
+ continue; |
+ } |
+ if (!overriddenNamedPTEntry.getValue().isAssignableTo(overridingType)) { |
+ NormalFormalParameter parameterToSelect = null; |
+ for (FormalParameter formalParameter in parameterNodeList) { |
+ if (formalParameter is DefaultFormalParameter && identical(formalParameter.kind, ParameterKind.NAMED)) { |
+ DefaultFormalParameter defaultFormalParameter = formalParameter as DefaultFormalParameter; |
+ NormalFormalParameter normalFormalParameter = defaultFormalParameter.parameter; |
+ if (overriddenNamedPTEntry.getKey() == normalFormalParameter.identifier.name) { |
+ parameterToSelect = normalFormalParameter; |
+ break; |
+ } |
+ } |
+ } |
+ if (parameterToSelect != null) { |
+ _errorReporter.reportError2(StaticWarningCode.INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE, parameterToSelect, [overridingType.displayName, overriddenNamedPTEntry.getValue().displayName, overriddenExecutable.enclosingElement.displayName]); |
+ return true; |
+ } |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * This verifies that all classes of the passed 'with' clause are valid. |
+ * @param node the 'with' clause to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#MIXIN_DECLARES_CONSTRUCTOR |
+ * @see CompileTimeErrorCode#MIXIN_INHERITS_FROM_NOT_OBJECT |
+ * @see CompileTimeErrorCode#MIXIN_REFERENCES_SUPER |
+ */ |
+ bool checkForAllMixinErrorCodes(WithClause withClause) { |
+ if (withClause == null) { |
+ return false; |
+ } |
+ bool problemReported = false; |
+ for (TypeName mixinName in withClause.mixinTypes) { |
+ Type2 mixinType = mixinName.type; |
+ if (mixinType is! InterfaceType) { |
+ continue; |
+ } |
+ ClassElement mixinElement = ((mixinType as InterfaceType)).element; |
+ problemReported = javaBooleanOr(problemReported, checkForMixinDeclaresConstructor(mixinName, mixinElement)); |
+ problemReported = javaBooleanOr(problemReported, checkForMixinInheritsNotFromObject(mixinName, mixinElement)); |
+ problemReported = javaBooleanOr(problemReported, checkForMixinReferencesSuper(mixinName, mixinElement)); |
+ } |
+ return problemReported; |
+ } |
+ |
+ /** |
* This checks that the return statement of the form <i>return e;</i> is not in a generative |
* constructor. |
* <p> |
@@ -7737,47 +10253,193 @@ |
Type2 expectedReturnType = functionType == null ? DynamicTypeImpl.instance : functionType.returnType; |
Expression returnExpression = node.expression; |
bool isGenerativeConstructor = _enclosingFunction is ConstructorElement && !((_enclosingFunction as ConstructorElement)).isFactory(); |
- if (returnExpression != null) { |
- if (isGenerativeConstructor) { |
- _errorReporter.reportError(CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR, returnExpression, []); |
- return true; |
+ if (isGenerativeConstructor) { |
+ if (returnExpression == null) { |
+ return false; |
} |
- if (!expectedReturnType.isVoid()) { |
- Type2 actualReturnType = getType(returnExpression); |
- if (!actualReturnType.isAssignableTo(expectedReturnType)) { |
- _errorReporter.reportError(StaticTypeWarningCode.RETURN_OF_INVALID_TYPE, returnExpression, [actualReturnType.name, expectedReturnType.name, _enclosingFunction.name]); |
- return true; |
- } |
+ _errorReporter.reportError2(CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR, returnExpression, []); |
+ return true; |
+ } |
+ if (returnExpression == null) { |
+ if (VoidTypeImpl.instance.isAssignableTo(expectedReturnType)) { |
+ return false; |
} |
+ _errorReporter.reportError2(StaticWarningCode.RETURN_WITHOUT_VALUE, node, []); |
+ return true; |
+ } |
+ Type2 staticReturnType = getStaticType(returnExpression); |
+ if (expectedReturnType.isVoid()) { |
+ if (staticReturnType.isVoid() || staticReturnType.isDynamic() || identical(staticReturnType, BottomTypeImpl.instance)) { |
+ return false; |
+ } |
+ _errorReporter.reportError2(StaticTypeWarningCode.RETURN_OF_INVALID_TYPE, returnExpression, [staticReturnType.displayName, expectedReturnType.displayName, _enclosingFunction.displayName]); |
+ return true; |
+ } |
+ bool isStaticAssignable = staticReturnType.isAssignableTo(expectedReturnType); |
+ Type2 propagatedReturnType = getPropagatedType(returnExpression); |
+ if (propagatedReturnType == null) { |
+ if (isStaticAssignable) { |
+ return false; |
+ } |
+ _errorReporter.reportError2(StaticTypeWarningCode.RETURN_OF_INVALID_TYPE, returnExpression, [staticReturnType.displayName, expectedReturnType.displayName, _enclosingFunction.displayName]); |
+ return true; |
} else { |
- if (!isGenerativeConstructor && !VoidTypeImpl.instance.isAssignableTo(expectedReturnType)) { |
- _errorReporter.reportError(StaticWarningCode.RETURN_WITHOUT_VALUE, node, []); |
+ bool isPropagatedAssignable = propagatedReturnType.isAssignableTo(expectedReturnType); |
+ if (isStaticAssignable || isPropagatedAssignable) { |
+ return false; |
} |
+ _errorReporter.reportError2(StaticTypeWarningCode.RETURN_OF_INVALID_TYPE, returnExpression, [staticReturnType.displayName, expectedReturnType.displayName, _enclosingFunction.displayName]); |
+ return true; |
} |
+ } |
+ |
+ /** |
+ * This verifies that the export namespace of the passed export directive does not export any name |
+ * already exported by other export directive. |
+ * @param node the export directive node to report problem on |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#AMBIGUOUS_EXPORT |
+ */ |
+ bool checkForAmbiguousExport(ExportDirective node) { |
+ if (node.element is! ExportElement) { |
+ return false; |
+ } |
+ ExportElement exportElement = node.element as ExportElement; |
+ LibraryElement exportedLibrary2 = exportElement.exportedLibrary; |
+ if (exportedLibrary2 == null) { |
+ return false; |
+ } |
+ Namespace namespace = new NamespaceBuilder().createExportNamespace(exportElement); |
+ Set<String> newNames = namespace.definedNames.keys.toSet(); |
+ for (String name in newNames) { |
+ ExportElement prevElement = _exportedNames[name]; |
+ if (prevElement != null && prevElement != exportElement) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.AMBIGUOUS_EXPORT, node, [name, prevElement.exportedLibrary.definingCompilationUnit.displayName, exportedLibrary2.definingCompilationUnit.displayName]); |
+ return true; |
+ } else { |
+ _exportedNames[name] = exportElement; |
+ } |
+ } |
return false; |
} |
+ |
/** |
* This verifies that the passed argument definition test identifier is a parameter. |
* @param node the {@link ArgumentDefinitionTest} to evaluate |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see CompileTimeErrorCode#ARGUMENT_DEFINITION_TEST_NON_PARAMETER |
*/ |
bool checkForArgumentDefinitionTestNonParameter(ArgumentDefinitionTest node) { |
SimpleIdentifier identifier2 = node.identifier; |
Element element2 = identifier2.element; |
if (element2 != null && element2 is! ParameterElement) { |
- _errorReporter.reportError(CompileTimeErrorCode.ARGUMENT_DEFINITION_TEST_NON_PARAMETER, identifier2, [identifier2.name]); |
+ _errorReporter.reportError2(CompileTimeErrorCode.ARGUMENT_DEFINITION_TEST_NON_PARAMETER, identifier2, [identifier2.name]); |
return true; |
} |
return false; |
} |
+ |
/** |
+ * This verifies that the passed arguments can be assigned to their corresponding parameters. |
+ * @param node the arguments to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#ARGUMENT_TYPE_NOT_ASSIGNABLE |
+ */ |
+ bool checkForArgumentTypeNotAssignable(ArgumentList argumentList) { |
+ if (argumentList == null) { |
+ return false; |
+ } |
+ bool problemReported = false; |
+ for (Expression argument in argumentList.arguments) { |
+ problemReported = javaBooleanOr(problemReported, checkForArgumentTypeNotAssignable2(argument)); |
+ } |
+ return problemReported; |
+ } |
+ |
+ /** |
+ * This verifies that the passed argument can be assigned to their corresponding parameters. |
+ * @param node the argument to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#ARGUMENT_TYPE_NOT_ASSIGNABLE |
+ */ |
+ bool checkForArgumentTypeNotAssignable2(Expression argument) { |
+ if (argument == null) { |
+ return false; |
+ } |
+ ParameterElement parameterElement2 = argument.parameterElement; |
+ if (parameterElement2 == null) { |
+ return false; |
+ } |
+ Type2 parameterType = parameterElement2.type; |
+ if (parameterType == null) { |
+ return false; |
+ } |
+ Type2 staticType = getStaticType(argument); |
+ if (staticType == null) { |
+ return false; |
+ } |
+ if (staticType.isAssignableTo(parameterType)) { |
+ return false; |
+ } |
+ Type2 propagatedType = getPropagatedType(argument); |
+ if (propagatedType != null && propagatedType.isAssignableTo(parameterType)) { |
+ return false; |
+ } |
+ _errorReporter.reportError2(StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, argument, [(propagatedType == null ? staticType : propagatedType).displayName, parameterType.displayName]); |
+ return true; |
+ } |
+ |
+ /** |
+ * This verifies that left hand side of the passed assignment expression is not final. |
+ * @param node the assignment expression to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#ASSIGNMENT_TO_FINAL |
+ */ |
+ bool checkForAssignmentToFinal(AssignmentExpression node) { |
+ Expression leftExpression = node.leftHandSide; |
+ return checkForAssignmentToFinal2(leftExpression); |
+ } |
+ |
+ /** |
+ * This verifies that the passed expression is not final. |
+ * @param node the expression to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#ASSIGNMENT_TO_FINAL |
+ */ |
+ bool checkForAssignmentToFinal2(Expression expression) { |
+ Element element = null; |
+ if (expression is Identifier) { |
+ element = ((expression as Identifier)).element; |
+ } |
+ if (expression is PropertyAccess) { |
+ element = ((expression as PropertyAccess)).propertyName.element; |
+ } |
+ if (element is VariableElement) { |
+ VariableElement leftVar = element as VariableElement; |
+ if (leftVar.isFinal()) { |
+ _errorReporter.reportError2(StaticWarningCode.ASSIGNMENT_TO_FINAL, expression, []); |
+ return true; |
+ } |
+ return false; |
+ } |
+ if (element is PropertyAccessorElement) { |
+ PropertyAccessorElement leftAccessor = element as PropertyAccessorElement; |
+ if (!leftAccessor.isSetter()) { |
+ _errorReporter.reportError2(StaticWarningCode.ASSIGNMENT_TO_FINAL, expression, []); |
+ return true; |
+ } |
+ return false; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
* This verifies that the passed identifier is not a keyword, and generates the passed error code |
* on the identifier if it is a keyword. |
* @param identifier the identifier to check to ensure that it is not a keyword |
* @param errorCode if the passed identifier is a keyword then this error code is created on the |
* identifier, the error code will be one of{@link CompileTimeErrorCode#BUILT_IN_IDENTIFIER_AS_TYPE_NAME},{@link CompileTimeErrorCode#BUILT_IN_IDENTIFIER_AS_TYPE_VARIABLE_NAME} or{@link CompileTimeErrorCode#BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME} |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see CompileTimeErrorCode#BUILT_IN_IDENTIFIER_AS_TYPE_NAME |
* @see CompileTimeErrorCode#BUILT_IN_IDENTIFIER_AS_TYPE_VARIABLE_NAME |
* @see CompileTimeErrorCode#BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME |
@@ -7785,15 +10447,16 @@ |
bool checkForBuiltInIdentifierAsName(SimpleIdentifier identifier, ErrorCode errorCode) { |
sc.Token token2 = identifier.token; |
if (identical(token2.type, sc.TokenType.KEYWORD)) { |
- _errorReporter.reportError(errorCode, identifier, [identifier.name]); |
+ _errorReporter.reportError2(errorCode, identifier, [identifier.name]); |
return true; |
} |
return false; |
} |
+ |
/** |
* This verifies that the passed variable declaration list does not have a built-in identifier. |
* @param node the variable declaration list to check |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see CompileTimeErrorCode#BUILT_IN_IDENTIFIER_AS_TYPE |
*/ |
bool checkForBuiltInIdentifierAsName2(VariableDeclarationList node) { |
@@ -7805,7 +10468,7 @@ |
sc.Token token2 = simpleIdentifier.token; |
if (identical(token2.type, sc.TokenType.KEYWORD)) { |
if (((token2 as sc.KeywordToken)).keyword != sc.Keyword.DYNAMIC) { |
- _errorReporter.reportError(CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, identifier, [identifier.name]); |
+ _errorReporter.reportError2(CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, identifier, [identifier.name]); |
return true; |
} |
} |
@@ -7813,29 +10476,86 @@ |
} |
return false; |
} |
+ |
/** |
+ * This verifies that the given switch case is terminated with 'break', 'continue', 'return' or |
+ * 'throw'. |
+ * @param node the switch case to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#CASE_BLOCK_NOT_TERMINATED |
+ */ |
+ bool checkForCaseBlockNotTerminated(SwitchCase node) { |
+ NodeList<Statement> statements2 = node.statements; |
+ if (statements2.isEmpty) { |
+ ASTNode parent2 = node.parent; |
+ if (parent2 is SwitchStatement) { |
+ SwitchStatement switchStatement = parent2 as SwitchStatement; |
+ NodeList<SwitchMember> members2 = switchStatement.members; |
+ int index = members2.indexOf(node); |
+ if (index != -1 && index < members2.length - 1) { |
+ return false; |
+ } |
+ } |
+ } else { |
+ Statement statement = statements2[statements2.length - 1]; |
+ if (statement is BreakStatement || statement is ContinueStatement || statement is ReturnStatement) { |
+ return false; |
+ } |
+ if (statement is ExpressionStatement) { |
+ Expression expression2 = ((statement as ExpressionStatement)).expression; |
+ if (expression2 is ThrowExpression) { |
+ return false; |
+ } |
+ } |
+ } |
+ _errorReporter.reportError4(StaticWarningCode.CASE_BLOCK_NOT_TERMINATED, node.keyword, []); |
+ return true; |
+ } |
+ |
+ /** |
+ * This verifies that the switch cases in the given switch statement is terminated with 'break', |
+ * 'continue', 'return' or 'throw'. |
+ * @param node the switch statement containing the cases to be checked |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#CASE_BLOCK_NOT_TERMINATED |
+ */ |
+ bool checkForCaseBlocksNotTerminated(SwitchStatement node) { |
+ bool foundError = false; |
+ NodeList<SwitchMember> members2 = node.members; |
+ int lastMember = members2.length - 1; |
+ for (int i = 0; i < lastMember; i++) { |
+ SwitchMember member = members2[i]; |
+ if (member is SwitchCase) { |
+ foundError = javaBooleanOr(foundError, checkForCaseBlockNotTerminated((member as SwitchCase))); |
+ } |
+ } |
+ return foundError; |
+ } |
+ |
+ /** |
* This verifies that the passed switch statement does not have a case expression with the |
* operator '==' overridden. |
* @param node the switch statement to evaluate |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see CompileTimeErrorCode#CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS |
*/ |
bool checkForCaseExpressionTypeImplementsEquals(SwitchStatement node) { |
Expression expression2 = node.expression; |
- Type2 type = expression2.staticType; |
+ Type2 type = getStaticType(expression2); |
if (type != null && type != _typeProvider.intType && type != _typeProvider.stringType) { |
Element element2 = type.element; |
if (element2 is ClassElement) { |
ClassElement classElement = element2 as ClassElement; |
MethodElement method = classElement.lookUpMethod("==", _currentLibrary); |
if (method != null && method.enclosingElement.type != _typeProvider.objectType) { |
- _errorReporter.reportError(CompileTimeErrorCode.CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS, expression2, [element2.name]); |
+ _errorReporter.reportError2(CompileTimeErrorCode.CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS, expression2, [element2.displayName]); |
return true; |
} |
} |
} |
return false; |
} |
+ |
/** |
* This verifies that the passed method declaration is abstract only if the enclosing class is |
* also abstract. |
@@ -7846,39 +10566,183 @@ |
bool checkForConcreteClassWithAbstractMember(MethodDeclaration node) { |
if (node.isAbstract() && _enclosingClass != null && !_enclosingClass.isAbstract()) { |
SimpleIdentifier methodName = node.name; |
- _errorReporter.reportError(StaticWarningCode.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER, methodName, [methodName.name, _enclosingClass.name]); |
+ _errorReporter.reportError2(StaticWarningCode.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER, methodName, [methodName.name, _enclosingClass.displayName]); |
return true; |
} |
return false; |
} |
+ |
+ /** |
+ * This verifies all possible conflicts of the constructor name with other constructors and |
+ * members of the same class. |
+ * @param node the constructor declaration to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#DUPLICATE_CONSTRUCTOR_DEFAULT |
+ * @see CompileTimeErrorCode#DUPLICATE_CONSTRUCTOR_NAME |
+ * @see CompileTimeErrorCode#CONFLICTING_CONSTRUCTOR_NAME_AND_FIELD |
+ * @see CompileTimeErrorCode#CONFLICTING_CONSTRUCTOR_NAME_AND_METHOD |
+ */ |
bool checkForConflictingConstructorNameAndMember(ConstructorDeclaration node) { |
ConstructorElement constructorElement = node.element; |
SimpleIdentifier constructorName = node.name; |
+ String name2 = constructorElement.name; |
+ ClassElement classElement = constructorElement.enclosingElement; |
+ List<ConstructorElement> constructors2 = classElement.constructors; |
+ for (ConstructorElement otherConstructor in constructors2) { |
+ if (identical(otherConstructor, constructorElement)) { |
+ continue; |
+ } |
+ if (name2 == otherConstructor.name) { |
+ if (name2 == null || name2.length == 0) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_DEFAULT, node, []); |
+ } else { |
+ _errorReporter.reportError2(CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_NAME, node, [name2]); |
+ } |
+ return true; |
+ } |
+ } |
if (constructorName != null && constructorElement != null && !constructorName.isSynthetic()) { |
- String name2 = constructorName.name; |
- ClassElement classElement = constructorElement.enclosingElement; |
List<FieldElement> fields2 = classElement.fields; |
for (FieldElement field in fields2) { |
if (field.name == name2) { |
- _errorReporter.reportError(CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_NAME_AND_FIELD, node, [name2]); |
+ _errorReporter.reportError2(CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_NAME_AND_FIELD, node, [name2]); |
return true; |
} |
} |
List<MethodElement> methods2 = classElement.methods; |
for (MethodElement method in methods2) { |
if (method.name == name2) { |
- _errorReporter.reportError(CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_NAME_AND_METHOD, node, [name2]); |
+ _errorReporter.reportError2(CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_NAME_AND_METHOD, node, [name2]); |
return true; |
} |
} |
} |
return false; |
} |
+ |
/** |
- * This verifies that the passed constructor declaration is not 'const' if it has a non-final |
+ * This verifies that the superclass of the enclosing class does not declare accessible static |
+ * member with the same name as the passed instance getter/setter method declaration. |
+ * @param node the method declaration to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#CONFLICTING_INSTANCE_GETTER_AND_SUPERCLASS_MEMBER |
+ * @see StaticWarningCode#CONFLICTING_INSTANCE_SETTER_AND_SUPERCLASS_MEMBER |
+ */ |
+ bool checkForConflictingInstanceGetterAndSuperclassMember(MethodDeclaration node) { |
+ if (node.isStatic()) { |
+ return false; |
+ } |
+ SimpleIdentifier nameNode = node.name; |
+ if (nameNode == null) { |
+ return false; |
+ } |
+ String name2 = nameNode.name; |
+ if (_enclosingClass == null) { |
+ return false; |
+ } |
+ InterfaceType enclosingType = _enclosingClass.type; |
+ ExecutableElement superElement; |
+ superElement = enclosingType.lookUpGetterInSuperclass(name2, _currentLibrary); |
+ if (superElement == null) { |
+ superElement = enclosingType.lookUpSetterInSuperclass(name2, _currentLibrary); |
+ } |
+ if (superElement == null) { |
+ superElement = enclosingType.lookUpMethodInSuperclass(name2, _currentLibrary); |
+ } |
+ if (superElement == null) { |
+ return false; |
+ } |
+ if (!superElement.isStatic()) { |
+ return false; |
+ } |
+ ClassElement superElementClass = superElement.enclosingElement as ClassElement; |
+ InterfaceType superElementType = superElementClass.type; |
+ if (node.isGetter()) { |
+ _errorReporter.reportError2(StaticWarningCode.CONFLICTING_INSTANCE_GETTER_AND_SUPERCLASS_MEMBER, nameNode, [superElementType.displayName]); |
+ } else { |
+ _errorReporter.reportError2(StaticWarningCode.CONFLICTING_INSTANCE_SETTER_AND_SUPERCLASS_MEMBER, nameNode, [superElementType.displayName]); |
+ } |
+ return true; |
+ } |
+ |
+ /** |
+ * This verifies that the enclosing class does not have an instance member with the same name as |
+ * the passed static getter method declaration. |
+ * @param node the method declaration to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#CONFLICTING_STATIC_GETTER_AND_INSTANCE_SETTER |
+ */ |
+ bool checkForConflictingStaticGetterAndInstanceSetter(MethodDeclaration node) { |
+ if (!node.isStatic()) { |
+ return false; |
+ } |
+ SimpleIdentifier nameNode = node.name; |
+ if (nameNode == null) { |
+ return false; |
+ } |
+ String name2 = nameNode.name; |
+ if (_enclosingClass == null) { |
+ return false; |
+ } |
+ InterfaceType enclosingType = _enclosingClass.type; |
+ ExecutableElement setter = enclosingType.lookUpSetter(name2, _currentLibrary); |
+ if (setter == null) { |
+ return false; |
+ } |
+ if (setter.isStatic()) { |
+ return false; |
+ } |
+ ClassElement setterClass = setter.enclosingElement as ClassElement; |
+ InterfaceType setterType = setterClass.type; |
+ _errorReporter.reportError2(StaticWarningCode.CONFLICTING_STATIC_GETTER_AND_INSTANCE_SETTER, nameNode, [setterType.displayName]); |
+ return true; |
+ } |
+ |
+ /** |
+ * This verifies that the enclosing class does not have an instance member with the same name as |
+ * the passed static getter method declaration. |
+ * @param node the method declaration to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#CONFLICTING_STATIC_SETTER_AND_INSTANCE_MEMBER |
+ */ |
+ bool checkForConflictingStaticSetterAndInstanceMember(MethodDeclaration node) { |
+ if (!node.isStatic()) { |
+ return false; |
+ } |
+ SimpleIdentifier nameNode = node.name; |
+ if (nameNode == null) { |
+ return false; |
+ } |
+ String name2 = nameNode.name; |
+ if (_enclosingClass == null) { |
+ return false; |
+ } |
+ InterfaceType enclosingType = _enclosingClass.type; |
+ ExecutableElement member; |
+ member = enclosingType.lookUpMethod(name2, _currentLibrary); |
+ if (member == null) { |
+ member = enclosingType.lookUpGetter(name2, _currentLibrary); |
+ } |
+ if (member == null) { |
+ member = enclosingType.lookUpSetter(name2, _currentLibrary); |
+ } |
+ if (member == null) { |
+ return false; |
+ } |
+ if (member.isStatic()) { |
+ return false; |
+ } |
+ ClassElement memberClass = member.enclosingElement as ClassElement; |
+ InterfaceType memberType = memberClass.type; |
+ _errorReporter.reportError2(StaticWarningCode.CONFLICTING_STATIC_SETTER_AND_INSTANCE_MEMBER, nameNode, [memberType.displayName]); |
+ return true; |
+ } |
+ |
+ /** |
+ * This verifies that the passed constructor declaration is 'const' then there are no non-final |
* instance variable. |
- * @param node the instance creation expression to evaluate |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @param node the constructor declaration to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see CompileTimeErrorCode#CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD |
*/ |
bool checkForConstConstructorWithNonFinalField(ConstructorDeclaration node) { |
@@ -7886,52 +10750,50 @@ |
return false; |
} |
ConstructorElement constructorElement = node.element; |
- if (constructorElement != null) { |
- ClassElement classElement = constructorElement.enclosingElement; |
- List<FieldElement> elements = classElement.fields; |
- for (FieldElement field in elements) { |
- if (!field.isFinal() && !field.isConst() && !field.isStatic() && !field.isSynthetic()) { |
- _errorReporter.reportError(CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD, node, []); |
- return true; |
- } |
- } |
+ ClassElement classElement = constructorElement.enclosingElement; |
+ if (!classElement.hasNonFinalField()) { |
+ return false; |
} |
- return false; |
+ _errorReporter.reportError2(CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD, node, []); |
+ return true; |
} |
+ |
/** |
* This verifies that the passed throw expression is not enclosed in a 'const' constructor |
* declaration. |
* @param node the throw expression expression to evaluate |
- * @return return {@code true} if and only if an error code is generated on the passed node |
- * @see CompileTimeErrorCode#CONST_EVAL_THROWS_EXCEPTION |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#CONST_CONSTRUCTOR_THROWS_EXCEPTION |
*/ |
bool checkForConstEvalThrowsException(ThrowExpression node) { |
if (_isEnclosingConstructorConst) { |
- _errorReporter.reportError(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, node, []); |
+ _errorReporter.reportError2(CompileTimeErrorCode.CONST_CONSTRUCTOR_THROWS_EXCEPTION, node, []); |
return true; |
} |
return false; |
} |
+ |
/** |
* This verifies that the passed normal formal parameter is not 'const'. |
* @param node the normal formal parameter to evaluate |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see CompileTimeErrorCode#CONST_FORMAL_PARAMETER |
*/ |
bool checkForConstFormalParameter(NormalFormalParameter node) { |
if (node.isConst()) { |
- _errorReporter.reportError(CompileTimeErrorCode.CONST_FORMAL_PARAMETER, node, []); |
+ _errorReporter.reportError2(CompileTimeErrorCode.CONST_FORMAL_PARAMETER, node, []); |
return true; |
} |
return false; |
} |
+ |
/** |
* This verifies that the passed instance creation expression is not being invoked on an abstract |
* class. |
* @param node the instance creation expression to evaluate |
* @param typeName the {@link TypeName} of the {@link ConstructorName} from the{@link InstanceCreationExpression}, this is the AST node that the error is attached to |
* @param type the type being constructed with this {@link InstanceCreationExpression} |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see StaticWarningCode#CONST_WITH_ABSTRACT_CLASS |
* @see StaticWarningCode#NEW_WITH_ABSTRACT_CLASS |
*/ |
@@ -7940,34 +10802,116 @@ |
ConstructorElement element2 = node.element; |
if (element2 != null && !element2.isFactory()) { |
if (identical(((node.keyword as sc.KeywordToken)).keyword, sc.Keyword.CONST)) { |
- _errorReporter.reportError(StaticWarningCode.CONST_WITH_ABSTRACT_CLASS, typeName, []); |
+ _errorReporter.reportError2(StaticWarningCode.CONST_WITH_ABSTRACT_CLASS, typeName, []); |
} else { |
- _errorReporter.reportError(StaticWarningCode.NEW_WITH_ABSTRACT_CLASS, typeName, []); |
+ _errorReporter.reportError2(StaticWarningCode.NEW_WITH_ABSTRACT_CLASS, typeName, []); |
} |
return true; |
} |
} |
return false; |
} |
+ |
/** |
- * This verifies that if the passed instance creation expression is 'const', then it is not being |
- * invoked on a constructor that is not 'const'. |
+ * This verifies that the passed 'const' instance creation expression is not being invoked on a |
+ * constructor that is not 'const'. |
+ * <p> |
+ * This method assumes that the instance creation was tested to be 'const' before being called. |
* @param node the instance creation expression to evaluate |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see CompileTimeErrorCode#CONST_WITH_NON_CONST |
*/ |
bool checkForConstWithNonConst(InstanceCreationExpression node) { |
ConstructorElement constructorElement = node.element; |
- if (node.isConst() && constructorElement != null && !constructorElement.isConst()) { |
- _errorReporter.reportError(CompileTimeErrorCode.CONST_WITH_NON_CONST, node, []); |
+ if (constructorElement != null && !constructorElement.isConst()) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.CONST_WITH_NON_CONST, node, []); |
return true; |
} |
return false; |
} |
+ |
/** |
+ * This verifies that the passed 'const' instance creation expression does not reference any type |
+ * parameters. |
+ * <p> |
+ * This method assumes that the instance creation was tested to be 'const' before being called. |
+ * @param node the instance creation expression to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#CONST_WITH_TYPE_PARAMETERS |
+ */ |
+ bool checkForConstWithTypeParameters(InstanceCreationExpression node) { |
+ ConstructorName constructorName2 = node.constructorName; |
+ if (constructorName2 == null) { |
+ return false; |
+ } |
+ TypeName typeName = constructorName2.type; |
+ return checkForConstWithTypeParameters2(typeName); |
+ } |
+ |
+ /** |
+ * This verifies that the passed type name does not reference any type parameters. |
+ * @param typeName the type name to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#CONST_WITH_TYPE_PARAMETERS |
+ */ |
+ bool checkForConstWithTypeParameters2(TypeName typeName) { |
+ if (typeName == null) { |
+ return false; |
+ } |
+ Identifier name2 = typeName.name; |
+ if (name2 == null) { |
+ return false; |
+ } |
+ if (name2.element is TypeVariableElement) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS, name2, []); |
+ } |
+ TypeArgumentList typeArguments2 = typeName.typeArguments; |
+ if (typeArguments2 != null) { |
+ bool hasError = false; |
+ for (TypeName argument in typeArguments2.arguments) { |
+ hasError = javaBooleanOr(hasError, checkForConstWithTypeParameters2(argument)); |
+ } |
+ return hasError; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * This verifies that if the passed 'const' instance creation expression is being invoked on the |
+ * resolved constructor. |
+ * <p> |
+ * This method assumes that the instance creation was tested to be 'const' before being called. |
+ * @param node the instance creation expression to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#CONST_WITH_UNDEFINED_CONSTRUCTOR |
+ * @see CompileTimeErrorCode#CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT |
+ */ |
+ bool checkForConstWithUndefinedConstructor(InstanceCreationExpression node) { |
+ if (node.element != null) { |
+ return false; |
+ } |
+ ConstructorName constructorName2 = node.constructorName; |
+ if (constructorName2 == null) { |
+ return false; |
+ } |
+ TypeName type2 = constructorName2.type; |
+ if (type2 == null) { |
+ return false; |
+ } |
+ Identifier className = type2.name; |
+ SimpleIdentifier name2 = constructorName2.name; |
+ if (name2 != null) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR, name2, [className, name2]); |
+ } else { |
+ _errorReporter.reportError2(CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT, constructorName2, [className]); |
+ } |
+ return true; |
+ } |
+ |
+ /** |
* This verifies that there are no default parameters in the passed function type alias. |
* @param node the function type alias to evaluate |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see CompileTimeErrorCode#DEFAULT_VALUE_IN_FUNCTION_TYPE_ALIAS |
*/ |
bool checkForDefaultValueInFunctionTypeAlias(FunctionTypeAlias node) { |
@@ -7978,25 +10922,90 @@ |
if (formalParameter is DefaultFormalParameter) { |
DefaultFormalParameter defaultFormalParameter = formalParameter as DefaultFormalParameter; |
if (defaultFormalParameter.defaultValue != null) { |
- _errorReporter.reportError(CompileTimeErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPE_ALIAS, node, []); |
+ _errorReporter.reportError2(CompileTimeErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPE_ALIAS, node, []); |
result = true; |
} |
} |
} |
return result; |
} |
+ |
/** |
+ * This verifies the passed import has unique name among other exported libraries. |
+ * @param node the export directive to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#EXPORT_DUPLICATED_LIBRARY_NAME |
+ */ |
+ bool checkForExportDuplicateLibraryName(ExportDirective node) { |
+ Element nodeElement = node.element; |
+ if (nodeElement is! ExportElement) { |
+ return false; |
+ } |
+ ExportElement nodeExportElement = nodeElement as ExportElement; |
+ LibraryElement nodeLibrary = nodeExportElement.exportedLibrary; |
+ if (nodeLibrary == null) { |
+ return false; |
+ } |
+ String name2 = nodeLibrary.name; |
+ LibraryElement prevLibrary = _nameToExportElement[name2]; |
+ if (prevLibrary != null) { |
+ if (prevLibrary != nodeLibrary) { |
+ _errorReporter.reportError2(StaticWarningCode.EXPORT_DUPLICATED_LIBRARY_NAME, node, [prevLibrary.definingCompilationUnit.displayName, nodeLibrary.definingCompilationUnit.displayName, name2]); |
+ return true; |
+ } |
+ } else { |
+ _nameToExportElement[name2] = nodeLibrary; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Check that if the visiting library is not system, then any passed library should not be SDK |
+ * internal library. |
+ * @param node the export directive to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#EXPORT_INTERNAL_LIBRARY |
+ */ |
+ bool checkForExportInternalLibrary(ExportDirective node) { |
+ if (_isInSystemLibrary) { |
+ return false; |
+ } |
+ Element element2 = node.element; |
+ if (element2 is! ExportElement) { |
+ return false; |
+ } |
+ ExportElement exportElement = element2 as ExportElement; |
+ DartSdk sdk = _currentLibrary.context.sourceFactory.dartSdk; |
+ String uri2 = exportElement.uri; |
+ SdkLibrary sdkLibrary = sdk.getSdkLibrary(uri2); |
+ if (sdkLibrary == null) { |
+ return false; |
+ } |
+ if (!sdkLibrary.isInternal()) { |
+ return false; |
+ } |
+ _errorReporter.reportError2(CompileTimeErrorCode.EXPORT_INTERNAL_LIBRARY, node, [node.uri]); |
+ return true; |
+ } |
+ |
+ /** |
* This verifies that the passed extends clause does not extend classes such as num or String. |
* @param node the extends clause to test |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see CompileTimeErrorCode#EXTENDS_DISALLOWED_CLASS |
*/ |
- bool checkForExtendsDisallowedClass(ExtendsClause extendsClause) => checkForExtendsOrImplementsDisallowedClass(extendsClause.superclass, CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS); |
+ bool checkForExtendsDisallowedClass(ExtendsClause extendsClause) { |
+ if (extendsClause == null) { |
+ return false; |
+ } |
+ return checkForExtendsOrImplementsDisallowedClass(extendsClause.superclass, CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS); |
+ } |
+ |
/** |
* This verifies that the passed type name does not extend or implement classes such as 'num' or |
* 'String'. |
* @param node the type name to test |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see #checkForExtendsDisallowedClass(ExtendsClause) |
* @see #checkForImplementsDisallowedClass(ImplementsClause) |
* @see CompileTimeErrorCode#EXTENDS_DISALLOWED_CLASS |
@@ -8019,29 +11028,76 @@ |
} |
} |
} |
- _errorReporter.reportError(errorCode, typeName, [disallowedType.name]); |
+ _errorReporter.reportError2(errorCode, typeName, [disallowedType.displayName]); |
return true; |
} |
} |
return false; |
} |
+ |
/** |
+ * This verifies that the passed constructor field initializer has compatible field and |
+ * initializer expression types. |
+ * @param node the constructor field initializer to test |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#CONST_FIELD_INITIALIZER_NOT_ASSIGNABLE |
+ * @see StaticWarningCode#FIELD_INITIALIZER_NOT_ASSIGNABLE |
+ */ |
+ bool checkForFieldInitializerNotAssignable(ConstructorFieldInitializer node) { |
+ Element fieldNameElement = node.fieldName.element; |
+ if (fieldNameElement is! FieldElement) { |
+ return false; |
+ } |
+ FieldElement fieldElement = fieldNameElement as FieldElement; |
+ Type2 fieldType = fieldElement.type; |
+ Expression expression2 = node.expression; |
+ if (expression2 == null) { |
+ return false; |
+ } |
+ Type2 staticType = getStaticType(expression2); |
+ if (staticType == null) { |
+ return false; |
+ } |
+ if (staticType.isAssignableTo(fieldType)) { |
+ return false; |
+ } |
+ Type2 propagatedType = getPropagatedType(expression2); |
+ if (propagatedType != null && propagatedType.isAssignableTo(fieldType)) { |
+ return false; |
+ } |
+ if (_isEnclosingConstructorConst) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.CONST_FIELD_INITIALIZER_NOT_ASSIGNABLE, expression2, [(propagatedType == null ? staticType : propagatedType).displayName, fieldType.displayName]); |
+ } else { |
+ _errorReporter.reportError2(StaticWarningCode.FIELD_INITIALIZER_NOT_ASSIGNABLE, expression2, [(propagatedType == null ? staticType : propagatedType).displayName, fieldType.displayName]); |
+ } |
+ return true; |
+ } |
+ |
+ /** |
* This verifies that the passed field formal parameter is in a constructor declaration. |
* @param node the field formal parameter to test |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see CompileTimeErrorCode#FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR |
*/ |
- bool checkForFieldInitializerOutsideConstructor(FieldFormalParameter node) { |
- ASTNode parent2 = node.parent; |
- if (parent2 != null) { |
- ASTNode grandparent = parent2.parent; |
- if (grandparent != null && grandparent is! ConstructorDeclaration && grandparent.parent is! ConstructorDeclaration) { |
- _errorReporter.reportError(CompileTimeErrorCode.FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR, node, []); |
+ bool checkForFieldInitializingFormalRedirectingConstructor(FieldFormalParameter node) { |
+ ConstructorDeclaration constructor = node.getAncestor(ConstructorDeclaration); |
+ if (constructor == null) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR, node, []); |
+ return true; |
+ } |
+ if (constructor.factoryKeyword != null) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.FIELD_INITIALIZER_FACTORY_CONSTRUCTOR, node, []); |
+ return true; |
+ } |
+ for (ConstructorInitializer initializer in constructor.initializers) { |
+ if (initializer is RedirectingConstructorInvocation) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR, node, []); |
return true; |
} |
} |
return false; |
} |
+ |
/** |
* This verifies that final fields that are declared, without any constructors in the enclosing |
* class, are initialized. Cases in which there is at least one constructor are handled at the end |
@@ -8066,6 +11122,7 @@ |
} |
return foundError; |
} |
+ |
/** |
* This verifies that the passed variable declaration list has only initialized variables if the |
* list is final or const. This method is called by{@link #checkForFinalNotInitialized(ClassDeclaration)},{@link #visitTopLevelVariableDeclaration(TopLevelVariableDeclaration)} and{@link #visitVariableDeclarationStatement(VariableDeclarationStatement)}. |
@@ -8079,31 +11136,142 @@ |
NodeList<VariableDeclaration> variables2 = node.variables; |
for (VariableDeclaration variable in variables2) { |
if (variable.initializer == null) { |
- _errorReporter.reportError(CompileTimeErrorCode.FINAL_NOT_INITIALIZED, variable, [variable.name.name]); |
+ _errorReporter.reportError2(CompileTimeErrorCode.FINAL_NOT_INITIALIZED, variable, [variable.name.name]); |
foundError = true; |
} |
} |
} |
return foundError; |
} |
+ |
/** |
* This verifies that the passed implements clause does not implement classes such as 'num' or |
* 'String'. |
* @param node the implements clause to test |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see CompileTimeErrorCode#IMPLEMENTS_DISALLOWED_CLASS |
*/ |
bool checkForImplementsDisallowedClass(ImplementsClause implementsClause) { |
+ if (implementsClause == null) { |
+ return false; |
+ } |
bool foundError = false; |
for (TypeName type in implementsClause.interfaces) { |
foundError = javaBooleanOr(foundError, checkForExtendsOrImplementsDisallowedClass(type, CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS)); |
} |
return foundError; |
} |
+ |
/** |
+ * This verifies that if the passed identifier is part of constructor initializer, then it does |
+ * not reference implicitly 'this' expression. |
+ * @param node the simple identifier to test |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#IMPLICIT_THIS_REFERENCE_IN_INITIALIZER |
+ */ |
+ bool checkForImplicitThisReferenceInInitializer(SimpleIdentifier node) { |
+ if (!_isInConstructorInitializer) { |
+ return false; |
+ } |
+ Element element2 = node.element; |
+ if (!(element2 is MethodElement || element2 is PropertyAccessorElement)) { |
+ return false; |
+ } |
+ ExecutableElement executableElement = element2 as ExecutableElement; |
+ if (executableElement.isStatic()) { |
+ return false; |
+ } |
+ Element enclosingElement2 = element2.enclosingElement; |
+ if (enclosingElement2 is! ClassElement) { |
+ return false; |
+ } |
+ ASTNode parent2 = node.parent; |
+ if (parent2 is MethodInvocation) { |
+ MethodInvocation invocation = parent2 as MethodInvocation; |
+ if (identical(invocation.methodName, node) && invocation.realTarget != null) { |
+ return false; |
+ } |
+ } |
+ { |
+ if (parent2 is PropertyAccess) { |
+ PropertyAccess access = parent2 as PropertyAccess; |
+ if (identical(access.propertyName, node) && access.realTarget != null) { |
+ return false; |
+ } |
+ } |
+ if (parent2 is PrefixedIdentifier) { |
+ PrefixedIdentifier prefixed = parent2 as PrefixedIdentifier; |
+ if (identical(prefixed.identifier, node)) { |
+ return false; |
+ } |
+ } |
+ } |
+ _errorReporter.reportError2(CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER, node, []); |
+ return true; |
+ } |
+ |
+ /** |
+ * This verifies the passed import has unique name among other imported libraries. |
+ * @param node the import directive to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#IMPORT_DUPLICATED_LIBRARY_NAME |
+ */ |
+ bool checkForImportDuplicateLibraryName(ImportDirective node) { |
+ Element nodeElement = node.element; |
+ if (nodeElement is! ImportElement) { |
+ return false; |
+ } |
+ ImportElement nodeImportElement = nodeElement as ImportElement; |
+ LibraryElement nodeLibrary = nodeImportElement.importedLibrary; |
+ if (nodeLibrary == null) { |
+ return false; |
+ } |
+ String name2 = nodeLibrary.name; |
+ LibraryElement prevLibrary = _nameToImportElement[name2]; |
+ if (prevLibrary != null) { |
+ if (prevLibrary != nodeLibrary) { |
+ _errorReporter.reportError2(StaticWarningCode.IMPORT_DUPLICATED_LIBRARY_NAME, node, [prevLibrary.definingCompilationUnit.displayName, nodeLibrary.definingCompilationUnit.displayName, name2]); |
+ return true; |
+ } |
+ } else { |
+ _nameToImportElement[name2] = nodeLibrary; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Check that if the visiting library is not system, then any passed library should not be SDK |
+ * internal library. |
+ * @param node the import directive to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#IMPORT_INTERNAL_LIBRARY |
+ */ |
+ bool checkForImportInternalLibrary(ImportDirective node) { |
+ if (_isInSystemLibrary) { |
+ return false; |
+ } |
+ Element element2 = node.element; |
+ if (element2 is! ImportElement) { |
+ return false; |
+ } |
+ ImportElement importElement = element2 as ImportElement; |
+ DartSdk sdk = _currentLibrary.context.sourceFactory.dartSdk; |
+ String uri2 = importElement.uri; |
+ SdkLibrary sdkLibrary = sdk.getSdkLibrary(uri2); |
+ if (sdkLibrary == null) { |
+ return false; |
+ } |
+ if (!sdkLibrary.isInternal()) { |
+ return false; |
+ } |
+ _errorReporter.reportError2(CompileTimeErrorCode.IMPORT_INTERNAL_LIBRARY, node, [node.uri]); |
+ return true; |
+ } |
+ |
+ /** |
* This verifies that the passed switch statement case expressions all have the same type. |
* @param node the switch statement to evaluate |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see CompileTimeErrorCode#INCONSISTENT_CASE_EXPRESSION_TYPES |
*/ |
bool checkForInconsistentCaseExpressionTypes(SwitchStatement node) { |
@@ -8115,11 +11283,11 @@ |
SwitchCase switchCase = switchMember as SwitchCase; |
Expression expression2 = switchCase.expression; |
if (firstType == null) { |
- firstType = expression2.staticType; |
+ firstType = getBestType(expression2); |
} else { |
- Type2 nType = expression2.staticType; |
+ Type2 nType = getBestType(expression2); |
if (firstType != nType) { |
- _errorReporter.reportError(CompileTimeErrorCode.INCONSISTENT_CASE_EXPRESSION_TYPES, expression2, [expression2.toSource(), firstType.name]); |
+ _errorReporter.reportError2(CompileTimeErrorCode.INCONSISTENT_CASE_EXPRESSION_TYPES, expression2, [expression2.toSource(), firstType.displayName]); |
foundError = true; |
} |
} |
@@ -8127,39 +11295,115 @@ |
} |
return foundError; |
} |
+ |
/** |
- * This verifies that the passed assignment expression represents a valid assignment. |
- * @param node the assignment expression to evaluate |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * For each class declaration, this method is called which verifies that all inherited members are |
+ * inherited consistently. |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticTypeWarningCode#INCONSISTENT_METHOD_INHERITANCE |
+ */ |
+ bool checkForInconsistentMethodInheritance() { |
+ _inheritanceManager.getMapOfMembersInheritedFromInterfaces(_enclosingClass); |
+ Set<AnalysisError> errors = _inheritanceManager.getErrors(_enclosingClass); |
+ if (errors == null || errors.isEmpty) { |
+ return false; |
+ } |
+ for (AnalysisError error in errors) { |
+ _errorReporter.reportError(error); |
+ } |
+ return true; |
+ } |
+ |
+ /** |
+ * Given an assignment using a compound assignment operator, this verifies that the given |
+ * assignment is valid. |
+ * @param node the assignment expression being tested |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see StaticTypeWarningCode#INVALID_ASSIGNMENT |
*/ |
bool checkForInvalidAssignment(AssignmentExpression node) { |
Expression lhs = node.leftHandSide; |
- Expression rhs = node.rightHandSide; |
+ if (lhs == null) { |
+ return false; |
+ } |
VariableElement leftElement = getVariableElement(lhs); |
- Type2 leftType = (leftElement == null) ? getType(lhs) : leftElement.type; |
- Type2 rightType = getType(rhs); |
+ Type2 leftType = (leftElement == null) ? getStaticType(lhs) : leftElement.type; |
+ MethodElement invokedMethod = node.element; |
+ if (invokedMethod == null) { |
+ return false; |
+ } |
+ Type2 rightType = invokedMethod.type.returnType; |
+ if (leftType == null || rightType == null) { |
+ return false; |
+ } |
if (!rightType.isAssignableTo(leftType)) { |
- _errorReporter.reportError(StaticTypeWarningCode.INVALID_ASSIGNMENT, rhs, [rightType.name, leftType.name]); |
+ _errorReporter.reportError2(StaticTypeWarningCode.INVALID_ASSIGNMENT, node.rightHandSide, [rightType.displayName, leftType.displayName]); |
return true; |
} |
return false; |
} |
+ |
/** |
+ * This verifies that the passed left hand side and right hand side represent a valid assignment. |
+ * @param lhs the left hand side expression |
+ * @param rhs the right hand side expression |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticTypeWarningCode#INVALID_ASSIGNMENT |
+ */ |
+ bool checkForInvalidAssignment2(Expression lhs, Expression rhs) { |
+ if (lhs == null || rhs == null) { |
+ return false; |
+ } |
+ VariableElement leftElement = getVariableElement(lhs); |
+ Type2 leftType = (leftElement == null) ? getStaticType(lhs) : leftElement.type; |
+ Type2 staticRightType = getStaticType(rhs); |
+ bool isStaticAssignable = staticRightType.isAssignableTo(leftType); |
+ Type2 propagatedRightType = getPropagatedType(rhs); |
+ if (propagatedRightType == null) { |
+ if (!isStaticAssignable) { |
+ _errorReporter.reportError2(StaticTypeWarningCode.INVALID_ASSIGNMENT, rhs, [staticRightType.displayName, leftType.displayName]); |
+ return true; |
+ } |
+ } else { |
+ bool isPropagatedAssignable = propagatedRightType.isAssignableTo(leftType); |
+ if (!isStaticAssignable && !isPropagatedAssignable) { |
+ _errorReporter.reportError2(StaticTypeWarningCode.INVALID_ASSIGNMENT, rhs, [staticRightType.displayName, leftType.displayName]); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * This verifies that the usage of the passed 'this' is valid. |
+ * @param node the 'this' expression to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#INVALID_REFERENCE_TO_THIS |
+ */ |
+ bool checkForInvalidReferenceToThis(ThisExpression node) { |
+ if (!isThisInValidContext(node)) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS, node, []); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
* Checks to ensure that first type argument to a map literal must be the 'String' type. |
* @param arguments a non-{@code null}, non-empty {@link TypeName} node list from the respective{@link MapLiteral} |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see CompileTimeErrorCode#INVALID_TYPE_ARGUMENT_FOR_KEY |
*/ |
bool checkForInvalidTypeArgumentForKey(NodeList<TypeName> arguments) { |
TypeName firstArgument = arguments[0]; |
Type2 firstArgumentType = firstArgument.type; |
if (firstArgumentType != null && firstArgumentType != _typeProvider.stringType) { |
- _errorReporter.reportError(CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_FOR_KEY, firstArgument, []); |
+ _errorReporter.reportError2(CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_FOR_KEY, firstArgument, []); |
return true; |
} |
return false; |
} |
+ |
/** |
* Checks to ensure that the passed {@link ListLiteral} or {@link MapLiteral} does not have a type |
* parameter as a type argument. |
@@ -8171,64 +11415,377 @@ |
bool foundError = false; |
for (TypeName typeName in arguments) { |
if (typeName.type is TypeVariableType) { |
- _errorReporter.reportError(errorCode, typeName, [typeName.name]); |
+ _errorReporter.reportError2(errorCode, typeName, [typeName.name]); |
foundError = true; |
} |
} |
return foundError; |
} |
+ |
/** |
+ * This verifies that the {@link #enclosingClass} does not define members with the same name as |
+ * the enclosing class. |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#MEMBER_WITH_CLASS_NAME |
+ */ |
+ bool checkForMemberWithClassName() { |
+ if (_enclosingClass == null) { |
+ return false; |
+ } |
+ String className = _enclosingClass.name; |
+ if (className == null) { |
+ return false; |
+ } |
+ bool problemReported = false; |
+ for (PropertyAccessorElement accessor in _enclosingClass.accessors) { |
+ if (className == accessor.name) { |
+ _errorReporter.reportError3(CompileTimeErrorCode.MEMBER_WITH_CLASS_NAME, accessor.nameOffset, className.length, []); |
+ problemReported = true; |
+ } |
+ } |
+ return problemReported; |
+ } |
+ |
+ /** |
+ * Check to make sure that all similarly typed accessors are of the same type (including inherited |
+ * accessors). |
+ * @param node The accessor currently being visited. |
+ */ |
+ void checkForMismatchedAccessorTypes(Declaration accessorDeclaration, String accessorTextName) { |
+ PropertyAccessorElement counterpartAccessor = null; |
+ ExecutableElement accessorElement = accessorDeclaration.element as ExecutableElement; |
+ if (accessorElement is! PropertyAccessorElement) { |
+ return; |
+ } |
+ PropertyAccessorElement propertyAccessorElement = accessorElement as PropertyAccessorElement; |
+ counterpartAccessor = propertyAccessorElement.correspondingSetter; |
+ if (counterpartAccessor == null) { |
+ return; |
+ } |
+ Type2 getterType = null; |
+ Type2 setterType = null; |
+ if (propertyAccessorElement.isGetter()) { |
+ getterType = getGetterType(propertyAccessorElement); |
+ setterType = getSetterType(counterpartAccessor); |
+ } else if (propertyAccessorElement.isSetter()) { |
+ setterType = getSetterType(propertyAccessorElement); |
+ counterpartAccessor = propertyAccessorElement.correspondingGetter; |
+ getterType = getGetterType(counterpartAccessor); |
+ } |
+ if (setterType != null && getterType != null && !getterType.isAssignableTo(setterType)) { |
+ _errorReporter.reportError2(StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES, accessorDeclaration, [accessorTextName, setterType.displayName, getterType.displayName]); |
+ } |
+ } |
+ |
+ /** |
+ * This verifies that the passed mixin does not have an explicitly declared constructor. |
+ * @param mixinName the node to report problem on |
+ * @param mixinElement the mixing to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#MIXIN_DECLARES_CONSTRUCTOR |
+ */ |
+ bool checkForMixinDeclaresConstructor(TypeName mixinName, ClassElement mixinElement) { |
+ for (ConstructorElement constructor in mixinElement.constructors) { |
+ if (!constructor.isSynthetic() && !constructor.isFactory()) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR, mixinName, [mixinElement.name]); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * This verifies that the passed mixin has the 'Object' superclass. |
+ * @param mixinName the node to report problem on |
+ * @param mixinElement the mixing to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#MIXIN_INHERITS_FROM_NOT_OBJECT |
+ */ |
+ bool checkForMixinInheritsNotFromObject(TypeName mixinName, ClassElement mixinElement) { |
+ InterfaceType mixinSupertype = mixinElement.supertype; |
+ if (mixinSupertype != null) { |
+ if (!mixinSupertype.isObject() || !mixinElement.isTypedef() && mixinElement.mixins.length != 0) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT, mixinName, [mixinElement.name]); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * This verifies that the passed mixin does not reference 'super'. |
+ * @param mixinName the node to report problem on |
+ * @param mixinElement the mixing to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#MIXIN_REFERENCES_SUPER |
+ */ |
+ bool checkForMixinReferencesSuper(TypeName mixinName, ClassElement mixinElement) { |
+ if (mixinElement.hasReferenceToSuper()) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.MIXIN_REFERENCES_SUPER, mixinName, [mixinElement.name]); |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * This verifies that the passed constructor has at most one 'super' initializer. |
+ * @param node the constructor declaration to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#MULTIPLE_SUPER_INITIALIZERS |
+ */ |
+ bool checkForMultipleSuperInitializers(ConstructorDeclaration node) { |
+ int numSuperInitializers = 0; |
+ for (ConstructorInitializer initializer in node.initializers) { |
+ if (initializer is SuperConstructorInvocation) { |
+ numSuperInitializers++; |
+ if (numSuperInitializers > 1) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.MULTIPLE_SUPER_INITIALIZERS, initializer, []); |
+ } |
+ } |
+ } |
+ return numSuperInitializers > 0; |
+ } |
+ |
+ /** |
* Checks to ensure that native function bodies can only in SDK code. |
* @param node the native function body to test |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see ParserErrorCode#NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE |
*/ |
bool checkForNativeFunctionBodyInNonSDKCode(NativeFunctionBody node) { |
if (!_isInSystemLibrary) { |
- _errorReporter.reportError(ParserErrorCode.NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE, node, []); |
+ _errorReporter.reportError2(ParserErrorCode.NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE, node, []); |
return true; |
} |
return false; |
} |
+ |
/** |
+ * This verifies that the passed 'new' instance creation expression invokes existing constructor. |
+ * <p> |
+ * This method assumes that the instance creation was tested to be 'new' before being called. |
+ * @param node the instance creation expression to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#NEW_WITH_UNDEFINED_CONSTRUCTOR |
+ */ |
+ bool checkForNewWithUndefinedConstructor(InstanceCreationExpression node) { |
+ if (node.element != null) { |
+ return false; |
+ } |
+ ConstructorName constructorName2 = node.constructorName; |
+ if (constructorName2 == null) { |
+ return false; |
+ } |
+ TypeName type2 = constructorName2.type; |
+ if (type2 == null) { |
+ return false; |
+ } |
+ Identifier className = type2.name; |
+ SimpleIdentifier name2 = constructorName2.name; |
+ if (name2 != null) { |
+ _errorReporter.reportError2(StaticWarningCode.NEW_WITH_UNDEFINED_CONSTRUCTOR, name2, [className, name2]); |
+ } else { |
+ _errorReporter.reportError2(StaticWarningCode.NEW_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT, constructorName2, [className]); |
+ } |
+ return true; |
+ } |
+ |
+ /** |
+ * This checks that passed class declaration overrides all members required by its superclasses |
+ * and interfaces. |
+ * @param node the {@link ClassDeclaration} to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE |
+ * @see StaticWarningCode#NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_TWO |
+ * @see StaticWarningCode#NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_THREE |
+ * @see StaticWarningCode#NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FOUR |
+ * @see StaticWarningCode#NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FIVE_PLUS |
+ */ |
+ bool checkForNonAbstractClassInheritsAbstractMember(ClassDeclaration node) { |
+ if (_enclosingClass.isAbstract()) { |
+ return false; |
+ } |
+ Set<ExecutableElement> missingOverrides = new Set<ExecutableElement>(); |
+ Set<String> methodsInEnclosingClass = new Set<String>(); |
+ Set<String> accessorsInEnclosingClass = new Set<String>(); |
+ List<MethodElement> methods2 = _enclosingClass.methods; |
+ for (MethodElement method in methods2) { |
+ javaSetAdd(methodsInEnclosingClass, method.name); |
+ } |
+ List<PropertyAccessorElement> accessors2 = _enclosingClass.accessors; |
+ for (PropertyAccessorElement accessor in accessors2) { |
+ javaSetAdd(accessorsInEnclosingClass, accessor.name); |
+ } |
+ Map<String, ExecutableElement> membersInheritedFromSuperclasses = _inheritanceManager.getMapOfMembersInheritedFromClasses(_enclosingClass); |
+ for (MapEntry<String, ExecutableElement> entry in getMapEntrySet(membersInheritedFromSuperclasses)) { |
+ ExecutableElement executableElt = entry.getValue(); |
+ if (executableElt is MethodElement) { |
+ MethodElement methodElt = executableElt as MethodElement; |
+ if (methodElt.isAbstract()) { |
+ String methodName = entry.getKey(); |
+ if (!methodsInEnclosingClass.contains(methodName)) { |
+ javaSetAdd(missingOverrides, executableElt); |
+ } |
+ } |
+ } else if (executableElt is PropertyAccessorElement) { |
+ PropertyAccessorElement propertyAccessorElt = executableElt as PropertyAccessorElement; |
+ if (propertyAccessorElt.isAbstract()) { |
+ String accessorName = entry.getKey(); |
+ if (!accessorsInEnclosingClass.contains(accessorName)) { |
+ javaSetAdd(missingOverrides, executableElt); |
+ } |
+ } |
+ } |
+ } |
+ Map<String, ExecutableElement> membersInheritedFromInterfaces = _inheritanceManager.getMapOfMembersInheritedFromInterfaces(_enclosingClass); |
+ for (MapEntry<String, ExecutableElement> entry in getMapEntrySet(membersInheritedFromInterfaces)) { |
+ ExecutableElement executableElt = entry.getValue(); |
+ ExecutableElement elt = membersInheritedFromSuperclasses[executableElt.name]; |
+ if (elt != null) { |
+ if (elt is MethodElement && !((elt as MethodElement)).isAbstract()) { |
+ continue; |
+ } else if (elt is PropertyAccessorElement && !((elt as PropertyAccessorElement)).isAbstract()) { |
+ continue; |
+ } |
+ } |
+ if (executableElt is MethodElement) { |
+ String methodName = entry.getKey(); |
+ if (!methodsInEnclosingClass.contains(methodName)) { |
+ javaSetAdd(missingOverrides, executableElt); |
+ } |
+ } else if (executableElt is PropertyAccessorElement) { |
+ String accessorName = entry.getKey(); |
+ if (!accessorsInEnclosingClass.contains(accessorName)) { |
+ javaSetAdd(missingOverrides, executableElt); |
+ } |
+ } |
+ } |
+ int missingOverridesSize = missingOverrides.length; |
+ if (missingOverridesSize == 0) { |
+ return false; |
+ } |
+ List<ExecutableElement> missingOverridesArray = new List.from(missingOverrides); |
+ if (missingOverridesSize == 1) { |
+ _errorReporter.reportError2(StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE, node.name, [missingOverridesArray[0].enclosingElement.displayName, missingOverridesArray[0].displayName]); |
+ } else if (missingOverridesSize == 2) { |
+ _errorReporter.reportError2(StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_TWO, node.name, [missingOverridesArray[0].enclosingElement.displayName, missingOverridesArray[0].displayName, missingOverridesArray[1].enclosingElement.displayName, missingOverridesArray[1].displayName]); |
+ } else if (missingOverridesSize == 3) { |
+ _errorReporter.reportError2(StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_THREE, node.name, [missingOverridesArray[0].enclosingElement.displayName, missingOverridesArray[0].displayName, missingOverridesArray[1].enclosingElement.displayName, missingOverridesArray[1].displayName, missingOverridesArray[2].enclosingElement.displayName, missingOverridesArray[2].displayName]); |
+ } else if (missingOverridesSize == 4) { |
+ _errorReporter.reportError2(StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FOUR, node.name, [missingOverridesArray[0].enclosingElement.displayName, missingOverridesArray[0].displayName, missingOverridesArray[1].enclosingElement.displayName, missingOverridesArray[1].displayName, missingOverridesArray[2].enclosingElement.displayName, missingOverridesArray[2].displayName, missingOverridesArray[3].enclosingElement.displayName, missingOverridesArray[3].displayName]); |
+ } else { |
+ _errorReporter.reportError2(StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FIVE_PLUS, node.name, [missingOverridesArray[0].enclosingElement.displayName, missingOverridesArray[0].displayName, missingOverridesArray[1].enclosingElement.displayName, missingOverridesArray[1].displayName, missingOverridesArray[2].enclosingElement.displayName, missingOverridesArray[2].displayName, missingOverridesArray[3].enclosingElement.displayName, missingOverridesArray[3].displayName, missingOverridesArray.length - 4]); |
+ } |
+ return true; |
+ } |
+ |
+ /** |
* Checks to ensure that the expressions that need to be of type bool, are. Otherwise an error is |
* reported on the expression. |
* @param condition the conditional expression to test |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see StaticTypeWarningCode#NON_BOOL_CONDITION |
*/ |
bool checkForNonBoolCondition(Expression condition) { |
- Type2 conditionType = getType(condition); |
+ Type2 conditionType = getStaticType(condition); |
if (conditionType != null && !conditionType.isAssignableTo(_typeProvider.boolType)) { |
- _errorReporter.reportError(StaticTypeWarningCode.NON_BOOL_CONDITION, condition, []); |
+ _errorReporter.reportError2(StaticTypeWarningCode.NON_BOOL_CONDITION, condition, []); |
return true; |
} |
return false; |
} |
+ |
/** |
* This verifies that the passed assert statement has either a 'bool' or '() -> bool' input. |
* @param node the assert statement to evaluate |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see StaticTypeWarningCode#NON_BOOL_EXPRESSION |
*/ |
bool checkForNonBoolExpression(AssertStatement node) { |
Expression expression = node.condition; |
- Type2 type = getType(expression); |
+ Type2 type = getStaticType(expression); |
if (type is InterfaceType) { |
if (!type.isAssignableTo(_typeProvider.boolType)) { |
- _errorReporter.reportError(StaticTypeWarningCode.NON_BOOL_EXPRESSION, expression, []); |
+ _errorReporter.reportError2(StaticTypeWarningCode.NON_BOOL_EXPRESSION, expression, []); |
return true; |
} |
} else if (type is FunctionType) { |
FunctionType functionType = type as FunctionType; |
if (functionType.typeArguments.length == 0 && !functionType.returnType.isAssignableTo(_typeProvider.boolType)) { |
- _errorReporter.reportError(StaticTypeWarningCode.NON_BOOL_EXPRESSION, expression, []); |
+ _errorReporter.reportError2(StaticTypeWarningCode.NON_BOOL_EXPRESSION, expression, []); |
return true; |
} |
} |
return false; |
} |
+ |
/** |
+ * This verifies the passed map literal either: |
+ * <ul> |
+ * <li>has {@code const modifier}</li> |
+ * <li>has explicit type arguments</li> |
+ * <li>is not start of the statement</li> |
+ * <ul> |
+ * @param node the map literal to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#NON_CONST_MAP_AS_EXPRESSION_STATEMENT |
+ */ |
+ bool checkForNonConstMapAsExpressionStatement(MapLiteral node) { |
+ if (node.modifier != null) { |
+ return false; |
+ } |
+ if (node.typeArguments != null) { |
+ return false; |
+ } |
+ Statement statement = node.getAncestor(ExpressionStatement); |
+ if (statement == null) { |
+ return false; |
+ } |
+ if (statement.beginToken != node.beginToken) { |
+ return false; |
+ } |
+ _errorReporter.reportError2(CompileTimeErrorCode.NON_CONST_MAP_AS_EXPRESSION_STATEMENT, node, []); |
+ return true; |
+ } |
+ |
+ /** |
+ * This verifies the passed method declaration of operator {@code \[\]=}, has {@code void} return |
+ * type. |
+ * @param node the method declaration to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#NON_VOID_RETURN_FOR_OPERATOR |
+ */ |
+ bool checkForNonVoidReturnTypeForOperator(MethodDeclaration node) { |
+ SimpleIdentifier name2 = node.name; |
+ if (name2.name != "[]=") { |
+ return false; |
+ } |
+ TypeName typeName = node.returnType; |
+ if (typeName != null) { |
+ Type2 type2 = typeName.type; |
+ if (type2 != null && !type2.isVoid()) { |
+ _errorReporter.reportError2(StaticWarningCode.NON_VOID_RETURN_FOR_OPERATOR, typeName, []); |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * This verifies the passed setter has no return type or the {@code void} return type. |
+ * @param typeName the type name to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#NON_VOID_RETURN_FOR_SETTER |
+ */ |
+ bool checkForNonVoidReturnTypeForSetter(TypeName typeName) { |
+ if (typeName != null) { |
+ Type2 type2 = typeName.type; |
+ if (type2 != null && !type2.isVoid()) { |
+ _errorReporter.reportError2(StaticWarningCode.NON_VOID_RETURN_FOR_SETTER, typeName, []); |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
* This verifies the passed operator-method declaration, does not have an optional parameter. |
* <p> |
* This method assumes that the method declaration was tested to be an operator declaration before |
@@ -8246,12 +11803,13 @@ |
NodeList<FormalParameter> formalParameters = parameterList.parameters; |
for (FormalParameter formalParameter in formalParameters) { |
if (formalParameter.kind.isOptional()) { |
- _errorReporter.reportError(CompileTimeErrorCode.OPTIONAL_PARAMETER_IN_OPERATOR, formalParameter, []); |
+ _errorReporter.reportError2(CompileTimeErrorCode.OPTIONAL_PARAMETER_IN_OPERATOR, formalParameter, []); |
foundError = true; |
} |
} |
return foundError; |
} |
+ |
/** |
* This checks for named optional parameters that begin with '_'. |
* @param node the default formal parameter to evaluate |
@@ -8264,13 +11822,267 @@ |
NormalFormalParameter parameter2 = node.parameter; |
SimpleIdentifier name = parameter2.identifier; |
if (!name.isSynthetic() && name.name.startsWith("_")) { |
- _errorReporter.reportError(CompileTimeErrorCode.PRIVATE_OPTIONAL_PARAMETER, node, []); |
+ _errorReporter.reportError2(CompileTimeErrorCode.PRIVATE_OPTIONAL_PARAMETER, node, []); |
return true; |
} |
} |
return false; |
} |
+ |
/** |
+ * This checks if the passed constructor declaration is the redirecting generative constructor and |
+ * references itself directly or indirectly. |
+ * @param node the constructor declaration to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#RECURSIVE_CONSTRUCTOR_REDIRECT |
+ */ |
+ bool checkForRecursiveConstructorRedirect(ConstructorDeclaration node) { |
+ if (node.factoryKeyword != null) { |
+ return false; |
+ } |
+ for (ConstructorInitializer initializer in node.initializers) { |
+ if (initializer is RedirectingConstructorInvocation) { |
+ ConstructorElement element2 = node.element; |
+ if (!hasRedirectingFactoryConstructorCycle(element2)) { |
+ return false; |
+ } |
+ _errorReporter.reportError2(CompileTimeErrorCode.RECURSIVE_CONSTRUCTOR_REDIRECT, initializer, []); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * This checks if the passed constructor declaration has redirected constructor and references |
+ * itself directly or indirectly. |
+ * @param node the constructor declaration to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#RECURSIVE_FACTORY_REDIRECT |
+ */ |
+ bool checkForRecursiveFactoryRedirect(ConstructorDeclaration node) { |
+ ConstructorName redirectedConstructorNode = node.redirectedConstructor; |
+ if (redirectedConstructorNode == null) { |
+ return false; |
+ } |
+ ConstructorElement element2 = node.element; |
+ if (!hasRedirectingFactoryConstructorCycle(element2)) { |
+ return false; |
+ } |
+ _errorReporter.reportError2(CompileTimeErrorCode.RECURSIVE_FACTORY_REDIRECT, redirectedConstructorNode, []); |
+ return true; |
+ } |
+ |
+ /** |
+ * This checks the class declaration is not a superinterface to itself. |
+ * @param classElt the class element to test |
+ * @param list a list containing the potentially cyclic implements path |
+ * @return {@code true} if and only if an error code is generated on the passed element |
+ * @see CompileTimeErrorCode#RECURSIVE_INTERFACE_INHERITANCE |
+ * @see CompileTimeErrorCode#RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS |
+ * @see CompileTimeErrorCode#RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS |
+ */ |
+ bool checkForRecursiveInterfaceInheritance(ClassElement classElt, List<ClassElement> list) { |
+ if (classElt == null) { |
+ return false; |
+ } |
+ InterfaceType supertype2 = classElt.supertype; |
+ list.add(classElt); |
+ if (list.length != 1 && _enclosingClass == classElt) { |
+ String enclosingClassName = _enclosingClass.displayName; |
+ if (list.length > 2) { |
+ String separator = ", "; |
+ int listLength = list.length; |
+ JavaStringBuilder builder = new JavaStringBuilder(); |
+ for (int i = 0; i < listLength; i++) { |
+ builder.append(list[i].displayName); |
+ if (i != listLength - 1) { |
+ builder.append(separator); |
+ } |
+ } |
+ _errorReporter.reportError3(CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE, _enclosingClass.nameOffset, enclosingClassName.length, [enclosingClassName, builder.toString()]); |
+ return true; |
+ } else if (list.length == 2) { |
+ ErrorCode errorCode = supertype2 != null && _enclosingClass == supertype2.element ? CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS : CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS; |
+ _errorReporter.reportError3(errorCode, _enclosingClass.nameOffset, enclosingClassName.length, [enclosingClassName]); |
+ return true; |
+ } |
+ } |
+ for (int i = 1; i < list.length - 1; i++) { |
+ if (classElt == list[i]) { |
+ list.removeAt(list.length - 1); |
+ return false; |
+ } |
+ } |
+ List<ClassElement> interfaceElements; |
+ List<InterfaceType> interfaceTypes = classElt.interfaces; |
+ if (supertype2 != null && !supertype2.isObject()) { |
+ interfaceElements = new List<ClassElement>(interfaceTypes.length + 1); |
+ interfaceElements[0] = supertype2.element; |
+ for (int i = 0; i < interfaceTypes.length; i++) { |
+ interfaceElements[i + 1] = interfaceTypes[i].element; |
+ } |
+ } else { |
+ interfaceElements = new List<ClassElement>(interfaceTypes.length); |
+ for (int i = 0; i < interfaceTypes.length; i++) { |
+ interfaceElements[i] = interfaceTypes[i].element; |
+ } |
+ } |
+ for (ClassElement classElt2 in interfaceElements) { |
+ if (checkForRecursiveInterfaceInheritance(classElt2, list)) { |
+ return true; |
+ } |
+ } |
+ list.removeAt(list.length - 1); |
+ return false; |
+ } |
+ |
+ /** |
+ * This checks the passed constructor declaration has a valid combination of redirected |
+ * constructor invocation(s), super constructor invocations and field initializers. |
+ * @param node the constructor declaration to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#MULTIPLE_REDIRECTING_CONSTRUCTOR_INVOCATIONS |
+ * @see CompileTimeErrorCode#SUPER_IN_REDIRECTING_CONSTRUCTOR |
+ * @see CompileTimeErrorCode#FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR |
+ */ |
+ bool checkForRedirectingConstructorErrorCodes(ConstructorDeclaration node) { |
+ int numProblems = 0; |
+ int numRedirections = 0; |
+ for (ConstructorInitializer initializer in node.initializers) { |
+ if (initializer is RedirectingConstructorInvocation) { |
+ if (numRedirections > 0) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.MULTIPLE_REDIRECTING_CONSTRUCTOR_INVOCATIONS, initializer, []); |
+ numProblems++; |
+ } |
+ numRedirections++; |
+ } |
+ } |
+ if (numRedirections > 0) { |
+ for (ConstructorInitializer initializer in node.initializers) { |
+ if (initializer is SuperConstructorInvocation) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.SUPER_IN_REDIRECTING_CONSTRUCTOR, initializer, []); |
+ numProblems++; |
+ } |
+ if (initializer is ConstructorFieldInitializer) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR, initializer, []); |
+ numProblems++; |
+ } |
+ } |
+ } |
+ return numProblems != 0; |
+ } |
+ |
+ /** |
+ * This checks if the passed constructor declaration has redirected constructor with compatible |
+ * function type. |
+ * @param node the constructor declaration to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#REDIRECT_TO_INVALID_RETURN_TYPE |
+ * @see StaticWarningCode#REDIRECT_TO_INVALID_FUNCTION_TYPE |
+ */ |
+ bool checkForRedirectToInvalidFunction(ConstructorDeclaration node) { |
+ ConstructorName redirectedNode = node.redirectedConstructor; |
+ if (redirectedNode == null) { |
+ return false; |
+ } |
+ ConstructorElement redirectedElement = redirectedNode.element; |
+ if (redirectedElement == null) { |
+ return false; |
+ } |
+ FunctionType redirectedType = redirectedElement.type; |
+ Type2 redirectedReturnType = redirectedType.returnType; |
+ FunctionType constructorType = node.element.type; |
+ Type2 constructorReturnType = constructorType.returnType; |
+ if (!redirectedReturnType.isSubtypeOf(constructorReturnType)) { |
+ _errorReporter.reportError2(StaticWarningCode.REDIRECT_TO_INVALID_RETURN_TYPE, redirectedNode, [redirectedReturnType, constructorReturnType]); |
+ return true; |
+ } |
+ if (!redirectedType.isSubtypeOf(constructorType)) { |
+ _errorReporter.reportError2(StaticWarningCode.REDIRECT_TO_INVALID_FUNCTION_TYPE, redirectedNode, [redirectedType, constructorType]); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * This checks if the passed constructor declaration has redirected constructor and references |
+ * itself directly or indirectly. TODO(scheglov) |
+ * @param node the constructor declaration to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#REDIRECT_TO_NON_CONST_CONSTRUCTOR |
+ */ |
+ bool checkForRedirectToNonConstConstructor(ConstructorDeclaration node) { |
+ ConstructorName redirectedConstructorNode = node.redirectedConstructor; |
+ if (redirectedConstructorNode == null) { |
+ return false; |
+ } |
+ ConstructorElement element2 = node.element; |
+ if (element2 == null) { |
+ return false; |
+ } |
+ if (!element2.isConst()) { |
+ return false; |
+ } |
+ ConstructorElement redirectedConstructor2 = element2.redirectedConstructor; |
+ if (redirectedConstructor2 == null) { |
+ return false; |
+ } |
+ if (redirectedConstructor2.isConst()) { |
+ return false; |
+ } |
+ _errorReporter.reportError2(CompileTimeErrorCode.REDIRECT_TO_NON_CONST_CONSTRUCTOR, redirectedConstructorNode, []); |
+ return true; |
+ } |
+ |
+ /** |
+ * This checks if the passed identifier is banned because it is part of the variable declaration |
+ * with the same name. |
+ * @param node the identifier to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#REFERENCE_TO_DECLARED_VARIABLE_IN_INITIALIZER |
+ */ |
+ bool checkForReferenceToDeclaredVariableInInitializer(SimpleIdentifier node) { |
+ ASTNode parent2 = node.parent; |
+ if (parent2 is PrefixedIdentifier) { |
+ PrefixedIdentifier prefixedIdentifier = parent2 as PrefixedIdentifier; |
+ if (identical(prefixedIdentifier.identifier, node)) { |
+ return false; |
+ } |
+ } |
+ if (parent2 is PropertyAccess) { |
+ PropertyAccess propertyAccess = parent2 as PropertyAccess; |
+ if (identical(propertyAccess.propertyName, node)) { |
+ return false; |
+ } |
+ } |
+ if (parent2 is MethodInvocation) { |
+ MethodInvocation methodInvocation = parent2 as MethodInvocation; |
+ if (methodInvocation.target != null && identical(methodInvocation.methodName, node)) { |
+ return false; |
+ } |
+ } |
+ if (parent2 is ConstructorName) { |
+ ConstructorName constructorName = parent2 as ConstructorName; |
+ if (identical(constructorName.name, node)) { |
+ return false; |
+ } |
+ } |
+ if (parent2 is Label) { |
+ Label label = parent2 as Label; |
+ if (identical(label.label, node)) { |
+ return false; |
+ } |
+ } |
+ String name2 = node.name; |
+ if (!_namesForReferenceToDeclaredVariableInInitializer.contains(name2)) { |
+ return false; |
+ } |
+ _errorReporter.reportError2(CompileTimeErrorCode.REFERENCE_TO_DECLARED_VARIABLE_IN_INITIALIZER, node, [name2]); |
+ return true; |
+ } |
+ |
+ /** |
* This checks that the rethrow is inside of a catch clause. |
* @param node the rethrow expression to evaluate |
* @return {@code true} if and only if an error code is generated on the passed node |
@@ -8278,19 +12090,74 @@ |
*/ |
bool checkForRethrowOutsideCatch(RethrowExpression node) { |
if (!_isInCatchClause) { |
- _errorReporter.reportError(CompileTimeErrorCode.RETHROW_OUTSIDE_CATCH, node, []); |
+ _errorReporter.reportError2(CompileTimeErrorCode.RETHROW_OUTSIDE_CATCH, node, []); |
return true; |
} |
return false; |
} |
+ |
/** |
+ * This checks that if the given "target" is the type reference then the "name" is not the |
+ * reference to a instance member. |
+ * @param target the target of the name access to evaluate |
+ * @param name the accessed name to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#STATIC_ACCESS_TO_INSTANCE_MEMBER |
+ */ |
+ bool checkForStaticAccessToInstanceMember(Expression target, SimpleIdentifier name2) { |
+ Element element2 = name2.element; |
+ if (element2 is! ExecutableElement) { |
+ return false; |
+ } |
+ ExecutableElement memberElement = element2 as ExecutableElement; |
+ if (memberElement.isStatic()) { |
+ return false; |
+ } |
+ if (!isTypeReference(target)) { |
+ return false; |
+ } |
+ _errorReporter.reportError2(StaticWarningCode.STATIC_ACCESS_TO_INSTANCE_MEMBER, name2, [name2.name]); |
+ return true; |
+ } |
+ |
+ /** |
+ * This checks that the type of the passed 'switch' expression is assignable to the type of the |
+ * 'case' members. |
+ * @param node the 'switch' statement to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see StaticWarningCode#SWITCH_EXPRESSION_NOT_ASSIGNABLE |
+ */ |
+ bool checkForSwitchExpressionNotAssignable(SwitchStatement node) { |
+ Expression expression2 = node.expression; |
+ Type2 expressionType = getStaticType(expression2); |
+ if (expressionType == null) { |
+ return false; |
+ } |
+ NodeList<SwitchMember> members2 = node.members; |
+ for (SwitchMember switchMember in members2) { |
+ if (switchMember is! SwitchCase) { |
+ continue; |
+ } |
+ SwitchCase switchCase = switchMember as SwitchCase; |
+ Expression caseExpression = switchCase.expression; |
+ Type2 caseType = getStaticType(caseExpression); |
+ if (expressionType.isAssignableTo(caseType)) { |
+ return false; |
+ } |
+ _errorReporter.reportError2(StaticWarningCode.SWITCH_EXPRESSION_NOT_ASSIGNABLE, expression2, [expressionType, caseType]); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
* This verifies that the type arguments in the passed instance creation expression are all within |
* their bounds as specified by the class element where the constructor \[that is being invoked\] is |
* declared. |
* @param node the instance creation expression to evaluate |
* @param typeName the {@link TypeName} of the {@link ConstructorName} from the{@link InstanceCreationExpression}, this is the AST node that the error is attached to |
* @param constructorElement the {@link ConstructorElement} from the instance creation expression |
- * @return return {@code true} if and only if an error code is generated on the passed node |
+ * @return {@code true} if and only if an error code is generated on the passed node |
* @see StaticTypeWarningCode#TYPE_ARGUMENT_NOT_MATCHING_BOUNDS |
*/ |
bool checkForTypeArgumentNotMatchingBounds(InstanceCreationExpression node, ConstructorElement constructorElement, TypeName typeName) { |
@@ -8304,7 +12171,7 @@ |
Type2 boundType = boundingElts[i].bound; |
if (argType != null && boundType != null) { |
if (!argType.isSubtypeOf(boundType)) { |
- _errorReporter.reportError(StaticTypeWarningCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, argTypeName, [argTypeName.name, boundingElts[i].name]); |
+ _errorReporter.reportError2(StaticTypeWarningCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, argTypeName, [argTypeName.name, boundingElts[i].displayName]); |
return true; |
} |
} |
@@ -8312,36 +12179,168 @@ |
} |
return false; |
} |
+ |
/** |
- * This verifies if the passed setter method declaration, has only one parameter. |
+ * This checks that if the passed generative constructor has no explicit super constructor |
+ * invocation, then super class has the default generative constructor. |
+ * @param node the constructor declaration to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT |
+ * @see CompileTimeErrorCode#NON_GENERATIVE_CONSTRUCTOR |
+ */ |
+ bool checkForUndefinedConstructorInInitializerImplicit(ConstructorDeclaration node) { |
+ if (node.factoryKeyword != null) { |
+ return false; |
+ } |
+ if (_enclosingClass == null) { |
+ return false; |
+ } |
+ InterfaceType superType = _enclosingClass.supertype; |
+ if (superType == null) { |
+ return false; |
+ } |
+ ClassElement superElement = superType.element; |
+ for (ConstructorInitializer constructorInitializer in node.initializers) { |
+ if (constructorInitializer is SuperConstructorInvocation) { |
+ return false; |
+ } |
+ } |
+ ConstructorElement superDefaultConstructor = superElement.unnamedConstructor; |
+ if (superDefaultConstructor != null) { |
+ if (superDefaultConstructor.isFactory()) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR, node.returnType, [superDefaultConstructor]); |
+ return true; |
+ } |
+ return false; |
+ } |
+ _errorReporter.reportError2(CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT, node.returnType, [superElement.name]); |
+ return true; |
+ } |
+ |
+ /** |
+ * This verifies the passed operator-method declaration, has correct number of parameters. |
* <p> |
- * This method assumes that the method declaration was tested to be a setter before being called. |
+ * This method assumes that the method declaration was tested to be an operator declaration before |
+ * being called. |
* @param node the method declaration to evaluate |
- * @return return {@code true} if and only if an error code is generated on the passed node |
- * @see CompileTimeErrorCode#WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR |
*/ |
- bool checkForWrongNumberOfParametersForSetter(MethodDeclaration node) { |
+ bool checkForWrongNumberOfParametersForOperator(MethodDeclaration node) { |
FormalParameterList parameterList = node.parameters; |
if (parameterList == null) { |
return false; |
} |
- NodeList<FormalParameter> formalParameters = parameterList.parameters; |
- int numberOfParameters = formalParameters.length; |
+ int numParameters = parameterList.parameters.length; |
+ SimpleIdentifier nameNode = node.name; |
+ if (nameNode == null) { |
+ return false; |
+ } |
+ String name2 = nameNode.name; |
+ int expected = -1; |
+ if ("[]=" == name2) { |
+ expected = 2; |
+ } else if ("<" == name2 || ">" == name2 || "<=" == name2 || ">=" == name2 || "==" == name2 || "+" == name2 || "/" == name2 || "~/" == name2 || "*" == name2 || "%" == name2 || "|" == name2 || "^" == name2 || "&" == name2 || "<<" == name2 || ">>" == name2 || "[]" == name2) { |
+ expected = 1; |
+ } else if ("~" == name2) { |
+ expected = 0; |
+ } |
+ if (expected != -1 && numParameters != expected) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR, nameNode, [name2, expected, numParameters]); |
+ return true; |
+ } |
+ if ("-" == name2 && numParameters > 1) { |
+ _errorReporter.reportError2(CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR_MINUS, nameNode, [numParameters]); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * This verifies if the passed setter parameter list have only one parameter. |
+ * <p> |
+ * This method assumes that the method declaration was tested to be a setter before being called. |
+ * @param setterName the name of the setter to report problems on |
+ * @param parameterList the parameter list to evaluate |
+ * @return {@code true} if and only if an error code is generated on the passed node |
+ * @see CompileTimeErrorCode#WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER |
+ */ |
+ bool checkForWrongNumberOfParametersForSetter(SimpleIdentifier setterName, FormalParameterList parameterList) { |
+ if (setterName == null) { |
+ return false; |
+ } |
+ if (parameterList == null) { |
+ return false; |
+ } |
+ int numberOfParameters = parameterList.parameters.length; |
if (numberOfParameters != 1) { |
- _errorReporter.reportError(CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER, node.name, [numberOfParameters]); |
+ _errorReporter.reportError2(CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER, setterName, [numberOfParameters]); |
return true; |
} |
return false; |
} |
+ |
/** |
- * Return the type of the given expression that is to be used for type analysis. |
+ * Return the propagated type of the given expression, or the static type if there is no |
+ * propagated type information. |
* @param expression the expression whose type is to be returned |
- * @return the type of the given expression |
+ * @return the propagated or static type of the given expression, whichever is best |
*/ |
- Type2 getType(Expression expression) { |
+ Type2 getBestType(Expression expression) { |
+ Type2 type = getPropagatedType(expression); |
+ if (type == null) { |
+ type = getStaticType(expression); |
+ } |
+ return type; |
+ } |
+ |
+ /** |
+ * Returns the Type (return type) for a given getter. |
+ * @param propertyAccessorElement |
+ * @return The type of the given getter. |
+ */ |
+ Type2 getGetterType(PropertyAccessorElement propertyAccessorElement) { |
+ FunctionType functionType = propertyAccessorElement.type; |
+ if (functionType != null) { |
+ return functionType.returnType; |
+ } else { |
+ return null; |
+ } |
+ } |
+ |
+ /** |
+ * Return the propagated type of the given expression that is to be used for type analysis. |
+ * @param expression the expression whose type is to be returned |
+ * @return the propagated type of the given expression |
+ */ |
+ Type2 getPropagatedType(Expression expression) => expression.propagatedType; |
+ |
+ /** |
+ * Returns the Type (first and only parameter) for a given setter. |
+ * @param propertyAccessorElement |
+ * @return The type of the given setter. |
+ */ |
+ Type2 getSetterType(PropertyAccessorElement propertyAccessorElement) { |
+ List<ParameterElement> setterParameters = propertyAccessorElement.parameters; |
+ if (setterParameters.length == 0) { |
+ return null; |
+ } |
+ return setterParameters[0].type; |
+ } |
+ |
+ /** |
+ * Return the static type of the given expression that is to be used for type analysis. |
+ * @param expression the expression whose type is to be returned |
+ * @return the static type of the given expression |
+ */ |
+ Type2 getStaticType(Expression expression) { |
Type2 type = expression.staticType; |
- return type == null ? _dynamicType : type; |
+ if (type == null) { |
+ return _dynamicType; |
+ } |
+ return type; |
} |
+ |
/** |
* Return the variable element represented by the given expression, or {@code null} if there is no |
* such element. |
@@ -8357,7 +12356,51 @@ |
} |
return null; |
} |
+ |
+ /** |
+ * @return {@code true} if the given constructor redirects to itself, directly or indirectly |
+ */ |
+ bool hasRedirectingFactoryConstructorCycle(ConstructorElement element) { |
+ Set<ConstructorElement> constructors = new Set<ConstructorElement>(); |
+ ConstructorElement current = element; |
+ while (current != null) { |
+ if (constructors.contains(current)) { |
+ return identical(current, element); |
+ } |
+ javaSetAdd(constructors, current); |
+ current = current.redirectedConstructor; |
+ if (current is ConstructorMember) { |
+ current = ((current as ConstructorMember)).baseElement; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * @param node the 'this' expression to analyze |
+ * @return {@code true} if the given 'this' expression is in the valid context |
+ */ |
+ bool isThisInValidContext(ThisExpression node) { |
+ for (ASTNode n = node; n != null; n = n.parent) { |
+ if (n is CompilationUnit) { |
+ return false; |
+ } |
+ if (n is ConstructorDeclaration) { |
+ ConstructorDeclaration constructor = n as ConstructorDeclaration; |
+ return constructor.factoryKeyword == null; |
+ } |
+ if (n is ConstructorFieldInitializer) { |
+ return false; |
+ } |
+ if (n is MethodDeclaration) { |
+ MethodDeclaration method = n as MethodDeclaration; |
+ return !method.isStatic(); |
+ } |
+ } |
+ return false; |
+ } |
} |
+ |
/** |
* This enum holds one of four states of a field initialization state through a constructor |
* signature, not initialized, initialized in the field declaration, initialized in the field |
@@ -8370,14 +12413,18 @@ |
static final INIT_STATE INIT_IN_DEFAULT_VALUE = new INIT_STATE('INIT_IN_DEFAULT_VALUE', 3); |
static final INIT_STATE INIT_IN_INITIALIZERS = new INIT_STATE('INIT_IN_INITIALIZERS', 4); |
static final List<INIT_STATE> values = [NOT_INIT, INIT_IN_DECLARATION, INIT_IN_FIELD_FORMAL, INIT_IN_DEFAULT_VALUE, INIT_IN_INITIALIZERS]; |
- final String __name; |
- final int __ordinal; |
- int get ordinal => __ordinal; |
- INIT_STATE(this.__name, this.__ordinal) { |
+ |
+ /// The name of this enum constant, as declared in the enum declaration. |
+ final String name; |
+ |
+ /// The position in the enum declaration. |
+ final int ordinal; |
+ INIT_STATE(this.name, this.ordinal) { |
} |
- int compareTo(INIT_STATE other) => __ordinal - other.__ordinal; |
- String toString() => __name; |
+ int compareTo(INIT_STATE other) => ordinal - other.ordinal; |
+ String toString() => name; |
} |
+ |
/** |
* The enumeration {@code ResolverErrorCode} defines the error codes used for errors detected by the |
* resolver. The convention for this class is for the name of the error code to indicate the problem |
@@ -8390,30 +12437,35 @@ |
static final ResolverErrorCode CONTINUE_LABEL_ON_SWITCH = new ResolverErrorCode('CONTINUE_LABEL_ON_SWITCH', 1, ErrorType.COMPILE_TIME_ERROR, "A continue label resolves to switch, must be loop or switch member"); |
static final ResolverErrorCode MISSING_LIBRARY_DIRECTIVE_WITH_PART = new ResolverErrorCode('MISSING_LIBRARY_DIRECTIVE_WITH_PART', 2, ErrorType.COMPILE_TIME_ERROR, "Libraries that have parts must have a library directive"); |
static final List<ResolverErrorCode> values = [BREAK_LABEL_ON_SWITCH_MEMBER, CONTINUE_LABEL_ON_SWITCH, MISSING_LIBRARY_DIRECTIVE_WITH_PART]; |
- final String __name; |
- final int __ordinal; |
- int get ordinal => __ordinal; |
+ |
+ /// The name of this enum constant, as declared in the enum declaration. |
+ final String name; |
+ |
+ /// The position in the enum declaration. |
+ final int ordinal; |
+ |
/** |
* The type of this error. |
*/ |
ErrorType _type; |
+ |
/** |
* The message template used to create the message to be displayed for this error. |
*/ |
String _message; |
+ |
/** |
* Initialize a newly created error code to have the given type and message. |
* @param type the type of this error |
* @param message the message template used to create the message to be displayed for the error |
*/ |
- ResolverErrorCode(this.__name, this.__ordinal, ErrorType type, String message) { |
+ ResolverErrorCode(this.name, this.ordinal, ErrorType type, String message) { |
this._type = type; |
this._message = message; |
} |
ErrorSeverity get errorSeverity => _type.severity; |
String get message => _message; |
ErrorType get type => _type; |
- bool needsRecompilation() => true; |
- int compareTo(ResolverErrorCode other) => __ordinal - other.__ordinal; |
- String toString() => __name; |
-} |
+ int compareTo(ResolverErrorCode other) => ordinal - other.ordinal; |
+ String toString() => name; |
+} |