<commit_before>#![allow(dead_code)]

use ast::*;
use visit::{Visitor};
use std::cell::RefCell;
use ptr::*;

pub type OptionalBinding = Option<B<Binding>>;

struct ExpEvaluator{
    value : i32
}

impl ExpEvaluator{
    fn get_value(&mut self, expr : & Expr)->i32{
        match expr{
            &Expr::NumExpr(value) => value,
            _ => 1
        }
    }
}

impl<'a> Visitor<'a> for ExpEvaluator{
    fn visit_expr(&mut self, expr: &'a Expr){
        match expr{
            &Expr::AddExpr(ref left, ref right) => {
                let v1 = self.get_value(left);
                let v2 = self.get_value(right);
                self.value = v1 + v2;
            },
            _ => {}
        }
    }
}

pub struct TypeChecker{
    //block_stack : Vec<RefCell<&'a  Block>>,
    pub sym_tab : Vec<(String, OptionalBinding)>,
    //decl_cnt : u32,
    //decl_cnt_stack : Vec<u32>,
    pub ty : TType
}

impl TypeChecker{
    pub fn new()->Self{
        TypeChecker {sym_tab : Vec::new(), ty : TType::TNil}
    }

    fn get_type_for(&self){//}->&TType{
        //self.block_stack
    }
}

impl<'a> Visitor<'a> for TypeChecker{
    fn visit_expr(&mut self, expr: &'a Expr){
        match expr{
            //FIXME remove NilExpr; this is only for unit testing
            &Expr::NilExpr => self.ty = TType::TString,
            &Expr::NumExpr(_) => self.ty = TType::TInt32,
            &Expr::StringExpr(_) => self.ty = TType::TString,
            &Expr::IdExpr(ref id) =>{
                //search in the symtab for id's existence and get the type
                let mut found = false;
                for &(ref _id, ref _binding) in &self.sym_tab{ //iterator returns a ref to tuple while iterating; so &(_,_) has to be used
                    if *_id == *id{
                        found = true;
                        self.ty = match **_binding.as_ref().unwrap(){
                            Binding::TypeBinding(ref ty) |
                            Binding::VarBinding(ref ty) |
                            Binding::FuncBinding(ref ty) => ty.clone()
                        };
                        break;
                    }
                }
                if !found{
                    panic!("{} not found", id);
                }
            },
            &Expr::AddExpr(ref left, ref right) => {
                self.visit_expr(left);
                let left_ty = self.ty.clone();
                if left_ty != TType::TInt32{
                    panic!("Expected left operand of int type")
                }
                self.visit_expr(right);
                if self.ty != TType::TInt32{
                    panic!("Expected right operand of int type")
                }
            },
            &Expr::DivExpr(ref left, ref right) => {
                self.visit_expr(left);
                let left_ty = self.ty.clone();
                if left_ty != TType::TInt32{
                    panic!("Expected left operand of int type")
                }
                self.visit_expr(right);
                if self.ty != TType::TInt32{
                    panic!("Expected right operand of int type")
                }

                match **right{
                    Expr::NumExpr(n) => if n == 0 {panic!("Denominator cannot be 0")},
                    _ => {}
                }
            },
            &Expr::SeqExpr(ref opt_expr_list) => {
                if opt_expr_list.is_none(){
                    self.ty = TType::TVoid;
                    return
                }
                for b_expr in opt_expr_list.as_ref().unwrap() {
                    self.visit_expr(&*b_expr);
                }
            },
            &Expr::IfThenExpr(ref conditional_expr, ref then_expr) => {
                self.visit_expr(conditional_expr);
                if self.ty != TType::TInt32{
                    panic!("Expected conditional expression of int type");
                }
                self.visit_expr(then_expr);
                if self.ty != TType::TVoid{
                    panic!("Expected if-body of void type")
                }
            },
            &Expr::WhileExpr(ref conditional_expr, ref body) => {
                self.visit_expr(conditional_expr);
                if self.ty != TType::TInt32{
                    panic!("Expected conditional expression of int type");
                }

                self.visit_expr(body);
                if self.ty != TType::TVoid{
                    panic!("Expected while-body of void type")
                }
            },
            &Expr::LetExpr(ref decls, ref opt_expr) => {
                self.sym_tab.push(("<marker>".to_string(), None));

                for dec in decls{ //decls is a &
                    self.visit_decl(dec);
                }

                match opt_expr {
                    &Some(ref b_expr) => {
                        self.visit_expr(&*expr);
                    },
                    _ => {}
                }
                //pop till marker and then pop marker
                while self.sym_tab.last().unwrap().0 != "<marker>".to_string(){
                    self.sym_tab.pop();
                }
                self.sym_tab.pop();
            },

            _ => {}
        }
    }

    fn visit_decl(&mut self, decl : &'a Decl){
        macro_rules! store_into_sym_tab {
            ($self_ : ident, $i : ident, $p : path) => {
                $self_.sym_tab.push(($i.clone(), Some(B($p($self_.ty.clone())))));
            }
        }
        match decl{
            &Decl::VarDec(ref id, ref ty, ref expr) => {
                self.visit_expr(expr);
                if *ty != self.ty{
                    panic!("Types mismatch")
                }
                store_into_sym_tab!(self, id, Binding::VarBinding);
            },
            &Decl::FunDec(ref id, ref params, ref ret_type, ref body, ref body_type) => {
                //self.visit_expr(&body);
                //if self.ty != *ret_type{
                if ret_type != body_type {
                    panic!("Return type doesn't match with the type of the last expression.")
                }
                store_into_sym_tab!(self, id, Binding::FuncBinding);
            },
            &Decl::TypeDec(ref id, ref ty) => {
                store_into_sym_tab!(self, id, Binding::TypeBinding);
            }
        }
    }
}

struct PrettyPrintVisitor;

impl<'a> Visitor<'a> for PrettyPrintVisitor{
    fn visit_expr(&mut self, expr:&'a Expr){
        match expr{
            &Expr::LetExpr(ref v, _) => {
                println!("(let");
                for d in v{
                    println!("\t(");

                }
                println!(")");
            },
            &Expr::AddExpr(ref left, ref right) => {
                self.visit_expr(left);
                println!(" Plus ");
                self.visit_expr(right);
            },
            &Expr::NumExpr(value) => {
                println!(" Num({}) ", value);
            },
            &Expr::IdExpr(ref value) => {
                println!(" Ident({}) ", value);
            },
            _ => {}
        }
    }

    fn visit_block(&mut self, block: &'a Block){
        for s in &block.statements{
            self.visit_stmt(&*s);
        }
    }

    fn visit_stmt(&mut self, stmt : &'a Stmt){
        match stmt{
            &Stmt::VarDeclStmt(ref local) => {
                println!("(var {} type {} init ", local.ident, local.ty);
                self.visit_expr(&*local.expr);
                println!(")");
            },
            &Stmt::ExprStmt(ref expr) => {
                self.visit_expr(expr);
            }
        }
    }
}

//FIXME remove this struct
struct SymbolTableBuilder<'a>{
    block_stack : Vec<RefCell<&'a  Block>>
}

impl<'a> SymbolTableBuilder<'a>{
    fn new()->Self{
        SymbolTableBuilder {block_stack : Vec::new()}
    }
}

impl<'a> Visitor<'a> for SymbolTableBuilder<'a>{
    fn visit_block(&mut self, block : &'a Block){
        self.block_stack.push(RefCell::new(block));
        for s in &block.statements{
            self.visit_stmt(s);
        }
        self.block_stack.pop();
    }

    fn visit_stmt(&mut self, stmt : &'a Stmt){
        match stmt{
            &Stmt::VarDeclStmt(ref local) => {
                    //FIXME deduce the correct type
                    let mut block = self.block_stack.last_mut().unwrap().borrow_mut();
                    block.sym_tab.borrow_mut().insert(local.ident.clone(), TType::TInt32);
                },
            &Stmt::ExprStmt(ref expr) => {
                let b =  &**expr; //*expr is deref B which is Box<T>; **expr is deref Box<T> which is T; &**expr is therefore &T
                match b {
                    &Expr::BlockExpr(ref block) => {
                        self.visit_block(block);
                    },
                    _ => {}
                }
            }
        }
    }
}

#[test]
fn test_pp_visit_add_expr(){
    let mut p = PrettyPrintVisitor;
    p.visit_expr(&Expr::AddExpr(B(Expr::NumExpr(1)), B(Expr::NumExpr(2))));
}

#[test]
fn test_pp_visit_block(){
    let mut p = PrettyPrintVisitor;
    let mut b = Block::new();
    p.visit_block(&b);
}

//#[test]
fn test_pp_visit_add(){
    let mut p = PrettyPrintVisitor;
    let mut b = Block::new();
    p.visit_block(&b);
}

#[test]
fn test_ty_set_for_num() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::NumExpr(23));
    assert_eq!(TType::TInt32, v.ty);
}

#[test]
fn test_ty_set_for_int_id() {
    let mut v = TypeChecker::new();
    v.sym_tab.push(("a".to_string(), Some(B(Binding::VarBinding(TType::TInt32)))));
    v.visit_expr(&Expr::IdExpr("a".to_string()));
    assert_eq!(TType::TInt32, v.ty);
}

#[test]
fn test_type_match_int_for_var_dec() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::VarDec("a".to_string(), TType::TInt32, B(Expr::NumExpr(4))));
    assert_eq!(TType::TInt32, v.ty);
    assert_eq!(v.sym_tab.len(), 1);
    assert_eq!(v.sym_tab[0].0, "a".to_string());
    //assert_eq!(v.sym_tab[0].1, TType::TInt32);
}

#[test]
fn test_type_match_string_for_var_dec() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::VarDec("a".to_string(), TType::TString, B(Expr::NilExpr)));
    assert_eq!(TType::TString, v.ty);
    assert_eq!(v.sym_tab.len(), 1);
    assert_eq!(v.sym_tab[0].0, "a".to_string());
    //assert_eq!(v.sym_tab[0].1, TType::TString);
}

#[test]
#[should_panic]
fn test_type_check_for_var_dec_type_mismatch() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::VarDec("a".to_string(), TType::TInt32, B(Expr::NilExpr)));
    assert_eq!(TType::TInt32, v.ty);
}

#[test]
fn test_correct_types_for_add_expr() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::AddExpr(B(Expr::NumExpr(4)), B(Expr::NumExpr(4))));
    assert_eq!(v.ty, TType::TInt32);
}

#[test]
#[should_panic(expected="Expected left operand of int type")]
fn test_left_type_invalid_for_add_expr() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::AddExpr(B(Expr::NilExpr), B(Expr::NumExpr(4))));
}

#[test]
#[should_panic(expected="Expected right operand of int type")]
fn test_right_type_invalid_for_add_expr() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::AddExpr(B(Expr::NumExpr(4)), B(Expr::NilExpr)));
}

