Package com.redhat.ceylon.compiler.typechecker.analyzer

Source Code of com.redhat.ceylon.compiler.typechecker.analyzer.SpecificationVisitor$SpecificationState

package com.redhat.ceylon.compiler.typechecker.analyzer;

import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.getLastExecutableStatement;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.isAlwaysSatisfied;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.isAtLeastOne;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.isEffectivelyBaseMemberExpression;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.isNeverSatisfied;
import static com.redhat.ceylon.compiler.typechecker.model.Util.getContainingDeclarationOfScope;

import com.redhat.ceylon.compiler.typechecker.model.Class;
import com.redhat.ceylon.compiler.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.model.Method;
import com.redhat.ceylon.compiler.typechecker.model.MethodOrValue;
import com.redhat.ceylon.compiler.typechecker.model.Parameter;
import com.redhat.ceylon.compiler.typechecker.model.Setter;
import com.redhat.ceylon.compiler.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.compiler.typechecker.model.Value;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;

/**
* Validates that non-variable values are well-defined
* within the local scope in which they occur. Checks
* that they are not used before they are defined, that
* they are always specified before they are used, and
* that they are never specified twice.
*
* @author Gavin King
*
*/
public class SpecificationVisitor extends Visitor {
   
    private final Declaration declaration;
   
    private SpecificationState specified =
            new SpecificationState(false, false);
    private boolean withinDeclaration = false;
    private boolean inLoop = false;
    private boolean declared = false;
    private boolean hasParameter = false;
    private Tree.Statement lastExecutableStatement;
    private boolean declarationSection = false;
    private boolean endsInBreakReturnThrow = false;
    private boolean inExtends = false;
    private boolean inAnonFunctionOrComprehension = false;
  private boolean withinAttributeInitializer = false;
   
    @Override
    public void visit(Tree.ExtendedType that) {
        boolean oie = inExtends;
        inExtends = declared;
        super.visit(that);
        inExtends = oie;
    }
   
    private final class ContinueVisitor extends Visitor {
        Tree.Continue node;
        boolean found;
        Tree.Statement lastContinue;
        ContinueVisitor(Tree.Statement lastContinue) {
           this.lastContinue = lastContinue;
           node = null;
           found = false;
        }
        @Override
        public void visit(Tree.Declaration that) {}
        @Override
        public void visit(Tree.WhileStatement that) {}
        @Override
        public void visit(Tree.ForStatement that) {}
        @Override
        public void visit(Tree.Continue that) {
            node = that;
            if (that==lastContinue) {
                found = true;
            }
        }
    }

    class SpecificationState {
        boolean definitely;
        boolean possibly;
        boolean exited;
        boolean byExits;
        SpecificationState(boolean definitely, boolean possibly) {
            this.definitely = definitely;
            this.possibly = possibly;
            this.exited = false;
            this.byExits = true;
        }
    }
   
    public SpecificationVisitor(Declaration declaration) {
        this.declaration = declaration;
    }
   
    private void declare() {
        declared = true;
    }
   
    private boolean beginDeclarationScope() {
        return declared;
    }
   
    private void endDeclarationScope(boolean d) {
        declared = d;
    }
   
    private boolean beginDisabledSpecificationScope() {
        boolean ca = withinDeclaration;
        withinDeclaration = true;
        return ca;
    }
   
    private void endDisabledSpecificationScope(boolean ca) {
        withinDeclaration = ca;
    }
   
    private void specify() {
        specified.definitely = true;
        specified.possibly = true;
    }
   
    private void exit() {
        specified.exited = true;
    }
   
    private SpecificationState beginSpecificationScope() {
        SpecificationState as = specified;
        specified = new SpecificationState(specified.definitely,
                specified.possibly);
        return as;
    }
   
    private void endSpecificationScope(SpecificationState as) {
        specified = as;
    }
   
    private boolean isVariable() {
        return (declaration instanceof TypedDeclaration)
            && ((TypedDeclaration) declaration).isVariable();
    }
   
    private boolean isLate() {
        return (declaration instanceof MethodOrValue)
            && ((MethodOrValue) declaration).isLate();
    }
   
    @Override
    public void visit(Tree.AnnotationList that) {}
   
    @Override
    public void visit(Tree.BaseMemberExpression that) {
        super.visit(that);
        visitReference(that);
    }

    @Override
    public void visit(Tree.MetaLiteral that) {
        super.visit(that);
        visitReference(that);
    }

    @Override
    public void visit(Tree.ExtendedTypeExpression that) {
        super.visit(that);
        visitReference(that);
    }

    @Override
    public void visit(Tree.CaseTypes that) {
        //the BaseMemberExpressions in the CaseTypes
        //list are actually types, not value refs!
    }
   
    @Override
    public void visit(Tree.SatisfiedTypes that) {
        //unnecessary ... for consistency nothing else
    }
   
    @Override
    public void visit(Tree.BaseTypeExpression that) {
        super.visit(that);
        visitReference(that);
    }

    @Override
    public void visit(Tree.QualifiedMemberExpression that) {
        super.visit(that);
        if (Util.isSelfReference(that.getPrimary())) {
            visitReference(that);
        }
    }

