mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 05:14:52 +00:00
[ty] resolve docstrings for modules (#19898)
This also reintroduces the `ResolvedDefinition::Module` variant because reverse-engineering it in several places is a bit confusing. In an ideal world we wouldn't have `ResolvedDefinition::FileWithRange` as it kinda kills the ability to do richer analysis, so I want to chip away at its scope wherever I can (currently it's used to point at asname parts of import statements when doing `ImportAliasResolution::PreserveAliases`, and also keyword arguments). This also makes a kind of odd change to allow a hover to *only* produce a docstring. This works around an oddity where hovering over a module name in an import fails to resolve to a `ty` even though hovering over uses of that imported name *does*. The two fixed tests reflect the two interesting cases here.
This commit is contained in:
parent
9f6146a13d
commit
f0b03c3e86
4 changed files with 93 additions and 49 deletions
|
@ -113,7 +113,14 @@ impl<'db> Definition<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Extract a docstring from a function or class body.
|
||||
/// Get the module-level docstring for the given file
|
||||
pub(crate) fn module_docstring(db: &dyn Db, file: File) -> Option<String> {
|
||||
let module = parsed_module(db, file).load(db);
|
||||
docstring_from_body(module.suite())
|
||||
.map(|docstring_expr| docstring_expr.value.to_str().to_owned())
|
||||
}
|
||||
|
||||
/// Extract a docstring from a function, module, or class body.
|
||||
fn docstring_from_body(body: &[ast::Stmt]) -> Option<&ast::ExprStringLiteral> {
|
||||
let stmt = body.first()?;
|
||||
// Require the docstring to be a standalone expression.
|
||||
|
|
|
@ -973,12 +973,11 @@ mod resolve_definition {
|
|||
use ruff_db::files::{File, FileRange};
|
||||
use ruff_db::parsed::{ParsedModuleRef, parsed_module};
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
use rustc_hash::FxHashSet;
|
||||
use tracing::trace;
|
||||
|
||||
use crate::module_resolver::file_to_module;
|
||||
use crate::semantic_index::definition::{Definition, DefinitionKind};
|
||||
use crate::semantic_index::definition::{Definition, DefinitionKind, module_docstring};
|
||||
use crate::semantic_index::scope::{NodeWithScopeKind, ScopeId};
|
||||
use crate::semantic_index::{global_scope, place_table, semantic_index, use_def_map};
|
||||
use crate::{Db, ModuleName, resolve_module, resolve_real_module};
|
||||
|
@ -992,6 +991,8 @@ mod resolve_definition {
|
|||
pub enum ResolvedDefinition<'db> {
|
||||
/// The import resolved to a specific definition within a module
|
||||
Definition(Definition<'db>),
|
||||
/// The import resolved to an entire module
|
||||
Module(File),
|
||||
/// The import resolved to a file with a specific range
|
||||
FileWithRange(FileRange),
|
||||
}
|
||||
|
@ -1000,9 +1001,18 @@ mod resolve_definition {
|
|||
fn file(&self, db: &'db dyn Db) -> File {
|
||||
match self {
|
||||
ResolvedDefinition::Definition(definition) => definition.file(db),
|
||||
ResolvedDefinition::Module(file) => *file,
|
||||
ResolvedDefinition::FileWithRange(file_range) => file_range.file(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn docstring(&self, db: &'db dyn Db) -> Option<String> {
|
||||
match self {
|
||||
ResolvedDefinition::Definition(definition) => definition.docstring(db),
|
||||
ResolvedDefinition::Module(file) => module_docstring(db, *file),
|
||||
ResolvedDefinition::FileWithRange(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve import definitions to their targets.
|
||||
|
@ -1070,10 +1080,7 @@ mod resolve_definition {
|
|||
|
||||
// For simple imports like "import os", we want to navigate to the module itself.
|
||||
// Return the module file directly instead of trying to find definitions within it.
|
||||
vec![ResolvedDefinition::FileWithRange(FileRange::new(
|
||||
module_file,
|
||||
TextRange::default(),
|
||||
))]
|
||||
vec![ResolvedDefinition::Module(module_file)]
|
||||
}
|
||||
|
||||
DefinitionKind::ImportFrom(import_from_def) => {
|
||||
|
@ -1283,24 +1290,19 @@ mod resolve_definition {
|
|||
}
|
||||
trace!("Built Definition Path: {path:?}");
|
||||
}
|
||||
ResolvedDefinition::FileWithRange(file_range) => {
|
||||
return if file_range.range() == TextRange::default() {
|
||||
trace!(
|
||||
"Found module mapping: {} => {}",
|
||||
stub_file.path(db),
|
||||
real_file.path(db)
|
||||
);
|
||||
// This is just a reference to a module, no need to do paths
|
||||
Some(vec![ResolvedDefinition::FileWithRange(FileRange::new(
|
||||
real_file,
|
||||
TextRange::default(),
|
||||
))])
|
||||
} else {
|
||||
// Not yet implemented -- in this case we want to recover something like a Definition
|
||||
// and build a Definition Path, but this input is a bit too abstract for now.
|
||||
trace!("Found arbitrary FileWithRange by stub mapping, giving up");
|
||||
None
|
||||
};
|
||||
ResolvedDefinition::Module(_) => {
|
||||
trace!(
|
||||
"Found module mapping: {} => {}",
|
||||
stub_file.path(db),
|
||||
real_file.path(db)
|
||||
);
|
||||
return Some(vec![ResolvedDefinition::Module(real_file)]);
|
||||
}
|
||||
ResolvedDefinition::FileWithRange(_) => {
|
||||
// Not yet implemented -- in this case we want to recover something like a Definition
|
||||
// and build a Definition Path, but this input is a bit too abstract for now.
|
||||
trace!("Found arbitrary FileWithRange while stub mapping, giving up");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue