Package com.google.dart.engine.internal.resolver

Source Code of com.google.dart.engine.internal.resolver.ScopedVisitor

/*
* Copyright (c) 2013, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.engine.internal.resolver;

import com.google.dart.engine.AnalysisEngine;
import com.google.dart.engine.ast.AstNode;
import com.google.dart.engine.ast.Block;
import com.google.dart.engine.ast.CatchClause;
import com.google.dart.engine.ast.ClassDeclaration;
import com.google.dart.engine.ast.ClassTypeAlias;
import com.google.dart.engine.ast.ConstructorDeclaration;
import com.google.dart.engine.ast.Declaration;
import com.google.dart.engine.ast.DeclaredIdentifier;
import com.google.dart.engine.ast.DoStatement;
import com.google.dart.engine.ast.FieldDeclaration;
import com.google.dart.engine.ast.ForEachStatement;
import com.google.dart.engine.ast.ForStatement;
import com.google.dart.engine.ast.FormalParameterList;
import com.google.dart.engine.ast.FunctionDeclaration;
import com.google.dart.engine.ast.FunctionDeclarationStatement;
import com.google.dart.engine.ast.FunctionExpression;
import com.google.dart.engine.ast.FunctionTypeAlias;
import com.google.dart.engine.ast.IfStatement;
import com.google.dart.engine.ast.Label;
import com.google.dart.engine.ast.LabeledStatement;
import com.google.dart.engine.ast.MethodDeclaration;
import com.google.dart.engine.ast.NodeList;
import com.google.dart.engine.ast.SimpleIdentifier;
import com.google.dart.engine.ast.Statement;
import com.google.dart.engine.ast.SwitchCase;
import com.google.dart.engine.ast.SwitchDefault;
import com.google.dart.engine.ast.SwitchMember;
import com.google.dart.engine.ast.SwitchStatement;
import com.google.dart.engine.ast.TopLevelVariableDeclaration;
import com.google.dart.engine.ast.VariableDeclaration;
import com.google.dart.engine.ast.VariableDeclarationStatement;
import com.google.dart.engine.ast.WhileStatement;
import com.google.dart.engine.ast.visitor.UnifyingAstVisitor;
import com.google.dart.engine.element.ClassElement;
import com.google.dart.engine.element.CompilationUnitElement;
import com.google.dart.engine.element.ConstructorElement;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.element.ExecutableElement;
import com.google.dart.engine.element.LabelElement;
import com.google.dart.engine.element.LibraryElement;
import com.google.dart.engine.element.VariableElement;
import com.google.dart.engine.error.AnalysisError;
import com.google.dart.engine.error.AnalysisErrorListener;
import com.google.dart.engine.error.ErrorCode;
import com.google.dart.engine.internal.scope.ClassScope;
import com.google.dart.engine.internal.scope.EnclosedScope;
import com.google.dart.engine.internal.scope.FunctionScope;
import com.google.dart.engine.internal.scope.FunctionTypeScope;
import com.google.dart.engine.internal.scope.LabelScope;
import com.google.dart.engine.internal.scope.LibraryScope;
import com.google.dart.engine.internal.scope.Scope;
import com.google.dart.engine.internal.scope.TypeParameterScope;
import com.google.dart.engine.scanner.Token;
import com.google.dart.engine.source.Source;

/**
* The abstract class {@code ScopedVisitor} maintains name and label scopes as an AST structure is
* being visited.
*
* @coverage dart.engine.resolver
*/
public abstract class ScopedVisitor extends UnifyingAstVisitor<Void> {
  /**
   * The element for the library containing the compilation unit being visited.
   */
  private LibraryElement definingLibrary;

  /**
   * The source representing the compilation unit being visited.
   */
  private Source source;

  /**
   * The error listener that will be informed of any errors that are found during resolution.
   */
  private AnalysisErrorListener errorListener;

  /**
   * The scope used to resolve identifiers.
   */
  private Scope nameScope;

  /**
   * The object used to access the types from the core library.
   */
  private TypeProvider typeProvider;

