Skip to content

Commit

Permalink
Merge pull request #195 from Glyphack/import-resolver-semantic-analyzer
Browse files Browse the repository at this point in the history
  • Loading branch information
Glyphack authored Oct 27, 2023
2 parents ad9f810 + d436757 commit f745827
Show file tree
Hide file tree
Showing 14 changed files with 1,001 additions and 77 deletions.
2 changes: 1 addition & 1 deletion enderpy/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fn symbols(path: &PathBuf) -> Result<()> {
let dir_of_path = path.parent().unwrap();
let python_executable = Some(get_python_executable()?);
let settings = Settings {
debug: false,
debug: true,
root: dir_of_path.to_path_buf(),
import_discovery: ImportDiscovery { python_executable },
follow_imports: enderpy_python_type_checker::settings::FollowImports::All,
Expand Down
10 changes: 10 additions & 0 deletions parser/src/parser/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,16 @@ pub struct Alias {
pub asname: Option<String>,
}

impl Alias {
pub fn name(&self) -> String {
if let Some(asname) = &self.asname {
asname.clone()
} else {
self.name.clone()
}
}
}

// https://docs.python.org/3/library/ast.html#ast.ImportFrom
#[derive(Debug, Clone)]
pub struct ImportFrom {
Expand Down
2 changes: 1 addition & 1 deletion typechecker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ env_logger = "0.10.0"
tempfile = "3.8.0"

[dev-dependencies]
insta = { version = "1.28.0", features = ["yaml"] }
insta = { version = "1.28.0", features = ["yaml", "filters"] }
13 changes: 10 additions & 3 deletions typechecker/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,10 @@ impl BuildManager {
};
let host = &ruff_python_resolver::host::StaticHost::new(vec![]);
for state in self.modules.iter_mut() {
state.1.populate_symbol_table();

state
.1
.resolve_file_imports(execution_environment, import_config, host)
.resolve_file_imports(execution_environment, import_config, host);
state.1.populate_symbol_table();
}
}

Expand Down Expand Up @@ -400,6 +399,8 @@ mod tests {
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../testdata/output/");
settings.set_description(contents);
settings.add_filter(r"module_name: .*.typechecker.test_data.inputs.symbol_table..*.py",
"module_name: [REDACTED]");
settings.bind(|| {
insta::assert_snapshot!(result);
});
Expand Down Expand Up @@ -442,6 +443,12 @@ mod tests {
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../testdata/output/");
settings.set_description(fs::read_to_string(path).unwrap());
settings.add_filter(
r"/.*/typechecker/test_data/inputs/symbol_table",
"[REDACTED]",
);
settings.add_filter(r"module_name: .*.typechecker.test_data.inputs.symbol_table..*.py",
"module_name: [REDACTED]");
settings.bind(|| {
insta::assert_snapshot!(result);
});
Expand Down
88 changes: 50 additions & 38 deletions typechecker/src/semantic_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,27 @@ use parser::ast::Statement;
use crate::{
ast_visitor::TraversalVisitor,
nodes::EnderpyFile,
ruff_python_import_resolver::{
import_result::ImportResult, module_descriptor::ImportModuleDescriptor,
},
symbol_table::{
Class, Declaration, DeclarationPath, Function, Paramter, SymbolScope, SymbolTable,
Alias, Class, Declaration, DeclarationPath, Function, Paramter, SymbolScope, SymbolTable,
SymbolTableNode, SymbolTableScope, SymbolTableType, Variable,
}, ruff_python_import_resolver::{import_result::ImportResult, module_descriptor::ImportModuleDescriptor},
},
};

#[allow(unused)]
pub struct SemanticAnalyzer {
pub globals: SymbolTable,
file: Box<EnderpyFile>,
pub imports: HashMap<String, Box<ImportResult>>,
/// Map of module name to import result
/// The imports inside the file are resolved by this map and
/// no other imports are resolved
/// Example:
/// if we have a file with the following imports this is how we use the map
/// import os -> imports.get("os")
/// from os import path -> imports.get("os")
pub imports: HashMap<String, ImportResult>,
// TODO: Replace errors with another type
errors: Vec<String>,

Expand All @@ -28,7 +38,7 @@ pub struct SemanticAnalyzer {

#[allow(unused)]
impl SemanticAnalyzer {
pub fn new(file: Box<EnderpyFile>, imports: HashMap<String, Box<ImportResult>>) -> Self {
pub fn new(file: Box<EnderpyFile>, imports: HashMap<String, ImportResult>) -> Self {
let globals = SymbolTable::new(crate::symbol_table::SymbolTableType::Module, 0);
SemanticAnalyzer {
globals,
Expand Down Expand Up @@ -90,25 +100,6 @@ impl SemanticAnalyzer {
}
}

fn create_import_alias_symbol(
&mut self,
alias: &parser::ast::Alias,
declaration_path: DeclarationPath,
import_result: Box<ImportResult>,
) {
let import_symbol_name = if let Some(asname) = &alias.asname {
asname.clone()
} else {
alias.name.clone()
};
let decl = Declaration::Alias(Box::new(crate::symbol_table::Alias {
declaration_path: declaration_path.clone(),
alias_node: alias.clone(),
import_result,
}));
self.create_symbol(import_symbol_name, decl);
}

fn add_arguments_definitions(&mut self, args: &parser::ast::Arguments) {
let defaults_len = args.defaults.len();
for (pos_only, index) in args.posonlyargs.iter().zip(0..args.posonlyargs.len()) {
Expand Down Expand Up @@ -278,33 +269,54 @@ impl TraversalVisitor for SemanticAnalyzer {
}
}

fn visit_import(&mut self, _i: &parser::ast::Import) {
for alias in &_i.names {
let import_result = match self.imports.get(
&ImportModuleDescriptor::from(alias).name()
) {
fn visit_import(&mut self, i: &parser::ast::Import) {
for alias in &i.names {
let import_result = match self
.imports
.get(&ImportModuleDescriptor::from(alias).name())
{
Some(result) => result.clone(),
None => Box::new(ImportResult::not_found()),
None => ImportResult::not_found(),
};
// TODO: Report unresolved import if import_result is None
self.create_import_alias_symbol(
alias,
DeclarationPath {
module_name: self.file.module_name().clone(),
node: alias.node,
},
let declaration_path = DeclarationPath {
module_name: self.file.module_name().clone(),
node: alias.node,
};

let declaration = Declaration::Alias(Box::new(Alias {
declaration_path,
import_from_node: None,
import_node: Some(i.clone()),
symbol_name: None,
import_result,
);
}));

self.create_symbol(alias.name(), declaration);
}
}

fn visit_import_from(&mut self, _i: &parser::ast::ImportFrom) {
for alias in &_i.names {
let _declaration_path = DeclarationPath {
let declaration_path = DeclarationPath {
module_name: self.file.module_name().clone(),
node: alias.node,
};
// self.create_import_alias_symbol(alias, declaration_path);
// TODO: Report unresolved import if import_result is None
let module_import_result =
match self.imports.get(&ImportModuleDescriptor::from(_i).name()) {
Some(result) => result.clone(),
None => ImportResult::not_found(),
};
let declaration = Declaration::Alias(Box::new(Alias {
declaration_path,
import_from_node: Some(_i.clone()),
import_node: None,
symbol_name: Some(alias.name()),
import_result: module_import_result,
}));

self.create_symbol(alias.name(), declaration);
}
}

Expand Down
26 changes: 16 additions & 10 deletions typechecker/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
use std::collections::HashMap;

use crate::{
ast_visitor::TraversalVisitor, nodes::EnderpyFile, semantic_analyzer::SemanticAnalyzer,
symbol_table::SymbolTable, diagnostic::Diagnostic, ruff_python_import_resolver::{import_result::ImportResult, self, module_descriptor::ImportModuleDescriptor},
ast_visitor::TraversalVisitor,
diagnostic::Diagnostic,
nodes::EnderpyFile,
ruff_python_import_resolver as ruff_python_resolver,
ruff_python_import_resolver::resolver
ruff_python_import_resolver::resolver,
ruff_python_import_resolver::{
self, import_result::ImportResult, module_descriptor::ImportModuleDescriptor,
},
semantic_analyzer::SemanticAnalyzer,
symbol_table::SymbolTable,
};

#[derive(Debug, Clone)]
Expand All @@ -13,7 +19,7 @@ pub struct State {
symbol_table: SymbolTable,
pub diagnostics: Vec<Diagnostic>,
// Map of import names to the result of the import
pub imports: HashMap<String, Box<ImportResult>>,
pub imports: HashMap<String, ImportResult>,
}

impl State {
Expand Down Expand Up @@ -43,18 +49,18 @@ impl State {
execution_environment: &ruff_python_resolver::execution_environment::ExecutionEnvironment,
import_config: &ruff_python_resolver::config::Config,
host: &ruff_python_resolver::host::StaticHost,
){
) {
for import in self.file.imports.iter() {
let import_descriptions = match import {
crate::nodes::ImportKinds::Import(i) => i
.names
.iter()
.map(
|x| ruff_python_resolver::module_descriptor::ImportModuleDescriptor::from(x) )
.map(|x| {
ruff_python_resolver::module_descriptor::ImportModuleDescriptor::from(x)
})
.collect::<Vec<ImportModuleDescriptor>>(),
crate::nodes::ImportKinds::ImportFrom(i) => {
vec![
ruff_python_resolver::module_descriptor::ImportModuleDescriptor::from(i) ]
vec![ruff_python_resolver::module_descriptor::ImportModuleDescriptor::from(i)]
}
};

Expand All @@ -66,7 +72,7 @@ impl State {
import_config,
host,
);
self.imports.insert(import_desc.name(), Box::new(resolved));
self.imports.insert(import_desc.name(), resolved);
}
}
}
Expand Down
27 changes: 19 additions & 8 deletions typechecker/src/symbol_table.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
use enderpy_python_parser::ast::{self, Node};
use std::{collections::HashMap, fmt::Display};

use crate::ruff_python_import_resolver::import_result::ImportResult;
use crate::{
nodes::ImportKinds,
ruff_python_import_resolver::{
import_result::ImportResult, module_descriptor::ImportModuleDescriptor,
},
};

#[derive(Debug, Clone)]
pub struct SymbolTable {
Expand Down Expand Up @@ -118,8 +123,14 @@ pub struct Paramter {
#[derive(Debug, Clone)]
pub struct Alias {
pub declaration_path: DeclarationPath,
pub alias_node: ast::Alias,
pub import_result: Box<ImportResult>,
/// The import node that this alias is for. Only one of import_node or import_from_node will be set
pub import_from_node: Option<ast::ImportFrom>,
pub import_node: Option<ast::Import>,
/// Name of the imported symbol in case of ImportFrom
/// e.g. From bar import baz -> baz is the symbol name
pub symbol_name: Option<String>,
/// The result of the import
pub import_result: ImportResult,
}

#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -273,11 +284,11 @@ impl std::fmt::Display for SymbolTableScope {
impl std::fmt::Display for Declaration {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Declaration::Variable(v) => write!(f, "{:?}", v),
Declaration::Function(fun) => write!(f, "{:?}", fun),
Declaration::Class(c) => write!(f, "{:?}", c),
Declaration::Parameter(p) => write!(f, "{:?}", p),
Declaration::Alias(a) => write!(f, "{:?}", a),
Declaration::Variable(v) => write!(f, "{:#?}", v),
Declaration::Function(fun) => write!(f, "{:#?}", fun),
Declaration::Class(c) => write!(f, "{:#?}", c),
Declaration::Parameter(p) => write!(f, "{:#?}", p),
Declaration::Alias(a) => write!(f, "{:#?}", a),
}
}
}
Empty file.
10 changes: 10 additions & 0 deletions typechecker/test_data/inputs/symbol_table/imports.py
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
import variables
import import_test

from variables import a
from variables import *

import os.path

from os import *

from os.path import join
Loading

0 comments on commit f745827

Please sign in to comment.