#[test]
fn test_var_hiding() {
    //let mut v =
}

#[test]
fn test_func_decl_correct_return_type() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::FunDec(String::from("foo"), None, TType::TInt32, B(Expr::NumExpr(4)), TType::TInt32));
}

#[test]
#[should_panic(expected="Return type doesn't match with the type of the last expression.")]
fn test_func_decl_incorrect_return_type() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::FunDec(String::from("foo"), None, TType::TString, B(Expr::NumExpr(4)), TType::TInt32));
}

#[test]
#[should_panic(expected="Expected conditional expression of int type")]
fn test_if_expr_with_incorrect_conditional_type() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::IfThenExpr(B(Expr::StringExpr(String::from("a"))), B(Expr::StringExpr(String::from("a")))));
}

#[test]
fn test_if_expr_with_int_type() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::IfThenExpr(B(Expr::NumExpr(1)), B(Expr::SeqExpr(None))));
    assert_eq!(v.ty, TType::TVoid);
}

#[test]
#[should_panic(expected="Expected if-body of void type")]
fn test_if_expr_with_int_type_conditional_and_int_type_as_body_type() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::IfThenExpr(B(Expr::NumExpr(1)), B(Expr::NumExpr(1))));
    assert_eq!(v.ty, TType::TInt32);
}

#[test]
#[should_panic(expected="Expected conditional expression of int type")]
fn test_while_expr_with_incorrect_conditional_type() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::WhileExpr(B(Expr::StringExpr(String::from("a"))), B(Expr::StringExpr(String::from("a")))));
}

#[test]
#[should_panic(expected="Expected while-body of void type")]
fn test_while_expr_with_int_type() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::WhileExpr(B(Expr::NumExpr(1)), B(Expr::StringExpr(String::from("a")))));
}

#[test]
#[should_panic(expected="Denominator cannot be 0")]
fn test_div_expr_with_0_as_denominator(){
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::DivExpr(B(Expr::NumExpr(1)), B(Expr::NumExpr(0))));
}

#[test]
fn test_div_expr_with_1_as_denominator(){
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::DivExpr(B(Expr::NumExpr(1)), B(Expr::NumExpr(1))));
    assert_eq!(v.ty, TType::TInt32);
}
<commit_msg>Add type checking for the for-expr<commit_after>#![allow(dead_code)]

use ast::*;
use visit::{Visitor};
use std::cell::RefCell;
use ptr::*;

pub type OptionalBinding = Option<B<Binding>>;

struct ExpEvaluator{
    value : i32
}

impl ExpEvaluator{
    fn get_value(&mut self, expr : & Expr)->i32{
        match expr{
            &Expr::NumExpr(value) => value,
            _ => 1
        }
    }
}

impl<'a> Visitor<'a> for ExpEvaluator{
    fn visit_expr(&mut self, expr: &'a Expr){
        match expr{
            &Expr::AddExpr(ref left, ref right) => {
                let v1 = self.get_value(left);
                let v2 = self.get_value(right);
                self.value = v1 + v2;
            },
            _ => {}
        }
    }
}

pub struct TypeChecker{
    //block_stack : Vec<RefCell<&'a  Block>>,
    pub sym_tab : Vec<(String, OptionalBinding)>,
    //decl_cnt : u32,
    //decl_cnt_stack : Vec<u32>,
    pub ty : TType
}

impl TypeChecker{
    pub fn new()->Self{
        TypeChecker {sym_tab : Vec::new(), ty : TType::TNil}
    }

    fn get_type_for(&self){//}->&TType{
        //self.block_stack
    }
}

impl<'a> Visitor<'a> for TypeChecker{
    fn visit_expr(&mut self, expr: &'a Expr){
        match expr{
            //FIXME remove NilExpr; this is only for unit testing
            &Expr::NilExpr => self.ty = TType::TString,
            &Expr::NumExpr(_) => self.ty = TType::TInt32,
            &Expr::StringExpr(_) => self.ty = TType::TString,
            &Expr::IdExpr(ref id) =>{
                //search in the symtab for id's existence and get the type
                let mut found = false;
                for &(ref _id, ref _binding) in &self.sym_tab{ //iterator returns a ref to tuple while iterating; so &(_,_) has to be used
                    if *_id == *id{
                        found = true;
                        self.ty = match **_binding.as_ref().unwrap(){
                            Binding::TypeBinding(ref ty) |
                            Binding::VarBinding(ref ty) |
                            Binding::FuncBinding(ref ty) => ty.clone()
                        };
                        break;
                    }
                }
                if !found{
                    panic!("{} not found", id);
                }
            },
            &Expr::AddExpr(ref left, ref right) => {
                self.visit_expr(left);
                let left_ty = self.ty.clone();
                if left_ty != TType::TInt32{
                    panic!("Expected left operand of int type")
                }
                self.visit_expr(right);
                if self.ty != TType::TInt32{
                    panic!("Expected right operand of int type")
                }
            },
            &Expr::DivExpr(ref left, ref right) => {
                self.visit_expr(left);
                let left_ty = self.ty.clone();
                if left_ty != TType::TInt32{
                    panic!("Expected left operand of int type")
                }
                self.visit_expr(right);
                if self.ty != TType::TInt32{
                    panic!("Expected right operand of int type")
                }

                match **right{
                    Expr::NumExpr(n) => if n == 0 {panic!("Denominator cannot be 0")},
                    _ => {}
                }
            },
            &Expr::SeqExpr(ref opt_expr_list) => {
                if opt_expr_list.is_none(){
                    self.ty = TType::TVoid;
                    return
                }
                for b_expr in opt_expr_list.as_ref().unwrap() {
                    self.visit_expr(&*b_expr);
                }
            },
            &Expr::IfThenExpr(ref conditional_expr, ref then_expr) => {
                self.visit_expr(conditional_expr);
                if self.ty != TType::TInt32{
                    panic!("Expected conditional expression of int type");
                }
                self.visit_expr(then_expr);
                if self.ty != TType::TVoid{
                    panic!("Expected if-body of void type")
                }
            },
            &Expr::WhileExpr(ref conditional_expr, ref body) => {
                self.visit_expr(conditional_expr);
                if self.ty != TType::TInt32{
                    panic!("Expected conditional expression of int type");
                }

                self.visit_expr(body);
                if self.ty != TType::TVoid{
                    panic!("Expected while-body of void type")
                }
            },
            &Expr::ForExpr(ref id, ref from, ref to, ref body) => {
                self.visit_expr(from);
                if self.ty != TType::TInt32{
                    panic!("Initializing expression type should be int in a for loop");
                }

                self.visit_expr(to);
                if self.ty != TType::TInt32{
                    panic!("To expression type should be int in a for loop");
                }
            },
            &Expr::LetExpr(ref decls, ref opt_expr) => {
                self.sym_tab.push(("<marker>".to_string(), None));

                for dec in decls{ //decls is a &
                    self.visit_decl(dec);
                }

                match opt_expr {
                    &Some(ref b_expr) => {
                        self.visit_expr(&*expr);
                    },
                    _ => {}
                }
                //pop till marker and then pop marker
                while self.sym_tab.last().unwrap().0 != "<marker>".to_string(){
                    self.sym_tab.pop();
                }
                self.sym_tab.pop();
            },

            _ => {}
        }
    }

    fn visit_decl(&mut self, decl : &'a Decl){
        macro_rules! store_into_sym_tab {
            ($self_ : ident, $i : ident, $p : path) => {
                $self_.sym_tab.push(($i.clone(), Some(B($p($self_.ty.clone())))));
            }
        }
        match decl{
            &Decl::VarDec(ref id, ref ty, ref expr) => {
                self.visit_expr(expr);
                if *ty != self.ty{
                    panic!("Types mismatch")
                }
                store_into_sym_tab!(self, id, Binding::VarBinding);
            },
            &Decl::FunDec(ref id, ref params, ref ret_type, ref body, ref body_type) => {
                //self.visit_expr(&body);
                //if self.ty != *ret_type{
                if ret_type != body_type {
                    panic!("Return type doesn't match with the type of the last expression.")
                }
                store_into_sym_tab!(self, id, Binding::FuncBinding);
            },
            &Decl::TypeDec(ref id, ref ty) => {
                store_into_sym_tab!(self, id, Binding::TypeBinding);
            }
        }
    }
}

struct PrettyPrintVisitor;

impl<'a> Visitor<'a> for PrettyPrintVisitor{
    fn visit_expr(&mut self, expr:&'a Expr){
        match expr{
            &Expr::LetExpr(ref v, _) => {
                println!("(let");
                for d in v{
                    println!("\t(");

                }
                println!(")");
            },
            &Expr::AddExpr(ref left, ref right) => {
                self.visit_expr(left);
                println!(" Plus ");
                self.visit_expr(right);
            },
            &Expr::NumExpr(value) => {
                println!(" Num({}) ", value);
            },
            &Expr::IdExpr(ref value) => {
                println!(" Ident({}) ", value);
            },
            _ => {}
        }
    }

    fn visit_block(&mut self, block: &'a Block){
        for s in &block.statements{
            self.visit_stmt(&*s);
        }
    }

    fn visit_stmt(&mut self, stmt : &'a Stmt){
        match stmt{
            &Stmt::VarDeclStmt(ref local) => {
                println!("(var {} type {} init ", local.ident, local.ty);
                self.visit_expr(&*local.expr);
                println!(")");
            },
            &Stmt::ExprStmt(ref expr) => {
                self.visit_expr(expr);
            }
        }
    }
}

//FIXME remove this struct
struct SymbolTableBuilder<'a>{
    block_stack : Vec<RefCell<&'a  Block>>
}

impl<'a> SymbolTableBuilder<'a>{
    fn new()->Self{
        SymbolTableBuilder {block_stack : Vec::new()}
    }
}

impl<'a> Visitor<'a> for SymbolTableBuilder<'a>{
    fn visit_block(&mut self, block : &'a Block){
        self.block_stack.push(RefCell::new(block));
        for s in &block.statements{
            self.visit_stmt(s);
        }
        self.block_stack.pop();
    }

    fn visit_stmt(&mut self, stmt : &'a Stmt){
        match stmt{
            &Stmt::VarDeclStmt(ref local) => {
                    //FIXME deduce the correct type
                    let mut block = self.block_stack.last_mut().unwrap().borrow_mut();
                    block.sym_tab.borrow_mut().insert(local.ident.clone(), TType::TInt32);
                },
            &Stmt::ExprStmt(ref expr) => {
                let b =  &**expr; //*expr is deref B which is Box<T>; **expr is deref Box<T> which is T; &**expr is therefore &T
                match b {
                    &Expr::BlockExpr(ref block) => {
                        self.visit_block(block);
                    },
                    _ => {}
                }
            }
        }
    }
}

#[test]
fn test_pp_visit_add_expr(){
    let mut p = PrettyPrintVisitor;
    p.visit_expr(&Expr::AddExpr(B(Expr::NumExpr(1)), B(Expr::NumExpr(2))));
}

