| Index: pkg/analyzer/lib/src/generated/resolver.dart
|
| diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
|
| index 3e6f1aaf36b7b3c9fcc49881c4bf781a61cfd87e..14b74b95650a91eedce2bbb78a4effa7aa1654be 100644
|
| --- a/pkg/analyzer/lib/src/generated/resolver.dart
|
| +++ b/pkg/analyzer/lib/src/generated/resolver.dart
|
| @@ -64,6 +64,14 @@ class AngularCompilationUnitBuilder {
|
| if (node is! SimpleStringLiteral) {
|
| return null;
|
| }
|
| + SimpleStringLiteral literal = node as SimpleStringLiteral;
|
| + // maybe has AngularElement
|
| + {
|
| + Element element = literal.toolkitElement;
|
| + if (element is AngularElement) {
|
| + return element;
|
| + }
|
| + }
|
| // prepare enclosing ClassDeclaration
|
| ClassDeclaration classDeclaration = node.getAncestor(ClassDeclaration);
|
| if (classDeclaration == null) {
|
| @@ -83,17 +91,17 @@ class AngularCompilationUnitBuilder {
|
| return toolkitObject;
|
| }
|
| }
|
| + // try selector
|
| + if (toolkitObject is AngularHasSelectorElement) {
|
| + AngularHasSelectorElement hasSelector = toolkitObject;
|
| + AngularSelectorElement selector = hasSelector.selector;
|
| + if (isNameCoveredByLiteral(selector, node)) {
|
| + return selector;
|
| + }
|
| + }
|
| // try properties of AngularComponentElement
|
| if (toolkitObject is AngularComponentElement) {
|
| AngularComponentElement component = toolkitObject;
|
| - // try selector
|
| - {
|
| - AngularSelectorElement selector = component.selector;
|
| - if (isNameCoveredByLiteral(selector, node)) {
|
| - return selector;
|
| - }
|
| - }
|
| - // try properties
|
| properties = component.properties;
|
| }
|
| // try properties of AngularDirectiveElement
|
| @@ -129,11 +137,17 @@ class AngularCompilationUnitBuilder {
|
| static AngularSelectorElement parseSelector(int offset, String text) {
|
| // [attribute]
|
| if (StringUtilities.startsWithChar(text, 0x5B) && StringUtilities.endsWithChar(text, 0x5D)) {
|
| - int nameOffset = offset + "[".length;
|
| + int nameOffset = offset + 1;
|
| String attributeName = text.substring(1, text.length - 1);
|
| // TODO(scheglov) report warning if there are spaces between [ and identifier
|
| return new HasAttributeSelectorElementImpl(attributeName, nameOffset);
|
| }
|
| + // .class
|
| + if (StringUtilities.startsWithChar(text, 0x2E)) {
|
| + int nameOffset = offset + 1;
|
| + String className = text.substring(1, text.length);
|
| + return new AngularHasClassSelectorElementImpl(className, nameOffset);
|
| + }
|
| // tag[attribute]
|
| if (StringUtilities.endsWithChar(text, 0x5D)) {
|
| int index = StringUtilities.indexOf1(text, 0, 0x5B);
|
| @@ -147,7 +161,7 @@ class AngularCompilationUnitBuilder {
|
| }
|
| // tag
|
| if (StringUtilities.isTagName(text)) {
|
| - return new IsTagSelectorElementImpl(text, offset);
|
| + return new AngularTagSelectorElementImpl(text, offset);
|
| }
|
| return null;
|
| }
|
| @@ -215,6 +229,11 @@ class AngularCompilationUnitBuilder {
|
| Source _source;
|
|
|
| /**
|
| + * The compilation unit with built Dart element models.
|
| + */
|
| + CompilationUnit _unit;
|
| +
|
| + /**
|
| * The [ClassDeclaration] that is currently being analyzed.
|
| */
|
| ClassDeclaration _classDeclaration;
|
| @@ -239,20 +258,21 @@ class AngularCompilationUnitBuilder {
|
| *
|
| * @param errorListener the listener to which errors will be reported.
|
| * @param source the source containing the unit that will be analyzed
|
| + * @param unit the compilation unit with built Dart element models
|
| */
|
| - AngularCompilationUnitBuilder(AnalysisErrorListener errorListener, Source source) {
|
| + AngularCompilationUnitBuilder(AnalysisErrorListener errorListener, Source source, CompilationUnit unit) {
|
| this._errorListener = errorListener;
|
| this._source = source;
|
| + this._unit = unit;
|
| }
|
|
|
| /**
|
| * Builds Angular specific element models and adds them to the existing Dart elements.
|
| - *
|
| - * @param unit the compilation unit with built Dart element models
|
| */
|
| - void build(CompilationUnit unit) {
|
| + void build() {
|
| + parseViews();
|
| // process classes
|
| - for (CompilationUnitMember unitMember in unit.declarations) {
|
| + for (CompilationUnitMember unitMember in _unit.declarations) {
|
| if (unitMember is ClassDeclaration) {
|
| this._classDeclaration = unitMember;
|
| this._classElement = _classDeclaration.element as ClassElementImpl;
|
| @@ -403,7 +423,8 @@ class AngularCompilationUnitBuilder {
|
| element.templateUriOffset = templateUriOffset;
|
| element.styleUri = styleUri;
|
| element.styleUriOffset = styleUriOffset;
|
| - element.properties = parseNgComponentProperties(true);
|
| + element.properties = parseNgComponentProperties();
|
| + element.scopeProperties = parseScopeProperties();
|
| _classToolkitObjects.add(element);
|
| }
|
| }
|
| @@ -411,12 +432,10 @@ class AngularCompilationUnitBuilder {
|
| /**
|
| * Parses [AngularPropertyElement]s from [annotation] and [classDeclaration].
|
| */
|
| - List<AngularPropertyElement> parseNgComponentProperties(bool fromFields) {
|
| + List<AngularPropertyElement> parseNgComponentProperties() {
|
| List<AngularPropertyElement> properties = [];
|
| parseNgComponentProperties_fromMap(properties);
|
| - if (fromFields) {
|
| - parseNgComponentProperties_fromFields(properties);
|
| - }
|
| + parseNgComponentProperties_fromFields(properties);
|
| return new List.from(properties);
|
| }
|
|
|
| @@ -582,7 +601,7 @@ class AngularCompilationUnitBuilder {
|
| int offset = _annotation.offset;
|
| AngularDirectiveElementImpl element = new AngularDirectiveElementImpl(offset);
|
| element.selector = selector;
|
| - element.properties = parseNgComponentProperties(false);
|
| + element.properties = parseNgComponentProperties();
|
| _classToolkitObjects.add(element);
|
| }
|
| }
|
| @@ -602,6 +621,25 @@ class AngularCompilationUnitBuilder {
|
| }
|
| }
|
|
|
| + List<AngularScopePropertyElement> parseScopeProperties() {
|
| + List<AngularScopePropertyElement> properties = [];
|
| + _classDeclaration.accept(new RecursiveASTVisitor_AngularCompilationUnitBuilder_parseScopeProperties(properties));
|
| + return new List.from(properties);
|
| + }
|
| +
|
| + /**
|
| + * Create [AngularViewElement] for each valid <code>view('template.html')</code> invocation,
|
| + * where <code>view</code> is <code>ViewFactory</code>.
|
| + */
|
| + void parseViews() {
|
| + List<AngularViewElement> views = [];
|
| + _unit.accept(new RecursiveASTVisitor_AngularCompilationUnitBuilder_parseViews(views));
|
| + if (!views.isEmpty) {
|
| + List<AngularViewElement> viewArray = new List.from(views);
|
| + (_unit.element as CompilationUnitElementImpl).angularViews = viewArray;
|
| + }
|
| + }
|
| +
|
| void reportError(ASTNode node, ErrorCode errorCode, List<Object> arguments) {
|
| int offset = node.offset;
|
| int length = node.length;
|
| @@ -622,6 +660,130 @@ class AngularCompilationUnitBuilder {
|
| }
|
| }
|
|
|
| +class RecursiveASTVisitor_AngularCompilationUnitBuilder_parseScopeProperties extends RecursiveASTVisitor<Object> {
|
| + List<AngularScopePropertyElement> properties;
|
| +
|
| + RecursiveASTVisitor_AngularCompilationUnitBuilder_parseScopeProperties(this.properties) : super();
|
| +
|
| + Object visitAssignmentExpression(AssignmentExpression node) {
|
| + addProperty(node);
|
| + return super.visitAssignmentExpression(node);
|
| + }
|
| +
|
| + void addProperty(AssignmentExpression node) {
|
| + // try to find "name" in scope[name]
|
| + SimpleStringLiteral nameNode = getNameNode(node.leftHandSide);
|
| + if (nameNode == null) {
|
| + return;
|
| + }
|
| + // prepare unique
|
| + String name = nameNode.stringValue;
|
| + if (hasPropertyWithName(name)) {
|
| + return;
|
| + }
|
| + // do add property
|
| + int nameOffset = nameNode.valueOffset;
|
| + AngularScopePropertyElement property = new AngularScopePropertyElementImpl(name, nameOffset, node.rightHandSide.bestType);
|
| + nameNode.toolkitElement = property;
|
| + properties.add(property);
|
| + }
|
| +
|
| + SimpleStringLiteral getNameNode(Expression node) {
|
| + if (node is IndexExpression) {
|
| + IndexExpression indexExpression = node;
|
| + Expression target = indexExpression.target;
|
| + Expression index = indexExpression.index;
|
| + if (index is SimpleStringLiteral && isContext(target)) {
|
| + return index;
|
| + }
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + bool hasPropertyWithName(String name) {
|
| + for (AngularScopePropertyElement property in properties) {
|
| + if (property.name == name) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + bool isContext(Expression target) {
|
| + if (target is PrefixedIdentifier) {
|
| + PrefixedIdentifier prefixed = target;
|
| + SimpleIdentifier prefix = prefixed.prefix;
|
| + SimpleIdentifier identifier = prefixed.identifier;
|
| + return (identifier.name == "context") && isScope(prefix);
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + bool isScope(Expression target) {
|
| + if (target != null) {
|
| + Type2 type = target.bestType;
|
| + if (type is InterfaceType) {
|
| + InterfaceType interfaceType = type;
|
| + return interfaceType.name == "Scope";
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +}
|
| +
|
| +class RecursiveASTVisitor_AngularCompilationUnitBuilder_parseViews extends RecursiveASTVisitor<Object> {
|
| + List<AngularViewElement> views;
|
| +
|
| + RecursiveASTVisitor_AngularCompilationUnitBuilder_parseViews(this.views) : super();
|
| +
|
| + Object visitMethodInvocation(MethodInvocation node) {
|
| + addView(node);
|
| + return super.visitMethodInvocation(node);
|
| + }
|
| +
|
| + void addView(MethodInvocation node) {
|
| + // only one argument
|
| + List<Expression> arguments = node.argumentList.arguments;
|
| + if (arguments.length != 1) {
|
| + return;
|
| + }
|
| + // String literal
|
| + Expression argument = arguments[0];
|
| + if (argument is! SimpleStringLiteral) {
|
| + return;
|
| + }
|
| + SimpleStringLiteral literal = argument as SimpleStringLiteral;
|
| + // just view('template')
|
| + if (node.realTarget != null) {
|
| + return;
|
| + }
|
| + // should be ViewFactory
|
| + if (!isViewFactory(node.methodName)) {
|
| + return;
|
| + }
|
| + // add AngularViewElement
|
| + String templateUri = literal.stringValue;
|
| + int templateUriOffset = literal.valueOffset;
|
| + views.add(new AngularViewElementImpl(templateUri, templateUriOffset));
|
| + }
|
| +
|
| + bool isViewFactory(Expression target) {
|
| + if (target is SimpleIdentifier) {
|
| + SimpleIdentifier identifier = target;
|
| + Element element = identifier.staticElement;
|
| + if (element is VariableElement) {
|
| + VariableElement variable = element;
|
| + Type2 type = variable.type;
|
| + if (type is InterfaceType) {
|
| + InterfaceType interfaceType = type;
|
| + return interfaceType.name == "ViewFactory";
|
| + }
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +}
|
| +
|
| /**
|
| * Instances of the class `CompilationUnitBuilder` build an element model for a single
|
| * compilation unit.
|
| @@ -828,7 +990,7 @@ class ElementBuilder extends RecursiveASTVisitor<Object> {
|
| _inFunction = wasInFunction;
|
| }
|
| SimpleIdentifier constructorName = node.name;
|
| - ConstructorElementImpl element = new ConstructorElementImpl(constructorName);
|
| + ConstructorElementImpl element = new ConstructorElementImpl.con1(constructorName);
|
| if (node.factoryKeyword != null) {
|
| element.factory = true;
|
| }
|
| @@ -1327,7 +1489,7 @@ class ElementBuilder extends RecursiveASTVisitor<Object> {
|
| * @return the [ConstructorElement]s array with the single default constructor element
|
| */
|
| List<ConstructorElement> createDefaultConstructors(InterfaceTypeImpl interfaceType) {
|
| - ConstructorElementImpl constructor = new ConstructorElementImpl(null);
|
| + ConstructorElementImpl constructor = new ConstructorElementImpl.con1(null);
|
| constructor.synthetic = true;
|
| constructor.returnType = interfaceType;
|
| FunctionTypeImpl type = new FunctionTypeImpl.con1(constructor);
|
| @@ -1855,7 +2017,7 @@ class HtmlUnitBuilder implements ht.XmlVisitor<Object> {
|
| * @return the HTML element that was built
|
| * @throws AnalysisException if the analysis could not be performed
|
| */
|
| - HtmlElementImpl buildHtmlElement(Source source) => buildHtmlElement2(source, source.modificationStamp, _context.parseHtmlUnit(source));
|
| + HtmlElementImpl buildHtmlElement(Source source) => buildHtmlElement2(source, _context.getModificationStamp(source), _context.parseHtmlUnit(source));
|
|
|
| /**
|
| * Build the HTML element for the given source.
|
| @@ -1924,7 +2086,7 @@ class HtmlUnitBuilder implements ht.XmlVisitor<Object> {
|
| parseUriWithException(scriptSourcePath);
|
| Source scriptSource = _context.sourceFactory.resolveUri(htmlSource, scriptSourcePath);
|
| script.scriptSource = scriptSource;
|
| - if (scriptSource == null || !scriptSource.exists()) {
|
| + if (!_context.exists(scriptSource)) {
|
| reportValueError(HtmlWarningCode.URI_DOES_NOT_EXIST, scriptAttribute, [scriptSourcePath]);
|
| }
|
| } on URISyntaxException catch (exception) {
|
| @@ -2863,6 +3025,12 @@ class DeadCodeVerifier extends RecursiveASTVisitor<Object> {
|
| * expression, or simple infinite loop such as `while(true)`.
|
| */
|
| class ExitDetector extends GeneralizingASTVisitor<bool> {
|
| + /**
|
| + * Set to `true` when a `break` is encountered, and reset to `false` when a
|
| + * `do`, `while`, `for` or `switch` block is entered.
|
| + */
|
| + bool _enclosingBlockContainsBreak = false;
|
| +
|
| bool visitArgumentList(ArgumentList node) => visitExpressions(node.arguments);
|
|
|
| bool visitAsExpression(AsExpression node) => node.expression.accept(this);
|
| @@ -2903,7 +3071,10 @@ class ExitDetector extends GeneralizingASTVisitor<bool> {
|
|
|
| bool visitBlockFunctionBody(BlockFunctionBody node) => node.block.accept(this);
|
|
|
| - bool visitBreakStatement(BreakStatement node) => false;
|
| + bool visitBreakStatement(BreakStatement node) {
|
| + _enclosingBlockContainsBreak = true;
|
| + return false;
|
| + }
|
|
|
| bool visitCascadeExpression(CascadeExpression node) {
|
| Expression target = node.target;
|
| @@ -2931,37 +3102,74 @@ class ExitDetector extends GeneralizingASTVisitor<bool> {
|
| bool visitContinueStatement(ContinueStatement node) => false;
|
|
|
| bool visitDoStatement(DoStatement node) {
|
| - Expression conditionExpression = node.condition;
|
| - if (conditionExpression.accept(this)) {
|
| - return true;
|
| - }
|
| - // TODO(jwren) Do we want to take all constant expressions into account?
|
| - if (conditionExpression is BooleanLiteral) {
|
| - BooleanLiteral booleanLiteral = conditionExpression;
|
| - if (booleanLiteral.value) {
|
| - return node.body.accept(this);
|
| + bool outerBreakValue = _enclosingBlockContainsBreak;
|
| + _enclosingBlockContainsBreak = false;
|
| + try {
|
| + Expression conditionExpression = node.condition;
|
| + if (conditionExpression.accept(this)) {
|
| + return true;
|
| }
|
| + // TODO(jwren) Do we want to take all constant expressions into account?
|
| + if (conditionExpression is BooleanLiteral) {
|
| + BooleanLiteral booleanLiteral = conditionExpression;
|
| + // If do {} while (true), and the body doesn't return or the body doesn't have a break, then
|
| + // return true.
|
| + bool blockReturns = node.body.accept(this);
|
| + if (booleanLiteral.value && (blockReturns || !_enclosingBlockContainsBreak)) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + } finally {
|
| + _enclosingBlockContainsBreak = outerBreakValue;
|
| }
|
| - return false;
|
| }
|
|
|
| bool visitEmptyStatement(EmptyStatement node) => false;
|
|
|
| bool visitExpressionStatement(ExpressionStatement node) => node.expression.accept(this);
|
|
|
| - bool visitForEachStatement(ForEachStatement node) => node.iterator.accept(this);
|
| + bool visitForEachStatement(ForEachStatement node) {
|
| + bool outerBreakValue = _enclosingBlockContainsBreak;
|
| + _enclosingBlockContainsBreak = false;
|
| + try {
|
| + return node.iterator.accept(this);
|
| + } finally {
|
| + _enclosingBlockContainsBreak = outerBreakValue;
|
| + }
|
| + }
|
|
|
| bool visitForStatement(ForStatement node) {
|
| - if (node.variables != null && visitVariableDeclarations(node.variables.variables)) {
|
| - return true;
|
| - }
|
| - if (node.initialization != null && node.initialization.accept(this)) {
|
| - return true;
|
| - }
|
| - if (node.condition != null && node.condition.accept(this)) {
|
| - return true;
|
| + bool outerBreakValue = _enclosingBlockContainsBreak;
|
| + _enclosingBlockContainsBreak = false;
|
| + try {
|
| + if (node.variables != null && visitVariableDeclarations(node.variables.variables)) {
|
| + return true;
|
| + }
|
| + if (node.initialization != null && node.initialization.accept(this)) {
|
| + return true;
|
| + }
|
| + Expression conditionExpression = node.condition;
|
| + if (conditionExpression != null && conditionExpression.accept(this)) {
|
| + return true;
|
| + }
|
| + if (visitExpressions(node.updaters)) {
|
| + return true;
|
| + }
|
| + // TODO(jwren) Do we want to take all constant expressions into account?
|
| + // If for(; true; ) (or for(;;)), and the body doesn't return or the body doesn't have a
|
| + // break, then return true.
|
| + bool implicitOrExplictTrue = conditionExpression == null || (conditionExpression is BooleanLiteral && conditionExpression.value);
|
| + if (implicitOrExplictTrue) {
|
| + bool blockReturns = node.body.accept(this);
|
| + if (blockReturns || !_enclosingBlockContainsBreak) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + } finally {
|
| + _enclosingBlockContainsBreak = outerBreakValue;
|
| }
|
| - return visitExpressions(node.updaters);
|
| }
|
|
|
| bool visitFunctionDeclarationStatement(FunctionDeclarationStatement node) => false;
|
| @@ -3057,25 +3265,31 @@ class ExitDetector extends GeneralizingASTVisitor<bool> {
|
| bool visitSwitchDefault(SwitchDefault node) => visitStatements(node.statements);
|
|
|
| bool visitSwitchStatement(SwitchStatement node) {
|
| - bool hasDefault = false;
|
| - NodeList<SwitchMember> memberList = node.members;
|
| - List<SwitchMember> members = new List.from(memberList);
|
| - for (int i = 0; i < members.length; i++) {
|
| - SwitchMember switchMember = members[i];
|
| - if (switchMember is SwitchDefault) {
|
| - hasDefault = true;
|
| - // If this is the last member and there are no statements, return false
|
| - if (switchMember.statements.isEmpty && i + 1 == members.length) {
|
| + bool outerBreakValue = _enclosingBlockContainsBreak;
|
| + _enclosingBlockContainsBreak = false;
|
| + try {
|
| + bool hasDefault = false;
|
| + NodeList<SwitchMember> memberList = node.members;
|
| + List<SwitchMember> members = new List.from(memberList);
|
| + for (int i = 0; i < members.length; i++) {
|
| + SwitchMember switchMember = members[i];
|
| + if (switchMember is SwitchDefault) {
|
| + hasDefault = true;
|
| + // If this is the last member and there are no statements, return false
|
| + if (switchMember.statements.isEmpty && i + 1 == members.length) {
|
| + return false;
|
| + }
|
| + }
|
| + // For switch members with no statements, don't visit the children, otherwise, return false if
|
| + // no return is found in the children statements
|
| + if (!switchMember.statements.isEmpty && !switchMember.accept(this)) {
|
| return false;
|
| }
|
| }
|
| - // For switch members with no statements, don't visit the children, otherwise, return false if
|
| - // no return is found in the children statements
|
| - if (!switchMember.statements.isEmpty && !switchMember.accept(this)) {
|
| - return false;
|
| - }
|
| + return hasDefault;
|
| + } finally {
|
| + _enclosingBlockContainsBreak = outerBreakValue;
|
| }
|
| - return hasDefault;
|
| }
|
|
|
| bool visitThisExpression(ThisExpression node) => false;
|
| @@ -3116,18 +3330,27 @@ class ExitDetector extends GeneralizingASTVisitor<bool> {
|
| }
|
|
|
| bool visitWhileStatement(WhileStatement node) {
|
| - Expression conditionExpression = node.condition;
|
| - if (conditionExpression.accept(this)) {
|
| - return true;
|
| - }
|
| - // TODO(jwren) Do we want to take all constant expressions into account?
|
| - if (conditionExpression is BooleanLiteral) {
|
| - BooleanLiteral booleanLiteral = conditionExpression;
|
| - if (booleanLiteral.value) {
|
| - return node.body.accept(this);
|
| + bool outerBreakValue = _enclosingBlockContainsBreak;
|
| + _enclosingBlockContainsBreak = false;
|
| + try {
|
| + Expression conditionExpression = node.condition;
|
| + if (conditionExpression.accept(this)) {
|
| + return true;
|
| + }
|
| + // TODO(jwren) Do we want to take all constant expressions into account?
|
| + if (conditionExpression is BooleanLiteral) {
|
| + BooleanLiteral booleanLiteral = conditionExpression;
|
| + // If while(true), and the body doesn't return or the body doesn't have a break, then return
|
| + // true.
|
| + bool blockReturns = node.body.accept(this);
|
| + if (booleanLiteral.value && (blockReturns || !_enclosingBlockContainsBreak)) {
|
| + return true;
|
| + }
|
| }
|
| + return false;
|
| + } finally {
|
| + _enclosingBlockContainsBreak = outerBreakValue;
|
| }
|
| - return false;
|
| }
|
|
|
| bool visitExpressions(NodeList<Expression> expressions) {
|
| @@ -3176,6 +3399,11 @@ class HintGenerator {
|
|
|
| bool _enableDart2JSHints = false;
|
|
|
| + /**
|
| + * The inheritance manager used to find overridden methods.
|
| + */
|
| + InheritanceManager _manager;
|
| +
|
| HintGenerator(List<CompilationUnit> compilationUnits, AnalysisContext context, AnalysisErrorListener errorListener) {
|
| this._compilationUnits = compilationUnits;
|
| this._context = context;
|
| @@ -3183,6 +3411,7 @@ class HintGenerator {
|
| LibraryElement library = compilationUnits[0].element.library;
|
| _importsVerifier = new ImportsVerifier(library);
|
| _enableDart2JSHints = context.analysisOptions.dart2jsHint;
|
| + _manager = new InheritanceManager(compilationUnits[0].element.library);
|
| }
|
|
|
| void generateForLibrary() {
|
| @@ -3210,15 +3439,16 @@ class HintGenerator {
|
|
|
| void generateForCompilationUnit(CompilationUnit unit, Source source) {
|
| ErrorReporter errorReporter = new ErrorReporter(_errorListener, source);
|
| - _importsVerifier.visitCompilationUnit(unit);
|
| + unit.accept(_importsVerifier);
|
| // dead code analysis
|
| - new DeadCodeVerifier(errorReporter).visitCompilationUnit(unit);
|
| + unit.accept(new DeadCodeVerifier(errorReporter));
|
| // dart2js analysis
|
| if (_enableDart2JSHints) {
|
| - new Dart2JSVerifier(errorReporter).visitCompilationUnit(unit);
|
| + unit.accept(new Dart2JSVerifier(errorReporter));
|
| }
|
| // Dart best practices
|
| - new BestPracticesVerifier(errorReporter).visitCompilationUnit(unit);
|
| + unit.accept(new BestPracticesVerifier(errorReporter));
|
| + unit.accept(new OverrideVerifier(_manager, errorReporter));
|
| // Find to-do comments
|
| new ToDoFinder(errorReporter).findIn(unit);
|
| }
|
| @@ -3565,6 +3795,77 @@ class ImportsVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
|
|
| /**
|
| + * Instances of the class `OverrideVerifier` visit all of the declarations in a compilation
|
| + * unit to verify that if they have an override annotation it is being used correctly.
|
| + */
|
| +class OverrideVerifier extends RecursiveASTVisitor<Object> {
|
| + /**
|
| + * The inheritance manager used to find overridden methods.
|
| + */
|
| + InheritanceManager _manager;
|
| +
|
| + /**
|
| + * The error reporter used to report errors.
|
| + */
|
| + ErrorReporter _errorReporter;
|
| +
|
| + /**
|
| + * Initialize a newly created verifier to look for inappropriate uses of the override annotation.
|
| + *
|
| + * @param manager the inheritance manager used to find overridden methods
|
| + * @param errorReporter the error reporter used to report errors
|
| + */
|
| + OverrideVerifier(InheritanceManager manager, ErrorReporter errorReporter) {
|
| + this._manager = manager;
|
| + this._errorReporter = errorReporter;
|
| + }
|
| +
|
| + Object visitMethodDeclaration(MethodDeclaration node) {
|
| + ExecutableElement element = node.element;
|
| + if (isOverride(element)) {
|
| + if (getOverriddenMember(element) == null) {
|
| + if (element is MethodElement) {
|
| + _errorReporter.reportError3(HintCode.OVERRIDE_ON_NON_OVERRIDING_METHOD, node.name, []);
|
| + } else if (element is PropertyAccessorElement) {
|
| + if (element.isGetter) {
|
| + _errorReporter.reportError3(HintCode.OVERRIDE_ON_NON_OVERRIDING_GETTER, node.name, []);
|
| + } else {
|
| + _errorReporter.reportError3(HintCode.OVERRIDE_ON_NON_OVERRIDING_SETTER, node.name, []);
|
| + }
|
| + }
|
| + }
|
| + }
|
| + return super.visitMethodDeclaration(node);
|
| + }
|
| +
|
| + /**
|
| + * Return the member that overrides the given member.
|
| + *
|
| + * @param member the member that overrides the returned member
|
| + * @return the member that overrides the given member
|
| + */
|
| + ExecutableElement getOverriddenMember(ExecutableElement member) {
|
| + LibraryElement library = member.library;
|
| + if (library == null) {
|
| + return null;
|
| + }
|
| + ClassElement classElement = member.getAncestor(ClassElement);
|
| + if (classElement == null) {
|
| + return null;
|
| + }
|
| + return _manager.lookupInheritance(classElement, member.name);
|
| + }
|
| +
|
| + /**
|
| + * Return `true` if the given element has an override annotation associated with it.
|
| + *
|
| + * @param element the element being tested
|
| + * @return `true` if the element has an override annotation associated with it
|
| + */
|
| + bool isOverride(Element element) => element != null && element.isOverride;
|
| +}
|
| +
|
| +/**
|
| * Instances of the class `PubVerifier` traverse an AST structure looking for deviations from
|
| * pub best practices.
|
| */
|
| @@ -3615,7 +3916,7 @@ class PubVerifier extends RecursiveASTVisitor<Object> {
|
| if (StringUtilities.startsWith4(fullName, fullNameIndex - 4, 0x2F, 0x6C, 0x69, 0x62)) {
|
| String relativePubspecPath = path.substring(0, pathIndex + 3) + _PUBSPEC_YAML;
|
| Source pubspecSource = _context.sourceFactory.resolveUri(source, relativePubspecPath);
|
| - if (pubspecSource != null && pubspecSource.exists()) {
|
| + if (_context.exists(pubspecSource)) {
|
| // Files inside the lib directory hierarchy should not reference files outside
|
| _errorReporter.reportError3(PubSuggestionCode.FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE, uriLiteral, []);
|
| }
|
| @@ -3657,7 +3958,7 @@ class PubVerifier extends RecursiveASTVisitor<Object> {
|
| Source source = getSource(uriLiteral);
|
| String relativePubspecPath = path.substring(0, pathIndex) + _PUBSPEC_YAML;
|
| Source pubspecSource = _context.sourceFactory.resolveUri(source, relativePubspecPath);
|
| - if (pubspecSource == null || !pubspecSource.exists()) {
|
| + if (!_context.exists(pubspecSource)) {
|
| return false;
|
| }
|
| String fullName = getSourceFullName(source);
|
| @@ -4971,33 +5272,36 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| * Checks if the given expression is the reference to the type, if it is then the
|
| * [ClassElement] is returned, otherwise `null` is returned.
|
| *
|
| - * @param expr the expression to evaluate
|
| + * @param expression the expression to evaluate
|
| * @return the [ClassElement] if the given expression is the reference to the type, and
|
| * `null` otherwise
|
| */
|
| - static ClassElementImpl getTypeReference(Expression expr) {
|
| - if (expr is Identifier) {
|
| - Identifier identifier = expr;
|
| - if (identifier.staticElement is ClassElementImpl) {
|
| - return identifier.staticElement as ClassElementImpl;
|
| + static ClassElementImpl getTypeReference(Expression expression) {
|
| + if (expression is Identifier) {
|
| + Element staticElement = expression.staticElement;
|
| + if (staticElement is ClassElementImpl) {
|
| + return staticElement;
|
| }
|
| }
|
| return null;
|
| }
|
|
|
| /**
|
| + * Return `true` if the given identifier is the return type of a constructor declaration.
|
| + *
|
| * @return `true` if the given identifier is the return type of a constructor declaration.
|
| */
|
| - static bool isConstructorReturnType(SimpleIdentifier node) {
|
| - ASTNode parent = node.parent;
|
| + static bool isConstructorReturnType(SimpleIdentifier identifier) {
|
| + ASTNode parent = identifier.parent;
|
| if (parent is ConstructorDeclaration) {
|
| - ConstructorDeclaration constructor = parent;
|
| - return identical(constructor.returnType, node);
|
| + return identical(parent.returnType, identifier);
|
| }
|
| return false;
|
| }
|
|
|
| /**
|
| + * Return `true` if the given identifier is the return type of a factory constructor.
|
| + *
|
| * @return `true` if the given identifier is the return type of a factory constructor
|
| * declaration.
|
| */
|
| @@ -5011,10 +5315,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| }
|
|
|
| /**
|
| - * Checks if the given 'super' expression is used in the valid context.
|
| + * Return `true` if the given 'super' expression is used in a valid context.
|
| *
|
| * @param node the 'super' expression to analyze
|
| - * @return `true` if the given 'super' expression is in the valid context
|
| + * @return `true` if the 'super' expression is in a valid context
|
| */
|
| static bool isSuperInValidContext(SuperExpression node) {
|
| for (ASTNode n = node; n != null; n = n.parent) {
|
| @@ -5113,22 +5417,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| Type2 propagatedType = getPropagatedType(leftHandSide);
|
| MethodElement propagatedMethod = lookUpMethod(leftHandSide, propagatedType, methodName);
|
| node.propagatedElement = propagatedMethod;
|
| - bool shouldReportMissingMember_static = shouldReportMissingMember(staticType, staticMethod);
|
| - bool shouldReportMissingMember_propagated = !shouldReportMissingMember_static && _enableHints ? shouldReportMissingMember(propagatedType, propagatedMethod) : false;
|
| - //
|
| - // If we are about to generate the hint (propagated version of this warning), then check
|
| - // that the member is not in a subtype of the propagated type.
|
| - //
|
| - if (shouldReportMissingMember_propagated) {
|
| - if (memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
|
| - shouldReportMissingMember_propagated = false;
|
| - }
|
| - }
|
| - if (shouldReportMissingMember_static || shouldReportMissingMember_propagated) {
|
| - ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_METHOD : HintCode.UNDEFINED_METHOD) as ErrorCode;
|
| - _resolver.reportErrorProxyConditionalAnalysisError3(shouldReportMissingMember_static ? staticType.element : propagatedType.element, errorCode, operator, [
|
| - methodName,
|
| - shouldReportMissingMember_static ? staticType.displayName : propagatedType.displayName]);
|
| + if (shouldReportMissingMember(staticType, staticMethod)) {
|
| + _resolver.reportErrorProxyConditionalAnalysisError3(staticType.element, StaticTypeWarningCode.UNDEFINED_METHOD, operator, [methodName, staticType.displayName]);
|
| + } else if (_enableHints && shouldReportMissingMember(propagatedType, propagatedMethod) && !memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
|
| + _resolver.reportErrorProxyConditionalAnalysisError3(propagatedType.element, HintCode.UNDEFINED_METHOD, operator, [methodName, propagatedType.displayName]);
|
| }
|
| }
|
| }
|
| @@ -5147,22 +5439,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| Type2 propagatedType = getPropagatedType(leftOperand);
|
| MethodElement propagatedMethod = lookUpMethod(leftOperand, propagatedType, methodName);
|
| node.propagatedElement = propagatedMethod;
|
| - bool shouldReportMissingMember_static = shouldReportMissingMember(staticType, staticMethod);
|
| - bool shouldReportMissingMember_propagated = !shouldReportMissingMember_static && _enableHints ? shouldReportMissingMember(propagatedType, propagatedMethod) : false;
|
| - //
|
| - // If we are about to generate the hint (propagated version of this warning), then check
|
| - // that the member is not in a subtype of the propagated type.
|
| - //
|
| - if (shouldReportMissingMember_propagated) {
|
| - if (memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
|
| - shouldReportMissingMember_propagated = false;
|
| - }
|
| - }
|
| - if (shouldReportMissingMember_static || shouldReportMissingMember_propagated) {
|
| - ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_OPERATOR : HintCode.UNDEFINED_OPERATOR) as ErrorCode;
|
| - _resolver.reportErrorProxyConditionalAnalysisError3(shouldReportMissingMember_static ? staticType.element : propagatedType.element, errorCode, operator, [
|
| - methodName,
|
| - shouldReportMissingMember_static ? staticType.displayName : propagatedType.displayName]);
|
| + if (shouldReportMissingMember(staticType, staticMethod)) {
|
| + _resolver.reportErrorProxyConditionalAnalysisError3(staticType.element, StaticTypeWarningCode.UNDEFINED_OPERATOR, operator, [methodName, staticType.displayName]);
|
| + } else if (_enableHints && shouldReportMissingMember(propagatedType, propagatedMethod) && !memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
|
| + _resolver.reportErrorProxyConditionalAnalysisError3(propagatedType.element, HintCode.UNDEFINED_OPERATOR, operator, [methodName, propagatedType.displayName]);
|
| }
|
| }
|
| }
|
| @@ -5170,11 +5450,7 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| }
|
|
|
| Object visitBreakStatement(BreakStatement node) {
|
| - SimpleIdentifier labelNode = node.label;
|
| - LabelElementImpl labelElement = lookupLabel(node, labelNode);
|
| - if (labelElement != null && labelElement.isOnSwitchMember) {
|
| - _resolver.reportError9(ResolverErrorCode.BREAK_LABEL_ON_SWITCH_MEMBER, labelNode, []);
|
| - }
|
| + lookupLabel(node, node.label);
|
| return null;
|
| }
|
|
|
| @@ -5275,17 +5551,18 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| ConstructorElement element = node.element;
|
| if (element is ConstructorElementImpl) {
|
| ConstructorElementImpl constructorElement = element;
|
| - // set redirected factory constructor
|
| ConstructorName redirectedNode = node.redirectedConstructor;
|
| if (redirectedNode != null) {
|
| + // set redirected factory constructor
|
| ConstructorElement redirectedElement = redirectedNode.staticElement;
|
| constructorElement.redirectedConstructor = redirectedElement;
|
| - }
|
| - // set redirected generate constructor
|
| - for (ConstructorInitializer initializer in node.initializers) {
|
| - if (initializer is RedirectingConstructorInvocation) {
|
| - ConstructorElement redirectedElement = initializer.staticElement;
|
| - constructorElement.redirectedConstructor = redirectedElement;
|
| + } else {
|
| + // set redirected generative constructor
|
| + for (ConstructorInitializer initializer in node.initializers) {
|
| + if (initializer is RedirectingConstructorInvocation) {
|
| + ConstructorElement redirectedElement = initializer.staticElement;
|
| + constructorElement.redirectedConstructor = redirectedElement;
|
| + }
|
| }
|
| }
|
| setMetadata(constructorElement, node);
|
| @@ -5298,11 +5575,6 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| ClassElement enclosingClass = _resolver.enclosingClass;
|
| FieldElement fieldElement = enclosingClass.getField(fieldName.name);
|
| fieldName.staticElement = fieldElement;
|
| - if (fieldElement == null || fieldElement.isSynthetic) {
|
| - _resolver.reportError9(CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTANT_FIELD, node, [fieldName]);
|
| - } else if (fieldElement.isStatic) {
|
| - _resolver.reportError9(CompileTimeErrorCode.INITIALIZER_FOR_STATIC_FIELD, node, [fieldName]);
|
| - }
|
| return null;
|
| }
|
|
|
| @@ -5312,13 +5584,16 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| return null;
|
| } else if (type is! InterfaceType) {
|
| // TODO(brianwilkerson) Report these errors.
|
| - ASTNode parent = node.parent;
|
| - if (parent is InstanceCreationExpression) {
|
| - if (parent.isConst) {
|
| - } else {
|
| - }
|
| - } else {
|
| - }
|
| + // ASTNode parent = node.getParent();
|
| + // if (parent instanceof InstanceCreationExpression) {
|
| + // if (((InstanceCreationExpression) parent).isConst()) {
|
| + // // CompileTimeErrorCode.CONST_WITH_NON_TYPE
|
| + // } else {
|
| + // // StaticWarningCode.NEW_WITH_NON_TYPE
|
| + // }
|
| + // } else {
|
| + // // This is part of a redirecting factory constructor; not sure which error code to use
|
| + // }
|
| return null;
|
| }
|
| // look up ConstructorElement
|
| @@ -5336,11 +5611,7 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| }
|
|
|
| Object visitContinueStatement(ContinueStatement node) {
|
| - SimpleIdentifier labelNode = node.label;
|
| - LabelElementImpl labelElement = lookupLabel(node, labelNode);
|
| - if (labelElement != null && labelElement.isOnSwitchStatement) {
|
| - _resolver.reportError9(ResolverErrorCode.CONTINUE_LABEL_ON_SWITCH, labelNode, []);
|
| - }
|
| + lookupLabel(node, node.label);
|
| return null;
|
| }
|
|
|
| @@ -5362,38 +5633,6 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| }
|
|
|
| Object visitFieldFormalParameter(FieldFormalParameter node) {
|
| - String fieldName = node.identifier.name;
|
| - ClassElement classElement = _resolver.enclosingClass;
|
| - if (classElement != null) {
|
| - FieldElement fieldElement = classElement.getField(fieldName);
|
| - if (fieldElement == null || fieldElement.isSynthetic) {
|
| - _resolver.reportError9(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTANT_FIELD, node, [fieldName]);
|
| - } else {
|
| - ParameterElement parameterElement = node.element;
|
| - if (parameterElement is FieldFormalParameterElementImpl) {
|
| - FieldFormalParameterElementImpl fieldFormal = parameterElement;
|
| - Type2 declaredType = fieldFormal.type;
|
| - Type2 fieldType = fieldElement.type;
|
| - if (fieldElement.isSynthetic) {
|
| - _resolver.reportError9(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTANT_FIELD, node, [fieldName]);
|
| - } else if (fieldElement.isStatic) {
|
| - _resolver.reportError9(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_STATIC_FIELD, node, [fieldName]);
|
| - } else if (declaredType != null && fieldType != null && !declaredType.isAssignableTo(fieldType)) {
|
| - _resolver.reportError9(StaticWarningCode.FIELD_INITIALIZING_FORMAL_NOT_ASSIGNABLE, node, [declaredType.displayName, fieldType.displayName]);
|
| - }
|
| - } else {
|
| - if (fieldElement.isSynthetic) {
|
| - _resolver.reportError9(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTANT_FIELD, node, [fieldName]);
|
| - } else if (fieldElement.isStatic) {
|
| - _resolver.reportError9(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_STATIC_FIELD, node, [fieldName]);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - // else {
|
| - // // TODO(jwren) Report error, constructor initializer variable is a top level element
|
| - // // (Either here or in ErrorVerifier#checkForAllFinalInitializedErrorCodes)
|
| - // }
|
| setMetadata2(node.element, node);
|
| return super.visitFieldFormalParameter(node);
|
| }
|
| @@ -5673,22 +5912,10 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| Type2 propagatedType = getPropagatedType(operand);
|
| MethodElement propagatedMethod = lookUpMethod(operand, propagatedType, methodName);
|
| node.propagatedElement = propagatedMethod;
|
| - bool shouldReportMissingMember_static = shouldReportMissingMember(staticType, staticMethod);
|
| - bool shouldReportMissingMember_propagated = !shouldReportMissingMember_static && _enableHints ? shouldReportMissingMember(propagatedType, propagatedMethod) : false;
|
| - //
|
| - // If we are about to generate the hint (propagated version of this warning), then check
|
| - // that the member is not in a subtype of the propagated type.
|
| - //
|
| - if (shouldReportMissingMember_propagated) {
|
| - if (memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
|
| - shouldReportMissingMember_propagated = false;
|
| - }
|
| - }
|
| - if (shouldReportMissingMember_static || shouldReportMissingMember_propagated) {
|
| - ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_OPERATOR : HintCode.UNDEFINED_OPERATOR) as ErrorCode;
|
| - _resolver.reportErrorProxyConditionalAnalysisError3(shouldReportMissingMember_static ? staticType.element : propagatedType.element, errorCode, node.operator, [
|
| - methodName,
|
| - shouldReportMissingMember_static ? staticType.displayName : propagatedType.displayName]);
|
| + if (shouldReportMissingMember(staticType, staticMethod)) {
|
| + _resolver.reportErrorProxyConditionalAnalysisError3(staticType.element, StaticTypeWarningCode.UNDEFINED_OPERATOR, node.operator, [methodName, staticType.displayName]);
|
| + } else if (_enableHints && shouldReportMissingMember(propagatedType, propagatedMethod) && !memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
|
| + _resolver.reportErrorProxyConditionalAnalysisError3(propagatedType.element, HintCode.UNDEFINED_OPERATOR, node.operator, [methodName, propagatedType.displayName]);
|
| }
|
| return null;
|
| }
|
| @@ -5758,26 +5985,14 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| String methodName = getPrefixOperator(node);
|
| Type2 staticType = getStaticType(operand);
|
| MethodElement staticMethod = lookUpMethod(operand, staticType, methodName);
|
| - node.staticElement = staticMethod;
|
| - Type2 propagatedType = getPropagatedType(operand);
|
| - MethodElement propagatedMethod = lookUpMethod(operand, propagatedType, methodName);
|
| - node.propagatedElement = propagatedMethod;
|
| - bool shouldReportMissingMember_static = shouldReportMissingMember(staticType, staticMethod);
|
| - bool shouldReportMissingMember_propagated = !shouldReportMissingMember_static && _enableHints ? shouldReportMissingMember(propagatedType, propagatedMethod) : false;
|
| - //
|
| - // If we are about to generate the hint (propagated version of this warning), then check
|
| - // that the member is not in a subtype of the propagated type.
|
| - //
|
| - if (shouldReportMissingMember_propagated) {
|
| - if (memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
|
| - shouldReportMissingMember_propagated = false;
|
| - }
|
| - }
|
| - if (shouldReportMissingMember_static || shouldReportMissingMember_propagated) {
|
| - ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_OPERATOR : HintCode.UNDEFINED_OPERATOR) as ErrorCode;
|
| - _resolver.reportErrorProxyConditionalAnalysisError3(shouldReportMissingMember_static ? staticType.element : propagatedType.element, errorCode, operator, [
|
| - methodName,
|
| - shouldReportMissingMember_static ? staticType.displayName : propagatedType.displayName]);
|
| + node.staticElement = staticMethod;
|
| + Type2 propagatedType = getPropagatedType(operand);
|
| + MethodElement propagatedMethod = lookUpMethod(operand, propagatedType, methodName);
|
| + node.propagatedElement = propagatedMethod;
|
| + if (shouldReportMissingMember(staticType, staticMethod)) {
|
| + _resolver.reportErrorProxyConditionalAnalysisError3(staticType.element, StaticTypeWarningCode.UNDEFINED_OPERATOR, operator, [methodName, staticType.displayName]);
|
| + } else if (_enableHints && shouldReportMissingMember(propagatedType, propagatedMethod) && !memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
|
| + _resolver.reportErrorProxyConditionalAnalysisError3(propagatedType.element, HintCode.UNDEFINED_OPERATOR, operator, [methodName, propagatedType.displayName]);
|
| }
|
| }
|
| return null;
|
| @@ -6054,16 +6269,7 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| */
|
| bool checkForUndefinedIndexOperator(IndexExpression node, Expression target, String methodName, MethodElement staticMethod, MethodElement propagatedMethod, Type2 staticType, Type2 propagatedType) {
|
| bool shouldReportMissingMember_static = shouldReportMissingMember(staticType, staticMethod);
|
| - bool shouldReportMissingMember_propagated = !shouldReportMissingMember_static && _enableHints ? shouldReportMissingMember(propagatedType, propagatedMethod) : false;
|
| - //
|
| - // If we are about to generate the hint (propagated version of this warning), then check
|
| - // that the member is not in a subtype of the propagated type.
|
| - //
|
| - if (shouldReportMissingMember_propagated) {
|
| - if (memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
|
| - shouldReportMissingMember_propagated = false;
|
| - }
|
| - }
|
| + bool shouldReportMissingMember_propagated = !shouldReportMissingMember_static && _enableHints && shouldReportMissingMember(propagatedType, propagatedMethod) && !memberFoundInSubclass(propagatedType.element, methodName, true, false);
|
| if (shouldReportMissingMember_static || shouldReportMissingMember_propagated) {
|
| sc.Token leftBracket = node.leftBracket;
|
| sc.Token rightBracket = node.rightBracket;
|
| @@ -6708,12 +6914,13 @@ class ElementResolver extends SimpleASTVisitor<Object> {
|
| return sc.TokenType.STAR;
|
| } else if (operator == sc.TokenType.TILDE_SLASH_EQ) {
|
| return sc.TokenType.TILDE_SLASH;
|
| + } else {
|
| + // Internal error: Unmapped assignment operator.
|
| + AnalysisEngine.instance.logger.logError("Failed to map ${operator.lexeme} to it's corresponding operator");
|
| + return operator;
|
| }
|
| break;
|
| }
|
| - // Internal error: Unmapped assignment operator.
|
| - AnalysisEngine.instance.logger.logError("Failed to map ${operator.lexeme} to it's corresponding operator");
|
| - return operator;
|
| }
|
|
|
| void resolveAnnotationConstructorInvocationArguments(Annotation annotation, ConstructorElement constructor) {
|
| @@ -7486,6 +7693,133 @@ class IncrementalResolver {
|
| */
|
| class InheritanceManager {
|
| /**
|
| + * Given some array of [ExecutableElement]s, this method creates a synthetic element as
|
| + * described in the Superinterfaces section of Inheritance and Overriding.
|
| + *
|
| + * TODO (jwren) Copy contents from the Spec into this javadoc.
|
| + *
|
| + * TODO (jwren) Associate a propagated type to the synthetic method element using least upper
|
| + * bound calls
|
| + */
|
| + static ExecutableElement computeMergedExecutableElement(List<ExecutableElement> elementArrayToMerge) {
|
| + int h = getNumOfPositionalParameters(elementArrayToMerge[0]);
|
| + int r = getNumOfRequiredParameters(elementArrayToMerge[0]);
|
| + Set<String> namedParametersList = new Set<String>();
|
| + for (int i = 1; i < elementArrayToMerge.length; i++) {
|
| + ExecutableElement element = elementArrayToMerge[i];
|
| + int numOfPositionalParams = getNumOfPositionalParameters(element);
|
| + if (h < numOfPositionalParams) {
|
| + h = numOfPositionalParams;
|
| + }
|
| + int numOfRequiredParams = getNumOfRequiredParameters(element);
|
| + if (r > numOfRequiredParams) {
|
| + r = numOfRequiredParams;
|
| + }
|
| + namedParametersList.addAll(getNamedParameterNames(element));
|
| + }
|
| + if (r > h) {
|
| + return null;
|
| + }
|
| + return createSyntheticExecutableElement(elementArrayToMerge, elementArrayToMerge[0].displayName, r, h - r, new List.from(namedParametersList));
|
| + }
|
| +
|
| + /**
|
| + * Used by [computeMergedExecutableElement] to actually create the
|
| + * synthetic element.
|
| + *
|
| + * @param elementArrayToMerge the array used to create the synthetic element
|
| + * @param name the name of the method, getter or setter
|
| + * @param numOfRequiredParameters the number of required parameters
|
| + * @param numOfPositionalParameters the number of positional parameters
|
| + * @param namedParameters the list of [String]s that are the named parameters
|
| + * @return the created synthetic element
|
| + */
|
| + static ExecutableElement createSyntheticExecutableElement(List<ExecutableElement> elementArrayToMerge, String name, int numOfRequiredParameters, int numOfPositionalParameters, List<String> namedParameters) {
|
| + DynamicTypeImpl dynamicType = DynamicTypeImpl.instance;
|
| + SimpleIdentifier nameIdentifier = new SimpleIdentifier(new sc.StringToken(sc.TokenType.IDENTIFIER, name, 0));
|
| + ExecutableElementImpl executable;
|
| + if (elementArrayToMerge[0] is MethodElement) {
|
| + MultiplyInheritedMethodElementImpl unionedMethod = new MultiplyInheritedMethodElementImpl(nameIdentifier);
|
| + unionedMethod.inheritedElements = elementArrayToMerge;
|
| + executable = unionedMethod;
|
| + } else {
|
| + MultiplyInheritedPropertyAccessorElementImpl unionedPropertyAccessor = new MultiplyInheritedPropertyAccessorElementImpl(nameIdentifier);
|
| + unionedPropertyAccessor.getter = (elementArrayToMerge[0] as PropertyAccessorElement).isGetter;
|
| + unionedPropertyAccessor.setter = (elementArrayToMerge[0] as PropertyAccessorElement).isSetter;
|
| + unionedPropertyAccessor.inheritedElements = elementArrayToMerge;
|
| + executable = unionedPropertyAccessor;
|
| + }
|
| + int numOfParameters = numOfRequiredParameters + numOfPositionalParameters + namedParameters.length;
|
| + List<ParameterElement> parameters = new List<ParameterElement>(numOfParameters);
|
| + int i = 0;
|
| + for (int j = 0; j < numOfRequiredParameters; j++, i++) {
|
| + ParameterElementImpl parameter = new ParameterElementImpl.con2("", 0);
|
| + parameter.type = dynamicType;
|
| + parameter.parameterKind = ParameterKind.REQUIRED;
|
| + parameters[i] = parameter;
|
| + }
|
| + for (int k = 0; k < numOfPositionalParameters; k++, i++) {
|
| + ParameterElementImpl parameter = new ParameterElementImpl.con2("", 0);
|
| + parameter.type = dynamicType;
|
| + parameter.parameterKind = ParameterKind.POSITIONAL;
|
| + parameters[i] = parameter;
|
| + }
|
| + for (int m = 0; m < namedParameters.length; m++, i++) {
|
| + ParameterElementImpl parameter = new ParameterElementImpl.con2(namedParameters[m], 0);
|
| + parameter.type = dynamicType;
|
| + parameter.parameterKind = ParameterKind.NAMED;
|
| + parameters[i] = parameter;
|
| + }
|
| + executable.returnType = dynamicType;
|
| + executable.parameters = parameters;
|
| + FunctionTypeImpl methodType = new FunctionTypeImpl.con1(executable);
|
| + executable.type = methodType;
|
| + return executable;
|
| + }
|
| +
|
| + /**
|
| + * Given some [ExecutableElement], return the list of named parameters.
|
| + */
|
| + static List<String> getNamedParameterNames(ExecutableElement executableElement) {
|
| + List<String> namedParameterNames = new List<String>();
|
| + List<ParameterElement> parameters = executableElement.parameters;
|
| + for (int i = 0; i < parameters.length; i++) {
|
| + ParameterElement parameterElement = parameters[i];
|
| + if (identical(parameterElement.parameterKind, ParameterKind.NAMED)) {
|
| + namedParameterNames.add(parameterElement.name);
|
| + }
|
| + }
|
| + return namedParameterNames;
|
| + }
|
| +
|
| + /**
|
| + * Given some [ExecutableElement] return the number of parameters of the specified kind.
|
| + */
|
| + static int getNumOfParameters(ExecutableElement executableElement, ParameterKind parameterKind) {
|
| + int parameterCount = 0;
|
| + List<ParameterElement> parameters = executableElement.parameters;
|
| + for (int i = 0; i < parameters.length; i++) {
|
| + ParameterElement parameterElement = parameters[i];
|
| + if (identical(parameterElement.parameterKind, parameterKind)) {
|
| + parameterCount++;
|
| + }
|
| + }
|
| + return parameterCount;
|
| + }
|
| +
|
| + /**
|
| + * Given some [ExecutableElement] return the number of positional parameters.
|
| + *
|
| + * Note: by positional we mean [ParameterKind#REQUIRED] or [ParameterKind#POSITIONAL].
|
| + */
|
| + static int getNumOfPositionalParameters(ExecutableElement executableElement) => getNumOfParameters(executableElement, ParameterKind.REQUIRED) + getNumOfParameters(executableElement, ParameterKind.POSITIONAL);
|
| +
|
| + /**
|
| + * Given some [ExecutableElement] return the number of required parameters.
|
| + */
|
| + static int getNumOfRequiredParameters(ExecutableElement executableElement) => getNumOfParameters(executableElement, ParameterKind.REQUIRED);
|
| +
|
| + /**
|
| * The [LibraryElement] that is managed by this manager.
|
| */
|
| LibraryElement _library;
|
| @@ -7700,7 +8034,7 @@ class InheritanceManager {
|
| //
|
| List<InterfaceType> mixins = classElt.mixins;
|
| for (int i = mixins.length - 1; i >= 0; i--) {
|
| - recordMapWithClassMembers(resultMap, mixins[i]);
|
| + recordMapWithClassMembersFromMixin(resultMap, mixins[i]);
|
| }
|
| _classLookup[classElt] = resultMap;
|
| return resultMap;
|
| @@ -7868,37 +8202,75 @@ class InheritanceManager {
|
| return resultMap;
|
| }
|
| //
|
| - // Union all of the maps together, grouping the ExecutableElements into sets.
|
| + // Union all of the lookupMaps together into unionMap, grouping the ExecutableElements into a
|
| + // list where none of the elements are equal where equality is determined by having equal
|
| + // function types. (We also take note too of the kind of the element: ()->int and () -> int may
|
| + // not be equal if one is a getter and the other is a method.)
|
| //
|
| - Map<String, Set<ExecutableElement>> unionMap = new Map<String, Set<ExecutableElement>>();
|
| + Map<String, List<ExecutableElement>> unionMap = new Map<String, List<ExecutableElement>>();
|
| for (MemberMap lookupMap in lookupMaps) {
|
| - for (int i = 0; i < lookupMap.size; i++) {
|
| + int lookupMapSize = lookupMap.size;
|
| + for (int i = 0; i < lookupMapSize; i++) {
|
| + // Get the string key, if null, break.
|
| String key = lookupMap.getKey(i);
|
| if (key == null) {
|
| break;
|
| }
|
| - Set<ExecutableElement> set = unionMap[key];
|
| - if (set == null) {
|
| - set = new Set<ExecutableElement>();
|
| - unionMap[key] = set;
|
| + // Get the list value out of the unionMap
|
| + List<ExecutableElement> list = unionMap[key];
|
| + // If we haven't created such a map for this key yet, do create it and put the list entry
|
| + // into the unionMap.
|
| + if (list == null) {
|
| + list = new List<ExecutableElement>();
|
| + unionMap[key] = list;
|
| + }
|
| + // Fetch the entry out of this lookupMap
|
| + ExecutableElement newExecutableElementEntry = lookupMap.getValue(i);
|
| + if (list.isEmpty) {
|
| + // If the list is empty, just the new value
|
| + list.add(newExecutableElementEntry);
|
| + } else {
|
| + // Otherwise, only add the newExecutableElementEntry if it isn't already in the list, this
|
| + // covers situation where a class inherits two methods (or two getters) that are
|
| + // identical.
|
| + bool alreadyInList = false;
|
| + bool isMethod1 = newExecutableElementEntry is MethodElement;
|
| + for (ExecutableElement executableElementInList in list) {
|
| + bool isMethod2 = executableElementInList is MethodElement;
|
| + if (identical(isMethod1, isMethod2) && executableElementInList.type == newExecutableElementEntry.type) {
|
| + alreadyInList = true;
|
| + break;
|
| + }
|
| + }
|
| + if (!alreadyInList) {
|
| + list.add(newExecutableElementEntry);
|
| + }
|
| }
|
| - set.add(lookupMap.getValue(i));
|
| }
|
| }
|
| //
|
| - // Loop through the entries in the union map, adding them to the resultMap appropriately.
|
| + // Loop through the entries in the unionMap, adding them to the resultMap appropriately.
|
| //
|
| - for (MapEntry<String, Set<ExecutableElement>> entry in getMapEntrySet(unionMap)) {
|
| + for (MapEntry<String, List<ExecutableElement>> entry in getMapEntrySet(unionMap)) {
|
| String key = entry.getKey();
|
| - Set<ExecutableElement> set = entry.getValue();
|
| - int numOfEltsWithMatchingNames = set.length;
|
| + List<ExecutableElement> list = entry.getValue();
|
| + int numOfEltsWithMatchingNames = list.length;
|
| if (numOfEltsWithMatchingNames == 1) {
|
| - resultMap.put(key, new JavaIterator(set).next());
|
| + //
|
| + // Example: class A inherits only 1 method named 'm'. Since it is the only such method, it
|
| + // is inherited.
|
| + // Another example: class A inherits 2 methods named 'm' from 2 different interfaces, but
|
| + // they both have the same signature, so it is the method inherited.
|
| + //
|
| + resultMap.put(key, list[0]);
|
| } else {
|
| + //
|
| + // Then numOfEltsWithMatchingNames > 1, check for the warning cases.
|
| + //
|
| bool allMethods = true;
|
| bool allSetters = true;
|
| bool allGetters = true;
|
| - for (ExecutableElement executableElement in set) {
|
| + for (ExecutableElement executableElement in list) {
|
| if (executableElement is PropertyAccessorElement) {
|
| allMethods = false;
|
| if (executableElement.isSetter) {
|
| @@ -7911,14 +8283,20 @@ class InheritanceManager {
|
| allSetters = false;
|
| }
|
| }
|
| + //
|
| + // If there isn't a mixture of methods with getters, then continue, otherwise create a
|
| + // warning.
|
| + //
|
| if (allMethods || allGetters || allSetters) {
|
| + //
|
| // Compute the element whose type is the subtype of all of the other types.
|
| - List<ExecutableElement> elements = new List.from(set);
|
| + //
|
| + List<ExecutableElement> elements = new List.from(list);
|
| List<FunctionType> executableElementTypes = new List<FunctionType>(numOfEltsWithMatchingNames);
|
| for (int i = 0; i < numOfEltsWithMatchingNames; i++) {
|
| executableElementTypes[i] = elements[i].type;
|
| }
|
| - bool foundSubtypeOfAllTypes = false;
|
| + List<int> subtypesOfAllOtherTypesIndexes = new List<int>();
|
| for (int i = 0; i < numOfEltsWithMatchingNames; i++) {
|
| FunctionType subtype = executableElementTypes[i];
|
| if (subtype == null) {
|
| @@ -7934,19 +8312,49 @@ class InheritanceManager {
|
| }
|
| }
|
| if (subtypeOfAllTypes) {
|
| - foundSubtypeOfAllTypes = true;
|
| - resultMap.put(key, elements[i]);
|
| - break;
|
| + subtypesOfAllOtherTypesIndexes.add(i);
|
| }
|
| }
|
| - if (!foundSubtypeOfAllTypes) {
|
| - reportError(classElt, classElt.nameOffset, classElt.displayName.length, StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE, [key]);
|
| + //
|
| + // The following is split into three cases determined by the number of elements in subtypesOfAllOtherTypes
|
| + //
|
| + if (subtypesOfAllOtherTypesIndexes.length == 1) {
|
| + //
|
| + // Example: class A inherited only 2 method named 'm'. One has the function type
|
| + // '() -> dynamic' and one has the function type '([int]) -> dynamic'. Since the second
|
| + // method is a subtype of all the others, it is the inherited method.
|
| + // Tests: InheritanceManagerTest.test_getMapOfMembersInheritedFromInterfaces_union_oneSubtype_*
|
| + //
|
| + resultMap.put(key, elements[subtypesOfAllOtherTypesIndexes[0]]);
|
| + } else {
|
| + if (subtypesOfAllOtherTypesIndexes.isEmpty) {
|
| + //
|
| + // Example: class A inherited only 2 method named 'm'. One has the function type
|
| + // '() -> int' and one has the function type '() -> String'. Since neither is a subtype
|
| + // of the other, we create a warning, and have this class inherit nothing.
|
| + //
|
| + String firstTwoFuntionTypesStr = "${executableElementTypes[0].toString()}, ${executableElementTypes[1].toString()}";
|
| + reportError(classElt, classElt.nameOffset, classElt.displayName.length, StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE, [key, firstTwoFuntionTypesStr]);
|
| + } else {
|
| + //
|
| + // Example: class A inherits 2 methods named 'm'. One has the function type
|
| + // '(int) -> dynamic' and one has the function type '(num) -> dynamic'. Since they are
|
| + // both a subtype of the other, a synthetic function '(dynamic) -> dynamic' is
|
| + // inherited.
|
| + // Tests: test_getMapOfMembersInheritedFromInterfaces_union_multipleSubtypes_*
|
| + //
|
| + List<ExecutableElement> elementArrayToMerge = new List<ExecutableElement>(subtypesOfAllOtherTypesIndexes.length);
|
| + for (int i = 0; i < elementArrayToMerge.length; i++) {
|
| + elementArrayToMerge[i] = elements[subtypesOfAllOtherTypesIndexes[i]];
|
| + }
|
| + ExecutableElement mergedExecutableElement = computeMergedExecutableElement(elementArrayToMerge);
|
| + if (mergedExecutableElement != null) {
|
| + resultMap.put(key, mergedExecutableElement);
|
| + }
|
| + }
|
| }
|
| } else {
|
| - if (!allMethods && !allGetters) {
|
| - reportError(classElt, classElt.nameOffset, classElt.displayName.length, StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD, [key]);
|
| - }
|
| - resultMap.remove(entry.getKey());
|
| + reportError(classElt, classElt.nameOffset, classElt.displayName.length, StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD, [key]);
|
| }
|
| }
|
| }
|
| @@ -8003,6 +8411,38 @@ class InheritanceManager {
|
| }
|
|
|
| /**
|
| + * Similar to [recordMapWithClassMembers], but only puts values
|
| + * into the map if the additional executable doesn't replace a concrete member with an abstract
|
| + * member, ex: NonErrorResolverTest.test_nonAbstractClassInheritsAbstractMemberOne_mixin_*()
|
| + *
|
| + * @param map some non-`null` map to put the methods and accessors from the passed
|
| + * [ClassElement] into
|
| + * @param type the type that will be recorded into the passed map
|
| + */
|
| + void recordMapWithClassMembersFromMixin(MemberMap map, InterfaceType type) {
|
| + List<MethodElement> methods = type.methods;
|
| + for (MethodElement method in methods) {
|
| + if (method.isAccessibleIn(_library) && !method.isStatic) {
|
| + String methodName = method.name;
|
| + ExecutableElement elementInMap = map.get(methodName);
|
| + if (elementInMap == null || (elementInMap != null && !method.isAbstract)) {
|
| + map.put(methodName, method);
|
| + }
|
| + }
|
| + }
|
| + List<PropertyAccessorElement> accessors = type.accessors;
|
| + for (PropertyAccessorElement accessor in accessors) {
|
| + if (accessor.isAccessibleIn(_library) && !accessor.isStatic) {
|
| + String accessorName = accessor.name;
|
| + ExecutableElement elementInMap = map.get(accessorName);
|
| + if (elementInMap == null || (elementInMap != null && !accessor.isAbstract)) {
|
| + map.put(accessorName, accessor);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| * This method is used to report errors on when they are found computing inheritance information.
|
| * See [ErrorVerifier#checkForInconsistentMethodInheritance] to see where these generated
|
| * error codes are reported back into the analysis engine.
|
| @@ -8292,7 +8732,7 @@ class Library {
|
| try {
|
| parseUriWithException(uriContent);
|
| Source source = _analysisContext.sourceFactory.resolveUri(librarySource, uriContent);
|
| - if (source == null || !source.exists()) {
|
| + if (!_analysisContext.exists(source)) {
|
| _errorListener.onError(new AnalysisError.con2(librarySource, uriLiteral.offset, uriLiteral.length, CompileTimeErrorCode.URI_DOES_NOT_EXIST, [uriContent]));
|
| }
|
| return source;
|
| @@ -8371,7 +8811,7 @@ class LibraryElementBuilder {
|
| /**
|
| * The name of the function used as an entry point.
|
| */
|
| - static String _ENTRY_POINT_NAME = "main";
|
| + static String ENTRY_POINT_NAME = "main";
|
|
|
| /**
|
| * Initialize a newly created library element builder.
|
| @@ -8417,7 +8857,7 @@ class LibraryElementBuilder {
|
| PartDirective partDirective = directive;
|
| StringLiteral partUri = partDirective.uri;
|
| Source partSource = library.getSource(partDirective);
|
| - if (partSource != null && partSource.exists()) {
|
| + if (_analysisContext.exists(partSource)) {
|
| hasPartDirective = true;
|
| CompilationUnitElementImpl part = builder.buildCompilationUnit(partSource, library.getAST(partSource));
|
| part.uri = library.getUri(partDirective);
|
| @@ -8493,7 +8933,7 @@ class LibraryElementBuilder {
|
| */
|
| FunctionElement findEntryPoint(CompilationUnitElementImpl element) {
|
| for (FunctionElement function in element.functions) {
|
| - if (function.name == _ENTRY_POINT_NAME) {
|
| + if (function.name == ENTRY_POINT_NAME) {
|
| return function;
|
| }
|
| }
|
| @@ -8955,6 +9395,13 @@ class LibraryResolver {
|
| LibraryElementImpl libraryElement = library.libraryElement;
|
| libraryElement.imports = new List.from(imports);
|
| libraryElement.exports = new List.from(exports);
|
| + if (libraryElement.entryPoint == null) {
|
| + Namespace namespace = new NamespaceBuilder().createExportNamespace2(libraryElement);
|
| + Element element = namespace.get(LibraryElementBuilder.ENTRY_POINT_NAME);
|
| + if (element is FunctionElement) {
|
| + libraryElement.entryPoint = element;
|
| + }
|
| + }
|
| }
|
| }
|
|
|
| @@ -9126,7 +9573,6 @@ class LibraryResolver {
|
| */
|
| Library createLibrary(Source librarySource) {
|
| Library library = new Library(analysisContext, _errorListener, librarySource);
|
| - library.definingCompilationUnit;
|
| _libraryMap[librarySource] = library;
|
| return library;
|
| }
|
| @@ -9158,7 +9604,7 @@ class LibraryResolver {
|
| * @return the library object that was created
|
| */
|
| Library createLibraryOrNull(Source librarySource) {
|
| - if (!librarySource.exists()) {
|
| + if (!analysisContext.exists(librarySource)) {
|
| return null;
|
| }
|
| Library library = new Library(analysisContext, _errorListener, librarySource);
|
| @@ -9247,7 +9693,7 @@ class LibraryResolver {
|
| try {
|
| for (Source source in library.compilationUnitSources) {
|
| CompilationUnit ast = library.getAST(source);
|
| - new AngularCompilationUnitBuilder(_errorListener, source).build(ast);
|
| + new AngularCompilationUnitBuilder(_errorListener, source, ast).build();
|
| }
|
| } finally {
|
| timeCounter.stop();
|
| @@ -9450,14 +9896,6 @@ class MemberMap {
|
| */
|
| class ProxyConditionalAnalysisError {
|
| /**
|
| - * Return `true` if the given element represents a class that has the proxy annotation.
|
| - *
|
| - * @param element the class being tested
|
| - * @return `true` if the given element represents a class that has the proxy annotation
|
| - */
|
| - static bool classHasProxyAnnotation(Element element) => (element is ClassElement) && element.isProxy;
|
| -
|
| - /**
|
| * The enclosing [ClassElement], this is what will determine if the error code should, or
|
| * should not, be generated on the source.
|
| */
|
| @@ -9469,8 +9907,8 @@ class ProxyConditionalAnalysisError {
|
| final AnalysisError analysisError;
|
|
|
| /**
|
| - * Instantiate a new ProxyConditionalErrorCode with some enclosing element and the conditional
|
| - * analysis error.
|
| + * Instantiate a new [ProxyConditionalAnalysisError] with some enclosing element and the
|
| + * conditional analysis error.
|
| *
|
| * @param enclosingElement the enclosing element
|
| * @param analysisError the conditional analysis error
|
| @@ -9484,7 +9922,12 @@ class ProxyConditionalAnalysisError {
|
| *
|
| * @return `true` iff the enclosing class has the proxy annotation
|
| */
|
| - bool shouldIncludeErrorCode() => !classHasProxyAnnotation(_enclosingElement);
|
| + bool shouldIncludeErrorCode() {
|
| + if (_enclosingElement is ClassElement) {
|
| + return !(_enclosingElement as ClassElement).isOrInheritsProxy;
|
| + }
|
| + return true;
|
| + }
|
| }
|
|
|
| /**
|
| @@ -13888,6 +14331,24 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| }
|
| if (classElement != null && superclassType != null) {
|
| classElement.supertype = superclassType;
|
| + ClassElement superclassElement = superclassType.element;
|
| + if (superclassElement != null) {
|
| + List<ConstructorElement> constructors = superclassElement.constructors;
|
| + int count = constructors.length;
|
| + if (count > 0) {
|
| + List<Type2> parameterTypes = TypeParameterTypeImpl.getTypes(superclassType.typeParameters);
|
| + List<Type2> argumentTypes = getArgumentTypes(node.superclass.typeArguments, parameterTypes);
|
| + InterfaceType classType = classElement.type;
|
| + List<ConstructorElement> implicitConstructors = new List<ConstructorElement>();
|
| + for (int i = 0; i < count; i++) {
|
| + ConstructorElement explicitConstructor = constructors[i];
|
| + if (!explicitConstructor.isFactory) {
|
| + implicitConstructors.add(createImplicitContructor(classType, explicitConstructor, parameterTypes, argumentTypes));
|
| + }
|
| + }
|
| + classElement.constructors = new List.from(implicitConstructors);
|
| + }
|
| + }
|
| }
|
| resolve(classElement, node.withClause, node.implementsClause);
|
| return null;
|
| @@ -14328,6 +14789,73 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| }
|
|
|
| /**
|
| + * Create an implicit constructor that is copied from the given constructor, but that is in the
|
| + * given class.
|
| + *
|
| + * @param classType the class in which the implicit constructor is defined
|
| + * @param explicitConstructor the constructor on which the implicit constructor is modeled
|
| + * @param parameterTypes the types to be replaced when creating parameters
|
| + * @param argumentTypes the types with which the parameters are to be replaced
|
| + * @return the implicit constructor that was created
|
| + */
|
| + ConstructorElement createImplicitContructor(InterfaceType classType, ConstructorElement explicitConstructor, List<Type2> parameterTypes, List<Type2> argumentTypes) {
|
| + ConstructorElementImpl implicitConstructor = new ConstructorElementImpl.con2(explicitConstructor.name, -1);
|
| + implicitConstructor.synthetic = true;
|
| + implicitConstructor.redirectedConstructor = explicitConstructor;
|
| + implicitConstructor.const2 = explicitConstructor.isConst;
|
| + implicitConstructor.returnType = classType;
|
| + List<ParameterElement> explicitParameters = explicitConstructor.parameters;
|
| + int count = explicitParameters.length;
|
| + if (count > 0) {
|
| + List<ParameterElement> implicitParameters = new List<ParameterElement>(count);
|
| + for (int i = 0; i < count; i++) {
|
| + ParameterElement explicitParameter = explicitParameters[i];
|
| + ParameterElementImpl implicitParameter = new ParameterElementImpl.con2(explicitParameter.name, -1);
|
| + implicitParameter.const3 = explicitParameter.isConst;
|
| + implicitParameter.final2 = explicitParameter.isFinal;
|
| + implicitParameter.parameterKind = explicitParameter.parameterKind;
|
| + implicitParameter.synthetic = true;
|
| + implicitParameter.type = explicitParameter.type.substitute2(argumentTypes, parameterTypes);
|
| + implicitParameters[i] = implicitParameter;
|
| + }
|
| + implicitConstructor.parameters = implicitParameters;
|
| + }
|
| + FunctionTypeImpl type = new FunctionTypeImpl.con1(implicitConstructor);
|
| + type.typeArguments = classType.typeArguments;
|
| + implicitConstructor.type = type;
|
| + return implicitConstructor;
|
| + }
|
| +
|
| + /**
|
| + * Return an array of argument types that corresponds to the array of parameter types and that are
|
| + * derived from the given list of type arguments.
|
| + *
|
| + * @param typeArguments the type arguments from which the types will be taken
|
| + * @param parameterTypes the parameter types that must be matched by the type arguments
|
| + * @return the argument types that correspond to the parameter types
|
| + */
|
| + List<Type2> getArgumentTypes(TypeArgumentList typeArguments, List<Type2> parameterTypes) {
|
| + DynamicTypeImpl dynamic = DynamicTypeImpl.instance;
|
| + int parameterCount = parameterTypes.length;
|
| + List<Type2> types = new List<Type2>(parameterCount);
|
| + if (typeArguments == null) {
|
| + for (int i = 0; i < parameterCount; i++) {
|
| + types[i] = dynamic;
|
| + }
|
| + } else {
|
| + NodeList<TypeName> arguments = typeArguments.arguments;
|
| + int argumentCount = Math.min(arguments.length, parameterCount);
|
| + for (int i = 0; i < argumentCount; i++) {
|
| + types[i] = arguments[i].type;
|
| + }
|
| + for (int i = argumentCount; i < parameterCount; i++) {
|
| + types[i] = dynamic;
|
| + }
|
| + }
|
| + return types;
|
| + }
|
| +
|
| + /**
|
| * Return the class element that represents the class whose name was provided.
|
| *
|
| * @param identifier the name from the declaration of a class
|
| @@ -16432,16 +16960,16 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| ExecutableElement _enclosingFunction;
|
|
|
| /**
|
| - * The number of return statements found in the method or function that we are currently visiting
|
| - * that have a return value.
|
| + * The return statements found in the method or function that we are currently visiting that have
|
| + * a return value.
|
| */
|
| - int _returnWithCount = 0;
|
| + List<ReturnStatement> _returnsWith = new List<ReturnStatement>();
|
|
|
| /**
|
| - * The number of return statements found in the method or function that we are currently visiting
|
| - * that do not have a return value.
|
| + * The return statements found in the method or function that we are currently visiting that do
|
| + * not have a return value.
|
| */
|
| - int _returnWithoutCount = 0;
|
| + List<ReturnStatement> _returnsWithout = new List<ReturnStatement>();
|
|
|
| /**
|
| * This map is initialized when visiting the contents of a class declaration. If the visitor is
|
| @@ -16544,16 +17072,27 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
|
|
| Object visitBlockFunctionBody(BlockFunctionBody node) {
|
| - int previousReturnWithCount = _returnWithCount;
|
| - int previousReturnWithoutCount = _returnWithoutCount;
|
| + List<ReturnStatement> previousReturnsWith = _returnsWith;
|
| + List<ReturnStatement> previousReturnsWithout = _returnsWithout;
|
| try {
|
| - _returnWithCount = 0;
|
| - _returnWithoutCount = 0;
|
| + _returnsWith = new List<ReturnStatement>();
|
| + _returnsWithout = new List<ReturnStatement>();
|
| super.visitBlockFunctionBody(node);
|
| checkForMixedReturns(node);
|
| } finally {
|
| - _returnWithCount = previousReturnWithCount;
|
| - _returnWithoutCount = previousReturnWithoutCount;
|
| + _returnsWith = previousReturnsWith;
|
| + _returnsWithout = previousReturnsWithout;
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + Object visitBreakStatement(BreakStatement node) {
|
| + SimpleIdentifier labelNode = node.label;
|
| + if (labelNode != null) {
|
| + Element labelElement = labelNode.staticElement;
|
| + if (labelElement is LabelElementImpl && labelElement.isOnSwitchMember) {
|
| + _errorReporter.reportError3(ResolverErrorCode.BREAK_LABEL_ON_SWITCH_MEMBER, labelNode, []);
|
| + }
|
| }
|
| return null;
|
| }
|
| @@ -16669,6 +17208,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| Object visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
|
| _isInConstructorInitializer = true;
|
| try {
|
| + checkForInvalidField(node);
|
| checkForFieldInitializerNotAssignable(node);
|
| return super.visitConstructorFieldInitializer(node);
|
| } finally {
|
| @@ -16676,6 +17216,17 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
| }
|
|
|
| + Object visitContinueStatement(ContinueStatement node) {
|
| + SimpleIdentifier labelNode = node.label;
|
| + if (labelNode != null) {
|
| + Element labelElement = labelNode.staticElement;
|
| + if (labelElement is LabelElementImpl && labelElement.isOnSwitchStatement) {
|
| + _errorReporter.reportError3(ResolverErrorCode.CONTINUE_LABEL_ON_SWITCH, labelNode, []);
|
| + }
|
| + }
|
| + return null;
|
| + }
|
| +
|
| Object visitDefaultFormalParameter(DefaultFormalParameter node) {
|
| checkForInvalidAssignment2(node.identifier, node.defaultValue);
|
| checkForDefaultValueInFunctionTypedParameter(node);
|
| @@ -16711,7 +17262,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| _isInStaticVariableDeclaration = node.isStatic;
|
| _isInInstanceVariableDeclaration = !_isInStaticVariableDeclaration;
|
| try {
|
| - checkForAllInvalidOverrideErrorCodes2(node);
|
| + checkForAllInvalidOverrideErrorCodes3(node);
|
| return super.visitFieldDeclaration(node);
|
| } finally {
|
| _isInStaticVariableDeclaration = false;
|
| @@ -16720,6 +17271,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
|
|
| Object visitFieldFormalParameter(FieldFormalParameter node) {
|
| + checkForValidField(node);
|
| checkForConstFormalParameter(node);
|
| checkForPrivateOptionalParameter(node);
|
| checkForFieldInitializingFormalRedirectingConstructor(node);
|
| @@ -16892,7 +17444,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| checkForConflictingInstanceMethodSetter(node);
|
| }
|
| checkForConcreteClassWithAbstractMember(node);
|
| - checkForAllInvalidOverrideErrorCodes3(node);
|
| + checkForAllInvalidOverrideErrorCodes4(node);
|
| return super.visitMethodDeclaration(node);
|
| } finally {
|
| _enclosingFunction = previousFunction;
|
| @@ -16978,9 +17530,9 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
|
|
| Object visitReturnStatement(ReturnStatement node) {
|
| if (node.expression == null) {
|
| - _returnWithoutCount++;
|
| + _returnsWithout.add(node);
|
| } else {
|
| - _returnWithCount++;
|
| + _returnsWith.add(node);
|
| }
|
| checkForAllReturnStatementErrorCodes(node);
|
| return super.visitReturnStatement(node);
|
| @@ -17195,6 +17747,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * This checks the passed executable element against override-error codes.
|
| *
|
| * @param executableElement a non-null [ExecutableElement] to evaluate
|
| + * @param overriddenExecutable the element that the executableElement is overriding
|
| * @param parameters the parameters of the executable element
|
| * @param errorNameTarget the node to report problems on
|
| * @return `true` if and only if an error code is generated on the passed node
|
| @@ -17210,10 +17763,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticWarningCode#INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE
|
| * @see StaticWarningCode#INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES
|
| */
|
| - bool checkForAllInvalidOverrideErrorCodes(ExecutableElement executableElement, List<ParameterElement> parameters, List<ASTNode> parameterLocations, SimpleIdentifier errorNameTarget) {
|
| - String executableElementName = executableElement.name;
|
| - bool executableElementPrivate = Identifier.isPrivateName(executableElementName);
|
| - ExecutableElement overriddenExecutable = _inheritanceManager.lookupInheritance(_enclosingClass, executableElementName);
|
| + bool checkForAllInvalidOverrideErrorCodes(ExecutableElement executableElement, ExecutableElement overriddenExecutable, List<ParameterElement> parameters, List<ASTNode> parameterLocations, SimpleIdentifier errorNameTarget) {
|
| bool isGetter = false;
|
| bool isSetter = false;
|
| if (executableElement is PropertyAccessorElement) {
|
| @@ -17221,12 +17771,14 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| isGetter = accessorElement.isGetter;
|
| isSetter = accessorElement.isSetter;
|
| }
|
| + String executableElementName = executableElement.name;
|
| // SWC.INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC
|
| if (overriddenExecutable == null) {
|
| if (!isGetter && !isSetter && !executableElement.isOperator) {
|
| Set<ClassElement> visitedClasses = new Set<ClassElement>();
|
| InterfaceType superclassType = _enclosingClass.supertype;
|
| ClassElement superclassElement = superclassType == null ? null : superclassType.element;
|
| + bool executableElementPrivate = Identifier.isPrivateName(executableElementName);
|
| while (superclassElement != null && !visitedClasses.contains(superclassElement)) {
|
| visitedClasses.add(superclassElement);
|
| LibraryElement superclassLibrary = superclassElement.library;
|
| @@ -17467,13 +18019,50 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
|
|
| /**
|
| + * This checks the passed executable element against override-error codes. This method computes
|
| + * the passed executableElement is overriding and calls
|
| + * [checkForAllInvalidOverrideErrorCodes]
|
| + * when the [InheritanceManager] returns a [MultiplyInheritedExecutableElement], this
|
| + * method loops through the array in the [MultiplyInheritedExecutableElement].
|
| + *
|
| + * @param executableElement a non-null [ExecutableElement] to evaluate
|
| + * @param parameters the parameters of the executable element
|
| + * @param errorNameTarget the node to report problems on
|
| + * @return `true` if and only if an error code is generated on the passed node
|
| + */
|
| + bool checkForAllInvalidOverrideErrorCodes2(ExecutableElement executableElement, List<ParameterElement> parameters, List<ASTNode> parameterLocations, SimpleIdentifier errorNameTarget) {
|
| + //
|
| + // Compute the overridden executable from the InheritanceManager
|
| + //
|
| + ExecutableElement overriddenExecutable = _inheritanceManager.lookupInheritance(_enclosingClass, executableElement.name);
|
| + //
|
| + // If the result is a MultiplyInheritedExecutableElement call
|
| + // checkForAllInvalidOverrideErrorCodes on all of the elements, until an error is found.
|
| + //
|
| + if (overriddenExecutable is MultiplyInheritedExecutableElement) {
|
| + MultiplyInheritedExecutableElement multiplyInheritedElement = overriddenExecutable;
|
| + List<ExecutableElement> overriddenElement = multiplyInheritedElement.inheritedElements;
|
| + for (int i = 0; i < overriddenElement.length; i++) {
|
| + if (checkForAllInvalidOverrideErrorCodes(executableElement, overriddenElement[i], parameters, parameterLocations, errorNameTarget)) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| + //
|
| + // Otherwise, just call checkForAllInvalidOverrideErrorCodes.
|
| + //
|
| + return checkForAllInvalidOverrideErrorCodes(executableElement, overriddenExecutable, parameters, parameterLocations, errorNameTarget);
|
| + }
|
| +
|
| + /**
|
| * This checks the passed field declaration against override-error codes.
|
| *
|
| * @param node the [MethodDeclaration] to evaluate
|
| * @return `true` if and only if an error code is generated on the passed node
|
| * @see #checkForAllInvalidOverrideErrorCodes(ExecutableElement)
|
| */
|
| - bool checkForAllInvalidOverrideErrorCodes2(FieldDeclaration node) {
|
| + bool checkForAllInvalidOverrideErrorCodes3(FieldDeclaration node) {
|
| if (_enclosingClass == null || node.isStatic) {
|
| return false;
|
| }
|
| @@ -17488,10 +18077,10 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| PropertyAccessorElement setter = element.setter;
|
| SimpleIdentifier fieldName = field.name;
|
| if (getter != null) {
|
| - hasProblems = javaBooleanOr(hasProblems, checkForAllInvalidOverrideErrorCodes(getter, ParameterElementImpl.EMPTY_ARRAY, ASTNode.EMPTY_ARRAY, fieldName));
|
| + hasProblems = javaBooleanOr(hasProblems, checkForAllInvalidOverrideErrorCodes2(getter, ParameterElementImpl.EMPTY_ARRAY, ASTNode.EMPTY_ARRAY, fieldName));
|
| }
|
| if (setter != null) {
|
| - hasProblems = javaBooleanOr(hasProblems, checkForAllInvalidOverrideErrorCodes(setter, setter.parameters, <ASTNode> [fieldName], fieldName));
|
| + hasProblems = javaBooleanOr(hasProblems, checkForAllInvalidOverrideErrorCodes2(setter, setter.parameters, <ASTNode> [fieldName], fieldName));
|
| }
|
| }
|
| return hasProblems;
|
| @@ -17504,7 +18093,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @return `true` if and only if an error code is generated on the passed node
|
| * @see #checkForAllInvalidOverrideErrorCodes(ExecutableElement)
|
| */
|
| - bool checkForAllInvalidOverrideErrorCodes3(MethodDeclaration node) {
|
| + bool checkForAllInvalidOverrideErrorCodes4(MethodDeclaration node) {
|
| if (_enclosingClass == null || node.isStatic || node.body is NativeFunctionBody) {
|
| return false;
|
| }
|
| @@ -17519,7 +18108,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| FormalParameterList formalParameterList = node.parameters;
|
| NodeList<FormalParameter> parameterList = formalParameterList != null ? formalParameterList.parameters : null;
|
| List<ASTNode> parameters = parameterList != null ? new List.from(parameterList) : null;
|
| - return checkForAllInvalidOverrideErrorCodes(executableElement, executableElement.parameters, parameters, methodName);
|
| + return checkForAllInvalidOverrideErrorCodes2(executableElement, executableElement.parameters, parameters, methodName);
|
| }
|
|
|
| /**
|
| @@ -17831,7 +18420,7 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| return true;
|
| }
|
| if (variable.isFinal) {
|
| - _errorReporter.reportError3(StaticWarningCode.ASSIGNMENT_TO_FINAL, expression, []);
|
| + _errorReporter.reportError3(StaticWarningCode.ASSIGNMENT_TO_FINAL, expression, [variable.name]);
|
| return true;
|
| }
|
| return false;
|
| @@ -18807,11 +19396,11 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| */
|
| bool checkForFieldInitializerNotAssignable(ConstructorFieldInitializer node) {
|
| // prepare field element
|
| - Element fieldNameElement = node.fieldName.staticElement;
|
| - if (fieldNameElement is! FieldElement) {
|
| + Element staticElement = node.fieldName.staticElement;
|
| + if (staticElement is! FieldElement) {
|
| return false;
|
| }
|
| - FieldElement fieldElement = fieldNameElement as FieldElement;
|
| + FieldElement fieldElement = staticElement as FieldElement;
|
| // prepare field type
|
| Type2 fieldType = fieldElement.type;
|
| // prepare expression type
|
| @@ -19214,7 +19803,13 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| return false;
|
| }
|
| if (!rightType.isAssignableTo(leftType)) {
|
| - _errorReporter.reportError3(StaticTypeWarningCode.INVALID_ASSIGNMENT, node.rightHandSide, [rightType.displayName, leftType.displayName]);
|
| + String leftName = leftType.displayName;
|
| + String rightName = rightType.displayName;
|
| + if (leftName == rightName) {
|
| + leftName = getExtendedDisplayName(leftType);
|
| + rightName = getExtendedDisplayName(rightType);
|
| + }
|
| + _errorReporter.reportError3(StaticTypeWarningCode.INVALID_ASSIGNMENT, node.rightHandSide, [rightName, leftName]);
|
| return true;
|
| }
|
| return false;
|
| @@ -19237,7 +19832,13 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| Type2 staticRightType = getStaticType(rhs);
|
| bool isStaticAssignable = staticRightType.isAssignableTo(leftType);
|
| if (!isStaticAssignable) {
|
| - _errorReporter.reportError3(StaticTypeWarningCode.INVALID_ASSIGNMENT, rhs, [staticRightType.displayName, leftType.displayName]);
|
| + String leftName = leftType.displayName;
|
| + String rightName = staticRightType.displayName;
|
| + if (leftName == rightName) {
|
| + leftName = getExtendedDisplayName(leftType);
|
| + rightName = getExtendedDisplayName(staticRightType);
|
| + }
|
| + _errorReporter.reportError3(StaticTypeWarningCode.INVALID_ASSIGNMENT, rhs, [rightName, leftName]);
|
| return true;
|
| }
|
| // TODO(brianwilkerson) Define a hint corresponding to the warning and report it if appropriate.
|
| @@ -19255,6 +19856,27 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
|
|
| /**
|
| + * Check the given initializer to ensure that the field being initialized is a valid field.
|
| + *
|
| + * @param node the field initializer being checked
|
| + */
|
| + void checkForInvalidField(ConstructorFieldInitializer node) {
|
| + SimpleIdentifier fieldName = node.fieldName;
|
| + Element staticElement = fieldName.staticElement;
|
| + if (staticElement is FieldElement) {
|
| + FieldElement fieldElement = staticElement;
|
| + if (fieldElement.isSynthetic) {
|
| + _errorReporter.reportError3(CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTANT_FIELD, node, [fieldName]);
|
| + } else if (fieldElement.isStatic) {
|
| + _errorReporter.reportError3(CompileTimeErrorCode.INITIALIZER_FOR_STATIC_FIELD, node, [fieldName]);
|
| + }
|
| + } else {
|
| + _errorReporter.reportError3(CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTANT_FIELD, node, [fieldName]);
|
| + return;
|
| + }
|
| + }
|
| +
|
| + /**
|
| * This verifies that the usage of the passed 'this' is valid.
|
| *
|
| * @param node the 'this' expression to evaluate
|
| @@ -19488,8 +20110,15 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| * @see StaticWarningCode#MIXED_RETURN_TYPES
|
| */
|
| bool checkForMixedReturns(BlockFunctionBody node) {
|
| - if (_returnWithCount > 0 && _returnWithoutCount > 0) {
|
| - _errorReporter.reportError3(StaticWarningCode.MIXED_RETURN_TYPES, node, []);
|
| + int withCount = _returnsWith.length;
|
| + int withoutCount = _returnsWithout.length;
|
| + if (withCount > 0 && withoutCount > 0) {
|
| + for (int i = 0; i < withCount; i++) {
|
| + _errorReporter.reportError6(StaticWarningCode.MIXED_RETURN_TYPES, _returnsWith[i].keyword, []);
|
| + }
|
| + for (int i = 0; i < withoutCount; i++) {
|
| + _errorReporter.reportError6(StaticWarningCode.MIXED_RETURN_TYPES, _returnsWithout[i].keyword, []);
|
| + }
|
| return true;
|
| }
|
| return false;
|
| @@ -19702,8 +20331,8 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| if (memberName == null) {
|
| break;
|
| }
|
| - // If the element is defined in Object, skip it.
|
| - if ((executableElt.enclosingElement as ClassElement).type.isObject) {
|
| + // If the element is not synthetic and can be determined to be defined in Object, skip it.
|
| + if (executableElt.enclosingElement != null && (executableElt.enclosingElement as ClassElement).type.isObject) {
|
| continue;
|
| }
|
| // Reference the type of the enclosing class
|
| @@ -19747,7 +20376,12 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| List<ExecutableElement> missingOverridesArray = new List.from(missingOverrides);
|
| List<String> stringMembersArrayListSet = new List<String>();
|
| for (int i = 0; i < missingOverridesArray.length; i++) {
|
| - String newStrMember = "${missingOverridesArray[i].enclosingElement.displayName}.${missingOverridesArray[i].displayName}";
|
| + String newStrMember;
|
| + if (missingOverridesArray[i].enclosingElement != null) {
|
| + newStrMember = "${missingOverridesArray[i].enclosingElement.displayName}.${missingOverridesArray[i].displayName}";
|
| + } else {
|
| + newStrMember = missingOverridesArray[i].displayName;
|
| + }
|
| if (!stringMembersArrayListSet.contains(newStrMember)) {
|
| stringMembersArrayListSet.add(newStrMember);
|
| }
|
| @@ -20533,6 +21167,36 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| return true;
|
| }
|
|
|
| + void checkForValidField(FieldFormalParameter node) {
|
| + ParameterElement element = node.element;
|
| + if (element is FieldFormalParameterElement) {
|
| + FieldElement fieldElement = element.field;
|
| + if (fieldElement == null || fieldElement.isSynthetic) {
|
| + _errorReporter.reportError3(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTANT_FIELD, node, [node.identifier.name]);
|
| + } else {
|
| + ParameterElement parameterElement = node.element;
|
| + if (parameterElement is FieldFormalParameterElementImpl) {
|
| + FieldFormalParameterElementImpl fieldFormal = parameterElement;
|
| + Type2 declaredType = fieldFormal.type;
|
| + Type2 fieldType = fieldElement.type;
|
| + if (fieldElement.isSynthetic) {
|
| + _errorReporter.reportError3(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTANT_FIELD, node, [node.identifier.name]);
|
| + } else if (fieldElement.isStatic) {
|
| + _errorReporter.reportError3(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_STATIC_FIELD, node, [node.identifier.name]);
|
| + } else if (declaredType != null && fieldType != null && !declaredType.isAssignableTo(fieldType)) {
|
| + _errorReporter.reportError3(StaticWarningCode.FIELD_INITIALIZING_FORMAL_NOT_ASSIGNABLE, node, [declaredType.displayName, fieldType.displayName]);
|
| + }
|
| + } else {
|
| + if (fieldElement.isSynthetic) {
|
| + _errorReporter.reportError3(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTANT_FIELD, node, [node.identifier.name]);
|
| + } else if (fieldElement.isStatic) {
|
| + _errorReporter.reportError3(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_STATIC_FIELD, node, [node.identifier.name]);
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| /**
|
| * This verifies the passed operator-method declaration, has correct number of parameters.
|
| *
|
| @@ -20660,6 +21324,24 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
|
|
| /**
|
| + * Return a display name for the given type that includes the path to the compilation unit in
|
| + * which the type is defined.
|
| + *
|
| + * @param type the type for which an extended display name is to be returned
|
| + * @return a display name that can help distiguish between two types with the same name
|
| + */
|
| + String getExtendedDisplayName(Type2 type) {
|
| + Element element = type.element;
|
| + if (element != null) {
|
| + Source source = element.source;
|
| + if (source != null) {
|
| + return "${type.displayName} (${source.fullName})";
|
| + }
|
| + }
|
| + return type.displayName;
|
| + }
|
| +
|
| + /**
|
| * Returns the Type (return type) for a given getter.
|
| *
|
| * @param propertyAccessorElement
|
| @@ -20932,88 +21614,6 @@ class ErrorVerifier extends RecursiveASTVisitor<Object> {
|
| }
|
|
|
| bool isUserDefinedObject(EvaluationResultImpl result) => result == null || (result is ValidResult && result.isUserDefinedObject);
|
| -
|
| - /**
|
| - * Return `true` iff the passed [ClassElement] has a concrete implementation of the
|
| - * passed accessor name in the superclass chain.
|
| - */
|
| - bool memberHasConcreteAccessorImplementationInSuperclassChain(ClassElement classElement, String accessorName, List<ClassElement> superclassChain) {
|
| - if (superclassChain.contains(classElement)) {
|
| - return false;
|
| - } else {
|
| - superclassChain.add(classElement);
|
| - }
|
| - for (PropertyAccessorElement accessor in classElement.accessors) {
|
| - if (accessor.name == accessorName) {
|
| - if (!accessor.isAbstract) {
|
| - return true;
|
| - }
|
| - }
|
| - }
|
| - for (InterfaceType mixinType in classElement.mixins) {
|
| - if (mixinType != null) {
|
| - ClassElement mixinElement = mixinType.element;
|
| - if (mixinElement != null) {
|
| - for (PropertyAccessorElement accessor in mixinElement.accessors) {
|
| - if (accessor.name == accessorName) {
|
| - if (!accessor.isAbstract) {
|
| - return true;
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| - InterfaceType superType = classElement.supertype;
|
| - if (superType != null) {
|
| - ClassElement superClassElt = superType.element;
|
| - if (superClassElt != null) {
|
| - return memberHasConcreteAccessorImplementationInSuperclassChain(superClassElt, accessorName, superclassChain);
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - /**
|
| - * Return `true` iff the passed [ClassElement] has a concrete implementation of the
|
| - * passed method name in the superclass chain.
|
| - */
|
| - bool memberHasConcreteMethodImplementationInSuperclassChain(ClassElement classElement, String methodName, List<ClassElement> superclassChain) {
|
| - if (superclassChain.contains(classElement)) {
|
| - return false;
|
| - } else {
|
| - superclassChain.add(classElement);
|
| - }
|
| - for (MethodElement method in classElement.methods) {
|
| - if (method.name == methodName) {
|
| - if (!method.isAbstract) {
|
| - return true;
|
| - }
|
| - }
|
| - }
|
| - for (InterfaceType mixinType in classElement.mixins) {
|
| - if (mixinType != null) {
|
| - ClassElement mixinElement = mixinType.element;
|
| - if (mixinElement != null) {
|
| - for (MethodElement method in mixinElement.methods) {
|
| - if (method.name == methodName) {
|
| - if (!method.isAbstract) {
|
| - return true;
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| - InterfaceType superType = classElement.supertype;
|
| - if (superType != null) {
|
| - ClassElement superClassElt = superType.element;
|
| - if (superClassElt != null) {
|
| - return memberHasConcreteMethodImplementationInSuperclassChain(superClassElt, methodName, superclassChain);
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| }
|
|
|
| /**
|
|
|