    @Override
    public void visit(Tree.QualifiedTypeExpression that) {
        super.visit(that);
        if (Util.isSelfReference(that.getPrimary())) {
            visitReference(that);
        }
    }

    private void visitReference(Tree.Primary that) {
        Declaration member;
        boolean assigned;
        boolean metamodel;
        if (that instanceof Tree.MemberOrTypeExpression) {
            Tree.MemberOrTypeExpression mte =
                    (Tree.MemberOrTypeExpression) that;
            member = mte.getDeclaration();
            assigned = mte.getAssigned();
            metamodel = false;
        }
        else if (that instanceof Tree.MetaLiteral) {
            Tree.MetaLiteral ml = (Tree.MetaLiteral) that;
            member = ml.getDeclaration();
            assigned = false;
            metamodel = true;
        }
        else {
            return;
        }
        //Declaration member = getDeclaration(that.getScope(), that.getUnit(), id, context);
        //TODO: check superclass members are not in declaration section!
        if (member==declaration &&
                member.isDefinedInScope(that.getScope())) {
            if (!declared) {
                //you are allowed to refer to later
                //declarations in a class declaration
                //section or interface
                if (withinAttributeInitializer &&
                    member instanceof Value &&
                    !(((Value) member).isTransient())) {
                  that.addError("reference to value within its initializer: '" +
                            member.getName() + "'", 1460);
                }
                else if (!metamodel && !isForwardReferenceable() && !hasParameter) {
                    if (declaration.getContainer() instanceof Class) {
                        that.addError("forward reference to class member in initializer: '" +
                                member.getName() +
                                "' is not yet declared (forward references must occur in declaration section)");
                    }
                    else {
                        that.addError("forward reference to local declaration: '" +
                                member.getName() + "' is not yet declared");
                    }
                }
            }
            else if (!specified.definitely ||
                    declaration.isFormal()) {
                //you are allowed to refer to formal
                //declarations in a class declaration
                //section or interface
                if (declaration.isFormal()) {
                    if (!isForwardReferenceable()) {
                        that.addError("formal member may not be used in initializer: '" +
                                member.getName() + "'");                   
                    }
                }
                else if (!declaration.isNative() && !metamodel) {
                    if (!isLate() || !isForwardReferenceable()) {
                        if (isVariable()) {
                            that.addError("not definitely initialized: '" +
                                    member.getName() + "'");                   
                        }
                        else {
                            that.addError("not definitely specified: '" +
                                    member.getName() + "'");
                        }
                    }
                }
            }
            if (!assigned && member.isDefault() &&
                    !isForwardReferenceable()) {
                that.addError("default member may not be used in initializer: '" +
                        member.getName() + "'");
            }
            if (inAnonFunctionOrComprehension &&
                specified.definitely &&
                isVariable()) {
                that.addError("variable member may not be captured by comprehension or function in extends clause: '"+
                        member.getName() + "'");
            }
        }
    }

    private boolean isForwardReferenceable() {
        return declarationSection ||
                declaration.isToplevel() ||
                declaration.isInterfaceMember();
    }
   
    @Override
    public void visit(Tree.LogicalOp that) {
        that.getLeftTerm().visit(this);
      SpecificationState ss = beginSpecificationScope();
        that.getRightTerm().visit(this);
      endSpecificationScope(ss);
    }
   
    @Override
    public void visit(Tree.Comprehension that) {
        boolean oicoaf = inAnonFunctionOrComprehension;
        inAnonFunctionOrComprehension = declared&&inExtends;
        super.visit(that);
        inAnonFunctionOrComprehension = oicoaf;
    }
   
    @Override
    public void visit(Tree.FunctionArgument that) {
      boolean c = beginDisabledSpecificationScope();
        boolean oicoaf = inAnonFunctionOrComprehension;
        inAnonFunctionOrComprehension = declared&&inExtends;
      SpecificationState ss = beginSpecificationScope();
      super.visit(that);
      endSpecificationScope(ss);
        inAnonFunctionOrComprehension = oicoaf;
        endDisabledSpecificationScope(c);
    }
   
    @Override
    public void visit(Tree.AssignOp that) {
        Tree.Term lt = that.getLeftTerm();
        if (isEffectivelyBaseMemberExpression(lt)) {
            Tree.StaticMemberOrTypeExpression m =
                    (Tree.StaticMemberOrTypeExpression) lt;
//            Declaration member = getTypedDeclaration(m.getScope(),
//                    name(m.getIdentifier()), null, false, m.getUnit());
            Declaration member = m.getDeclaration();
            if (member==declaration) {
                if (that.getRightTerm()!=null) {
                    that.getRightTerm().visit(this);
                }
                checkVariable(lt, that);
                specify();
                lt.visit(this);
            }
            else {
                super.visit(that);
            }
        }
    }
   
    @Override
    public void visit(Tree.AssignmentOp that) {
        super.visit(that);
        checkVariable(that.getLeftTerm(), that);
    }

    @Override
    public void visit(Tree.PostfixOperatorExpression that) {
        super.visit(that);
        checkVariable(that.getTerm(), that);
    }
   