#[test]
fn test_pp_visit_block(){
    let mut p = PrettyPrintVisitor;
    let mut b = Block::new();
    p.visit_block(&b);
}

//#[test]
fn test_pp_visit_add(){
    let mut p = PrettyPrintVisitor;
    let mut b = Block::new();
    p.visit_block(&b);
}

#[test]
fn test_ty_set_for_num() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::NumExpr(23));
    assert_eq!(TType::TInt32, v.ty);
}

#[test]
fn test_ty_set_for_int_id() {
    let mut v = TypeChecker::new();
    v.sym_tab.push(("a".to_string(), Some(B(Binding::VarBinding(TType::TInt32)))));
    v.visit_expr(&Expr::IdExpr("a".to_string()));
    assert_eq!(TType::TInt32, v.ty);
}

#[test]
fn test_type_match_int_for_var_dec() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::VarDec("a".to_string(), TType::TInt32, B(Expr::NumExpr(4))));
    assert_eq!(TType::TInt32, v.ty);
    assert_eq!(v.sym_tab.len(), 1);
    assert_eq!(v.sym_tab[0].0, "a".to_string());
    //assert_eq!(v.sym_tab[0].1, TType::TInt32);
}

#[test]
fn test_type_match_string_for_var_dec() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::VarDec("a".to_string(), TType::TString, B(Expr::NilExpr)));
    assert_eq!(TType::TString, v.ty);
    assert_eq!(v.sym_tab.len(), 1);
    assert_eq!(v.sym_tab[0].0, "a".to_string());
    //assert_eq!(v.sym_tab[0].1, TType::TString);
}

#[test]
#[should_panic]
fn test_type_check_for_var_dec_type_mismatch() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::VarDec("a".to_string(), TType::TInt32, B(Expr::NilExpr)));
    assert_eq!(TType::TInt32, v.ty);
}

#[test]
fn test_correct_types_for_add_expr() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::AddExpr(B(Expr::NumExpr(4)), B(Expr::NumExpr(4))));
    assert_eq!(v.ty, TType::TInt32);
}

#[test]
#[should_panic(expected="Expected left operand of int type")]
fn test_left_type_invalid_for_add_expr() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::AddExpr(B(Expr::NilExpr), B(Expr::NumExpr(4))));
}

#[test]
#[should_panic(expected="Expected right operand of int type")]
fn test_right_type_invalid_for_add_expr() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::AddExpr(B(Expr::NumExpr(4)), B(Expr::NilExpr)));
}

#[test]
fn test_var_hiding() {
    //let mut v =
}

#[test]
fn test_func_decl_correct_return_type() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::FunDec(String::from("foo"), None, TType::TInt32, B(Expr::NumExpr(4)), TType::TInt32));
}

#[test]
#[should_panic(expected="Return type doesn't match with the type of the last expression.")]
fn test_func_decl_incorrect_return_type() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::FunDec(String::from("foo"), None, TType::TString, B(Expr::NumExpr(4)), TType::TInt32));
}

#[test]
#[should_panic(expected="Expected conditional expression of int type")]
fn test_if_expr_with_incorrect_conditional_type() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::IfThenExpr(B(Expr::StringExpr(String::from("a"))), B(Expr::StringExpr(String::from("a")))));
}

#[test]
fn test_if_expr_with_int_type() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::IfThenExpr(B(Expr::NumExpr(1)), B(Expr::SeqExpr(None))));
    assert_eq!(v.ty, TType::TVoid);
}

#[test]
#[should_panic(expected="Expected if-body of void type")]
fn test_if_expr_with_int_type_conditional_and_int_type_as_body_type() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::IfThenExpr(B(Expr::NumExpr(1)), B(Expr::NumExpr(1))));
    assert_eq!(v.ty, TType::TInt32);
}

#[test]
#[should_panic(expected="Expected conditional expression of int type")]
fn test_while_expr_with_incorrect_conditional_type() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::WhileExpr(B(Expr::StringExpr(String::from("a"))), B(Expr::StringExpr(String::from("a")))));
}

#[test]
#[should_panic(expected="Expected while-body of void type")]
fn test_while_expr_with_int_type() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::WhileExpr(B(Expr::NumExpr(1)), B(Expr::StringExpr(String::from("a")))));
}

#[test]
#[should_panic(expected="Denominator cannot be 0")]
fn test_div_expr_with_0_as_denominator(){
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::DivExpr(B(Expr::NumExpr(1)), B(Expr::NumExpr(0))));
}

#[test]
fn test_div_expr_with_1_as_denominator(){
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::DivExpr(B(Expr::NumExpr(1)), B(Expr::NumExpr(1))));
    assert_eq!(v.ty, TType::TInt32);
}

#[test]
#[should_panic(expected="Initializing expression type should be int in a for loop")]
fn test_for_loop_expr_init_type(){
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::ForExpr(String::from("i"),
                                B(Expr::StringExpr(String::from("adsd"))),
                                B(Expr::NumExpr(1)),
                                B(Expr::NumExpr(2))));
}
<|endoftext|>


<commit_before>// Zinc, the bare metal stack for rust.
// Copyright 2014 Lionel Flandrin <lionel@svkt.org>
//
// Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
//
// 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.

// extern crate regex;

use std::rc::Rc;
use syntax::ext::base::ExtCtxt;
use regex::Regex;

use builder::{Builder, TokenString, add_node_dependency};
use node;

pub fn attach(builder: &mut Builder, _: &mut ExtCtxt, node: Rc<node::Node>) {
  node.materializer.set(Some(verify as fn(&mut Builder, &mut ExtCtxt, Rc<node::Node>)));
  for timer_node in node.subnodes().iter() {
    timer_node.materializer.set(Some(build_timer as fn(&mut Builder, &mut ExtCtxt, Rc<node::Node>)));
    add_node_dependency(&node, timer_node);
    super::add_node_dependency_on_clock(builder, timer_node);
  }
}

pub fn verify(_: &mut Builder, cx: &mut ExtCtxt, node: Rc<node::Node>) {
  node.expect_no_attributes(cx);
}

fn build_timer(builder: &mut Builder, cx: &mut ExtCtxt, node: Rc<node::Node>) {

  let error = |err: &str | {
    cx.parse_sess().span_diagnostic.span_err(node.path_span, err);
  };

  if !node.expect_attributes(cx, &[
      ("prescale", node::IntAttribute),
      ("mode", node::StrAttribute)]) {
    return
  }

  if node.name.is_none() {
    cx.parse_sess().span_diagnostic.span_err(node.name_span,
        "timer node must have a name");
    return
  }

  let name = TokenString(node.name.clone().unwrap());
  let prescale = node.get_int_attr("prescale").unwrap() as u32;
  let mode = node.get_string_attr("mode").unwrap();

  // Timer path is in the form "w?[0-5][A-B]":
  // - 'w' denotes a wide (32/64bit) counter.
  // - The number is the timer ID.
  // - The letter says which counter to use within that timer (each timer has
  //   two counters, A and B which can be configured independantly.

  let (wide_timer, id) =
    match Regex::new(r"(w?)([0-5])").unwrap().captures(node.path.as_str()) {
      Some(c) => {
        (c.at(1) != Some(""), c.at(2))
      }
      None => {
        error(
          format!("invalid timer index `{}`, it should match `w?[0-5]`",
                  node.path).as_str());
        return;
      }
  };

  let mode = TokenString(
    format!("zinc::hal::tiva_c::timer::Mode::{}",
            match mode.as_str() {
              "periodic"   => "Periodic",
              "one-shot"   => "OneShot",
              "RTC"        => "RTC",
              "edge-count" => "EdgeCount",
              "edge-time"  => "EdgeTime",
              "PWM"        => "PWM",
              _            => {
                error(format!("unknown mode {}, expected one of \
                               periodic, one-shot, RTC, edge-count, edge-time \
                               or PWM", mode).as_str());
                return;
              }}));

  let timer_name = TokenString(
    format!("zinc::hal::tiva_c::timer::TimerId::{}{}",
            if wide_timer {
              "TimerW"
            } else {
              "Timer"
            }, id.unwrap()));

  node.set_type_name("zinc::hal::tiva_c::timer::Timer".to_string());

  let st = quote_stmt!(&*cx,
      let $name = zinc::hal::tiva_c::timer::Timer::new(
          $timer_name, $mode, $prescale);
  );
  builder.add_main_statement(st.unwrap());
}
<commit_msg>Minor cleanup<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Lionel Flandrin <lionel@svkt.org>
//
// Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
//
// 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.

use std::rc::Rc;
use syntax::ext::base::ExtCtxt;
use regex::Regex;

use builder::{Builder, TokenString, add_node_dependency};
use node;

pub fn attach(builder: &mut Builder, _: &mut ExtCtxt, node: Rc<node::Node>) {
  node.materializer.set(Some(verify as fn(&mut Builder, &mut ExtCtxt, Rc<node::Node>)));
  for timer_node in node.subnodes().iter() {
    timer_node.materializer.set(Some(build_timer as fn(&mut Builder, &mut ExtCtxt, Rc<node::Node>)));
    add_node_dependency(&node, timer_node);
    super::add_node_dependency_on_clock(builder, timer_node);
  }
}

pub fn verify(_: &mut Builder, cx: &mut ExtCtxt, node: Rc<node::Node>) {
  node.expect_no_attributes(cx);
}

fn build_timer(builder: &mut Builder, cx: &mut ExtCtxt, node: Rc<node::Node>) {

  let error = |err: &str | {
    cx.parse_sess().span_diagnostic.span_err(node.path_span, err);
  };

  if !node.expect_attributes(cx, &[
      ("prescale", node::IntAttribute),
      ("mode", node::StrAttribute)]) {
    return
  }

  if node.name.is_none() {
    cx.parse_sess().span_diagnostic.span_err(node.name_span,
        "timer node must have a name");
    return
  }

  let name = TokenString(node.name.clone().unwrap());
  let prescale = node.get_int_attr("prescale").unwrap() as u32;
  let mode = node.get_string_attr("mode").unwrap();

  // Timer path is in the form "w?[0-5][A-B]":
  // - 'w' denotes a wide (32/64bit) counter.
  // - The number is the timer ID.
  // - The letter says which counter to use within that timer (each timer has
  //   two counters, A and B which can be configured independantly.

  let (wide_timer, id) =
    match Regex::new(r"(w?)([0-5])").unwrap().captures(node.path.as_str()) {
      Some(c) => {
        (c.at(1) != Some(""), c.at(2))
      }
      None => {
        error(
          format!("invalid timer index `{}`, it should match `w?[0-5]`",
                  node.path).as_str());
        return;
      }
  };

  let mode = TokenString(
    format!("zinc::hal::tiva_c::timer::Mode::{}",
            match mode.as_str() {
              "periodic"   => "Periodic",
              "one-shot"   => "OneShot",
              "RTC"        => "RTC",
              "edge-count" => "EdgeCount",
              "edge-time"  => "EdgeTime",
              "PWM"        => "PWM",
              _            => {
                error(format!("unknown mode {}, expected one of \
                               periodic, one-shot, RTC, edge-count, edge-time \
                               or PWM", mode).as_str());
                return;
              }}));

  let timer_name = TokenString(
    format!("zinc::hal::tiva_c::timer::TimerId::{}{}",
            if wide_timer {
              "TimerW"
            } else {
              "Timer"
            }, id.unwrap()));

  node.set_type_name("zinc::hal::tiva_c::timer::Timer".to_string());

  let st = quote_stmt!(&*cx,
      let $name = zinc::hal::tiva_c::timer::Timer::new(
          $timer_name, $mode, $prescale);
  );
  builder.add_main_statement(st.unwrap());
}
<|endoftext|>