  /**
   * The scope used to resolve labels for {@code break} and {@code continue} statements, or
   * {@code null} if no labels have been defined in the current context.
   */
  private LabelScope labelScope;

  /**
   * Initialize a newly created visitor to resolve the nodes in a compilation unit.
   *
   * @param library the library containing the compilation unit being resolved
   * @param source the source representing the compilation unit being visited
   * @param typeProvider the object used to access the types from the core library
   */
  public ScopedVisitor(Library library, Source source, TypeProvider typeProvider) {
    this.definingLibrary = library.getLibraryElement();
    this.source = source;
    LibraryScope libraryScope = library.getLibraryScope();
    this.errorListener = libraryScope.getErrorListener();
    this.nameScope = libraryScope;
    this.typeProvider = typeProvider;
  }

  /**
   * Initialize a newly created visitor to resolve the nodes in a compilation unit.
   *
   * @param definingLibrary the element for the library containing the compilation unit being
   *          visited
   * @param source the source representing the compilation unit being visited
   * @param typeProvider the object used to access the types from the core library
   * @param errorListener the error listener that will be informed of any errors that are found
   *          during resolution
   */
  public ScopedVisitor(LibraryElement definingLibrary, Source source, TypeProvider typeProvider,
      AnalysisErrorListener errorListener) {
    this.definingLibrary = definingLibrary;
    this.source = source;
    this.errorListener = errorListener;
    this.nameScope = new LibraryScope(definingLibrary, errorListener);
    this.typeProvider = typeProvider;
  }

  /**
   * Initialize a newly created visitor to resolve the nodes in a compilation unit.
   *
   * @param definingLibrary the element for the library containing the compilation unit being
   *          visited
   * @param source the source representing the compilation unit being visited
   * @param typeProvider the object used to access the types from the core library
   * @param nameScope the scope used to resolve identifiers in the node that will first be visited
   * @param errorListener the error listener that will be informed of any errors that are found
   *          during resolution
   */
  public ScopedVisitor(LibraryElement definingLibrary, Source source, TypeProvider typeProvider,
      Scope nameScope, AnalysisErrorListener errorListener) {
    this.definingLibrary = definingLibrary;
    this.source = source;
    this.errorListener = errorListener;
    this.nameScope = nameScope;
    this.typeProvider = typeProvider;
  }

  /**
   * Initialize a newly created visitor to resolve the nodes in a compilation unit.
   *
   * @param library the library containing the compilation unit being resolved
   * @param source the source representing the compilation unit being visited
   * @param typeProvider the object used to access the types from the core library
   */
  public ScopedVisitor(ResolvableLibrary library, Source source, TypeProvider typeProvider) {
    this.definingLibrary = library.getLibraryElement();
    this.source = source;
    LibraryScope libraryScope = library.getLibraryScope();
    this.errorListener = libraryScope.getErrorListener();
    this.nameScope = libraryScope;
    this.typeProvider = typeProvider;
  }

  /**
   * Return the library element for the library containing the compilation unit being resolved.
   *
   * @return the library element for the library containing the compilation unit being resolved
   */
  public LibraryElement getDefiningLibrary() {
    return definingLibrary;
  }

  /**
   * Return the object used to access the types from the core library.
   *
   * @return the object used to access the types from the core library
   */
  public TypeProvider getTypeProvider() {
    return typeProvider;
  }

  /**
   * Replaces the current {@link Scope} with the enclosing {@link Scope}.
   *
   * @return the enclosing {@link Scope}.
   */
  public Scope popNameScope() {
    nameScope = nameScope.getEnclosingScope();
    return nameScope;
  }

  /**
   * Pushes a new {@link Scope} into the visitor.
   *
   * @return the new {@link Scope}.
   */
  public Scope pushNameScope() {
    Scope newScope = new EnclosedScope(nameScope);
    nameScope = newScope;
    return nameScope;
  }

