Skip to content

Commit

Permalink
Resolve classes and functions relative to script name
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Apr 16, 2024
1 parent effd518 commit 7cb7348
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 21 deletions.
11 changes: 6 additions & 5 deletions crates/ruff_linter/src/checkers/ast/analyze/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,8 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
pylint::rules::manual_from_import(checker, stmt, alias, names);
}
if checker.enabled(Rule::ImportSelf) {
if let Some(diagnostic) = pylint::rules::import_self(alias, checker.module_path)
if let Some(diagnostic) =
pylint::rules::import_self(alias, checker.module.path())
{
checker.diagnostics.push(diagnostic);
}
Expand Down Expand Up @@ -771,7 +772,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}
if checker.enabled(Rule::BannedApi) {
if let Some(module) =
helpers::resolve_imported_module_path(level, module, checker.module_path)
helpers::resolve_imported_module_path(level, module, checker.module.path())
{
flake8_tidy_imports::rules::banned_api(
checker,
Expand Down Expand Up @@ -800,7 +801,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}
if checker.enabled(Rule::BannedModuleLevelImports) {
if let Some(module) =
helpers::resolve_imported_module_path(level, module, checker.module_path)
helpers::resolve_imported_module_path(level, module, checker.module.path())
{
flake8_tidy_imports::rules::banned_module_level_imports(
checker,
Expand Down Expand Up @@ -888,7 +889,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
stmt,
level,
module,
checker.module_path,
checker.module.path(),
checker.settings.flake8_tidy_imports.ban_relative_imports,
) {
checker.diagnostics.push(diagnostic);
Expand Down Expand Up @@ -988,7 +989,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}
if checker.enabled(Rule::ImportSelf) {
if let Some(diagnostic) =
pylint::rules::import_from_self(level, module, names, checker.module_path)
pylint::rules::import_from_self(level, module, names, checker.module.path())
{
checker.diagnostics.push(diagnostic);
}
Expand Down
4 changes: 2 additions & 2 deletions crates/ruff_linter/src/checkers/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ pub(crate) struct Checker<'a> {
/// The [`Path`] to the package containing the current file.
package: Option<&'a Path>,
/// The module representation of the current file (e.g., `foo.bar`).
module_path: Option<&'a [String]>,
module: Module<'a>,
/// The [`PySourceType`] of the current file.
pub(crate) source_type: PySourceType,
/// The [`CellOffsets`] for the current file, if it's a Jupyter notebook.
Expand Down Expand Up @@ -174,7 +174,7 @@ impl<'a> Checker<'a> {
noqa,
path,
package,
module_path: module.path(),
module,
source_type,
locator,
stylist,
Expand Down
7 changes: 5 additions & 2 deletions crates/ruff_python_semantic/src/analyze/visibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ fn stem(path: &str) -> &str {
/// A Python module can either be defined as a module path (i.e., the dot-separated path to the
/// module) or, if the module can't be resolved, as a file path (i.e., the path to the file defining
/// the module).
#[derive(Debug)]
#[derive(Debug, Copy, Clone)]
pub enum ModuleSource<'a> {
/// A module path is a dot-separated path to the module.
Path(&'a [String]),
Expand All @@ -152,7 +152,7 @@ pub enum ModuleSource<'a> {

impl ModuleSource<'_> {
/// Return the `Visibility` of the module.
pub(crate) fn to_visibility(&self) -> Visibility {
pub(crate) fn to_visibility(self) -> Visibility {
match self {
Self::Path(path) => {
if path.iter().any(|m| is_private_module(m)) {
Expand All @@ -176,6 +176,7 @@ impl ModuleSource<'_> {
}
}

/// Infer the [`Visibility`] of a function from its name.
pub(crate) fn function_visibility(function: &ast::StmtFunctionDef) -> Visibility {
if function.name.starts_with('_') {
Visibility::Private
Expand All @@ -184,6 +185,7 @@ pub(crate) fn function_visibility(function: &ast::StmtFunctionDef) -> Visibility
}
}

/// Infer the [`Visibility`] of a method from its name and decorators.
pub fn method_visibility(function: &ast::StmtFunctionDef) -> Visibility {
// Is this a setter or deleter?
if function.decorator_list.iter().any(|decorator| {
Expand All @@ -208,6 +210,7 @@ pub fn method_visibility(function: &ast::StmtFunctionDef) -> Visibility {
Visibility::Private
}

/// Infer the [`Visibility`] of a class from its name.
pub(crate) fn class_visibility(class: &ast::StmtClassDef) -> Visibility {
if class.name.starts_with('_') {
Visibility::Private
Expand Down
5 changes: 3 additions & 2 deletions crates/ruff_python_semantic/src/definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ impl DefinitionId {
}
}

#[derive(Debug, is_macro::Is)]
#[derive(Debug, Copy, Clone, is_macro::Is)]
pub enum ModuleKind {
/// A Python file that represents a module within a package.
Module,
Expand All @@ -33,14 +33,15 @@ pub enum ModuleKind {
}

/// A Python module.
#[derive(Debug)]
#[derive(Debug, Copy, Clone)]
pub struct Module<'a> {
pub kind: ModuleKind,
pub source: ModuleSource<'a>,
pub python_ast: &'a [Stmt],
}

impl<'a> Module<'a> {
/// Return the fully-qualified path of the module.
pub fn path(&self) -> Option<&'a [String]> {
if let ModuleSource::Path(path) = self.source {
Some(path)
Expand Down
32 changes: 22 additions & 10 deletions crates/ruff_python_semantic/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use crate::Imported;
/// A semantic model for a Python module, to enable querying the module's semantic information.
pub struct SemanticModel<'a> {
typing_modules: &'a [String],
module_path: Option<&'a [String]>,
module: Module<'a>,

/// Stack of all AST nodes in the program.
nodes: Nodes<'a>,
Expand Down Expand Up @@ -134,7 +134,7 @@ impl<'a> SemanticModel<'a> {
pub fn new(typing_modules: &'a [String], path: &Path, module: Module<'a>) -> Self {
Self {
typing_modules,
module_path: module.path(),
module,
nodes: Nodes::default(),
node_id: None,
branches: Branches::default(),
Expand Down Expand Up @@ -739,7 +739,7 @@ impl<'a> SemanticModel<'a> {
.first()
.map_or(false, |segment| *segment == ".")
{
from_relative_import(self.module_path?, qualified_name.segments(), tail)?
from_relative_import(self.module.path()?, qualified_name.segments(), tail)?
} else {
qualified_name
.segments()
Expand All @@ -766,13 +766,25 @@ impl<'a> SemanticModel<'a> {
}
BindingKind::ClassDefinition(_) | BindingKind::FunctionDefinition(_) => {
let value_name = UnqualifiedName::from_expr(value)?;
let resolved: QualifiedName = self
.module_path?
.iter()
.map(String::as_str)
.chain(value_name.segments().iter().copied())
.collect();
Some(resolved)

// If we have a fully-qualified path for the module, use it.
if let Some(path) = self.module.path() {
Some(
path.iter()
.map(String::as_str)
.chain(value_name.segments().iter().copied())
.collect(),
)
} else if let Some(name) = self.module.name() {
// Otherwise, if we're in (e.g.) a script, use the module name.
Some(
std::iter::once(name)
.chain(value_name.segments().iter().copied())
.collect(),
)
} else {
None
}
}
_ => None,
}
Expand Down

0 comments on commit 7cb7348

Please sign in to comment.