2021-08-21 15:11:01 +08:00
|
|
|
use crate::typecheck::typedef::TypeEnum;
|
|
|
|
|
2021-07-22 15:36:37 +08:00
|
|
|
use super::type_inferencer::Inferencer;
|
|
|
|
use super::typedef::Type;
|
2021-07-22 17:07:49 +08:00
|
|
|
use rustpython_parser::ast::{self, Expr, ExprKind, Stmt, StmtKind};
|
2021-08-27 12:36:51 +08:00
|
|
|
use std::{collections::HashSet, iter::once};
|
2021-07-22 15:36:37 +08:00
|
|
|
|
|
|
|
impl<'a> Inferencer<'a> {
|
2021-07-22 17:07:49 +08:00
|
|
|
fn check_pattern(
|
|
|
|
&mut self,
|
|
|
|
pattern: &Expr<Option<Type>>,
|
2021-08-27 12:36:51 +08:00
|
|
|
defined_identifiers: &mut HashSet<String>,
|
2021-07-27 14:39:53 +08:00
|
|
|
) -> Result<(), String> {
|
2021-07-22 17:07:49 +08:00
|
|
|
match &pattern.node {
|
|
|
|
ExprKind::Name { id, .. } => {
|
|
|
|
if !defined_identifiers.contains(id) {
|
2021-08-27 12:36:51 +08:00
|
|
|
defined_identifiers.insert(id.clone());
|
2021-07-22 17:07:49 +08:00
|
|
|
}
|
2021-07-27 14:39:53 +08:00
|
|
|
Ok(())
|
2021-07-22 17:07:49 +08:00
|
|
|
}
|
|
|
|
ExprKind::Tuple { elts, .. } => {
|
|
|
|
for elt in elts.iter() {
|
2021-07-27 14:39:53 +08:00
|
|
|
self.check_pattern(elt, defined_identifiers)?;
|
2021-07-22 17:07:49 +08:00
|
|
|
}
|
2021-07-27 14:39:53 +08:00
|
|
|
Ok(())
|
|
|
|
}
|
2021-08-21 15:11:01 +08:00
|
|
|
ExprKind::Subscript { value, slice, .. } => {
|
|
|
|
self.check_expr(value, defined_identifiers)?;
|
|
|
|
self.check_expr(slice, defined_identifiers)?;
|
|
|
|
if let TypeEnum::TTuple { .. } = &*self.unifier.get_ty(value.custom.unwrap()) {
|
|
|
|
return Err(format!(
|
|
|
|
"Error at {}: cannot assign to tuple element",
|
|
|
|
value.location
|
|
|
|
));
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2021-08-05 14:55:09 +08:00
|
|
|
_ => self.check_expr(pattern, defined_identifiers),
|
2021-07-22 17:07:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-22 15:36:37 +08:00
|
|
|
fn check_expr(
|
|
|
|
&mut self,
|
|
|
|
expr: &Expr<Option<Type>>,
|
2021-08-27 12:36:51 +08:00
|
|
|
defined_identifiers: &mut HashSet<String>,
|
2021-07-22 15:36:37 +08:00
|
|
|
) -> Result<(), String> {
|
2021-07-22 17:07:49 +08:00
|
|
|
// there are some cases where the custom field is None
|
2021-07-22 15:36:37 +08:00
|
|
|
if let Some(ty) = &expr.custom {
|
2021-07-28 17:25:19 +08:00
|
|
|
if !self.unifier.is_concrete(*ty, &self.function_data.bound_variables) {
|
2021-07-22 15:36:37 +08:00
|
|
|
return Err(format!(
|
2021-07-22 17:07:49 +08:00
|
|
|
"expected concrete type at {} but got {}",
|
2021-07-22 15:36:37 +08:00
|
|
|
expr.location,
|
2021-07-28 17:25:19 +08:00
|
|
|
self.unifier.get_ty(*ty).get_type_name()
|
2021-07-22 15:36:37 +08:00
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
match &expr.node {
|
|
|
|
ExprKind::Name { id, .. } => {
|
|
|
|
if !defined_identifiers.contains(id) {
|
2021-08-27 11:13:43 +08:00
|
|
|
if self.function_data.resolver.get_identifier_def(id).is_some() {
|
2021-08-27 12:36:51 +08:00
|
|
|
defined_identifiers.insert(id.clone());
|
2021-08-27 11:13:43 +08:00
|
|
|
} else {
|
|
|
|
return Err(format!(
|
|
|
|
"unknown identifier {} (use before def?) at {}",
|
|
|
|
id, expr.location
|
|
|
|
));
|
|
|
|
}
|
2021-07-22 15:36:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ExprKind::List { elts, .. }
|
|
|
|
| ExprKind::Tuple { elts, .. }
|
|
|
|
| ExprKind::BoolOp { values: elts, .. } => {
|
|
|
|
for elt in elts.iter() {
|
|
|
|
self.check_expr(elt, defined_identifiers)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ExprKind::Attribute { value, .. } => {
|
2021-07-22 17:07:49 +08:00
|
|
|
self.check_expr(value, defined_identifiers)?;
|
2021-07-22 15:36:37 +08:00
|
|
|
}
|
|
|
|
ExprKind::BinOp { left, right, .. } => {
|
2021-07-22 17:07:49 +08:00
|
|
|
self.check_expr(left, defined_identifiers)?;
|
|
|
|
self.check_expr(right, defined_identifiers)?;
|
2021-07-22 15:36:37 +08:00
|
|
|
}
|
|
|
|
ExprKind::UnaryOp { operand, .. } => {
|
2021-07-22 17:07:49 +08:00
|
|
|
self.check_expr(operand, defined_identifiers)?;
|
2021-07-22 15:36:37 +08:00
|
|
|
}
|
2021-08-05 14:55:09 +08:00
|
|
|
ExprKind::Compare { left, comparators, .. } => {
|
2021-07-22 15:36:37 +08:00
|
|
|
for elt in once(left.as_ref()).chain(comparators.iter()) {
|
|
|
|
self.check_expr(elt, defined_identifiers)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ExprKind::Subscript { value, slice, .. } => {
|
2021-07-22 17:07:49 +08:00
|
|
|
self.check_expr(value, defined_identifiers)?;
|
|
|
|
self.check_expr(slice, defined_identifiers)?;
|
2021-07-22 15:36:37 +08:00
|
|
|
}
|
|
|
|
ExprKind::IfExp { test, body, orelse } => {
|
2021-07-22 17:07:49 +08:00
|
|
|
self.check_expr(test, defined_identifiers)?;
|
|
|
|
self.check_expr(body, defined_identifiers)?;
|
|
|
|
self.check_expr(orelse, defined_identifiers)?;
|
2021-07-22 15:36:37 +08:00
|
|
|
}
|
|
|
|
ExprKind::Slice { lower, upper, step } => {
|
2021-08-05 14:55:09 +08:00
|
|
|
for elt in [lower.as_ref(), upper.as_ref(), step.as_ref()].iter().flatten() {
|
2021-07-22 15:36:37 +08:00
|
|
|
self.check_expr(elt, defined_identifiers)?;
|
|
|
|
}
|
|
|
|
}
|
2021-07-22 17:07:49 +08:00
|
|
|
ExprKind::Lambda { args, body } => {
|
2021-08-27 12:36:51 +08:00
|
|
|
let mut defined_identifiers = defined_identifiers.clone();
|
2021-07-22 17:07:49 +08:00
|
|
|
for arg in args.args.iter() {
|
|
|
|
if !defined_identifiers.contains(&arg.node.arg) {
|
2021-08-27 12:36:51 +08:00
|
|
|
defined_identifiers.insert(arg.node.arg.clone());
|
2021-07-22 17:07:49 +08:00
|
|
|
}
|
|
|
|
}
|
2021-08-27 11:13:43 +08:00
|
|
|
self.check_expr(body, &mut defined_identifiers)?;
|
2021-07-22 17:07:49 +08:00
|
|
|
}
|
2021-08-05 14:55:09 +08:00
|
|
|
ExprKind::ListComp { elt, generators, .. } => {
|
2021-07-22 17:07:49 +08:00
|
|
|
// in our type inference stage, we already make sure that there is only 1 generator
|
2021-08-05 14:55:09 +08:00
|
|
|
let ast::Comprehension { target, iter, ifs, .. } = &generators[0];
|
2021-07-22 17:07:49 +08:00
|
|
|
self.check_expr(iter, defined_identifiers)?;
|
2021-08-27 12:36:51 +08:00
|
|
|
let mut defined_identifiers = defined_identifiers.clone();
|
2021-07-27 14:39:53 +08:00
|
|
|
self.check_pattern(target, &mut defined_identifiers)?;
|
2021-07-22 17:07:49 +08:00
|
|
|
for term in once(elt.as_ref()).chain(ifs.iter()) {
|
2021-08-27 11:13:43 +08:00
|
|
|
self.check_expr(term, &mut defined_identifiers)?;
|
2021-07-22 17:07:49 +08:00
|
|
|
}
|
|
|
|
}
|
2021-08-05 14:55:09 +08:00
|
|
|
ExprKind::Call { func, args, keywords } => {
|
2021-07-22 17:07:49 +08:00
|
|
|
for expr in once(func.as_ref())
|
|
|
|
.chain(args.iter())
|
|
|
|
.chain(keywords.iter().map(|v| v.node.value.as_ref()))
|
|
|
|
{
|
|
|
|
self.check_expr(expr, defined_identifiers)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ExprKind::Constant { .. } => {}
|
|
|
|
_ => {
|
|
|
|
println!("{:?}", expr.node);
|
|
|
|
unimplemented!()
|
|
|
|
}
|
2021-07-22 15:36:37 +08:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2021-07-22 17:07:49 +08:00
|
|
|
|
2021-07-26 16:00:29 +08:00
|
|
|
// check statements for proper identifier def-use and return on all paths
|
2021-07-22 17:07:49 +08:00
|
|
|
fn check_stmt(
|
|
|
|
&mut self,
|
|
|
|
stmt: &Stmt<Option<Type>>,
|
2021-08-27 12:36:51 +08:00
|
|
|
defined_identifiers: &mut HashSet<String>,
|
2021-07-22 17:07:49 +08:00
|
|
|
) -> Result<bool, String> {
|
|
|
|
match &stmt.node {
|
2021-08-05 14:55:09 +08:00
|
|
|
StmtKind::For { target, iter, body, orelse, .. } => {
|
2021-07-22 17:07:49 +08:00
|
|
|
self.check_expr(iter, defined_identifiers)?;
|
|
|
|
for stmt in orelse.iter() {
|
|
|
|
self.check_stmt(stmt, defined_identifiers)?;
|
|
|
|
}
|
|
|
|
let mut defined_identifiers = defined_identifiers.clone();
|
2021-07-27 14:39:53 +08:00
|
|
|
self.check_pattern(target, &mut defined_identifiers)?;
|
2021-07-22 17:07:49 +08:00
|
|
|
for stmt in body.iter() {
|
|
|
|
self.check_stmt(stmt, &mut defined_identifiers)?;
|
|
|
|
}
|
|
|
|
Ok(false)
|
|
|
|
}
|
|
|
|
StmtKind::If { test, body, orelse } => {
|
|
|
|
self.check_expr(test, defined_identifiers)?;
|
|
|
|
let mut body_identifiers = defined_identifiers.clone();
|
|
|
|
let mut orelse_identifiers = defined_identifiers.clone();
|
|
|
|
let body_returned = self.check_block(body, &mut body_identifiers)?;
|
|
|
|
let orelse_returned = self.check_block(orelse, &mut orelse_identifiers)?;
|
|
|
|
|
|
|
|
for ident in body_identifiers.iter() {
|
|
|
|
if !defined_identifiers.contains(ident) && orelse_identifiers.contains(ident) {
|
2021-08-27 12:36:51 +08:00
|
|
|
defined_identifiers.insert(ident.clone());
|
2021-07-22 17:07:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(body_returned && orelse_returned)
|
|
|
|
}
|
|
|
|
StmtKind::While { test, body, orelse } => {
|
|
|
|
self.check_expr(test, defined_identifiers)?;
|
|
|
|
let mut defined_identifiers = defined_identifiers.clone();
|
|
|
|
self.check_block(body, &mut defined_identifiers)?;
|
|
|
|
self.check_block(orelse, &mut defined_identifiers)?;
|
|
|
|
Ok(false)
|
|
|
|
}
|
|
|
|
StmtKind::Expr { value } => {
|
|
|
|
self.check_expr(value, defined_identifiers)?;
|
|
|
|
Ok(false)
|
|
|
|
}
|
|
|
|
StmtKind::Assign { targets, value, .. } => {
|
|
|
|
self.check_expr(value, defined_identifiers)?;
|
|
|
|
for target in targets {
|
2021-07-27 14:39:53 +08:00
|
|
|
self.check_pattern(target, defined_identifiers)?;
|
2021-07-22 17:07:49 +08:00
|
|
|
}
|
|
|
|
Ok(false)
|
|
|
|
}
|
|
|
|
StmtKind::AnnAssign { target, value, .. } => {
|
|
|
|
if let Some(value) = value {
|
|
|
|
self.check_expr(value, defined_identifiers)?;
|
2021-07-27 14:39:53 +08:00
|
|
|
self.check_pattern(target, defined_identifiers)?;
|
2021-07-22 17:07:49 +08:00
|
|
|
}
|
|
|
|
Ok(false)
|
|
|
|
}
|
2021-07-26 16:00:29 +08:00
|
|
|
StmtKind::Return { value } => {
|
|
|
|
if let Some(value) = value {
|
|
|
|
self.check_expr(value, defined_identifiers)?;
|
|
|
|
}
|
|
|
|
Ok(true)
|
|
|
|
}
|
|
|
|
StmtKind::Raise { exc, .. } => {
|
|
|
|
if let Some(value) = exc {
|
|
|
|
self.check_expr(value, defined_identifiers)?;
|
|
|
|
}
|
|
|
|
Ok(true)
|
|
|
|
}
|
|
|
|
// break, raise, etc.
|
2021-07-22 17:07:49 +08:00
|
|
|
_ => Ok(false),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn check_block(
|
|
|
|
&mut self,
|
|
|
|
block: &[Stmt<Option<Type>>],
|
2021-08-27 12:36:51 +08:00
|
|
|
defined_identifiers: &mut HashSet<String>,
|
2021-07-22 17:07:49 +08:00
|
|
|
) -> Result<bool, String> {
|
|
|
|
let mut ret = false;
|
|
|
|
for stmt in block {
|
|
|
|
if ret {
|
|
|
|
return Err(format!("dead code at {:?}", stmt.location));
|
|
|
|
}
|
|
|
|
if self.check_stmt(stmt, defined_identifiers)? {
|
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(ret)
|
|
|
|
}
|
2021-07-22 15:36:37 +08:00
|
|
|
}
|