  @Override
  public Void visitBlock(Block node) {
    Scope outerScope = nameScope;
    try {
      EnclosedScope enclosedScope = new EnclosedScope(nameScope);
      hideNamesDefinedInBlock(enclosedScope, node);
      nameScope = enclosedScope;
      super.visitBlock(node);
    } finally {
      nameScope = outerScope;
    }
    return null;
  }

  @Override
  public Void visitCatchClause(CatchClause node) {
    SimpleIdentifier exception = node.getExceptionParameter();
    if (exception != null) {
      Scope outerScope = nameScope;
      try {
        nameScope = new EnclosedScope(nameScope);
        nameScope.define(exception.getStaticElement());
        SimpleIdentifier stackTrace = node.getStackTraceParameter();
        if (stackTrace != null) {
          nameScope.define(stackTrace.getStaticElement());
        }
        super.visitCatchClause(node);
      } finally {
        nameScope = outerScope;
      }
    } else {
      super.visitCatchClause(node);
    }
    return null;
  }

  @Override
  public Void visitClassDeclaration(ClassDeclaration node) {
    ClassElement classElement = node.getElement();
    Scope outerScope = nameScope;
    try {
      if (classElement == null) {
        AnalysisEngine.getInstance().getLogger().logInformation(
            "Missing element for class declaration " + node.getName().getName() + " in "
                + getDefiningLibrary().getSource().getFullName(),
            new Exception());
        super.visitClassDeclaration(node);
      } else {
        nameScope = new TypeParameterScope(nameScope, classElement);
        visitClassDeclarationInScope(node);
        nameScope = new ClassScope(nameScope, classElement);
        visitClassMembersInScope(node);
      }
    } finally {
      nameScope = outerScope;
    }
    return null;
  }

  @Override
  public Void visitClassTypeAlias(ClassTypeAlias node) {
    Scope outerScope = nameScope;
    try {
      ClassElement element = node.getElement();
      nameScope = new ClassScope(new TypeParameterScope(nameScope, element), element);
      super.visitClassTypeAlias(node);
    } finally {
      nameScope = outerScope;
    }
    return null;
  }

  @Override
  public Void visitConstructorDeclaration(ConstructorDeclaration node) {
    ConstructorElement constructorElement = node.getElement();
    Scope outerScope = nameScope;
    try {
      if (constructorElement == null) {
        StringBuilder builder = new StringBuilder();
        builder.append("Missing element for constructor ");
        builder.append(node.getReturnType().getName());
        if (node.getName() != null) {
          builder.append(".");
          builder.append(node.getName().getName());
        }
        builder.append(" in ");
        builder.append(getDefiningLibrary().getSource().getFullName());
        AnalysisEngine.getInstance().getLogger().logInformation(builder.toString(), new Exception());
      } else {
        nameScope = new FunctionScope(nameScope, constructorElement);
      }
      super.visitConstructorDeclaration(node);
    } finally {
      nameScope = outerScope;
    }
    return null;
  }

  @Override
  public Void visitDeclaredIdentifier(DeclaredIdentifier node) {
    VariableElement element = node.getElement();
    if (element != null) {
      nameScope.define(element);
    }
    super.visitDeclaredIdentifier(node);
    return null;
  }

  @Override
  public Void visitDoStatement(DoStatement node) {
    LabelScope outerLabelScope = labelScope;
    try {
      labelScope = new LabelScope(labelScope, false, false);
      visitStatementInScope(node.getBody());
      safelyVisit(node.getCondition());
    } finally {
      labelScope = outerLabelScope;
    }
    return null;
  }

  @Override
  public Void visitForEachStatement(ForEachStatement node) {
    Scope outerNameScope = nameScope;
    LabelScope outerLabelScope = labelScope;
    try {
      nameScope = new EnclosedScope(nameScope);
      labelScope = new LabelScope(outerLabelScope, false, false);
      visitForEachStatementInScope(node);
    } finally {
      labelScope = outerLabelScope;
      nameScope = outerNameScope;
    }
    return null;
  }

