## Summary As part of my continued quest to separate semantic model-building from diagnostic emission, this PR moves our unresolved-reference rules to a deferred pass. So, rather than emitting diagnostics as we encounter unresolved references, we now track those unresolved references on the semantic model (just like resolved references), and after traversal, emit the relevant rules for any unresolved references.
133 lines
3.3 KiB
Rust
133 lines
3.3 KiB
Rust
use bitflags::bitflags;
|
|
use ruff_text_size::TextRange;
|
|
use std::ops::Deref;
|
|
|
|
use ruff_index::{newtype_index, IndexSlice, IndexVec};
|
|
use ruff_python_ast::source_code::Locator;
|
|
|
|
use crate::context::ExecutionContext;
|
|
use crate::scope::ScopeId;
|
|
use crate::Exceptions;
|
|
|
|
/// A resolved read reference to a name in a program.
|
|
#[derive(Debug, Clone)]
|
|
pub struct ResolvedReference {
|
|
/// The scope in which the reference is defined.
|
|
scope_id: ScopeId,
|
|
/// The range of the reference in the source code.
|
|
range: TextRange,
|
|
/// The context in which the reference occurs.
|
|
context: ExecutionContext,
|
|
}
|
|
|
|
impl ResolvedReference {
|
|
pub const fn scope_id(&self) -> ScopeId {
|
|
self.scope_id
|
|
}
|
|
|
|
pub const fn range(&self) -> TextRange {
|
|
self.range
|
|
}
|
|
|
|
pub const fn context(&self) -> ExecutionContext {
|
|
self.context
|
|
}
|
|
}
|
|
|
|
/// Id uniquely identifying a read reference in a program.
|
|
#[newtype_index]
|
|
pub struct ResolvedReferenceId;
|
|
|
|
/// The references of a program indexed by [`ResolvedReferenceId`].
|
|
#[derive(Debug, Default)]
|
|
pub(crate) struct ResolvedReferences(IndexVec<ResolvedReferenceId, ResolvedReference>);
|
|
|
|
impl ResolvedReferences {
|
|
/// Pushes a new [`ResolvedReference`] and returns its [`ResolvedReferenceId`].
|
|
pub(crate) fn push(
|
|
&mut self,
|
|
scope_id: ScopeId,
|
|
range: TextRange,
|
|
context: ExecutionContext,
|
|
) -> ResolvedReferenceId {
|
|
self.0.push(ResolvedReference {
|
|
scope_id,
|
|
range,
|
|
context,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Deref for ResolvedReferences {
|
|
type Target = IndexSlice<ResolvedReferenceId, ResolvedReference>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
/// An unresolved read reference to a name in a program.
|
|
#[derive(Debug, Clone)]
|
|
pub struct UnresolvedReference {
|
|
/// The range of the reference in the source code.
|
|
range: TextRange,
|
|
/// The set of exceptions that were handled when resolution was attempted.
|
|
exceptions: Exceptions,
|
|
/// Flags indicating the context in which the reference occurs.
|
|
flags: UnresolvedReferenceFlags,
|
|
}
|
|
|
|
impl UnresolvedReference {
|
|
pub const fn range(&self) -> TextRange {
|
|
self.range
|
|
}
|
|
|
|
pub const fn exceptions(&self) -> Exceptions {
|
|
self.exceptions
|
|
}
|
|
|
|
pub const fn wildcard_import(&self) -> bool {
|
|
self.flags
|
|
.contains(UnresolvedReferenceFlags::WILDCARD_IMPORT)
|
|
}
|
|
|
|
pub fn name<'a>(&self, locator: &Locator<'a>) -> &'a str {
|
|
locator.slice(self.range)
|
|
}
|
|
}
|
|
|
|
bitflags! {
|
|
#[derive(Copy, Clone, Debug)]
|
|
pub struct UnresolvedReferenceFlags: u8 {
|
|
/// The unresolved reference appeared in a context that includes a wildcard import.
|
|
const WILDCARD_IMPORT = 1 << 0;
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub(crate) struct UnresolvedReferences(Vec<UnresolvedReference>);
|
|
|
|
impl UnresolvedReferences {
|
|
/// Pushes a new [`UnresolvedReference`].
|
|
pub(crate) fn push(
|
|
&mut self,
|
|
range: TextRange,
|
|
exceptions: Exceptions,
|
|
flags: UnresolvedReferenceFlags,
|
|
) {
|
|
self.0.push(UnresolvedReference {
|
|
range,
|
|
exceptions,
|
|
flags,
|
|
});
|
|
}
|
|
}
|
|
|
|
impl Deref for UnresolvedReferences {
|
|
type Target = Vec<UnresolvedReference>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|