Add `NameInfo::defined_block_id`

This commit is contained in:
Shunsuke Shibayama 2022-12-27 15:58:40 +09:00
parent 2590cd6ab4
commit 4c99de6125
11 changed files with 684 additions and 244 deletions

20
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,20 @@
repos:
- repo: local
hooks:
- id: rustfmt
name: rustfmt
description: Check if all files follow the rustfmt style
entry: cargo fmt --all
language: system
pass_filenames: false
- id: cargo-test
name: Cargo test
entry: cargo test
language: system
pass_filenames: false
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-merge-conflict

16
Cargo.lock generated
View File

@ -206,7 +206,7 @@ checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "els"
version = "0.1.13-nightly.0"
source = "git+https://github.com/erg-lang/erg-language-server?branch=main#ba96e0d1e86f496600b659c4a3fd70ec215587f3"
source = "git+https://github.com/erg-lang/erg-language-server?branch=main#8cef57fb31a5c1505f23041b2720925650f18539"
dependencies = [
"erg_common",
"erg_compiler",
@ -227,7 +227,7 @@ dependencies = [
[[package]]
name = "erg_common"
version = "0.6.0"
source = "git+https://github.com/erg-lang/erg?branch=main#16b50347d4e97b9b193e068a2d5fdba2340771ab"
source = "git+https://github.com/erg-lang/erg?branch=main#ee8492eb4205e4ea479d9b7c804a471b61797559"
dependencies = [
"hermit-abi",
"libc",
@ -237,7 +237,7 @@ dependencies = [
[[package]]
name = "erg_compiler"
version = "0.6.0"
source = "git+https://github.com/erg-lang/erg?branch=main#16b50347d4e97b9b193e068a2d5fdba2340771ab"
source = "git+https://github.com/erg-lang/erg?branch=main#ee8492eb4205e4ea479d9b7c804a471b61797559"
dependencies = [
"erg_common",
"erg_parser",
@ -246,7 +246,7 @@ dependencies = [
[[package]]
name = "erg_parser"
version = "0.6.0"
source = "git+https://github.com/erg-lang/erg?branch=main#16b50347d4e97b9b193e068a2d5fdba2340771ab"
source = "git+https://github.com/erg-lang/erg?branch=main#ee8492eb4205e4ea479d9b7c804a471b61797559"
dependencies = [
"erg_common",
"unicode-xid 0.2.4",
@ -716,18 +716,18 @@ checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]]
name = "serde"
version = "1.0.151"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.151"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8"
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
"proc-macro2",
"quote",

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
use erg_common::switch_lang;
use erg_common::config::Input;
use erg_common::error::{Location, ErrorCore, ErrorKind, SubMessage};
use erg_common::error::{ErrorCore, ErrorKind, Location, SubMessage};
use erg_common::switch_lang;
use erg_compiler::error::CompileError;
pub(crate) fn reassign_func_error(
@ -27,11 +27,7 @@ pub(crate) fn reassign_func_error(
)
}
pub(crate) fn self_not_found_error(
input: Input,
loc: Location,
caused_by: String,
) -> CompileError {
pub(crate) fn self_not_found_error(input: Input, loc: Location, caused_by: String) -> CompileError {
CompileError::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
@ -50,11 +46,7 @@ pub(crate) fn self_not_found_error(
)
}
pub(crate) fn init_var_error(
input: Input,
loc: Location,
caused_by: String,
) -> CompileError {
pub(crate) fn init_var_error(input: Input, loc: Location, caused_by: String) -> CompileError {
CompileError::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],

View File

@ -1,9 +1,9 @@
use std::io::Write;
use std::path::PathBuf;
use erg_common::log;
use erg_common::config::Input;
use erg_compiler::hir::{HIR, Expr};
use erg_common::log;
use erg_compiler::hir::{Expr, HIR};
use erg_compiler::ty::HasType;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -31,7 +31,11 @@ fn escape_type(typ: String) -> String {
}
pub fn gen_decl_er(hir: HIR, status: CheckStatus) -> DeclFile {
let mut code = if status.is_failed() { "# failed\n".to_string() } else { "# succeed\n".to_string() };
let mut code = if status.is_failed() {
"# failed\n".to_string()
} else {
"# succeed\n".to_string()
};
for chunk in hir.module.into_iter() {
match chunk {
Expr::Def(def) => {
@ -55,7 +59,11 @@ pub fn gen_decl_er(hir: HIR, status: CheckStatus) -> DeclFile {
pub fn dump_decl_er(input: Input, hir: HIR, status: CheckStatus) {
let file = gen_decl_er(hir, status);
let mut path = if let Input::File(path) = input { path } else { PathBuf::new() };
let mut path = if let Input::File(path) = input {
path
} else {
PathBuf::new()
};
path.pop();
path.push("__pycache__");
let pycache_dir = path.as_path();

View File

@ -1,6 +1,6 @@
mod convert;
mod gen_decl;
mod error;
mod gen_decl;
pub use convert::*;
pub use gen_decl::*;

View File

@ -1,14 +1,14 @@
use erg_common::config::ErgConfig;
use erg_common::error::{ErrorCore, ErrorKind, MultiErrorDisplay};
use erg_common::style::{BLUE, GREEN, RED, RESET, YELLOW};
use erg_common::traits::{Runnable, Stream};
use erg_common::style::{GREEN, BLUE, RED, YELLOW, RESET};
use erg_common::config::{ErgConfig};
use erg_common::error::{MultiErrorDisplay, ErrorCore, ErrorKind};
use erg_compiler::artifact::{BuildRunnable, CompleteArtifact, IncompleteArtifact, Buildable};
use erg_compiler::artifact::{BuildRunnable, Buildable, CompleteArtifact, IncompleteArtifact};
use erg_compiler::context::Context;
use erg_compiler::erg_parser::ast::AST;
use erg_compiler::error::{CompileErrors, CompileError};
use erg_compiler::error::{CompileError, CompileErrors};
use erg_compiler::lower::ASTLowerer;
use py2erg::{CheckStatus, ShadowingMode};
use py2erg::dump_decl_er;
use py2erg::{CheckStatus, ShadowingMode};
use rustpython_parser::parser;
use crate::handle_err;
@ -22,13 +22,10 @@ pub struct PythonAnalyzer {
impl Runnable for PythonAnalyzer {
type Err = CompileError;
type Errs = CompileErrors;
const NAME: &'static str = "Python Analyzer";
const NAME: &'static str = "Python Analyzer";
fn new(cfg: ErgConfig) -> Self {
let checker = ASTLowerer::new(cfg.clone());
Self {
checker,
cfg,
}
Self { checker, cfg }
}
#[inline]
fn cfg(&self) -> &ErgConfig {
@ -74,7 +71,11 @@ impl PythonAnalyzer {
Runnable::new(cfg)
}
pub fn analyze(&mut self, py_code: String, mode: &str) -> Result<CompleteArtifact, IncompleteArtifact> {
pub fn analyze(
&mut self,
py_code: String,
mode: &str,
) -> Result<CompleteArtifact, IncompleteArtifact> {
let filename = self.cfg.input.filename();
let py_program = parser::parse_program(&py_code).map_err(|err| {
let core = ErrorCore::new(
@ -82,9 +83,9 @@ impl PythonAnalyzer {
err.to_string(),
0,
ErrorKind::SyntaxError,
erg_common::error::Location::Line(err.location.row())
erg_common::error::Location::Line(err.location.row()),
);
let err = CompileError::new(core, self.cfg.input.clone(), "".into());
let err = CompileError::new(core, self.cfg.input.clone(), "".into());
IncompleteArtifact::new(None, CompileErrors::from(err), CompileErrors::empty())
})?;
let shadowing = if cfg!(feature = "debug") {
@ -99,11 +100,16 @@ impl PythonAnalyzer {
match self.checker.lower(erg_ast, mode) {
Ok(mut artifact) => {
artifact.warns.extend(warns);
artifact.warns = handle_err::filter_errors(self.checker.get_mod_ctx(), artifact.warns);
artifact.warns =
handle_err::filter_errors(self.checker.get_mod_ctx(), artifact.warns);
if errors.is_empty() {
Ok(artifact)
} else {
Err(IncompleteArtifact::new(Some(artifact.object), errors, artifact.warns))
Err(IncompleteArtifact::new(
Some(artifact.object),
errors,
artifact.warns,
))
}
}
Err(iart) => {
@ -123,32 +129,52 @@ impl PythonAnalyzer {
match self.analyze(py_code, "exec") {
Ok(artifact) => {
if !artifact.warns.is_empty() {
println!("{YELLOW}Found {} warnings{RESET}: {}", artifact.warns.len(), self.cfg.input.filename());
println!(
"{YELLOW}Found {} warnings{RESET}: {}",
artifact.warns.len(),
self.cfg.input.filename()
);
artifact.warns.fmt_all_stderr();
}
println!("{GREEN}All checks OK{RESET}: {}", self.cfg.input.filename());
if self.cfg.output_dir.is_some() {
dump_decl_er(self.cfg.input.clone(), artifact.object, CheckStatus::Succeed);
dump_decl_er(
self.cfg.input.clone(),
artifact.object,
CheckStatus::Succeed,
);
println!("A declaration file has been generated to __pycache__ directory.");
}
std::process::exit(0);
}
Err(artifact) => {
if !artifact.warns.is_empty() {
println!("{YELLOW}Found {} warnings{RESET}: {}", artifact.warns.len(), self.cfg.input.filename());
println!(
"{YELLOW}Found {} warnings{RESET}: {}",
artifact.warns.len(),
self.cfg.input.filename()
);
artifact.warns.fmt_all_stderr();
}
let code = if artifact.errors.is_empty() {
println!("{GREEN}All checks OK{RESET}: {}", self.cfg.input.filename());
0
} else {
println!("{RED}Found {} errors{RESET}: {}", artifact.errors.len(), self.cfg.input.filename());
println!(
"{RED}Found {} errors{RESET}: {}",
artifact.errors.len(),
self.cfg.input.filename()
);
artifact.errors.fmt_all_stderr();
1
};
// Even if type checking fails, some APIs are still valid, so generate a file
if self.cfg.output_dir.is_some() {
dump_decl_er(self.cfg.input.clone(), artifact.object.unwrap(), CheckStatus::Failed);
dump_decl_er(
self.cfg.input.clone(),
artifact.object.unwrap(),
CheckStatus::Failed,
);
println!("A declaration file has been generated to __pycache__ directory.");
}
std::process::exit(code);

View File

@ -1,11 +1,14 @@
use erg_common::error::ErrorKind;
use erg_common::log;
// use erg_common::style::{remove_style, StyledString, Color};
use erg_compiler::error::{CompileErrors, CompileError};
use erg_compiler::context::Context;
use erg_compiler::error::{CompileError, CompileErrors};
pub(crate) fn filter_errors(ctx: &Context, errors: CompileErrors) -> CompileErrors {
errors.into_iter().filter_map(|error| filter_error(ctx, error)).collect()
errors
.into_iter()
.filter_map(|error| filter_error(ctx, error))
.collect()
}
fn filter_error(_ctx: &Context, error: CompileError) -> Option<CompileError> {
@ -18,7 +21,10 @@ fn filter_error(_ctx: &Context, error: CompileError) -> Option<CompileError> {
ErrorKind::VisibilityError => None,
// exclude doc strings
ErrorKind::UnusedWarning => {
let code = error.input.reread_lines(error.core.loc.ln_begin().unwrap(), error.core.loc.ln_end().unwrap());
let code = error.input.reread_lines(
error.core.loc.ln_begin().unwrap(),
error.core.loc.ln_end().unwrap(),
);
if code[0].trim().starts_with("\"\"\"") {
None
} else {

View File

@ -1,5 +1,5 @@
mod handle_err;
mod analyze;
mod handle_err;
use std::env;
use std::path::PathBuf;
@ -7,13 +7,16 @@ use std::str::FromStr;
use analyze::PythonAnalyzer;
use els::Server;
use erg_common::config::{Input, ErgConfig};
use erg_common::config::{ErgConfig, Input};
use erg_common::spawn::exec_new_thread;
pub fn parse_args() -> ErgConfig {
let mut args = env::args();
args.next(); // "pylyzer"
let mut cfg = ErgConfig{ python_compatible_mode: true, ..ErgConfig::default() };
let mut cfg = ErgConfig {
python_compatible_mode: true,
..ErgConfig::default()
};
while let Some(arg) = args.next() {
match &arg[..] {
"--" => {

View File

@ -15,8 +15,14 @@ for i in [1, 2, 3]:
j = i + "aa"
print(j)
i: int # OK
i = 1
i: str # ERR
i = "aa" if True else "bb"
i: str # OK
while "aaa": # ERR
print("invalid")
i += 1 # ERR
break
class C:
@ -27,9 +33,3 @@ print(dic["c"]) # ERR
a = [1, 2, 3]
print(a[4]) # ERR
i: int # OK
i = 1
i: str # ERR
i = "aa" if True else "bb"
i: str # OK

View File

@ -1,12 +1,16 @@
use std::path::PathBuf;
use erg_common::traits::Stream;
use erg_common::config::{ErgConfig, Input};
use erg_compiler::artifact::{IncompleteArtifact, CompleteArtifact};
use erg_common::traits::Stream;
use erg_compiler::artifact::{CompleteArtifact, IncompleteArtifact};
use pylyzer::PythonAnalyzer;
pub fn exec_analyzer(file_path: &'static str) -> Result<CompleteArtifact, IncompleteArtifact> {
let cfg = ErgConfig { python_compatible_mode: true, input: Input::File(PathBuf::from(file_path)), ..Default::default() };
let cfg = ErgConfig {
python_compatible_mode: true,
input: Input::File(PathBuf::from(file_path)),
..Default::default()
};
let mut analyzer = PythonAnalyzer::new(cfg);
let py_code = analyzer.cfg.input.read();
analyzer.analyze(py_code, "exec")
@ -27,7 +31,7 @@ pub fn expect(file_path: &'static str, warns: usize, errors: usize) {
#[test]
fn exec_test() {
expect("tests/test.py", 0, 9);
expect("tests/test.py", 0, 10);
}
#[test]