<commit_before>// Zinc, the bare metal stack for rust.
// Copyright 2014 Ben Gamari <bgamari@gmail.com>
//
// Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
//
// 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.

use std::iter::FromIterator;
use std::ops::Deref;

use syntax::ast;
use syntax::ptr::P;
use syntax::ast_util::empty_generics;
use syntax::codemap::{respan, mk_sp};
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ToTokens;
use syntax::parse::token;

use super::Builder;
use super::utils;
use super::super::node;

/// A visitor to build the struct for each register
pub struct BuildRegStructs<'a> {
  builder: &'a mut Builder,
  cx: &'a ExtCtxt<'a>,
}

impl<'a> node::RegVisitor for BuildRegStructs<'a> {
  fn visit_prim_reg(&mut self, path: &Vec<String>, reg: &node::Reg,
                    fields: &Vec<node::Field>) {
    let width = match reg.ty {
      node::RegType::RegPrim(ref width, _) => width.node,
      _ => panic!("visit_prim_reg called with non-primitive register"),
    };
    for field in fields.iter() {
      for item in build_field_type(self.cx, path, reg, field).into_iter() {
        self.builder.push_item(item);
      }
    }

    for item in build_reg_struct(self.cx, path, reg, &width).into_iter() {
      self.builder.push_item(item);
    }
  }
}

impl<'a> BuildRegStructs<'a> {
  pub fn new(builder: &'a mut Builder, cx: &'a ExtCtxt<'a>)
             -> BuildRegStructs<'a> {
    BuildRegStructs {builder: builder, cx: cx}
  }
}

/// Build a field type if necessary (e.g. in the case of an `EnumField`)
fn build_field_type(cx: &ExtCtxt, path: &Vec<String>,
                    reg: &node::Reg, field: &node::Field)
                    -> Vec<P<ast::Item>> {
  match field.ty.node {
    node::FieldType::EnumField { ref variants, .. } => {
      // FIXME(bgamari): We construct a path, then only take the last
      // segment, this could be more efficient
      let name: ast::Ident =
        utils::field_type_path(cx, path, reg, field)
        .segments.last().unwrap().identifier;
      let enum_def: ast::EnumDef = ast::EnumDef {
        variants: FromIterator::from_iter(
          variants.iter().map(|v| P(build_enum_variant(cx, v)))),
      };
      let attrs: Vec<ast::Attribute> = vec!(
        utils::list_attribute(cx, "allow",
                              vec!("dead_code",
                                   "non_camel_case_types",
                                   "missing_docs"),
                              field.name.span));
      let ty_item: P<ast::Item> = P(ast::Item {
        ident: name,
        id: ast::DUMMY_NODE_ID,
        node: ast::ItemEnum(enum_def, empty_generics()),
        vis: ast::Public,
        attrs: attrs,
        span: field.ty.span,
      });
      vec!(ty_item)
    },
    _ => Vec::new()
  }
}

/// Produce a register struct if necessary (for primitive typed registers).
/// In this case `None` indicates no struct is necessary, not failure.
/// For instance,
///
///     pub struct REG {_value: u32}
fn build_reg_struct(cx: &ExtCtxt, path: &Vec<String>,
    reg: &node::Reg, _width: &node::RegWidth) -> Vec<P<ast::Item>> {
  let packed_ty =
    utils::reg_primitive_type(cx, reg)
    .expect("Unexpected non-primitive reg");

  let reg_doc = match reg.docstring {
    Some(d) => token::get_ident(d.node).to_string(),
    None => "no documentation".to_string(),
  };
  let docstring = format!("Register `{}`: {}",
                          reg.name.node,
                          reg_doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  let ty_name = utils::path_ident(cx, path);
  let item = quote_item!(cx,
    $doc_attr
    #[derive(Clone)]
    #[allow(non_camel_case_types)]
    pub struct $ty_name {
      value: VolatileCell<$packed_ty>,
    }
  );
  let mut item: ast::Item = item.unwrap().deref().clone();
  item.span = reg.name.span;
  let copy_impl = quote_item!(cx, impl ::core::marker::Copy for $ty_name {}).unwrap();
  vec!(P(item), copy_impl)
}

/// Build a variant of an `EnumField`
fn build_enum_variant(cx: &ExtCtxt, variant: &node::Variant)
                      -> ast::Variant {
  let doc = match variant.docstring {
    Some(d) => token::get_ident(d.node).to_string(),
    None => "no documentation".to_string(),
  };
  let docstring = format!("`0x{:x}`. {}",
                          variant.value.node,
                          doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));
  respan(
    mk_sp(variant.name.span.lo, variant.value.span.hi),
    ast::Variant_ {
      name: cx.ident_of(variant.name.node.as_str()),
      attrs: vec!(doc_attr),
      kind: ast::TupleVariantKind(Vec::new()),
      id: ast::DUMMY_NODE_ID,
      disr_expr: Some(utils::expr_int(cx, respan(variant.value.span,
                                                 variant.value.node as i64))),
      vis: ast::Inherited,
    }
  )
}
<commit_msg>Added derive(PartialEq) for ioreg enums<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Ben Gamari <bgamari@gmail.com>
//
// Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
//
// 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.

use std::iter::FromIterator;
use std::ops::Deref;

use syntax::ast;
use syntax::ptr::P;
use syntax::ast_util::empty_generics;
use syntax::codemap::{respan, mk_sp};
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ToTokens;
use syntax::parse::token;

use super::Builder;
use super::utils;
use super::super::node;

/// A visitor to build the struct for each register
pub struct BuildRegStructs<'a> {
  builder: &'a mut Builder,
  cx: &'a ExtCtxt<'a>,
}

impl<'a> node::RegVisitor for BuildRegStructs<'a> {
  fn visit_prim_reg(&mut self, path: &Vec<String>, reg: &node::Reg,
                    fields: &Vec<node::Field>) {
    let width = match reg.ty {
      node::RegType::RegPrim(ref width, _) => width.node,
      _ => panic!("visit_prim_reg called with non-primitive register"),
    };
    for field in fields.iter() {
      for item in build_field_type(self.cx, path, reg, field).into_iter() {
        self.builder.push_item(item);
      }
    }

    for item in build_reg_struct(self.cx, path, reg, &width).into_iter() {
      self.builder.push_item(item);
    }
  }
}

impl<'a> BuildRegStructs<'a> {
  pub fn new(builder: &'a mut Builder, cx: &'a ExtCtxt<'a>)
             -> BuildRegStructs<'a> {
    BuildRegStructs {builder: builder, cx: cx}
  }
}

/// Build a field type if necessary (e.g. in the case of an `EnumField`)
fn build_field_type(cx: &ExtCtxt, path: &Vec<String>,
                    reg: &node::Reg, field: &node::Field)
                    -> Vec<P<ast::Item>> {
  match field.ty.node {
    node::FieldType::EnumField { ref variants, .. } => {
      // FIXME(bgamari): We construct a path, then only take the last
      // segment, this could be more efficient
      let name: ast::Ident =
        utils::field_type_path(cx, path, reg, field)
        .segments.last().unwrap().identifier;
      let enum_def: ast::EnumDef = ast::EnumDef {
        variants: FromIterator::from_iter(
          variants.iter().map(|v| P(build_enum_variant(cx, v)))),
      };
      let attrs: Vec<ast::Attribute> = vec!(
        utils::list_attribute(cx, "derive",
                              vec!("PartialEq"),
                              field.name.span),
        utils::list_attribute(cx, "allow",
                              vec!("dead_code",
                                   "non_camel_case_types",
                                   "missing_docs"),
                              field.name.span));
      let ty_item: P<ast::Item> = P(ast::Item {
        ident: name,
        id: ast::DUMMY_NODE_ID,
        node: ast::ItemEnum(enum_def, empty_generics()),
        vis: ast::Public,
        attrs: attrs,
        span: field.ty.span,
      });
      vec!(ty_item)
    },
    _ => Vec::new()
  }
}

/// Produce a register struct if necessary (for primitive typed registers).
/// In this case `None` indicates no struct is necessary, not failure.
/// For instance,
///
///     pub struct REG {_value: u32}
fn build_reg_struct(cx: &ExtCtxt, path: &Vec<String>,
    reg: &node::Reg, _width: &node::RegWidth) -> Vec<P<ast::Item>> {
  let packed_ty =
    utils::reg_primitive_type(cx, reg)
    .expect("Unexpected non-primitive reg");

  let reg_doc = match reg.docstring {
    Some(d) => token::get_ident(d.node).to_string(),
    None => "no documentation".to_string(),
  };
  let docstring = format!("Register `{}`: {}",
                          reg.name.node,
                          reg_doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  let ty_name = utils::path_ident(cx, path);
  let item = quote_item!(cx,
    $doc_attr
    #[derive(Clone)]
    #[allow(non_camel_case_types)]
    pub struct $ty_name {
      value: VolatileCell<$packed_ty>,
    }
  );
  let mut item: ast::Item = item.unwrap().deref().clone();
  item.span = reg.name.span;
  let copy_impl = quote_item!(cx, impl ::core::marker::Copy for $ty_name {}).unwrap();
  vec!(P(item), copy_impl)
}

/// Build a variant of an `EnumField`
fn build_enum_variant(cx: &ExtCtxt, variant: &node::Variant)
                      -> ast::Variant {
  let doc = match variant.docstring {
    Some(d) => token::get_ident(d.node).to_string(),
    None => "no documentation".to_string(),
  };
  let docstring = format!("`0x{:x}`. {}",
                          variant.value.node,
                          doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));
  respan(
    mk_sp(variant.name.span.lo, variant.value.span.hi),
    ast::Variant_ {
      name: cx.ident_of(variant.name.node.as_str()),
      attrs: vec!(doc_attr),
      kind: ast::TupleVariantKind(Vec::new()),
      id: ast::DUMMY_NODE_ID,
      disr_expr: Some(utils::expr_int(cx, respan(variant.value.span,
                                                 variant.value.node as i64))),
      vis: ast::Inherited,
    }
  )
}
<|endoftext|>