    @Override
    public void visit(Tree.PrefixOperatorExpression that) {
        super.visit(that);
        checkVariable(that.getTerm(), that);
    }
   
    private void checkVariable(Tree.Term term, Node node) {
        if (isEffectivelyBaseMemberExpression(term)) {  //Note: other cases handled in ExpressionVisitor
            Tree.StaticMemberOrTypeExpression mte =
                    (Tree.StaticMemberOrTypeExpression) term;
            Declaration member = mte.getDeclaration();
            if (member==declaration) {
              if ((declaration.isFormal() || declaration.isDefault()) &&
                      !isForwardReferenceable()) {
                  term.addError("member is formal or default and may not be assigned here: '" +
                  member.getName() + "'");
              }
              else if (!isVariable() && !isLate()) {
                    if (member instanceof Value) {
                        if (node instanceof Tree.AssignOp) {
                            term.addError("value is not a variable and may not be assigned here: '" +
                                    member.getName() + "'", 803);
                        }
                        else {
                            term.addError("value is not a variable: '" +
                                    member.getName() + "'", 800);
                        }
                    }
                    else {
                        term.addError("not a variable value: '" +
                                member.getName() + "'");
                    }
                }
            }
        }
    }

    private Tree.Continue lastContinue;
    private Tree.Statement lastContinueStatement;
   
    @Override
    public void visit(Tree.Block that) {
        boolean oe = endsInBreakReturnThrow;
        Tree.Continue olc = lastContinue;
        Tree.Statement olcs = lastContinueStatement;
        //rather nasty way of detecting that the continue
        //occurs in another conditional branch of the
        //statement containing this block, even though we
        //did not find it in _this_ branch
        boolean continueInSomeBranchOfCurrentConditional =
                lastContinue!=null &&
                lastContinueStatement==null;
        boolean blockEndsInBreakReturnThrow =
                blockEndsInBreakReturnThrow(that);
        endsInBreakReturnThrow = endsInBreakReturnThrow ||
                blockEndsInBreakReturnThrow;
        Tree.Continue last = null;
        Tree.Statement lastStatement = null;
        for (Tree.Statement st: that.getStatements()) {
            ContinueVisitor cv = new ContinueVisitor(olc);
            st.visit(cv);
            if (cv.node!=null) {
                last = cv.node;
                lastStatement = st;
            }
            if (cv.found) {
                olc = null;
                olcs = null;
            }
        }
        if (blockEndsInBreakReturnThrow ||
                continueInSomeBranchOfCurrentConditional) {
            lastContinue = last;
            lastContinueStatement = lastStatement;
        }
        super.visit(that);
        endsInBreakReturnThrow = oe;
        lastContinue = olc;
        lastContinueStatement = olcs;
    }

    private boolean blockEndsInBreakReturnThrow(Tree.Block that) {
        int size = that.getStatements().size();
        if (size>0) {
            Tree.Statement s = that.getStatements().get(size-1);
            return s instanceof Tree.Break ||
                    s instanceof Tree.Return ||
                    s instanceof Tree.Throw;
        }
        else {
            return false;
        }
    }
   
    @Override
    public void visit(Tree.ForClause that) {
        boolean oe = endsInBreakReturnThrow;
        Tree.Continue olc = lastContinue;
        lastContinue = null;
        endsInBreakReturnThrow = false;
        super.visit(that);
        endsInBreakReturnThrow = oe;
        lastContinue = olc;
    }
   
    @Override
    public void visit(Tree.WhileClause that) {
        boolean oe = endsInBreakReturnThrow;
        Tree.Continue olc = lastContinue;
        lastContinue = null;
        endsInBreakReturnThrow = false;
        super.visit(that);
        endsInBreakReturnThrow = oe;
        lastContinue = olc;
    }
   
    @Override
    public void visit(Tree.CompilationUnit that) {
      for (Tree.Declaration st: that.getDeclarations()) {
        withinAttributeInitializer =
            (st instanceof Tree.AttributeDeclaration) &&
            st.getDeclarationModel()==declaration &&
            !(((Tree.AttributeDeclaration) st).getSpecifierOrInitializerExpression()
                instanceof Tree.LazySpecifierExpression);
        st.visit(this);
        withinAttributeInitializer = false;
      }
    }

    @Override
    public void visit(Tree.Body that) {
      for (Tree.Statement st: that.getStatements()) {
        withinAttributeInitializer =
            (st instanceof Tree.AttributeDeclaration) &&
            ((Tree.AttributeDeclaration) st).getDeclarationModel()==declaration &&
            !(((Tree.AttributeDeclaration) st).getSpecifierOrInitializerExpression()
                instanceof Tree.LazySpecifierExpression);
        st.visit(this);
        withinAttributeInitializer = false;
      }
    }
   
    private String longdesc() {
        if (declaration instanceof Value) {
            return "value is neither variable nor late and";
        }
        else if (declaration instanceof Method) {
            return "function";
        }
        else {
            return "declaration";
        }
    }
   
    private String shortdesc() {
        if (declaration instanceof Value) {
            return "value";
        }
        else if (declaration instanceof Method) {
            return "function";
        }
        else {
            return "declaration";
        }
    }

