Track unresolved references in the semantic model (#5902)

## 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.
This commit is contained in:
Charlie Marsh 2023-07-19 18:19:55 -04:00 committed by GitHub
parent 23cde4d1f5
commit 963f240e46
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 261 additions and 202 deletions

View file

@ -1,13 +1,17 @@
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 Reference {
pub struct ResolvedReference {
/// The scope in which the reference is defined.
scope_id: ScopeId,
/// The range of the reference in the source code.
@ -16,7 +20,7 @@ pub struct Reference {
context: ExecutionContext,
}
impl Reference {
impl ResolvedReference {
pub const fn scope_id(&self) -> ScopeId {
self.scope_id
}
@ -32,21 +36,21 @@ impl Reference {
/// Id uniquely identifying a read reference in a program.
#[newtype_index]
pub struct ReferenceId;
pub struct ResolvedReferenceId;
/// The references of a program indexed by [`ReferenceId`].
/// The references of a program indexed by [`ResolvedReferenceId`].
#[derive(Debug, Default)]
pub(crate) struct References(IndexVec<ReferenceId, Reference>);
pub(crate) struct ResolvedReferences(IndexVec<ResolvedReferenceId, ResolvedReference>);
impl References {
/// Pushes a new [`Reference`] and returns its [`ReferenceId`].
impl ResolvedReferences {
/// Pushes a new [`ResolvedReference`] and returns its [`ResolvedReferenceId`].
pub(crate) fn push(
&mut self,
scope_id: ScopeId,
range: TextRange,
context: ExecutionContext,
) -> ReferenceId {
self.0.push(Reference {
) -> ResolvedReferenceId {
self.0.push(ResolvedReference {
scope_id,
range,
context,
@ -54,8 +58,73 @@ impl References {
}
}
impl Deref for References {
type Target = IndexSlice<ReferenceId, Reference>;
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