<commit_before>use hir::{db::AstDatabase, HirDisplay, Type};
use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
use syntax::{
    ast::{BlockExpr, ExprStmt},
    AstNode,
};
use text_edit::TextEdit;

use crate::{fix, Assist, Diagnostic, DiagnosticsContext};

// Diagnostic: type-mismatch
//
// This diagnostic is triggered when the type of an expression does not match
// the expected type.
pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic {
    let mut diag = Diagnostic::new(
        "type-mismatch",
        format!(
            "expected {}, found {}",
            d.expected.display(ctx.sema.db),
            d.actual.display(ctx.sema.db)
        ),
        ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
    )
    .with_fixes(fixes(ctx, d));
    if diag.fixes.is_none() {
        diag.experimental = true;
    }
    diag
}

fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assist>> {
    let mut fixes = Vec::new();

    add_reference(ctx, d, &mut fixes);
    add_missing_ok_or_some(ctx, d, &mut fixes);
    remove_semicolon(ctx, d, &mut fixes);
    str_ref_to_string(ctx, d, &mut fixes);

    if fixes.is_empty() {
        None
    } else {
        Some(fixes)
    }
}

fn add_reference(
    ctx: &DiagnosticsContext<'_>,
    d: &hir::TypeMismatch,
    acc: &mut Vec<Assist>,
) -> Option<()> {
    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
    let expr_node = d.expr.value.to_node(&root);

    let range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range;

    let (_, mutability) = d.expected.as_reference()?;
    let actual_with_ref = Type::reference(&d.actual, mutability);
    if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) {
        return None;
    }

    let ampersands = format!("&{}", mutability.as_keyword_for_ref());

    let edit = TextEdit::insert(expr_node.syntax().text_range().start(), ampersands);
    let source_change =
        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
    acc.push(fix("add_reference_here", "Add reference here", source_change, range));
    Some(())
}

fn add_missing_ok_or_some(
    ctx: &DiagnosticsContext<'_>,
    d: &hir::TypeMismatch,
    acc: &mut Vec<Assist>,
) -> Option<()> {
    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
    let expr = d.expr.value.to_node(&root);
    let expr_range = expr.syntax().text_range();
    let scope = ctx.sema.scope(expr.syntax())?;

    let expected_adt = d.expected.as_adt()?;
    let expected_enum = expected_adt.as_enum()?;

    let famous_defs = FamousDefs(&ctx.sema, scope.krate());
    let core_result = famous_defs.core_result_Result();
    let core_option = famous_defs.core_option_Option();

    if Some(expected_enum) != core_result && Some(expected_enum) != core_option {
        return None;
    }

    let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" };

    let wrapped_actual_ty = expected_adt.ty_with_args(ctx.sema.db, &[d.actual.clone()]);

    if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) {
        return None;
    }

    let mut builder = TextEdit::builder();
    builder.insert(expr.syntax().text_range().start(), format!("{}(", variant_name));
    builder.insert(expr.syntax().text_range().end(), ")".to_string());
    let source_change =
        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), builder.finish());
    let name = format!("Wrap in {}", variant_name);
    acc.push(fix("wrap_in_constructor", &name, source_change, expr_range));
    Some(())
}

fn remove_semicolon(
    ctx: &DiagnosticsContext<'_>,
    d: &hir::TypeMismatch,
    acc: &mut Vec<Assist>,
) -> Option<()> {
    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
    let expr = d.expr.value.to_node(&root);
    if !d.actual.is_unit() {
        return None;
    }
    let block = BlockExpr::cast(expr.syntax().clone())?;
    let expr_before_semi =
        block.statements().last().and_then(|s| ExprStmt::cast(s.syntax().clone()))?;
    let type_before_semi = ctx.sema.type_of_expr(&expr_before_semi.expr()?)?.original();
    if !type_before_semi.could_coerce_to(ctx.sema.db, &d.expected) {
        return None;
    }
    let semicolon_range = expr_before_semi.semicolon_token()?.text_range();

    let edit = TextEdit::delete(semicolon_range);
    let source_change =
        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);

    acc.push(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon_range));
    Some(())
}

fn str_ref_to_string(
    ctx: &DiagnosticsContext<'_>,
    d: &hir::TypeMismatch,
    acc: &mut Vec<Assist>,
) -> Option<()> {
    let expected = d.expected.display(ctx.sema.db);
    let actual = d.actual.display(ctx.sema.db);

    if expected.to_string() != "String" || actual.to_string() != "&str" {
        return None;
    }

    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
    let expr = d.expr.value.to_node(&root);
    let expr_range = expr.syntax().text_range();

    let to_string = format!(".to_string()");

    let edit = TextEdit::insert(expr.syntax().text_range().end(), to_string);
    let source_change =
        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
    acc.push(fix("str_ref_to_string", "Add .to_string() here", source_change, expr_range));

    Some(())
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_diagnostics, check_fix, check_no_fix};

    #[test]
    fn missing_reference() {
        check_diagnostics(
            r#"
fn main() {
    test(123);
       //^^^ 💡 error: expected &i32, found i32
}
fn test(arg: &i32) {}
"#,
        );
    }

    #[test]
    fn test_add_reference_to_int() {
        check_fix(
            r#"
fn main() {
    test(123$0);
}
fn test(arg: &i32) {}
            "#,
            r#"
fn main() {
    test(&123);
}
fn test(arg: &i32) {}
            "#,
        );
    }

    #[test]
    fn test_add_mutable_reference_to_int() {
        check_fix(
            r#"
fn main() {
    test($0123);
}
fn test(arg: &mut i32) {}
            "#,
            r#"
fn main() {
    test(&mut 123);
}
fn test(arg: &mut i32) {}
            "#,
        );
    }

    #[test]
    fn test_add_reference_to_array() {
        check_fix(
            r#"
//- minicore: coerce_unsized
fn main() {
    test($0[1, 2, 3]);
}
fn test(arg: &[i32]) {}
            "#,
            r#"
fn main() {
    test(&[1, 2, 3]);
}
fn test(arg: &[i32]) {}
            "#,
        );
    }

    #[test]
    fn test_add_reference_with_autoderef() {
        check_fix(
            r#"
//- minicore: coerce_unsized, deref
struct Foo;
struct Bar;
impl core::ops::Deref for Foo {
    type Target = Bar;
}

fn main() {
    test($0Foo);
}
fn test(arg: &Bar) {}
            "#,
            r#"
struct Foo;
struct Bar;
impl core::ops::Deref for Foo {
    type Target = Bar;
}

fn main() {
    test(&Foo);
}
fn test(arg: &Bar) {}
            "#,
        );
    }

    #[test]
    fn test_add_reference_to_method_call() {
        check_fix(
            r#"
fn main() {
    Test.call_by_ref($0123);
}
struct Test;
impl Test {
    fn call_by_ref(&self, arg: &i32) {}
}
            "#,
            r#"
fn main() {
    Test.call_by_ref(&123);
}
struct Test;
impl Test {
    fn call_by_ref(&self, arg: &i32) {}
}
            "#,
        );
    }

    #[test]
    fn test_add_reference_to_let_stmt() {
        check_fix(
            r#"
fn main() {
    let test: &i32 = $0123;
}
            "#,
            r#"
fn main() {
    let test: &i32 = &123;
}
            "#,
        );
    }

    #[test]
    fn test_add_mutable_reference_to_let_stmt() {
        check_fix(
            r#"
fn main() {
    let test: &mut i32 = $0123;
}
            "#,
            r#"
fn main() {
    let test: &mut i32 = &mut 123;
}
            "#,
        );
    }

    #[test]
    fn test_wrap_return_type_option() {
        check_fix(
            r#"
//- minicore: option, result
fn div(x: i32, y: i32) -> Option<i32> {
    if y == 0 {
        return None;
    }
    x / y$0
}
"#,
            r#"
fn div(x: i32, y: i32) -> Option<i32> {
    if y == 0 {
        return None;
    }
    Some(x / y)
}
"#,
        );
    }

    #[test]
    fn const_generic_type_mismatch() {
        check_diagnostics(
            r#"
            pub struct Rate<const N: u32>;
            fn f<const N: u64>() -> Rate<N> { // FIXME: add some error
                loop {}
            }
            fn run(t: Rate<5>) {
            }
            fn main() {
                run(f()) // FIXME: remove this error
                  //^^^ error: expected Rate<5>, found Rate<_>
            }
"#,
        );
    }

    #[test]
    fn const_generic_unknown() {
        check_diagnostics(
            r#"
            pub struct Rate<T, const NOM: u32, const DENOM: u32>(T);
            fn run(t: Rate<u32, 1, 1>) {
            }
            fn main() {
                run(Rate::<_, _, _>(5));
            }
"#,
        );
    }

    #[test]
    fn test_wrap_return_type_option_tails() {
        check_fix(
            r#"
//- minicore: option, result
fn div(x: i32, y: i32) -> Option<i32> {
    if y == 0 {
        Some(0)
    } else if true {
        100$0
    } else {
        None
    }
}
"#,
            r#"
fn div(x: i32, y: i32) -> Option<i32> {
    if y == 0 {
        Some(0)
    } else if true {
        Some(100)
    } else {
        None
    }
}
"#,
        );
    }

    #[test]
    fn test_wrap_return_type() {
        check_fix(
            r#"
//- minicore: option, result
fn div(x: i32, y: i32) -> Result<i32, ()> {
    if y == 0 {
        return Err(());
    }
    x / y$0
}
"#,
            r#"
fn div(x: i32, y: i32) -> Result<i32, ()> {
    if y == 0 {
        return Err(());
    }
    Ok(x / y)
}
"#,
        );
    }

    #[test]
    fn test_wrap_return_type_handles_generic_functions() {
        check_fix(
            r#"
//- minicore: option, result
fn div<T>(x: T) -> Result<T, i32> {
    if x == 0 {
        return Err(7);
    }
    $0x
}
"#,
            r#"
fn div<T>(x: T) -> Result<T, i32> {
    if x == 0 {
        return Err(7);
    }
    Ok(x)
}
"#,
        );
    }

    #[test]
    fn test_wrap_return_type_handles_type_aliases() {
        check_fix(
            r#"
//- minicore: option, result
type MyResult<T> = Result<T, ()>;

fn div(x: i32, y: i32) -> MyResult<i32> {
    if y == 0 {
        return Err(());
    }
    x $0/ y
}
"#,
            r#"
type MyResult<T> = Result<T, ()>;

fn div(x: i32, y: i32) -> MyResult<i32> {
    if y == 0 {
        return Err(());
    }
    Ok(x / y)
}
"#,
        );
    }

    #[test]
    fn test_in_const_and_static() {
        check_fix(
            r#"
//- minicore: option, result
static A: Option<()> = {($0)};
            "#,
            r#"
static A: Option<()> = {Some(())};
            "#,
        );
        check_fix(
            r#"
//- minicore: option, result
const _: Option<()> = {($0)};
            "#,
            r#"
const _: Option<()> = {Some(())};
            "#,
        );
    }

    #[test]
    fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
        check_no_fix(
            r#"
//- minicore: option, result
fn foo() -> Result<(), i32> { 0$0 }
"#,
        );
    }

    #[test]
    fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
        check_no_fix(
            r#"
//- minicore: option, result
enum SomeOtherEnum { Ok(i32), Err(String) }

fn foo() -> SomeOtherEnum { 0$0 }
"#,
        );
    }

    #[test]
    fn remove_semicolon() {
        check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);
    }

    #[test]
    fn str_ref_to_string() {
        check_fix(
            r#"
struct String;

fn test() -> String {
    "a"$0
}
            "#,
            r#"
struct String;

fn test() -> String {
    "a".to_string()
}
            "#,
        );
    }
}
<commit_msg>Auto merge of #12796 - mmirate:patch-1, r=Veykril<commit_after>use hir::{db::AstDatabase, HirDisplay, Type};
use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
use syntax::{
    ast::{BlockExpr, ExprStmt},
    AstNode,
};
use text_edit::TextEdit;

