Skip to content

Commit

Permalink
Add implicit imports
Browse files Browse the repository at this point in the history
  • Loading branch information
Glyphack committed Jun 2, 2024
1 parent c10395d commit 5e890df
Show file tree
Hide file tree
Showing 14 changed files with 243 additions and 64 deletions.
60 changes: 49 additions & 11 deletions enderpy/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::{
fs,
fs, io,
path::{Path, PathBuf},
};

Expand All @@ -25,7 +25,7 @@ fn main() -> Result<()> {
fn symbols(path: &Path) -> Result<()> {
let dir_of_path = path.parent().unwrap();
let typeshed_path = get_typeshed_path()?;
let settings = Settings { typeshed_path };
let settings = Settings::from_typeshed(typeshed_path);
let manager = BuildManager::new(settings);

let root = find_project_root(dir_of_path);
Expand All @@ -40,13 +40,30 @@ fn symbols(path: &Path) -> Result<()> {
}

fn get_python_executable() -> Result<PathBuf> {
let output = std::process::Command::new("python")
.arg("-c")
.arg("import sys; print(sys.executable)")
.output()
.into_diagnostic()?;
let path = String::from_utf8(output.stdout).into_diagnostic()?;
Ok(PathBuf::from(path))
let possible_executables = ["python3", "python"];
for executable_name in possible_executables {
let res = std::process::Command::new(executable_name)
.arg("-c")
.arg("import sys; print(sys.executable)")
.output();
match res {
Ok(output) => {
let mut path = String::from_utf8(output.stdout).into_diagnostic()?;
// Like calling trim but I didn't want to re-allocate the str slice
while path.ends_with("\n") || path.ends_with("\r") {
path.pop();
}
return Ok(PathBuf::from(path));
}
Err(e) => {
if e.kind() != io::ErrorKind::NotFound {
bail!("Unknown error when looking for python executable: {e}");
}
}
}
}

bail!("Failed to find Python executable.");
}

fn get_typeshed_path() -> Result<PathBuf> {
Expand Down Expand Up @@ -100,9 +117,12 @@ fn check(path: &Path) -> Result<()> {
bail!("Path must be a file");
}
let root = find_project_root(path);
let _python_executable = Some(get_python_executable()?);
let python_executable = Some(get_python_executable()?);
let typeshed_path = get_typeshed_path()?;
let settings = Settings { typeshed_path };
let settings = Settings {
typeshed_path,
python_executable,
};
let build_manager = BuildManager::new(settings);
build_manager.build(root);
build_manager.build_one(root, path);
Expand All @@ -124,3 +144,21 @@ fn check(path: &Path) -> Result<()> {
fn watch() -> Result<()> {
todo!()
}

#[cfg(test)]
mod tests {
use crate::get_python_executable;

#[test]
fn test_get_python_successfully() {
let executable = get_python_executable().expect("No python executable found!");
// Makes sure the python executable is working, without relying on a specific filename
assert!(executable.is_file());
let output = std::process::Command::new(executable)
.arg("-c")
.arg("print('Working')")
.output()
.unwrap();
assert_eq!(String::from_utf8(output.stdout).unwrap().trim(), "Working");
}
}
2 changes: 1 addition & 1 deletion lsp/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ async fn main() {
.parent()
.unwrap()
.join("typeshed");
let settings = Settings { typeshed_path };
let settings = Settings::from_typeshed(typeshed_path);
let manager = BuildManager::new(settings);
let (service, socket) = LspService::new(|client| Backend { client, manager });
Server::new(stdin, stdout, socket).serve(service).await;
Expand Down
15 changes: 15 additions & 0 deletions typechecker/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ impl BuildManager {
let module = EnderpyFile::new(path, true);
self.modules.insert(module.path(), module);
}
for (_, implicit_import) in imported_module.1.implicit_imports.iter() {
let module = EnderpyFile::new(&implicit_import.path, true);
self.modules.insert(module.path(), module);
}
}
log::debug!("Imports resolved");
for mut module in self.modules.iter_mut() {
Expand Down Expand Up @@ -198,6 +202,17 @@ impl BuildManager {
let e = EnderpyFile::new(resolved_path, true);
files_to_resolve.push(e);
}

for (_, implicit_import) in resolved.implicit_imports.iter() {
let source = match std::fs::read_to_string(&implicit_import.path) {
Ok(source) => source,
Err(e) => {
panic!("cannot read implicit import");
}
};
let e = EnderpyFile::new(&implicit_import.path, true);
files_to_resolve.push(e);
}
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions typechecker/src/checker.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use std::path::PathBuf;

use ast::{Expression, Statement};
use enderpy_python_parser as parser;
use enderpy_python_parser::ast::{self, *};

use super::{type_evaluator::TypeEvaluator, types::PythonType};
use crate::types::ModuleRef;
use crate::{
ast_visitor::TraversalVisitor, diagnostic::CharacterSpan, file::EnderpyFile,
symbol_table::SymbolTable,
Expand Down Expand Up @@ -164,6 +167,17 @@ impl TraversalVisitor for TypeChecker {
for alias in _i.names.iter() {
self.infer_name_type(&alias.name, alias.node.start, alias.node.end)
}

// Just to show type module when modules are hovered in imports.
let start = _i.node.start + 5;
let stop = start + _i.module.len() as u32 + 1;
self.types.insert(Interval {
start,
stop,
val: PythonType::Module(ModuleRef {
module_path: PathBuf::new(),
}),
});
}

fn visit_if(&mut self, i: &parser::ast::If) {
Expand Down
4 changes: 3 additions & 1 deletion typechecker/src/semantic_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,8 @@ impl TraversalVisitor for SemanticAnalyzer {
declaration_path,
import_from_node: None,
import_node: Some(i.clone()),
symbol_name: Some(alias.name()),
symbol_name: None,
module_name: Some(alias.name()),
import_result,
});

Expand Down Expand Up @@ -377,6 +378,7 @@ impl TraversalVisitor for SemanticAnalyzer {
import_from_node: Some(_i.clone()),
import_node: None,
symbol_name: Some(alias.name()),
module_name: None,
import_result: module_import_result.clone(),
});

Expand Down
9 changes: 9 additions & 0 deletions typechecker/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use serde::Deserialize;
#[allow(unused)]
pub struct Settings {
pub typeshed_path: PathBuf,
pub python_executable: Option<PathBuf>,
}

impl Settings {
Expand All @@ -20,10 +21,18 @@ impl Settings {
s.try_deserialize()
}

pub fn from_typeshed(typeshed_path: PathBuf) -> Self {
Settings {
typeshed_path,
python_executable: None,
}
}

pub fn test_settings() -> Self {
let file_dir = env::current_dir().unwrap();
Settings {
typeshed_path: file_dir.parent().unwrap().join("typeshed"),
python_executable: None,
}
}
}
3 changes: 3 additions & 0 deletions typechecker/src/symbol_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,9 @@ pub struct Alias {
/// 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>,
/// Name of imported module in case of Import
/// e.g. import os.path -> os.path is the module name
pub module_name: Option<String>,
/// The result of the import
pub import_result: ImportResult,
}
Expand Down
Loading

0 comments on commit 5e890df

Please sign in to comment.