1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use rustpython_parser::error::{LexicalErrorType, ParseError, ParseErrorType};
use rustpython_parser::location::Location;
use rustpython_parser::token::Tok;

use std::error::Error;
use std::fmt;

#[derive(Debug)]
pub struct CompileError {
    pub statement: Option<String>,
    pub error: CompileErrorType,
    pub location: Location,
    pub source_path: Option<String>,
}

impl CompileError {
    pub fn update_statement_info(&mut self, statement: String) {
        self.statement = Some(statement);
    }

    pub fn update_source_path(&mut self, source_path: &str) {
        debug_assert!(self.source_path.is_none());
        self.source_path = Some(source_path.to_owned());
    }
}

impl From<ParseError> for CompileError {
    fn from(error: ParseError) -> Self {
        CompileError {
            statement: None,
            error: CompileErrorType::Parse(error.error),
            location: error.location,
            source_path: None,
        }
    }
}

#[derive(Debug)]
pub enum CompileErrorType {
    /// Invalid assignment, cannot store value in target.
    Assign(&'static str),
    /// Invalid delete
    Delete(&'static str),
    /// Expected an expression got a statement
    ExpectExpr,
    /// Parser error
    Parse(ParseErrorType),
    SyntaxError(String),
    /// Multiple `*` detected
    MultipleStarArgs,
    /// Misplaced `*` expression
    InvalidStarExpr,
    /// Break statement outside of loop.
    InvalidBreak,
    /// Continue statement outside of loop.
    InvalidContinue,
    InvalidReturn,
    InvalidYield,
    InvalidYieldFrom,
    InvalidAwait,
    AsyncYieldFrom,
    AsyncReturnValue,
    InvalidFuturePlacement,
    InvalidFutureFeature(String),
}

impl CompileError {
    pub fn is_indentation_error(&self) -> bool {
        if let CompileErrorType::Parse(parse) = &self.error {
            match parse {
                ParseErrorType::Lexical(LexicalErrorType::IndentationError) => true,
                ParseErrorType::UnrecognizedToken(token, expected) => {
                    *token == Tok::Indent || expected.clone() == Some("Indent".to_owned())
                }
                _ => false,
            }
        } else {
            false
        }
    }

    pub fn is_tab_error(&self) -> bool {
        if let CompileErrorType::Parse(parse) = &self.error {
            if let ParseErrorType::Lexical(lex) = parse {
                if let LexicalErrorType::TabError = lex {
                    return true;
                }
            }
        }
        false
    }
}

impl fmt::Display for CompileError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let error_desc = match &self.error {
            CompileErrorType::Assign(target) => format!("can't assign to {}", target),
            CompileErrorType::Delete(target) => format!("can't delete {}", target),
            CompileErrorType::ExpectExpr => "Expecting expression, got statement".to_owned(),
            CompileErrorType::Parse(err) => err.to_string(),
            CompileErrorType::SyntaxError(err) => err.to_string(),
            CompileErrorType::MultipleStarArgs => {
                "two starred expressions in assignment".to_owned()
            }
            CompileErrorType::InvalidStarExpr => "can't use starred expression here".to_owned(),
            CompileErrorType::InvalidBreak => "'break' outside loop".to_owned(),
            CompileErrorType::InvalidContinue => "'continue' outside loop".to_owned(),
            CompileErrorType::InvalidReturn => "'return' outside function".to_owned(),
            CompileErrorType::InvalidYield => "'yield' outside function".to_owned(),
            CompileErrorType::InvalidYieldFrom => "'yield from' outside function".to_owned(),
            CompileErrorType::InvalidAwait => "'await' outside async function".to_owned(),
            CompileErrorType::AsyncYieldFrom => "'yield from' inside async function".to_owned(),
            CompileErrorType::AsyncReturnValue => {
                "'return' with value inside async generator".to_owned()
            }
            CompileErrorType::InvalidFuturePlacement => {
                "from __future__ imports must occur at the beginning of the file".to_owned()
            }
            CompileErrorType::InvalidFutureFeature(feat) => {
                format!("future feature {} is not defined", feat)
            }
        };

        if let Some(statement) = &self.statement {
            if self.location.column() > 0 {
                if let Some(line) = statement.lines().nth(self.location.row() - 1) {
                    // visualize the error, when location and statement are provided
                    return write!(f, "{}", self.location.visualize(line, &error_desc));
                }
            }
        }

        // print line number
        write!(f, "{} at {}", error_desc, self.location)
    }
}

impl Error for CompileError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        None
    }
}