    @Override
    public void visit(Tree.SpecifierStatement that) {
        Tree.Term m = that.getBaseMemberExpression();
        boolean parameterized = false;
        while (m instanceof Tree.ParameterizedExpression) {
          m = ((Tree.ParameterizedExpression) m).getPrimary();
          parameterized = true;
        }
        if (m instanceof Tree.BaseMemberExpression) {
            Tree.BaseMemberExpression bme = (Tree.BaseMemberExpression) m;
//            Declaration member = getTypedDeclaration(bme.getScope(),
//                    name(bme.getIdentifier()), null, false, bme.getUnit());
          Declaration member = bme.getDeclaration();
          if (member==declaration) {
            if ((declaration.isFormal() || declaration.isDefault()) &&
                    !isForwardReferenceable()) {
                //TODO: is this error correct?! look at the condition above
                bme.addError("member is formal and may not be specified: '" +
                  member.getName() + "' is declared formal");
            }
            if (that.getRefinement()) {
                declare();
            }
                Tree.SpecifierExpression se = that.getSpecifierExpression();
        boolean lazy = se instanceof Tree.LazySpecifierExpression;
              if (declaration instanceof Value) {
                Value value = (Value) declaration;
                  if (!value.isVariable() &&
                          lazy!=value.isTransient()) {
                  // check that all assignments to a non-variable, in
                    // different paths of execution, all use the same
                    // kind of specifier, all =>, or all =
                    // TODO: sometimes this error appears only because
                    //       of a later line which illegally reassigns
                  se.addError("value must be specified using => lazy specifier: '" +
                          member.getName() + "'");
                  }
                  if (lazy) {
                      if (value.isVariable()) {
                          se.addError("variable value may not be specified using => lazy specifier: '" +
                                  member.getName() + "'");
                      }
                      else if (value.isLate()) {
                          se.addError("late reference may not be specified using => lazy specifier: '" +
                                  member.getName() + "'");
                      }
                  }
              }
              if (!lazy || !parameterized) {
                se.visit(this);
              }
              boolean constant = !isVariable() && !isLate();
              if (constant && !declaration.isDefinedInScope(that.getScope())) {
                  //this error is added by ExpressionVisitor
//                    that.addError("inherited member is not variable and may not be specified here: '" +
//                            member.getName() + "'");
              }
              else if (!declared && constant) {
                    bme.addError(shortdesc() +
                            " is not yet declared: '" +
                            member.getName() + "'");
              }
              else if (inLoop && constant &&
                      !(endsInBreakReturnThrow && lastContinue==null)) {
                if (specified.definitely) {
                  bme.addError(longdesc() +
                          " is aready definitely specified: '" +
                      member.getName() + "'", 803);
                }
                else {
                    bme.addError(longdesc() +
                          " is not definitely unspecified in loop: '" +
                      member.getName() + "'", 803);
                }
              }
                else if (withinDeclaration && constant &&
                        !that.getRefinement()) {
                    Declaration dec = getContainingDeclarationOfScope(that.getScope());
                    if (dec!=null && dec.equals(member)) {
                        bme.addError("cannot specify " + shortdesc() +
                                " from within its own body: '" + member.getName() + "'");
                    }
                    else {
                        bme.addError("cannot specify " + shortdesc() +
                                " declared in outer scope: '" + member.getName() + "'", 803);
                    }
                }
              else if (specified.possibly && constant) {
                if (specified.definitely) {
                    bme.addError(longdesc() +
                          " is aready definitely specified: '" +
                      member.getName() + "'", 803);
                }
                else {
                    bme.addError(longdesc() +
                          " is not definitely unspecified: '" +
                      member.getName() + "'", 803);
                }
              }
              else {
                  specify();
                  m.visit(this);
              }
              if (lazy && parameterized) {
                  se.visit(this);
              }
          }
          else {
              super.visit(that);
          }
        }
        else {
            super.visit(that);
        }
    }
   
    @Override
    public void visit(Tree.Declaration that) {
        boolean oe = endsInBreakReturnThrow;
        Tree.Continue olc = lastContinue;
        lastContinue = null;
        endsInBreakReturnThrow = false;
        if (that.getDeclarationModel()==declaration) {
            inLoop = false;
            beginDisabledSpecificationScope();
            declare();
            super.visit(that);
            endDisabledSpecificationScope(false);
            inLoop = false;
        }
        else {
            boolean l = inLoop;
            inLoop = false;
            boolean c = beginDisabledSpecificationScope();
            boolean d = beginDeclarationScope();
            SpecificationState as = beginSpecificationScope();
            super.visit(that);
            endDisabledSpecificationScope(c);
            endDeclarationScope(d);
            endSpecificationScope(as);
            inLoop = l;
        }
        endsInBreakReturnThrow = oe;
        lastContinue = olc;
    }