  @Override
  public Void visitFormalParameterList(FormalParameterList node) {
    super.visitFormalParameterList(node);
    // We finished resolving function signature, now include formal parameters scope.
    if (nameScope instanceof FunctionScope) {
      ((FunctionScope) nameScope).defineParameters();
    }
    if (nameScope instanceof FunctionTypeScope) {
      ((FunctionTypeScope) nameScope).defineParameters();
    }
    return null;
  }

  @Override
  public Void visitForStatement(ForStatement node) {
    Scope outerNameScope = nameScope;
    LabelScope outerLabelScope = labelScope;
    try {
      nameScope = new EnclosedScope(nameScope);
      labelScope = new LabelScope(outerLabelScope, false, false);
      visitForStatementInScope(node);
    } finally {
      labelScope = outerLabelScope;
      nameScope = outerNameScope;
    }
    return null;
  }

  @Override
  public Void visitFunctionDeclaration(FunctionDeclaration node) {
    ExecutableElement functionElement = node.getElement();
    Scope outerScope = nameScope;
    try {
      if (functionElement == null) {
        AnalysisEngine.getInstance().getLogger().logInformation(
            "Missing element for top-level function " + node.getName().getName() + " in "
                + getDefiningLibrary().getSource().getFullName(),
            new Exception());
      } else {
        nameScope = new FunctionScope(nameScope, functionElement);
      }
      super.visitFunctionDeclaration(node);
    } finally {
      nameScope = outerScope;
    }
    if (functionElement != null
        && !(functionElement.getEnclosingElement() instanceof CompilationUnitElement)) {
      nameScope.define(functionElement);
    }
    return null;
  }

  @Override
  public Void visitFunctionExpression(FunctionExpression node) {
    if (node.getParent() instanceof FunctionDeclaration) {
      // We have already created a function scope and don't need to do so again.
      super.visitFunctionExpression(node);
    } else {
      Scope outerScope = nameScope;
      try {
        ExecutableElement functionElement = node.getElement();
        if (functionElement == null) {
          StringBuilder builder = new StringBuilder();
          builder.append("Missing element for function ");
          AstNode parent = node.getParent();
          while (parent != null) {
            if (parent instanceof Declaration) {
              Element parentElement = ((Declaration) parent).getElement();
              builder.append(parentElement == null ? "<unknown> " : (parentElement.getName() + " "));
            }
            parent = parent.getParent();
          }
          builder.append("in ");
          builder.append(getDefiningLibrary().getSource().getFullName());
          AnalysisEngine.getInstance().getLogger().logInformation(
              builder.toString(),
              new Exception());
        } else {
          nameScope = new FunctionScope(nameScope, functionElement);
        }
        super.visitFunctionExpression(node);
      } finally {
        nameScope = outerScope;
      }
    }
    return null;
  }

  @Override
  public Void visitFunctionTypeAlias(FunctionTypeAlias node) {
    Scope outerScope = nameScope;
    try {
      nameScope = new FunctionTypeScope(nameScope, node.getElement());
      super.visitFunctionTypeAlias(node);
    } finally {
      nameScope = outerScope;
    }
    return null;
  }

  @Override
  public Void visitIfStatement(IfStatement node) {
    safelyVisit(node.getCondition());
    visitStatementInScope(node.getThenStatement());
    visitStatementInScope(node.getElseStatement());
    return null;
  }

  @Override
  public Void visitLabeledStatement(LabeledStatement node) {
    LabelScope outerScope = addScopesFor(node.getLabels());
    try {
      super.visitLabeledStatement(node);
    } finally {
      labelScope = outerScope;
    }
    return null;
  }

  @Override
  public Void visitMethodDeclaration(MethodDeclaration node) {
    Scope outerScope = nameScope;
    try {
      ExecutableElement methodElement = node.getElement();
      if (methodElement == null) {
        AnalysisEngine.getInstance().getLogger().logInformation(
            "Missing element for method " + node.getName().getName() + " in "
                + getDefiningLibrary().getSource().getFullName(),
            new Exception());
      } else {
        nameScope = new FunctionScope(nameScope, methodElement);
      }
      super.visitMethodDeclaration(node);
    } finally {
      nameScope = outerScope;
    }
    return null;
  }

