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

import com.redhat.ceylon.compiler.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.model.ProducedType;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;

/**
 * Validates the position in which covariant and contravariant
 * type parameters appear in the schemas of declarations.
 * 
 * @author Gavin King
 *
 */
public class TypeArgumentVisitor extends Visitor {
    
    private boolean contravariant = false;
    private Declaration parameterizedDeclaration;
    
    private void flip() {
        contravariant = !contravariant;
    }
    
    public TypeArgumentVisitor() {}
    
    @Override public void visit(Tree.ParameterList that) {
        flip();
        super.visit(that);
        flip();
    }
    
    @Override public void visit(Tree.TypeConstraint that) {
        super.visit(that);
        parameterizedDeclaration = that.getDeclarationModel().getDeclaration();
        flip();
        if (that.getSatisfiedTypes()!=null) {
            for (Tree.Type t: that.getSatisfiedTypes().getTypes()) {
                check(t, false);
            }
        }
        flip();
        parameterizedDeclaration = null;
    }
    
    @Override public void visit(Tree.Parameter that) {
        boolean topLevel = parameterizedDeclaration==null;
        if (topLevel) {
            parameterizedDeclaration = that.getDeclarationModel().getDeclaration();
        }
        super.visit(that);
        check(that.getType(), false);
        if (topLevel) {
            parameterizedDeclaration = null;
        }
    }
    
    @Override public void visit(Tree.TypedDeclaration that) {
        super.visit(that);
        check(that.getType(), that.getDeclarationModel().isVariable());
    }
    
    @Override public void visit(Tree.ClassOrInterface that) {
        super.visit(that);
        if (that.getSatisfiedTypes()!=null) {
            for (Tree.Type t: that.getSatisfiedTypes().getTypes()) {
                check(t, false);
            }
        }
    }
    
    @Override public void visit(Tree.AnyClass that) {
        super.visit(that);
        if (that.getExtendedType()!=null) {
            check(that.getExtendedType().getType(), false);
        }
    }
    
    private void check(Tree.Primary that, boolean variable) {
        if (that!=null) {
            ProducedType pt = that.getTypeModel();
            if ( pt!=null && !pt.checkVariance(!contravariant && !variable, contravariant && !variable, parameterizedDeclaration) ) {
                that.addError("incorrect variance: " + pt.getProducedTypeName());
            }
        }
    }
    
}

@@ -182,16 +182,83 @@
 edType;%0A
+import com.redhat.ceylon.compiler.typechecker.model.TypeParameter;%0A
 import c
@@ -1017,33 +1017,37 @@
 t);%0A        
-p
+TypeP
 arameter
 izedDeclarat
@@ -1030,39 +1030,28 @@
 ypeParameter
-izedDeclaration
+ dec
  = that.getD
@@ -1059,32 +1059,101 @@
 clarationModel()
+;%0A        if (dec!=null) %7B%0A            parameterizedDeclaration = dec
 .getDeclaration(
@@ -1159,32 +1159,36 @@
 ();%0A        
+  
+  
 flip();%0A
         if (
@@ -1167,32 +1167,36 @@
         flip();%0A
+    
         if (that
@@ -1229,32 +1229,36 @@
 ) %7B%0A            
+  
+  
 for (Tree.Type t
@@ -1306,32 +1306,36 @@
                 
+    
 check(t, false);
@@ -1339,32 +1339,36 @@
 e);%0A            
+    
 %7D%0A        %7D%0A    
@@ -1361,26 +1361,32 @@
         
+ 
-%7D%0A
+   %7D%0A  
         
 flip();%0A
@@ -1373,32 +1373,34 @@
 %7D%0A          
+  
 flip();%0A
         para
@@ -1379,32 +1379,36 @@
         flip();%0A
+    
         paramete
@@ -1428,24 +1428,34 @@
 ion = null;%0A
+        %7D%0A
     %7D%0A    %0A 

fix npe