    @Override
    public void visit(Tree.TypedArgument that) {
        if (that.getDeclarationModel()==declaration) {
            inLoop = false;
            beginDisabledSpecificationScope();
            super.visit(that);
            declare();
            endDisabledSpecificationScope(false);
            inLoop = false;
        }
        else {
            boolean l = inLoop;
            inLoop = false;
            boolean c = beginDisabledSpecificationScope();
            boolean d = beginDeclarationScope();
            SpecificationState as = beginSpecificationScope();
            super.visit(that);
            endDisabledSpecificationScope(c);
            endDeclarationScope(d);
            endSpecificationScope(as);
            inLoop = l;
        }
    }
   
    @Override
    public void visit(Tree.MethodDeclaration that) {
        if (that.getDeclarationModel()==declaration) {
            if (that.getSpecifierExpression()!=null) {
                specify();
                super.visit(that);
            }
            else {
                super.visit(that);
              if (declaration.isToplevel() &&
                      !declaration.isNative()) {
                  that.addError("toplevel function must be specified: '" +
                          declaration.getName() + "' may not be forward declared");
              }
              else if (declaration.isClassMember() &&
                      !declaration.isNative() &&
                      !declaration.isFormal() &&
                      that.getDeclarationModel().getInitializerParameter()==null &&
                      declarationSection) {
                  that.addError("forward declaration may not occur in declaration section: '" +
                              declaration.getName() + "'", 1450);
              }
              else if (declaration.isInterfaceMember() &&
                      !declaration.isNative() &&
                  !declaration.isFormal()) {
                  that.addError("interface method must be formal or specified: '" +
                          declaration.getName() + "'", 1400);
              }
            }
        }
        else {
            super.visit(that);
        }
    }
   
    @Override
    public void visit(Tree.MethodDefinition that) {
        if (that.getDeclarationModel()==declaration) {
            declare();
            specify();
        }
        super.visit(that);
    }
   
    @Override
    public void visit(Tree.MethodArgument that) {
        if (that.getDeclarationModel()==declaration) {
            declare();
            specify();
        }
        super.visit(that);
    }
   
    @Override
    public void visit(Tree.Variable that) {
        super.visit(that);
        if (that.getDeclarationModel()==declaration) {
            specify();
        }
    }
   
    @Override
    public void visit(Tree.Parameter that) {
        super.visit(that);
        if (that.getParameterModel().getModel()==declaration) {
            specify();
        }
    }
   
    @Override
    public void visit(Tree.InitializerParameter that) {
        super.visit(that);
        Parameter d = that.getParameterModel();
        Declaration a = that.getScope().getDirectMember(d.getName(), null, false);
        if (a!=null && a==declaration) {
            specify();
            hasParameter = true;
        }
    }
   
    @Override
    public void visit(Tree.TypeParameterDeclaration that) {
        super.visit(that);
        if (that.getDeclarationModel()==declaration) {
            specify();
        }
    }
   
    @Override
    public void visit(Tree.AttributeDeclaration that) {
        if (that.getDeclarationModel()==declaration) {
          Tree.SpecifierOrInitializerExpression sie =
                  that.getSpecifierOrInitializerExpression();
            if (sie!=null) {
                super.visit(that);
                specify();
            }
            else {
              super.visit(that);
              if (declaration.isToplevel() &&
                      !declaration.isNative() &&
                      !isLate()) {
                  if (isVariable()) {
                      that.addError("toplevel variable value must be initialized: '" +
                              declaration.getName() + "'");
                  }
                  else {
                      that.addError("toplevel value must be specified: '" +
                              declaration.getName() + "'");
                  }
              }
              else if (declaration.isClassOrInterfaceMember() &&
                      !declaration.isNative() &&
                      !declaration.isFormal() &&
                      that.getDeclarationModel().getInitializerParameter()==null &&
                      !that.getDeclarationModel().isLate() &&
                      declarationSection) {
                  that.addError("forward declaration may not occur in declaration section: '" +
                              declaration.getName() + "'", 1450);
              }
            }
        }
        else {
          super.visit(that);
        }
    }
   
    @Override
    public void visit(Tree.AttributeGetterDefinition that) {
        if (that.getDeclarationModel()==declaration) {
            declare();
            super.visit(that);       
            specify();
        }
        else {
            super.visit(that);       
        }
    }
   
    @Override
    public void visit(Tree.AttributeSetterDefinition that) {
        Setter d = that.getDeclarationModel();
        if (d==declaration ||
            d.getParameter().getModel()==declaration) {
            declare();
            specify();
        }
        super.visit(that);       
    }
   
    @Override
    public void visit(Tree.AttributeArgument that) {
        if (that.getDeclarationModel()==declaration) {
            declare();
            specify();
        }
        super.visit(that);       
    }
   
    @Override
    public void visit(Tree.ObjectDefinition that) {
        if (that.getDeclarationModel()==declaration) {
            declare();
            specify();
        }
        super.visit(that);
    }
   
    @Override
    public void visit(Tree.ObjectArgument that) {
        if (that.getDeclarationModel()==declaration) {
            declare();
            specify();
        }
        super.visit(that);
    }
   
    @Override
    public void visit(Tree.AnyClass that) {
        if (that.getDeclarationModel()==declaration) {
            declare();
            specify();
        }
        super.visit(that);
    }
   