  @Override
  public Void visitSwitchCase(SwitchCase node) {
    node.getExpression().accept(this);
    Scope outerNameScope = nameScope;
    try {
      nameScope = new EnclosedScope(nameScope);
      node.getStatements().accept(this);
    } finally {
      nameScope = outerNameScope;
    }
    return null;
  }

  @Override
  public Void visitSwitchDefault(SwitchDefault node) {
    Scope outerNameScope = nameScope;
    try {
      nameScope = new EnclosedScope(nameScope);
      node.getStatements().accept(this);
    } finally {
      nameScope = outerNameScope;
    }
    return null;
  }

  @Override
  public Void visitSwitchStatement(SwitchStatement node) {
    LabelScope outerScope = labelScope;
    try {
      labelScope = new LabelScope(outerScope, true, false);
      for (SwitchMember member : node.getMembers()) {
        for (Label label : member.getLabels()) {
          SimpleIdentifier labelName = label.getLabel();
          LabelElement labelElement = (LabelElement) labelName.getStaticElement();
          labelScope = new LabelScope(labelScope, labelName.getName(), labelElement);
        }
      }
      super.visitSwitchStatement(node);
    } finally {
      labelScope = outerScope;
    }
    return null;
  }

  @Override
  public Void visitVariableDeclaration(VariableDeclaration node) {
    super.visitVariableDeclaration(node);
    if (!(node.getParent().getParent() instanceof TopLevelVariableDeclaration)
        && !(node.getParent().getParent() instanceof FieldDeclaration)) {
      VariableElement element = node.getElement();
      if (element != null) {
        nameScope.define(element);
      }
    }
    return null;
  }

  @Override
  public Void visitWhileStatement(WhileStatement node) {
    LabelScope outerScope = labelScope;
    try {
      labelScope = new LabelScope(outerScope, false, false);
      safelyVisit(node.getCondition());
      visitStatementInScope(node.getBody());
    } finally {
      labelScope = outerScope;
    }
    return null;
  }

  /**
   * Return the label scope in which the current node is being resolved.
   *
   * @return the label scope in which the current node is being resolved
   */
  protected LabelScope getLabelScope() {
    return labelScope;
  }

  /**
   * Return the name scope in which the current node is being resolved.
   *
   * @return the name scope in which the current node is being resolved
   */
  protected Scope getNameScope() {
    return nameScope;
  }

  /**
   * Return the source.
   *
   * @return the source
   */
  protected Source getSource() {
    return source;
  }

  /**
   * Report an error with the given error code and arguments.
   *
   * @param errorCode the error code of the error to be reported
   * @param node the node specifying the location of the error
   * @param arguments the arguments to the error, used to compose the error message
   */
  protected void reportErrorForNode(ErrorCode errorCode, AstNode node, Object... arguments) {
    errorListener.onError(new AnalysisError(
        source,
        node.getOffset(),
        node.getLength(),
        errorCode,
        arguments));
  }

  /**
   * Report an error with the given error code and arguments.
   *
   * @param errorCode the error code of the error to be reported
   * @param offset the offset of the location of the error
   * @param length the length of the location of the error
   * @param arguments the arguments to the error, used to compose the error message
   */
  protected void reportErrorForOffset(ErrorCode errorCode, int offset, int length,
      Object... arguments) {
    errorListener.onError(new AnalysisError(source, offset, length, errorCode, arguments));
  }

  /**
   * Report an error with the given error code and arguments.
   *
   * @param errorCode the error code of the error to be reported
   * @param token the token specifying the location of the error
   * @param arguments the arguments to the error, used to compose the error message
   */
  protected void reportErrorForToken(ErrorCode errorCode, Token token, Object... arguments) {
    errorListener.onError(new AnalysisError(
        source,
        token.getOffset(),
        token.getLength(),
        errorCode,
        arguments));
  }

  /**
   * Visit the given AST node if it is not null.
   *
   * @param node the node to be visited
   */
  protected void safelyVisit(AstNode node) {
    if (node != null) {
      node.accept(this);
    }
  }