use crate::{fix, Assist, Diagnostic, DiagnosticsContext};

// Diagnostic: type-mismatch
//
// This diagnostic is triggered when the type of an expression does not match
// the expected type.
pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic {
    let mut diag = Diagnostic::new(
        "type-mismatch",
        format!(
            "expected {}, found {}",
            d.expected.display(ctx.sema.db),
            d.actual.display(ctx.sema.db)
        ),
        ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
    )
    .with_fixes(fixes(ctx, d));
    if diag.fixes.is_none() {
        diag.experimental = true;
    }
    diag
}

fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assist>> {
    let mut fixes = Vec::new();

    add_reference(ctx, d, &mut fixes);
    add_missing_ok_or_some(ctx, d, &mut fixes);
    remove_semicolon(ctx, d, &mut fixes);
    str_ref_to_owned(ctx, d, &mut fixes);

    if fixes.is_empty() {
        None
    } else {
        Some(fixes)
    }
}

fn add_reference(
    ctx: &DiagnosticsContext<'_>,
    d: &hir::TypeMismatch,
    acc: &mut Vec<Assist>,
) -> Option<()> {
    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
    let expr_node = d.expr.value.to_node(&root);

    let range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range;

    let (_, mutability) = d.expected.as_reference()?;
    let actual_with_ref = Type::reference(&d.actual, mutability);
    if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) {
        return None;
    }

    let ampersands = format!("&{}", mutability.as_keyword_for_ref());

    let edit = TextEdit::insert(expr_node.syntax().text_range().start(), ampersands);
    let source_change =
        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
    acc.push(fix("add_reference_here", "Add reference here", source_change, range));
    Some(())
}

fn add_missing_ok_or_some(
    ctx: &DiagnosticsContext<'_>,
    d: &hir::TypeMismatch,
    acc: &mut Vec<Assist>,
) -> Option<()> {
    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
    let expr = d.expr.value.to_node(&root);
    let expr_range = expr.syntax().text_range();
    let scope = ctx.sema.scope(expr.syntax())?;

    let expected_adt = d.expected.as_adt()?;
    let expected_enum = expected_adt.as_enum()?;

    let famous_defs = FamousDefs(&ctx.sema, scope.krate());
    let core_result = famous_defs.core_result_Result();
    let core_option = famous_defs.core_option_Option();

    if Some(expected_enum) != core_result && Some(expected_enum) != core_option {
        return None;
    }

    let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" };

    let wrapped_actual_ty = expected_adt.ty_with_args(ctx.sema.db, &[d.actual.clone()]);

    if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) {
        return None;
    }

    let mut builder = TextEdit::builder();
    builder.insert(expr.syntax().text_range().start(), format!("{}(", variant_name));
    builder.insert(expr.syntax().text_range().end(), ")".to_string());
    let source_change =
        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), builder.finish());
    let name = format!("Wrap in {}", variant_name);
    acc.push(fix("wrap_in_constructor", &name, source_change, expr_range));
    Some(())
}

fn remove_semicolon(
    ctx: &DiagnosticsContext<'_>,
    d: &hir::TypeMismatch,
    acc: &mut Vec<Assist>,
) -> Option<()> {
    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
    let expr = d.expr.value.to_node(&root);
    if !d.actual.is_unit() {
        return None;
    }
    let block = BlockExpr::cast(expr.syntax().clone())?;
    let expr_before_semi =
        block.statements().last().and_then(|s| ExprStmt::cast(s.syntax().clone()))?;
    let type_before_semi = ctx.sema.type_of_expr(&expr_before_semi.expr()?)?.original();
    if !type_before_semi.could_coerce_to(ctx.sema.db, &d.expected) {
        return None;
    }
    let semicolon_range = expr_before_semi.semicolon_token()?.text_range();

    let edit = TextEdit::delete(semicolon_range);
    let source_change =
        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);

    acc.push(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon_range));
    Some(())
}

fn str_ref_to_owned(
    ctx: &DiagnosticsContext<'_>,
    d: &hir::TypeMismatch,
    acc: &mut Vec<Assist>,
) -> Option<()> {
    let expected = d.expected.display(ctx.sema.db);
    let actual = d.actual.display(ctx.sema.db);

    if expected.to_string() != "String" || actual.to_string() != "&str" {
        return None;
    }

    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
    let expr = d.expr.value.to_node(&root);
    let expr_range = expr.syntax().text_range();

    let to_owned = format!(".to_owned()");

    let edit = TextEdit::insert(expr.syntax().text_range().end(), to_owned);
    let source_change =
        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
    acc.push(fix("str_ref_to_owned", "Add .to_owned() here", source_change, expr_range));

    Some(())
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_diagnostics, check_fix, check_no_fix};

    #[test]
    fn missing_reference() {
        check_diagnostics(
            r#"
fn main() {
    test(123);
       //^^^ 💡 error: expected &i32, found i32
}
fn test(arg: &i32) {}
"#,
        );
    }

    #[test]
    fn test_add_reference_to_int() {
        check_fix(
            r#"
fn main() {
    test(123$0);
}
fn test(arg: &i32) {}
            "#,
            r#"
fn main() {
    test(&123);
}
fn test(arg: &i32) {}
            "#,
        );
    }

    #[test]
    fn test_add_mutable_reference_to_int() {
        check_fix(
            r#"
fn main() {
    test($0123);
}
fn test(arg: &mut i32) {}
            "#,
            r#"
fn main() {
    test(&mut 123);
}
fn test(arg: &mut i32) {}
            "#,
        );
    }

    #[test]
    fn test_add_reference_to_array() {
        check_fix(
            r#"
//- minicore: coerce_unsized
fn main() {
    test($0[1, 2, 3]);
}
fn test(arg: &[i32]) {}
            "#,
            r#"
fn main() {
    test(&[1, 2, 3]);
}
fn test(arg: &[i32]) {}
            "#,
        );
    }

    #[test]
    fn test_add_reference_with_autoderef() {
        check_fix(
            r#"
//- minicore: coerce_unsized, deref
struct Foo;
struct Bar;
impl core::ops::Deref for Foo {
    type Target = Bar;
}

fn main() {
    test($0Foo);
}
fn test(arg: &Bar) {}
            "#,
            r#"
struct Foo;
struct Bar;
impl core::ops::Deref for Foo {
    type Target = Bar;
}

fn main() {
    test(&Foo);
}
fn test(arg: &Bar) {}
            "#,
        );
    }

    #[test]
    fn test_add_reference_to_method_call() {
        check_fix(
            r#"
fn main() {
    Test.call_by_ref($0123);
}
struct Test;
impl Test {
    fn call_by_ref(&self, arg: &i32) {}
}
            "#,
            r#"
fn main() {
    Test.call_by_ref(&123);
}
struct Test;
impl Test {
    fn call_by_ref(&self, arg: &i32) {}
}
            "#,
        );
    }

    #[test]
    fn test_add_reference_to_let_stmt() {
        check_fix(
            r#"
fn main() {
    let test: &i32 = $0123;
}
            "#,
            r#"
fn main() {
    let test: &i32 = &123;
}
            "#,
        );
    }

    #[test]
    fn test_add_mutable_reference_to_let_stmt() {
        check_fix(
            r#"
fn main() {
    let test: &mut i32 = $0123;
}
            "#,
            r#"
fn main() {
    let test: &mut i32 = &mut 123;
}
            "#,
        );
    }

    #[test]
    fn test_wrap_return_type_option() {
        check_fix(
            r#"
//- minicore: option, result
fn div(x: i32, y: i32) -> Option<i32> {
    if y == 0 {
        return None;
    }
    x / y$0
}
"#,
            r#"
fn div(x: i32, y: i32) -> Option<i32> {
    if y == 0 {
        return None;
    }
    Some(x / y)
}
"#,
        );
    }

    #[test]
    fn const_generic_type_mismatch() {
        check_diagnostics(
            r#"
            pub struct Rate<const N: u32>;
            fn f<const N: u64>() -> Rate<N> { // FIXME: add some error
                loop {}
            }
            fn run(t: Rate<5>) {
            }
            fn main() {
                run(f()) // FIXME: remove this error
                  //^^^ error: expected Rate<5>, found Rate<_>
            }
"#,
        );
    }

    #[test]
    fn const_generic_unknown() {
        check_diagnostics(
            r#"
            pub struct Rate<T, const NOM: u32, const DENOM: u32>(T);
            fn run(t: Rate<u32, 1, 1>) {
            }
            fn main() {
                run(Rate::<_, _, _>(5));
            }
"#,
        );
    }

    #[test]
    fn test_wrap_return_type_option_tails() {
        check_fix(
            r#"
//- minicore: option, result
fn div(x: i32, y: i32) -> Option<i32> {
    if y == 0 {
        Some(0)
    } else if true {
        100$0
    } else {
        None
    }
}
"#,
            r#"
fn div(x: i32, y: i32) -> Option<i32> {
    if y == 0 {
        Some(0)
    } else if true {
        Some(100)
    } else {
        None
    }
}
"#,
        );
    }

    #[test]
    fn test_wrap_return_type() {
        check_fix(
            r#"
//- minicore: option, result
fn div(x: i32, y: i32) -> Result<i32, ()> {
    if y == 0 {
        return Err(());
    }
    x / y$0
}
"#,
            r#"
fn div(x: i32, y: i32) -> Result<i32, ()> {
    if y == 0 {
        return Err(());
    }
    Ok(x / y)
}
"#,
        );
    }

    #[test]
    fn test_wrap_return_type_handles_generic_functions() {
        check_fix(
            r#"
//- minicore: option, result
fn div<T>(x: T) -> Result<T, i32> {
    if x == 0 {
        return Err(7);
    }
    $0x
}
"#,
            r#"
fn div<T>(x: T) -> Result<T, i32> {
    if x == 0 {
        return Err(7);
    }
    Ok(x)
}
"#,
        );
    }

    #[test]
    fn test_wrap_return_type_handles_type_aliases() {
        check_fix(
            r#"
//- minicore: option, result
type MyResult<T> = Result<T, ()>;

fn div(x: i32, y: i32) -> MyResult<i32> {
    if y == 0 {
        return Err(());
    }
    x $0/ y
}
"#,
            r#"
type MyResult<T> = Result<T, ()>;

fn div(x: i32, y: i32) -> MyResult<i32> {
    if y == 0 {
        return Err(());
    }
    Ok(x / y)
}
"#,
        );
    }

    #[test]
    fn test_in_const_and_static() {
        check_fix(
            r#"
//- minicore: option, result
static A: Option<()> = {($0)};
            "#,
            r#"
static A: Option<()> = {Some(())};
            "#,
        );
        check_fix(
            r#"
//- minicore: option, result
const _: Option<()> = {($0)};
            "#,
            r#"
const _: Option<()> = {Some(())};
            "#,
        );
    }

    #[test]
    fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
        check_no_fix(
            r#"
//- minicore: option, result
fn foo() -> Result<(), i32> { 0$0 }
"#,
        );
    }

    #[test]
    fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
        check_no_fix(
            r#"
//- minicore: option, result
enum SomeOtherEnum { Ok(i32), Err(String) }

fn foo() -> SomeOtherEnum { 0$0 }
"#,
        );
    }

    #[test]
    fn remove_semicolon() {
        check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);
    }

    #[test]
    fn str_ref_to_owned() {
        check_fix(
            r#"
struct String;

fn test() -> String {
    "a"$0
}
            "#,
            r#"
struct String;

fn test() -> String {
    "a".to_owned()
}
            "#,
        );
    }
}
<|endoftext|>