    @Override
    public void visit(Tree.ClassBody that) {
        Tree.Declaration d = getDeclaration(that);
        if (d!=null) {
            Tree.Statement les = getLastExecutableStatement(that);
            declarationSection = les==null;
            lastExecutableStatement = les;
            super.visit(that);       
            declarationSection = false;
            lastExecutableStatement = null;
            if (isSharedDeclarationUninitialized()) {
                d.addError("must be definitely specified by class initializer: '" +
                        d.getDeclarationModel().getName(that.getUnit()) + "'", 1401);
            }
        }
        else {
            super.visit(that);
        }
    }

    private Tree.Declaration getDeclaration(Tree.ClassBody that) {
        for (Tree.Statement s: that.getStatements()) {
            if (s instanceof Tree.Declaration) {
                Tree.Declaration d = (Tree.Declaration) s;
                if (d.getDeclarationModel()==declaration) {
                    return d;
                }
            }
        }
        return null;
    }
   
    @Override
    public void visit(Tree.Statement that) {
        super.visit(that);
        checkDeclarationSection(that);
    }

    private void checkDeclarationSection(Tree.Statement that) {
        declarationSection = declarationSection ||
                that==lastExecutableStatement;
    }
   
    @Override
    public void visit(Tree.AnyInterface that) {
        if (that.getDeclarationModel()==declaration) {
            declare();
            specify();
        }
        super.visit(that);       
    }
   
    @Override
    public void visit(Tree.TypeAliasDeclaration that) {
        if (that.getDeclarationModel()==declaration) {
            declare();
            specify();
        }
        super.visit(that);       
    }
   
    public void visit(Tree.Return that) {
        super.visit(that);
        if (!withinDeclaration) {
            if (isSharedDeclarationUninitialized()) {
                that.addError("must be definitely specified by class initializer: '" +
                        declaration.getName(that.getUnit()) + "'");
            }
        }
        exit();
    }

    private boolean isSharedDeclarationUninitialized() {
        return (declaration.isShared() ||
            declaration.getOtherInstanceAccess()) &&
                !declaration.isFormal() &&
                !declaration.isNative() &&
                !isLate() &&
                !specified.definitely;
    }
   
    @Override
    public void visit(Tree.Throw that) {
        super.visit(that);
        exit();
    }
   
    @Override
    public void visit(Tree.Assertion that) {
        super.visit(that);
        if (isNeverSatisfied(that.getConditionList())) {
            exit();
        }
    }
   
    @Override
    public void visit(Tree.Break that) {
        super.visit(that);
        exit();
        if (!specified.definitely) {
            specified.byExits = false;
        }
    }

    @Override
    public void visit(Tree.Continue that) {
        super.visit(that);
        exit();
        if (lastContinue==that) {
            lastContinue=null;
        }
    }
   
    @Override
    public void visit(Tree.IfStatement that) {       
        if (that==lastContinueStatement) {
            lastContinueStatement=null;
        }

        Tree.IfClause ifClause = that.getIfClause();
        if (ifClause!=null) {
            Tree.ConditionList cl = ifClause.getConditionList();
            if (cl!=null) {
                cl.visit(this);
            }
        }
       
        boolean d = beginDeclarationScope();
        SpecificationState as = beginSpecificationScope();
        if (ifClause!=null) {
            Tree.Block block = ifClause.getBlock();
            if (block!=null) {
                block.visit(this);
            }
        }
        boolean definitelyAssignedByIfClause = specified.definitely || specified.exited;
        boolean possiblyAssignedByIfClause = specified.possibly;
        boolean possiblyExitedFromIfClause = specified.exited;
        boolean specifiedByExitsFromIfClause = specified.byExits;
        endDeclarationScope(d);
        endSpecificationScope(as);
       
        boolean definitelyAssignedByElseClause;
        boolean possiblyAssignedByElseClause;
        boolean possiblyExitedFromElseClause;
        boolean specifiedByExitsFromElseClause;
        Tree.ElseClause elseClause = that.getElseClause();
        if (elseClause!=null) {
            d = beginDeclarationScope();
            as = beginSpecificationScope();
            elseClause.visit(this);
            definitelyAssignedByElseClause = specified.definitely || specified.exited;
            possiblyAssignedByElseClause = specified.possibly;
            possiblyExitedFromElseClause = specified.exited;
            specifiedByExitsFromElseClause = specified.byExits;
            endDeclarationScope(d);
            endSpecificationScope(as);
        }
        else {
            definitelyAssignedByElseClause = false;
            possiblyAssignedByElseClause = false;
            possiblyExitedFromElseClause = false;
            specifiedByExitsFromElseClause = true;
        }
       
        if (isAlwaysSatisfied(ifClause.getConditionList())) {
            specified.definitely = specified.definitely || definitelyAssignedByIfClause;
            specified.possibly = specified.possibly || possiblyAssignedByIfClause;
            specified.exited = specified.exited || possiblyExitedFromIfClause;
            specified.byExits = specified.byExits && specifiedByExitsFromIfClause;
        }
        else if (isNeverSatisfied(ifClause.getConditionList())) {
            specified.definitely = specified.definitely || definitelyAssignedByElseClause;
            specified.possibly = specified.possibly || possiblyAssignedByElseClause;
            specified.exited = specified.exited || possiblyExitedFromElseClause;
            specified.byExits = specified.byExits && specifiedByExitsFromElseClause;
        }
        else {
            specified.definitely = specified.definitely || (definitelyAssignedByIfClause && definitelyAssignedByElseClause);
            specified.possibly = specified.possibly || possiblyAssignedByIfClause || possiblyAssignedByElseClause;
            specified.exited = specified.exited || possiblyExitedFromIfClause || possiblyExitedFromElseClause;
            specified.byExits = specified.byExits && specifiedByExitsFromIfClause && specifiedByExitsFromElseClause;
        }
       
        checkDeclarationSection(that);
    }
   