  protected void visitClassDeclarationInScope(ClassDeclaration node) {
    safelyVisit(node.getName());
    safelyVisit(node.getTypeParameters());
    safelyVisit(node.getExtendsClause());
    safelyVisit(node.getWithClause());
    safelyVisit(node.getImplementsClause());
    safelyVisit(node.getNativeClause());
  }

  protected void visitClassMembersInScope(ClassDeclaration node) {
    safelyVisit(node.getDocumentationComment());
    node.getMetadata().accept(this);
    node.getMembers().accept(this);
  }

  /**
   * Visit the given statement after it's scope has been created. This replaces the normal call to
   * the inherited visit method so that ResolverVisitor can intervene when type propagation is
   * enabled.
   *
   * @param node the statement to be visited
   */
  protected void visitForEachStatementInScope(ForEachStatement node) {
    //
    // We visit the iterator before the loop variable because the loop variable cannot be in scope
    // while visiting the iterator.
    //
    safelyVisit(node.getIdentifier());
    safelyVisit(node.getIterator());
    safelyVisit(node.getLoopVariable());
    visitStatementInScope(node.getBody());
  }

  /**
   * Visit the given statement after it's scope has been created. This replaces the normal call to
   * the inherited visit method so that ResolverVisitor can intervene when type propagation is
   * enabled.
   *
   * @param node the statement to be visited
   */
  protected void visitForStatementInScope(ForStatement node) {
    safelyVisit(node.getVariables());
    safelyVisit(node.getInitialization());
    safelyVisit(node.getCondition());
    node.getUpdaters().accept(this);
    visitStatementInScope(node.getBody());
  }

  /**
   * Visit the given statement after it's scope has been created. This is used by ResolverVisitor to
   * correctly visit the 'then' and 'else' statements of an 'if' statement.
   *
   * @param node the statement to be visited
   */
  protected void visitStatementInScope(Statement node) {
    if (node instanceof Block) {
      // Don't create a scope around a block because the block will create it's own scope.
      visitBlock((Block) node);
    } else if (node != null) {
      Scope outerNameScope = nameScope;
      try {
        nameScope = new EnclosedScope(nameScope);
        node.accept(this);
      } finally {
        nameScope = outerNameScope;
      }
    }
  }

  /**
   * Add scopes for each of the given labels.
   *
   * @param labels the labels for which new scopes are to be added
   * @return the scope that was in effect before the new scopes were added
   */
  private LabelScope addScopesFor(NodeList<Label> labels) {
    LabelScope outerScope = labelScope;
    for (Label label : labels) {
      SimpleIdentifier labelNameNode = label.getLabel();
      String labelName = labelNameNode.getName();
      LabelElement labelElement = (LabelElement) labelNameNode.getStaticElement();
      labelScope = new LabelScope(labelScope, labelName, labelElement);
    }
    return outerScope;
  }

  /**
   * Marks the local declarations of the given {@link Block} hidden in the enclosing scope.
   * According to the scoping rules name is hidden if block defines it, but name is defined after
   * its declaration statement.
   */
  private void hideNamesDefinedInBlock(EnclosedScope scope, Block block) {
    NodeList<Statement> statements = block.getStatements();
    int statementCount = statements.size();
    for (int i = 0; i < statementCount; i++) {
      Statement statement = statements.get(i);
      if (statement instanceof VariableDeclarationStatement) {
        VariableDeclarationStatement vds = (VariableDeclarationStatement) statement;
        NodeList<VariableDeclaration> variables = vds.getVariables().getVariables();
        int variableCount = variables.size();
        for (int j = 0; j < variableCount; j++) {
          scope.hide(variables.get(j).getElement());
        }
      } else if (statement instanceof FunctionDeclarationStatement) {
        FunctionDeclarationStatement fds = (FunctionDeclarationStatement) statement;
        scope.hide(fds.getFunctionDeclaration().getElement());
      }
    }
  }
}
TOP

Related Classes of com.google.dart.engine.internal.resolver.ScopedVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.