<commit_before>//! Library to read and write protocol buffers data.
//!
//! Rust files from `.proto` files can be generated with
//! `protobuf-codegen` crates.
//! See readme on the [project github page](https://github.com/stepancheg/rust-protobuf).

#![deny(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]

#[cfg(feature = "bytes")]
extern crate bytes;
#[cfg(feature = "with-serde")]
extern crate serde;
#[macro_use]
#[cfg(feature = "with-serde")]
extern crate serde_derive;
pub use crate::clear::Clear;
pub use crate::coded_input_stream::CodedInputStream;
pub use crate::coded_output_stream::CodedOutputStream;
pub use crate::enums::ProtobufEnum;
pub use crate::enums::ProtobufEnumOrUnknown;
pub use crate::message::Message;
pub use crate::message_dyn::MessageDyn;
pub use crate::message_field::MessageField;
pub use crate::oneof::Oneof;
pub use crate::unknown::UnknownFields;
pub use crate::unknown::UnknownFieldsIter;
pub use crate::unknown::UnknownValue;
pub use crate::unknown::UnknownValueRef;
pub use crate::unknown::UnknownValues;
pub use crate::unknown::UnknownValuesIter;
pub mod wire_format;
#[cfg(feature = "bytes")]
pub use crate::chars::Chars;
pub use crate::error::ProtobufError;
pub use crate::error::ProtobufResult;

// generated
pub mod descriptor;
// TODO: move plugin to mod codegen
pub mod plugin;
pub mod rustproto;

mod clear;
mod coded_input_stream;
mod coded_output_stream;
mod enums;
mod error;
pub mod ext;
pub mod json;
mod lazy_v2;
mod message;
mod message_dyn;
mod message_field;
mod oneof;
pub mod reflect;
pub mod rt;
pub mod text_format;
pub mod well_known_types;
mod well_known_types_util;

// used by test
#[cfg(test)]
#[path = "../../protobuf-test-common/src/hex.rs"]
mod hex;

mod cached_size;
mod chars;
mod unknown;
mod varint;
mod zigzag;

mod misc;

mod buf_read_iter;
mod buf_read_or_reader;

// TODO: does not work: https://github.com/rust-lang/rust/issues/67295
#[cfg(doctest)]
mod doctest_pb;

/// This symbol is in generated `version.rs`, include here for IDE
#[cfg(never)]
pub const VERSION: &str = "";
/// This symbol is in generated `version.rs`, include here for IDE
#[cfg(never)]
#[doc(hidden)]
pub const VERSION_IDENT: &str = "";
include!(concat!(env!("OUT_DIR"), "/version.rs"));
<commit_msg>It's OK for plugin to live in protobuf crate<commit_after>//! Library to read and write protocol buffers data.
//!
//! Rust files from `.proto` files can be generated with
//! `protobuf-codegen` crates.
//! See readme on the [project github page](https://github.com/stepancheg/rust-protobuf).

#![deny(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]

#[cfg(feature = "bytes")]
extern crate bytes;
#[cfg(feature = "with-serde")]
extern crate serde;
#[macro_use]
#[cfg(feature = "with-serde")]
extern crate serde_derive;
pub use crate::clear::Clear;
pub use crate::coded_input_stream::CodedInputStream;
pub use crate::coded_output_stream::CodedOutputStream;
pub use crate::enums::ProtobufEnum;
pub use crate::enums::ProtobufEnumOrUnknown;
pub use crate::message::Message;
pub use crate::message_dyn::MessageDyn;
pub use crate::message_field::MessageField;
pub use crate::oneof::Oneof;
pub use crate::unknown::UnknownFields;
pub use crate::unknown::UnknownFieldsIter;
pub use crate::unknown::UnknownValue;
pub use crate::unknown::UnknownValueRef;
pub use crate::unknown::UnknownValues;
pub use crate::unknown::UnknownValuesIter;
pub mod wire_format;
#[cfg(feature = "bytes")]
pub use crate::chars::Chars;
pub use crate::error::ProtobufError;
pub use crate::error::ProtobufResult;

// generated
pub mod descriptor;
pub mod plugin;
pub mod rustproto;

mod clear;
mod coded_input_stream;
mod coded_output_stream;
mod enums;
mod error;
pub mod ext;
pub mod json;
mod lazy_v2;
mod message;
mod message_dyn;
mod message_field;
mod oneof;
pub mod reflect;
pub mod rt;
pub mod text_format;
pub mod well_known_types;
mod well_known_types_util;

// used by test
#[cfg(test)]
#[path = "../../protobuf-test-common/src/hex.rs"]
mod hex;

mod cached_size;
mod chars;
mod unknown;
mod varint;
mod zigzag;

mod misc;

mod buf_read_iter;
mod buf_read_or_reader;

// TODO: does not work: https://github.com/rust-lang/rust/issues/67295
#[cfg(doctest)]
mod doctest_pb;

/// This symbol is in generated `version.rs`, include here for IDE
#[cfg(never)]
pub const VERSION: &str = "";
/// This symbol is in generated `version.rs`, include here for IDE
#[cfg(never)]
#[doc(hidden)]
pub const VERSION_IDENT: &str = "";
include!(concat!(env!("OUT_DIR"), "/version.rs"));
<|endoftext|>


<commit_before>#[macro_use]
extern crate conrod;
use conrod::{widget, Borderable, Colorable, Positionable, Sizeable, Widget};
use conrod::backend::glium::glium;
use conrod::backend::glium::glium::{DisplayBuild, Surface};

struct Board {
	width: usize,
	height: usize,
	data: Vec<bool>,
}

impl Board {
	fn new( width: usize, height: usize) -> Board {
		Board {
			width: width,
			height: height,
			data: vec![true; width* height],
		}
	}

	fn get(&self, x: usize, y: usize) -> bool {
		assert!( x <= self.width  &&  y <= self.height );
		self.data[ y * self.width + x ]
	}

	fn set(&mut self, x: usize, y: usize, new_value: bool) {
		assert!( x <= self.width  &&  y <= self.height );
		self.data[ y * self.width + x ] = new_value;
	}

}

struct AppState {
	board: Board,
}

impl AppState {
	fn new() -> AppState {
		const BOARD_WIDTH: usize = 8;
		const BOARD_HEIGHT: usize = 8;

		AppState {
			board: Board::new(BOARD_WIDTH, BOARD_HEIGHT),
		}
	}
}

fn main() {
	const WIDTH: u32 = 400;
	const HEIGHT: u32 = 400;

	let mut app_state = AppState::new();

	// Build the window.
	let display = glium::glutin::WindowBuilder::new()
		.with_vsync()
		.with_dimensions(WIDTH, HEIGHT)
		.with_title("Game of Life")
		.with_multisampling(4)
		.build_glium()
		.unwrap();

	// construct our `Ui`.
	let mut ui = conrod::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build();

	// Generate the widget identifiers.
	widget_ids!(struct Ids {
		title,
		info,
		board
	});
	let ids = Ids::new(ui.widget_id_generator());

	// Add a `Font` to the `Ui`'s `font::Map` from file.
	const FONT_PATH: &'static str =
		concat!(env!("CARGO_MANIFEST_DIR"), "/assets/fonts/NotoSans/NotoSans-Regular.ttf");
	ui.fonts.insert_from_file(FONT_PATH).unwrap();

	// A type used for converting `conrod::render::Primitives` into `Command`s that can be used
	// for drawing to the glium `Surface`.
	let mut renderer = conrod::backend::glium::Renderer::new(&display).unwrap();

	// The image map describing each of our widget->image mappings (in our case, none).
	let image_map = conrod::image::Map::<glium::texture::Texture2d>::new();

	// Poll events from the window.
	let mut last_update = std::time::Instant::now();
	let mut ui_needs_update = true;
	'main: loop {

		// We don't want to loop any faster than 60 FPS, so wait until it has been at least
		// 16ms since the last yield.
		let sixteen_ms = std::time::Duration::from_millis(16);
		let duration_since_last_update = std::time::Instant::now().duration_since(last_update);
		if duration_since_last_update < sixteen_ms {
			std::thread::sleep(sixteen_ms - duration_since_last_update);
		}

		// Collect all pending events.
		let mut events: Vec<_> = display.poll_events().collect();

		// If there are no events and the `Ui` does not need updating, wait for the next event.
		if events.is_empty() && !ui_needs_update {
			events.extend(display.wait_events().next());
		}

		// Reset the needs_update flag and time this update.
		ui_needs_update = false;
		last_update = std::time::Instant::now();

		// Handle all events.
		for event in events {

			// Use the `winit` backend feature to convert the winit event to a conrod one.
			if let Some(event) = conrod::backend::winit::convert(event.clone(), &display) {
				ui.handle_event(event);
				ui_needs_update = true;
			}

			match event {
				// Break from the loop upon `Escape`.
				glium::glutin::Event::KeyboardInput(_, _, Some(glium::glutin::VirtualKeyCode::Escape)) |
				glium::glutin::Event::Closed =>
					break 'main,
				_ => {},
			}
		}

		// Instantiate all widgets in the GUI.
		{
			let ui = &mut ui.set_widgets();

			// "Hello World!" in the middle of the screen.
			widget::Text::new("Game of Life")
				.mid_top_of(ui.window)
				.color(conrod::color::WHITE)
				.font_size(32)
				.set(ids.title, ui);
			widget::Text::new("Start")
				.down(5.0)
				.color(conrod::color::WHITE)
				.font_size(8)
				.set(ids.info, ui);

			// A demonstration using widget_matrix to easily draw a matrix of any kind of widget.
			let (cols, rows) = (8, 8);
			let mut elements = widget::Matrix::new(cols, rows)
				.down(20.0)
				.w_h(260.0, 260.0)
				.set(ids.board, ui);

			// The `Matrix` widget returns an `Elements`, which can be used similar to an `Iterator`.
			while let Some(elem) = elements.next(ui) {
				let (col, row) = (elem.col, elem.row);

				// Color effect for fun.
				let (r, g, b, a) = (
					0.5 + (elem.col as f32 / cols as f32) / 2.0,
					0.75,
					1.0 - (elem.row as f32 / rows as f32) / 2.0,
					1.0
				);

				// We can use `Element`s to instantiate any kind of widget we like.
				// The `Element` does all of the positioning and sizing work for us.
				// Here, we use the `Element` to `set` a `Toggle` widget for us.
				let toggle = widget::Toggle::new(app_state.board.get(col, row))
					.rgba(r, g, b, a)
					.border(1.0);
				if let Some(new_value) = elem.set(toggle, ui).last() {
					app_state.board.set(col, row, new_value);
				}
			}

		}

		// Render the `Ui` and then display it on the screen.
		if let Some(primitives) = ui.draw_if_changed() {
			renderer.fill(&display, primitives, &image_map);
			let mut target = display.draw();
			target.clear_color(0.0, 0.0, 0.0, 1.0);
			renderer.draw(&display, &mut target, &image_map).unwrap();
			target.finish().unwrap();
		}
	}
}
<commit_msg>Compiling but not (yet) working game of life simulation.<commit_after>#[macro_use]
extern crate conrod;
use conrod::{widget, Borderable, Colorable, Labelable, Positionable, Sizeable, Widget};
use conrod::backend::glium::glium;
use conrod::backend::glium::glium::{DisplayBuild, Surface};
use std::num::Wrapping;

