package com.google.dart.engine.services.completion; import com.google.dart.engine.ast.Annotation; import com.google.dart.engine.ast.ArgumentList; import com.google.dart.engine.ast.AstNode; import com.google.dart.engine.ast.CatchClause; import com.google.dart.engine.ast.ClassDeclaration; import com.google.dart.engine.ast.CompilationUnitMember; import com.google.dart.engine.ast.ConstructorInitializer; import com.google.dart.engine.ast.Declaration; import com.google.dart.engine.ast.Directive; import com.google.dart.engine.ast.DoStatement; import com.google.dart.engine.ast.Expression; import com.google.dart.engine.ast.FieldDeclaration; import com.google.dart.engine.ast.ForEachStatement; import com.google.dart.engine.ast.FunctionExpression; import com.google.dart.engine.ast.FunctionTypeAlias; import com.google.dart.engine.ast.Identifier; import com.google.dart.engine.ast.InstanceCreationExpression; import com.google.dart.engine.ast.MethodDeclaration; import com.google.dart.engine.ast.PrefixedIdentifier; import com.google.dart.engine.ast.PropertyAccess; import com.google.dart.engine.ast.SimpleFormalParameter; import com.google.dart.engine.ast.SimpleIdentifier; import com.google.dart.engine.ast.SwitchStatement; import com.google.dart.engine.ast.TypeArgumentList; import com.google.dart.engine.ast.TypeName; import com.google.dart.engine.ast.VariableDeclaration; import com.google.dart.engine.ast.VariableDeclarationList; import com.google.dart.engine.ast.WhileStatement; import com.google.dart.engine.ast.WithClause; import com.google.dart.engine.ast.visitor.GeneralizingAstVisitor; import com.google.dart.engine.element.ClassElement; /** * @coverage com.google.dart.engine.services.completion */ class ContextAnalyzer extends GeneralizingAstVisitor<Void> { CompletionState state; AstNode completionNode; AstNode child; boolean inExpression; boolean inIdentifier; boolean inTypeName; boolean maybeInvocationArgument = true; ContextAnalyzer(CompletionState state, AstNode completionNode) { this.state = state; this.completionNode = completionNode; } @Override public Void visitAnnotation(Annotation node) { state.requiresConst(true); return super.visitAnnotation(node); } @Override public Void visitCatchClause(CatchClause node) { if (node.getExceptionType() == child) { state.prohibitsLiterals(); } return null; } @Override public Void visitCompilationUnitMember(CompilationUnitMember node) { if (!(node instanceof ClassDeclaration)) { state.prohibitThis(); } return super.visitCompilationUnitMember(node); } @Override public Void visitConstructorInitializer(ConstructorInitializer node) { state.prohibitThis(); return super.visitConstructorInitializer(node); } @Override public Void visitDirective(Directive node) { state.prohibitsLiterals(); return super.visitDirective(node); } @Override public Void visitDoStatement(DoStatement node) { if (child == node.getCondition()) { state.includesLiterals(); } return super.visitDoStatement(node); } @Override public Void visitExpression(Expression node) { inExpression = true; state.includesLiterals(); mayBeSetParameterElement(node); return super.visitExpression(node); } @Override public Void visitFieldDeclaration(FieldDeclaration node) { state.prohibitThis(); return super.visitFieldDeclaration(node); } @Override public Void visitForEachStatement(ForEachStatement node) { if (child == node.getIterator()) { state.includesLiterals(); } return super.visitForEachStatement(node); } @Override public Void visitFunctionExpression(FunctionExpression node) { if (node.getParent() instanceof Declaration) { // Function expressions that are part of a declaration are not to be treated as expressions. return visitNode(node); } else { return visitExpression(node); } } @Override public Void visitFunctionTypeAlias(FunctionTypeAlias node) { if (inTypeName || node.getReturnType() == null) { // This may be an incomplete class type alias state.includesUndefinedDeclarationTypes(); } return super.visitFunctionTypeAlias(node); } @Override public Void visitIdentifier(Identifier node) { mayBeSetParameterElement(node); // Identifiers cannot safely be generalized to expressions, so just walk up one level. // LibraryIdentifier is never an expression. PrefixedIdentifier may be an expression, but // not in a catch-clause or a declaration. SimpleIdentifier may be an expression, but not // in a constructor name, label, or where PrefixedIdentifier is not. return visitNode(node); } @Override public Void visitInstanceCreationExpression(InstanceCreationExpression node) { state.requiresConst(node.isConst()); if (completionNode.getParent().getParent() == child) { state.mustBeInstantiableType(); } return super.visitInstanceCreationExpression(node); } @Override public Void visitMethodDeclaration(MethodDeclaration node) { state.sourceDeclarationIsStatic(node.isStatic()); if (child == node.getReturnType()) { state.includesUndefinedDeclarationTypes(); } if (node.isStatic()) { state.prohibitThis(); } return super.visitMethodDeclaration(node); } @Override public Void visitNode(AstNode node) { // Walk UP the tree, not down. AstNode parent = node.getParent(); updateIfShouldGetTargetParameter(node, parent); if (parent != null) { child = node; parent.accept(this); } return null; } @Override public Void visitPrefixedIdentifier(PrefixedIdentifier node) { if (node == completionNode || node.getIdentifier() == completionNode) { SimpleIdentifier prefix = node.getPrefix(); if (isClassLiteral(prefix)) { state.prohibitsInstanceReferences(); } else { state.prohibitsStaticReferences(); } } return super.visitPrefixedIdentifier(node); } @Override public Void visitPropertyAccess(PropertyAccess node) { if (node == completionNode || node.getPropertyName() == completionNode) { Expression target = node.getRealTarget(); if (isClassLiteral(target)) { state.prohibitsInstanceReferences(); } else { state.prohibitsStaticReferences(); } } return super.visitPropertyAccess(node); } @Override public Void visitSimpleFormalParameter(SimpleFormalParameter node) { state.includesUndefinedTypes(); return super.visitSimpleFormalParameter(node); } @Override public Void visitSimpleIdentifier(SimpleIdentifier node) { inIdentifier = true; return super.visitSimpleIdentifier(node); } @Override public Void visitSwitchStatement(SwitchStatement node) { if (child == node.getExpression()) { state.includesLiterals(); } return super.visitSwitchStatement(node); } @Override public Void visitTypeArgumentList(TypeArgumentList node) { state.prohibitsUndefinedTypes(); return super.visitTypeArgumentList(node); } @Override public Void visitTypeName(TypeName node) { inTypeName = true; return super.visitTypeName(node); } @Override public Void visitVariableDeclaration(VariableDeclaration node) { if (node.getName() == completionNode) { state.prohibitsLiterals(); } return super.visitVariableDeclaration(node); } @Override public Void visitVariableDeclarationList(VariableDeclarationList node) { state.includesUndefinedDeclarationTypes(); return super.visitVariableDeclarationList(node); } @Override public Void visitWhileStatement(WhileStatement node) { if (child == node.getCondition()) { state.includesLiterals(); } return super.visitWhileStatement(node); } @Override public Void visitWithClause(WithClause node) { state.mustBeMixin(); return super.visitWithClause(node); } private boolean isClassLiteral(Expression expression) { return expression instanceof Identifier && ((Identifier) expression).getStaticElement() instanceof ClassElement; } private void mayBeSetParameterElement(Expression node) { if (!maybeInvocationArgument) { return; } if (node.getParent() instanceof ArgumentList) { if (state.targetParameter == null) { state.targetParameter = node.getBestParameterElement(); } } } private void updateIfShouldGetTargetParameter(AstNode node, AstNode parent) { if (!maybeInvocationArgument) { return; } // prefix.node if (parent instanceof PrefixedIdentifier) { if (((PrefixedIdentifier) parent).getIdentifier() == node) { return; } } // something unknown maybeInvocationArgument = false; } }