    @Override
    public void visit(Tree.TryCatchStatement that) {
        if (that==lastContinueStatement) {
            lastContinueStatement=null;
        }
       
        boolean d = beginDeclarationScope();
        SpecificationState as = beginSpecificationScope();
        Tree.TryClause tryClause = that.getTryClause();
        if (tryClause!=null ) {
            tryClause.visit(this);
        }
        boolean definitelyAssignedByTryClause = specified.definitely || specified.exited;
        boolean possiblyAssignedByTryClause = specified.possibly;
        boolean possiblyExitedFromTryClause = specified.exited;
        boolean specifiedByExitsFromTryClause = specified.byExits;
        endDeclarationScope(d);
        endSpecificationScope(as);
        specified.possibly = specified.possibly || possiblyAssignedByTryClause;
        specified.exited = specified.exited || possiblyExitedFromTryClause;
       
        boolean definitelyAssignedByEveryCatchClause = true;
        boolean possiblyAssignedBySomeCatchClause = false;
        boolean possiblyExitedFromSomeCatchClause = false;
        boolean specifiedByExitsFromEveryCatchClause = true;
        for (Tree.CatchClause cc: that.getCatchClauses()) {
            d = beginDeclarationScope();
            as = beginSpecificationScope();
            cc.visit(this);
            definitelyAssignedByEveryCatchClause = definitelyAssignedByEveryCatchClause && (specified.definitely || specified.exited);
            possiblyAssignedBySomeCatchClause = possiblyAssignedBySomeCatchClause || specified.possibly;
            possiblyExitedFromSomeCatchClause = possiblyExitedFromSomeCatchClause || specified.exited;
            specifiedByExitsFromEveryCatchClause = specifiedByExitsFromEveryCatchClause && specified.byExits;
            endDeclarationScope(d);
            endSpecificationScope(as);
        }
        specified.possibly = specified.possibly || possiblyAssignedBySomeCatchClause;
        specified.exited = specified.exited || possiblyExitedFromSomeCatchClause;
       
        boolean definitelyAssignedByFinallyClause;
        boolean possiblyAssignedByFinallyClause;
        boolean possiblyExitedFromFinallyClause;
        boolean specifiedByExitsFromFinallyClause;
        Tree.FinallyClause finallyClause = that.getFinallyClause();
        if (finallyClause!=null) {
            d = beginDeclarationScope();
            as = beginSpecificationScope();
            finallyClause.visit(this);
            definitelyAssignedByFinallyClause = specified.definitely || specified.exited;
            possiblyAssignedByFinallyClause = specified.possibly;
            possiblyExitedFromFinallyClause = specified.exited;
            specifiedByExitsFromFinallyClause = specified.byExits;
            endDeclarationScope(d);
            endSpecificationScope(as);
        }
        else {
            definitelyAssignedByFinallyClause = false;
            possiblyAssignedByFinallyClause = false;
            possiblyExitedFromFinallyClause = false;
            specifiedByExitsFromFinallyClause = true;
        }
        specified.possibly = specified.possibly || possiblyAssignedByFinallyClause;
        specified.definitely = specified.definitely || definitelyAssignedByFinallyClause
                || (definitelyAssignedByTryClause && definitelyAssignedByEveryCatchClause);
        specified.exited = specified.exited || possiblyExitedFromFinallyClause;
        specified.byExits = specified.byExits ||  specifiedByExitsFromFinallyClause ||
                (specifiedByExitsFromEveryCatchClause && specifiedByExitsFromTryClause);
       
        checkDeclarationSection(that);
    }
   