// nb_live = number of live neighbour among the 8 surrounding cells
// Conway rules:
// * Cell survives if:
// < 2 => die
// == 2 => survive
// > 3 => die
// == 3 => survive
//
// * Cell is born if:
// == 3 become alive

fn conway_survives() -> Vec<usize> {
	vec![2,3]
}

fn conway_borns() -> Vec<usize> {
	vec![3]
}

struct Board {
	width: usize,
	height: usize,
	data: Vec<bool>,
	survives_by_count: [bool; 9], // note that the center cell is included in the count, so index is #neighbor+1
	borns_by_count: [bool; 8],
}

impl Board {
	fn new( width: usize, height: usize, survives: &Vec<usize>, borns: &Vec<usize> ) -> Board {
		let mut survives_by_count = [false; 9];
		for count in survives.iter() {
			survives_by_count[count+1] = true; // center cell is included in the count, hence the +1
		}
		let mut borns_by_count = [false; 8];
		for count in borns.iter() {
			borns_by_count[count+0] = true;
		}

		Board {
			width: width,
			height: height,
			data: vec![true; width* height],
			survives_by_count: survives_by_count,
			borns_by_count: borns_by_count,
		}
	}

	fn get(&self, x: usize, y: usize) -> bool {
		assert!( x <= self.width  &&  y <= self.height );
		self.data[ y * self.width + x ]
	}

	fn set(&mut self, x: usize, y: usize, new_value: bool) {
		assert!( x <= self.width  &&  y <= self.height );
		self.data[ y * self.width + x ] = new_value;
	}

	fn advance_simulation(&mut self) {
		let mut next_board: Vec<bool> = Vec::with_capacity( self.data.len() );
		for y_test in 0..(self.height-1) {
			for x_test in 0..(self.width-1) {
				next_board[x_test + y_test * self.width] = self.cell_next_state(x_test, y_test);
			}
		}
		self.data = next_board;
	}

	fn cell_next_state(&self, x: usize, y: usize) -> bool {
		let nb_neighbor = self.count_neighbor(x,y);
		if self.get(x, y) {
			self.survives_by_count[nb_neighbor + 1]
		} else {
			self.borns_by_count[nb_neighbor]
		}
	}

	fn cell_alive(&self, x: usize, y: usize) -> usize {
		((x < self.width  &&  y < self.height)  &&  self.data[x + y*self.width]) as usize
	}

	fn count_neighbor( &self, x: usize, y: usize ) -> usize {
		let Wrapping(x_minus_1) = Wrapping(x) - Wrapping(1);
		let Wrapping(y_minus_1) = Wrapping(y) - Wrapping(1);
		self.cell_alive(x_minus_1, y_minus_1) + self.cell_alive(x, y_minus_1) + self.cell_alive(x+1, y_minus_1) +
		self.cell_alive(x_minus_1, y) + self.cell_alive(x, y) + self.cell_alive(x+1, y) +
		self.cell_alive(x_minus_1, y+1) + self.cell_alive(x, y+1) + self.cell_alive(x+1, y+1)
	}

}

struct AppState {
	board: Board,
	simulating: bool,
}

impl AppState {
	fn new() -> AppState {
		const BOARD_WIDTH: usize = 8;
		const BOARD_HEIGHT: usize = 8;

		AppState {
			board: Board::new(BOARD_WIDTH, BOARD_HEIGHT, &conway_survives(), &conway_borns()),
			simulating: false,
		}
	}
}

fn main() {
	const WIDTH: u32 = 400;
	const HEIGHT: u32 = 400;

	let mut app_state = AppState::new();

	// Build the window.
	let display = glium::glutin::WindowBuilder::new()
		.with_vsync()
		.with_dimensions(WIDTH, HEIGHT)
		.with_title("Game of Life")
		.with_multisampling(4)
		.build_glium()
		.unwrap();

	// construct our `Ui`.
	let mut ui = conrod::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build();

	// Generate the widget identifiers.
	widget_ids!(struct Ids {
		title,
		info,
		board,
		start_stop_button
	});
	let ids = Ids::new(ui.widget_id_generator());

	// Add a `Font` to the `Ui`'s `font::Map` from file.
	const FONT_PATH: &'static str =
		concat!(env!("CARGO_MANIFEST_DIR"), "/assets/fonts/NotoSans/NotoSans-Regular.ttf");
	ui.fonts.insert_from_file(FONT_PATH).unwrap();

	// A type used for converting `conrod::render::Primitives` into `Command`s that can be used
	// for drawing to the glium `Surface`.
	let mut renderer = conrod::backend::glium::Renderer::new(&display).unwrap();

	// The image map describing each of our widget->image mappings (in our case, none).
	let image_map = conrod::image::Map::<glium::texture::Texture2d>::new();

	// Poll events from the window.
	let mut last_update = std::time::Instant::now();
	let mut ui_needs_update = true;
	'main: loop {

		// We don't want to loop any faster than 60 FPS, so wait until it has been at least
		// 16ms since the last yield.
		let sixteen_ms = std::time::Duration::from_millis(16);
		let duration_since_last_update = std::time::Instant::now().duration_since(last_update);
		if duration_since_last_update < sixteen_ms {
			std::thread::sleep(sixteen_ms - duration_since_last_update);
		}

		// Collect all pending events.
		let mut events: Vec<_> = display.poll_events().collect();

		// If there are no events and the `Ui` does not need updating, wait for the next event.
		if events.is_empty() && !ui_needs_update {
			events.extend(display.wait_events().next());
		}

		// Reset the needs_update flag and time this update.
		ui_needs_update = false;
		last_update = std::time::Instant::now();

		// Handle all events.
		for event in events {

			// Use the `winit` backend feature to convert the winit event to a conrod one.
			if let Some(event) = conrod::backend::winit::convert(event.clone(), &display) {
				ui.handle_event(event);
				ui_needs_update = true;
			}

			match event {
				// Break from the loop upon `Escape`.
				glium::glutin::Event::KeyboardInput(_, _, Some(glium::glutin::VirtualKeyCode::Escape)) |
				glium::glutin::Event::Closed =>
					break 'main,
				_ => {},
			}
		}

		if app_state.simulating {
			app_state.board.advance_simulation();
		}

		// Instantiate all widgets in the GUI.
		{
			let ui = &mut ui.set_widgets();

			// "Hello World!" in the middle of the screen.
			widget::Text::new("Game of Life")
				.mid_top_of(ui.window)
				.color(conrod::color::WHITE)
				.font_size(32)
				.set(ids.title, ui);
	/*		widget::Text::new("Start")
				.down(5.0)
				.color(conrod::color::WHITE)
				.font_size(8)
				.set(ids.info, ui
*/
			let start_stop_label = if app_state.simulating { "Pause simulation" } else { "Start simulation"};
			let start_stop_button = widget::Button::new()
				.label(start_stop_label)
				.down_from(ids.title, 5.0)
				.set(ids.start_stop_button, ui);
			for _press in start_stop_button {
				app_state.simulating = !app_state.simulating;
			}

			// A demonstration using widget_matrix to easily draw a matrix of any kind of widget.
			let (cols, rows) = (8, 8);
			let mut elements = widget::Matrix::new(cols, rows)
				.down(20.0)
				.w_h(260.0, 260.0)
				.set(ids.board, ui);

			// The `Matrix` widget returns an `Elements`, which can be used similar to an `Iterator`.
			while let Some(elem) = elements.next(ui) {
				let (col, row) = (elem.col, elem.row);

				// Color effect for fun.
				let (r, g, b, a) = (
					0.5 + (elem.col as f32 / cols as f32) / 2.0,
					0.75,
					1.0 - (elem.row as f32 / rows as f32) / 2.0,
					1.0
				);

				// We can use `Element`s to instantiate any kind of widget we like.
				// The `Element` does all of the positioning and sizing work for us.
				// Here, we use the `Element` to `set` a `Toggle` widget for us.
				let toggle = widget::Toggle::new(app_state.board.get(col, row))
					.rgba(r, g, b, a)
					.border(1.0);
				if let Some(new_value) = elem.set(toggle, ui).last() {
					app_state.board.set(col, row, new_value);
				}
			}

		}

		// Render the `Ui` and then display it on the screen.
		if let Some(primitives) = ui.draw_if_changed() {
			renderer.fill(&display, primitives, &image_map);
			let mut target = display.draw();
			target.clear_color(0.0, 0.0, 0.0, 1.0);
			renderer.draw(&display, &mut target, &image_map).unwrap();
			target.finish().unwrap();
		}
	}
}
<|endoftext|>