    @Override
    public void visit(Tree.SwitchStatement that) {
        if (that==lastContinueStatement) {
            lastContinueStatement=null;
        }
       
        Tree.SwitchClause switchClause = that.getSwitchClause();
        if (switchClause!=null) {
            switchClause.visit(this);
        }
        boolean definitelyAssignedByEveryCaseClause = true;
        boolean possiblyAssignedBySomeCaseClause = false;
        boolean possiblyExitedFromSomeCaseClause = false;
        boolean specifiedByExitsFromEveryCaseClause = true;
       
        Tree.SwitchCaseList switchCaseList = that.getSwitchCaseList();
        for (Tree.CaseClause cc: switchCaseList.getCaseClauses()) {
            boolean d = beginDeclarationScope();
            SpecificationState as = beginSpecificationScope();
            cc.visit(this);
            definitelyAssignedByEveryCaseClause = definitelyAssignedByEveryCaseClause && (specified.definitely || specified.exited);
            possiblyAssignedBySomeCaseClause = possiblyAssignedBySomeCaseClause || specified.possibly;
            possiblyExitedFromSomeCaseClause = possiblyExitedFromSomeCaseClause || specified.exited;
            specifiedByExitsFromEveryCaseClause = specifiedByExitsFromEveryCaseClause && specified.byExits;
            endDeclarationScope(d);
            endSpecificationScope(as);
        }
       
        Tree.ElseClause elseClause = switchCaseList.getElseClause();
        if (elseClause!=null) {
            boolean d = beginDeclarationScope();
            SpecificationState as = beginSpecificationScope();
            elseClause.visit(this);
            definitelyAssignedByEveryCaseClause = definitelyAssignedByEveryCaseClause && (specified.definitely || specified.exited);
            possiblyAssignedBySomeCaseClause = possiblyAssignedBySomeCaseClause || specified.possibly;
            possiblyExitedFromSomeCaseClause = possiblyExitedFromSomeCaseClause || specified.exited;
            specifiedByExitsFromEveryCaseClause = specifiedByExitsFromEveryCaseClause && specified.byExits;
            endDeclarationScope(d);
            endSpecificationScope(as);
        }

        specified.possibly = specified.possibly || possiblyAssignedBySomeCaseClause;
        specified.definitely = specified.definitely || definitelyAssignedByEveryCaseClause;
        specified.exited = specified.exited || possiblyExitedFromSomeCaseClause;
        specified.byExits = specified.byExits && specifiedByExitsFromEveryCaseClause;
       
        checkDeclarationSection(that);
    }
   
    @Override
    public void visit(Tree.WhileStatement that) {
      Tree.WhileClause whileClause = that.getWhileClause();
        if (whileClause!=null) {
            Tree.ConditionList cl = whileClause.getConditionList();
        if (cl!=null) {
          cl.visit(this);
        }
      }
     
        boolean d = beginDeclarationScope();
        SpecificationState as = beginSpecificationScope();
        Tree.Block block = whileClause.getBlock();
        if (block!=null) {
            if (isVariable() || isLate()) {
                block.visit(this);
            }
            else {
                boolean c = inLoop;
                inLoop = true;
                block.visit(this);
                inLoop = c;
            }
        }
        boolean possiblyAssignedByWhileClause = specified.possibly;
        boolean definitelyAssignedByWhileClause = specified.definitely;
       
        endDeclarationScope(d);
        endSpecificationScope(as);
       
        specified.definitely = specified.definitely || (definitelyAssignedByWhileClause &&
                 isAlwaysSatisfied(whileClause.getConditionList()));
        specified.possibly = specified.possibly || (possiblyAssignedByWhileClause &&
                 !isNeverSatisfied(whileClause.getConditionList()));
       
        checkDeclarationSection(that);
    }
   
    /*@Override
    public void visit(Tree.DoClause that) {
        if (isVariable()) {
            boolean d = beginDeclarationScope();
            super.visit(that);
            endDeclarationScope(d);
        }
        else {
            boolean c = beginDisabledSpecificationScope();
            boolean d = beginDeclarationScope();
            super.visit(that);
            endDisabledSpecificationScope(c);
            endDeclarationScope(d);
        }
    }*/

    @Override
    public void visit(Tree.ForStatement that) {
        boolean d = beginDeclarationScope();
        SpecificationState as = beginSpecificationScope();
        boolean atLeastOneIteration = false;
        Tree.ForClause forClause = that.getForClause();
        if (forClause!=null) {
            if (isVariable() || isLate()) {
                forClause.visit(this);
            }
            else {
                boolean c = inLoop;
                inLoop = true;
                forClause.visit(this);
                inLoop = c;
            }
            atLeastOneIteration = isAtLeastOne(forClause);
        }
        boolean possiblyExitedFromForClause = specified.exited && !specified.byExits;
        boolean possiblyAssignedByForClause = specified.possibly;

        endDeclarationScope(d);
        endSpecificationScope(as);

        boolean definitelyAssignedByElseClause;
        boolean possiblyAssignedByElseClause;
        Tree.ElseClause elseClause = that.getElseClause();
        if (elseClause!=null) {
            d = beginDeclarationScope();
            as = beginSpecificationScope();
            elseClause.visit(this);
            definitelyAssignedByElseClause = specified.definitely || specified.exited;
            possiblyAssignedByElseClause = specified.possibly;
            endDeclarationScope(d);
            endSpecificationScope(as);
        }
        else {
            definitelyAssignedByElseClause = false;
            possiblyAssignedByElseClause = false;
        }
       
        specified.definitely = specified.definitely || (!possiblyExitedFromForClause && (definitelyAssignedByElseClause|atLeastOneIteration));
        specified.possibly = specified.possibly || possiblyAssignedByForClause || possiblyAssignedByElseClause;
       
        checkDeclarationSection(that);
    }
     
}
TOP

Related Classes of com.redhat.ceylon.compiler.typechecker.analyzer.SpecificationVisitor$SpecificationState

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.