1use std::ops::Range;
2
3use ruff_db::{PythonFile, files::File, parsed::ParsedModuleRef};
4use ruff_index::newtype_index;
5use ruff_python_ast::{self as ast, NodeIndex};
6
7use crate::{
8 Db, Program, ProgramFile, SemanticIndex, ast_node_ref::AstNodeRef, definition::Definition,
9 node_key::NodeKey, semantic_index,
10};
11
12#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
14pub struct ScopeId<'db> {
15 #[returns(copy)]
16 pub program_file: ProgramFile<'db>,
17
18 #[returns(copy)]
19 pub file_scope_id: FileScopeId,
20}
21
22impl get_size2::GetSize for ScopeId<'_> {}
24
25impl<'db> ScopeId<'db> {
26 pub fn file(self, db: &dyn Db) -> File {
27 self.program_file(db).file(db)
28 }
29
30 pub fn python_file(self, db: &'db dyn Db) -> PythonFile<'db> {
31 self.program_file(db).python_file(db)
32 }
33
34 pub fn program(self, db: &'db dyn Db) -> Program<'db> {
35 self.program_file(db).program(db)
36 }
37
38 pub fn is_annotation(self, db: &'db dyn Db) -> bool {
39 self.node(db).scope_kind().is_annotation()
40 }
41
42 pub fn node(self, db: &'db dyn Db) -> &'db NodeWithScopeKind {
43 self.scope(db).node()
44 }
45
46 pub fn accepts_type_context(self, db: &'db dyn Db) -> bool {
48 matches!(
49 self.node(db),
50 NodeWithScopeKind::Lambda(_)
51 | NodeWithScopeKind::ListComprehension(_)
52 | NodeWithScopeKind::SetComprehension(_)
53 | NodeWithScopeKind::DictComprehension(_)
54 | NodeWithScopeKind::GeneratorExpression(_)
55 )
56 }
57
58 pub fn scope(self, db: &'db dyn Db) -> &'db Scope {
59 semantic_index(db, self.program_file(db)).scope(self.file_scope_id(db))
60 }
61
62 pub fn class_definition_of_method(self, db: &'db dyn Db) -> Option<Definition<'db>> {
64 semantic_index(db, self.program_file(db)).class_definition_of_method(self.file_scope_id(db))
65 }
66
67 pub fn is_method_scope(self, db: &'db dyn Db) -> bool {
68 self.class_definition_of_method(db).is_some()
69 }
70
71 pub fn name<'ast>(self, db: &'db dyn Db, module: &'ast ParsedModuleRef) -> &'ast str {
72 match self.node(db) {
73 NodeWithScopeKind::Module => "<module>",
74 NodeWithScopeKind::Class(class) | NodeWithScopeKind::ClassTypeParameters(class) => {
75 class.node(module).name.as_str()
76 }
77 NodeWithScopeKind::Function(function)
78 | NodeWithScopeKind::FunctionTypeParameters(function) => {
79 function.node(module).name.as_str()
80 }
81 NodeWithScopeKind::TypeAlias(type_alias)
82 | NodeWithScopeKind::TypeAliasTypeParameters(type_alias) => type_alias
83 .node(module)
84 .name
85 .as_name_expr()
86 .map(|name| name.id.as_str())
87 .unwrap_or("<type alias>"),
88 NodeWithScopeKind::Lambda(_) => "<lambda>",
89 NodeWithScopeKind::ListComprehension(_) => "<listcomp>",
90 NodeWithScopeKind::SetComprehension(_) => "<setcomp>",
91 NodeWithScopeKind::DictComprehension(_) => "<dictcomp>",
92 NodeWithScopeKind::GeneratorExpression(_) => "<generator>",
93 }
94 }
95}
96
97#[newtype_index]
99#[derive(Ord, PartialOrd, get_size2::GetSize)]
100pub struct FileScopeId;
101
102impl FileScopeId {
103 pub fn global() -> Self {
105 FileScopeId::from_u32(0)
106 }
107
108 pub fn is_global(self) -> bool {
109 self == FileScopeId::global()
110 }
111
112 pub fn to_scope_id<'db>(self, db: &'db dyn Db, file: ProgramFile<'db>) -> ScopeId<'db> {
113 let index = semantic_index(db, file);
114 index.scope_ids_by_scope[self]
115 }
116
117 pub fn is_generator_function(self, index: &SemanticIndex) -> bool {
118 index.generator_functions.contains(&self)
119 }
120
121 pub fn is_async_comprehension(self, index: &SemanticIndex) -> bool {
122 index.async_comprehensions.contains(&self)
123 }
124}
125
126#[derive(Debug, get_size2::GetSize)]
127pub struct Scope {
128 parent: Option<FileScopeId>,
130
131 node: NodeWithScopeKind,
133
134 descendants: Range<FileScopeId>,
136}
137
138impl Scope {
139 pub(super) fn new(
140 parent: Option<FileScopeId>,
141 node: NodeWithScopeKind,
142 descendants: Range<FileScopeId>,
143 ) -> Self {
144 Scope {
145 parent,
146 node,
147 descendants,
148 }
149 }
150
151 pub fn parent(&self) -> Option<FileScopeId> {
152 self.parent
153 }
154
155 pub fn node(&self) -> &NodeWithScopeKind {
156 &self.node
157 }
158
159 pub fn kind(&self) -> ScopeKind {
160 self.node().scope_kind()
161 }
162
163 pub fn visibility(&self) -> ScopeVisibility {
164 self.kind().visibility()
165 }
166
167 pub(crate) fn descendants(&self) -> Range<FileScopeId> {
168 self.descendants.clone()
169 }
170
171 pub(super) fn extend_descendants(&mut self, children_end: FileScopeId) {
172 self.descendants = self.descendants.start..children_end;
173 }
174
175 pub fn is_eager(&self) -> bool {
176 self.kind().is_eager()
177 }
178}
179
180#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, get_size2::GetSize)]
181pub enum ScopeVisibility {
182 Private,
184 Public,
186}
187
188impl ScopeVisibility {
189 pub(crate) const fn is_public(self) -> bool {
190 matches!(self, ScopeVisibility::Public)
191 }
192
193 pub const fn is_private(self) -> bool {
194 matches!(self, ScopeVisibility::Private)
195 }
196}
197
198#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, get_size2::GetSize)]
199pub(crate) enum ScopeLaziness {
200 Lazy,
202 Eager,
204}
205
206impl ScopeLaziness {
207 pub(crate) const fn is_eager(self) -> bool {
208 matches!(self, ScopeLaziness::Eager)
209 }
210
211 pub(crate) const fn is_lazy(self) -> bool {
212 matches!(self, ScopeLaziness::Lazy)
213 }
214}
215
216#[derive(Copy, Clone, Debug, PartialEq, Eq)]
217pub enum ScopeKind {
218 Module,
219 TypeParams,
220 Class,
221 Function,
222 Lambda,
223 Comprehension,
224 TypeAlias,
225}
226
227impl ScopeKind {
228 pub(crate) const fn is_eager(self) -> bool {
229 self.laziness().is_eager()
230 }
231
232 pub(crate) const fn laziness(self) -> ScopeLaziness {
233 match self {
234 ScopeKind::Module
235 | ScopeKind::Class
236 | ScopeKind::Comprehension
237 | ScopeKind::TypeParams => ScopeLaziness::Eager,
238 ScopeKind::Function | ScopeKind::Lambda | ScopeKind::TypeAlias => ScopeLaziness::Lazy,
239 }
240 }
241
242 const fn visibility(self) -> ScopeVisibility {
243 match self {
244 ScopeKind::Module | ScopeKind::Class => ScopeVisibility::Public,
245 ScopeKind::TypeParams
246 | ScopeKind::TypeAlias
247 | ScopeKind::Function
248 | ScopeKind::Lambda
249 | ScopeKind::Comprehension => ScopeVisibility::Private,
250 }
251 }
252
253 pub const fn is_function_like(self) -> bool {
254 matches!(
257 self,
258 ScopeKind::TypeParams
259 | ScopeKind::Function
260 | ScopeKind::Lambda
261 | ScopeKind::TypeAlias
262 | ScopeKind::Comprehension
263 )
264 }
265
266 pub const fn is_class(self) -> bool {
267 matches!(self, ScopeKind::Class)
268 }
269
270 pub const fn is_module(self) -> bool {
271 matches!(self, ScopeKind::Module)
272 }
273
274 pub(crate) const fn is_annotation(self) -> bool {
275 matches!(self, ScopeKind::TypeParams | ScopeKind::TypeAlias)
276 }
277
278 pub const fn is_non_lambda_function(self) -> bool {
279 matches!(self, ScopeKind::Function)
280 }
281}
282
283#[derive(Copy, Clone, Debug)]
285pub enum NodeWithScopeRef<'a> {
286 Module,
287 Class(&'a ast::StmtClassDef),
288 Function(&'a ast::StmtFunctionDef),
289 Lambda(&'a ast::ExprLambda),
290 FunctionTypeParameters(&'a ast::StmtFunctionDef),
291 ClassTypeParameters(&'a ast::StmtClassDef),
292 TypeAlias(&'a ast::StmtTypeAlias),
293 TypeAliasTypeParameters(&'a ast::StmtTypeAlias),
294 ListComprehension(&'a ast::ExprListComp),
295 SetComprehension(&'a ast::ExprSetComp),
296 DictComprehension(&'a ast::ExprDictComp),
297 GeneratorExpression(&'a ast::ExprGenerator),
298}
299
300impl NodeWithScopeRef<'_> {
301 pub(super) fn to_kind(self, module: &ParsedModuleRef) -> NodeWithScopeKind {
305 match self {
306 NodeWithScopeRef::Module => NodeWithScopeKind::Module,
307 NodeWithScopeRef::Class(class) => {
308 NodeWithScopeKind::Class(AstNodeRef::new(module, class))
309 }
310 NodeWithScopeRef::Function(function) => {
311 NodeWithScopeKind::Function(AstNodeRef::new(module, function))
312 }
313 NodeWithScopeRef::TypeAlias(type_alias) => {
314 NodeWithScopeKind::TypeAlias(AstNodeRef::new(module, type_alias))
315 }
316 NodeWithScopeRef::TypeAliasTypeParameters(type_alias) => {
317 NodeWithScopeKind::TypeAliasTypeParameters(AstNodeRef::new(module, type_alias))
318 }
319 NodeWithScopeRef::Lambda(lambda) => {
320 NodeWithScopeKind::Lambda(AstNodeRef::new(module, lambda))
321 }
322 NodeWithScopeRef::FunctionTypeParameters(function) => {
323 NodeWithScopeKind::FunctionTypeParameters(AstNodeRef::new(module, function))
324 }
325 NodeWithScopeRef::ClassTypeParameters(class) => {
326 NodeWithScopeKind::ClassTypeParameters(AstNodeRef::new(module, class))
327 }
328 NodeWithScopeRef::ListComprehension(comprehension) => {
329 NodeWithScopeKind::ListComprehension(AstNodeRef::new(module, comprehension))
330 }
331 NodeWithScopeRef::SetComprehension(comprehension) => {
332 NodeWithScopeKind::SetComprehension(AstNodeRef::new(module, comprehension))
333 }
334 NodeWithScopeRef::DictComprehension(comprehension) => {
335 NodeWithScopeKind::DictComprehension(AstNodeRef::new(module, comprehension))
336 }
337 NodeWithScopeRef::GeneratorExpression(generator) => {
338 NodeWithScopeKind::GeneratorExpression(AstNodeRef::new(module, generator))
339 }
340 }
341 }
342
343 pub(crate) fn node_key(self) -> NodeWithScopeKey {
344 match self {
345 NodeWithScopeRef::Module => NodeWithScopeKey::Module,
346 NodeWithScopeRef::Class(class) => NodeWithScopeKey::Class(NodeKey::from_node(class)),
347 NodeWithScopeRef::Function(function) => {
348 NodeWithScopeKey::Function(NodeKey::from_node(function))
349 }
350 NodeWithScopeRef::Lambda(lambda) => {
351 NodeWithScopeKey::Lambda(NodeKey::from_node(lambda))
352 }
353 NodeWithScopeRef::FunctionTypeParameters(function) => {
354 NodeWithScopeKey::FunctionTypeParameters(NodeKey::from_node(function))
355 }
356 NodeWithScopeRef::ClassTypeParameters(class) => {
357 NodeWithScopeKey::ClassTypeParameters(NodeKey::from_node(class))
358 }
359 NodeWithScopeRef::TypeAlias(type_alias) => {
360 NodeWithScopeKey::TypeAlias(NodeKey::from_node(type_alias))
361 }
362 NodeWithScopeRef::TypeAliasTypeParameters(type_alias) => {
363 NodeWithScopeKey::TypeAliasTypeParameters(NodeKey::from_node(type_alias))
364 }
365 NodeWithScopeRef::ListComprehension(comprehension) => {
366 NodeWithScopeKey::ListComprehension(NodeKey::from_node(comprehension))
367 }
368 NodeWithScopeRef::SetComprehension(comprehension) => {
369 NodeWithScopeKey::SetComprehension(NodeKey::from_node(comprehension))
370 }
371 NodeWithScopeRef::DictComprehension(comprehension) => {
372 NodeWithScopeKey::DictComprehension(NodeKey::from_node(comprehension))
373 }
374 NodeWithScopeRef::GeneratorExpression(generator) => {
375 NodeWithScopeKey::GeneratorExpression(NodeKey::from_node(generator))
376 }
377 }
378 }
379}
380
381#[derive(Clone, Debug, get_size2::GetSize)]
383pub enum NodeWithScopeKind {
384 Module,
385 Class(AstNodeRef<ast::StmtClassDef>),
386 ClassTypeParameters(AstNodeRef<ast::StmtClassDef>),
387 Function(AstNodeRef<ast::StmtFunctionDef>),
388 FunctionTypeParameters(AstNodeRef<ast::StmtFunctionDef>),
389 TypeAliasTypeParameters(AstNodeRef<ast::StmtTypeAlias>),
390 TypeAlias(AstNodeRef<ast::StmtTypeAlias>),
391 Lambda(AstNodeRef<ast::ExprLambda>),
392 ListComprehension(AstNodeRef<ast::ExprListComp>),
393 SetComprehension(AstNodeRef<ast::ExprSetComp>),
394 DictComprehension(AstNodeRef<ast::ExprDictComp>),
395 GeneratorExpression(AstNodeRef<ast::ExprGenerator>),
396}
397
398impl NodeWithScopeKind {
399 pub const fn scope_kind(&self) -> ScopeKind {
400 match self {
401 Self::Module => ScopeKind::Module,
402 Self::Class(_) => ScopeKind::Class,
403 Self::Function(_) => ScopeKind::Function,
404 Self::Lambda(_) => ScopeKind::Lambda,
405 Self::FunctionTypeParameters(_)
406 | Self::ClassTypeParameters(_)
407 | Self::TypeAliasTypeParameters(_) => ScopeKind::TypeParams,
408 Self::TypeAlias(_) => ScopeKind::TypeAlias,
409 Self::ListComprehension(_)
410 | Self::SetComprehension(_)
411 | Self::DictComprehension(_)
412 | Self::GeneratorExpression(_) => ScopeKind::Comprehension,
413 }
414 }
415
416 pub fn as_class(&self) -> Option<&AstNodeRef<ast::StmtClassDef>> {
417 match self {
418 Self::Class(class) => Some(class),
419 _ => None,
420 }
421 }
422
423 pub fn expect_class(&self) -> &AstNodeRef<ast::StmtClassDef> {
424 self.as_class().expect("expected class")
425 }
426
427 pub fn as_function(&self) -> Option<&AstNodeRef<ast::StmtFunctionDef>> {
428 match self {
429 Self::Function(function) => Some(function),
430 _ => None,
431 }
432 }
433
434 pub fn expect_function(&self) -> &AstNodeRef<ast::StmtFunctionDef> {
435 self.as_function().expect("expected function")
436 }
437
438 fn as_type_alias(&self) -> Option<&AstNodeRef<ast::StmtTypeAlias>> {
439 match self {
440 Self::TypeAlias(type_alias) => Some(type_alias),
441 _ => None,
442 }
443 }
444
445 pub fn expect_type_alias(&self) -> &AstNodeRef<ast::StmtTypeAlias> {
446 self.as_type_alias().expect("expected type alias")
447 }
448
449 pub fn node_index(&self) -> Option<NodeIndex> {
454 match self {
455 Self::Module => None,
456 Self::Class(class) => Some(class.index()),
457 Self::ClassTypeParameters(class) => Some(class.index()),
458 Self::Function(function) => Some(function.index()),
459 Self::FunctionTypeParameters(function) => Some(function.index()),
460 Self::TypeAlias(type_alias) => Some(type_alias.index()),
461 Self::TypeAliasTypeParameters(type_alias) => Some(type_alias.index()),
462 Self::Lambda(lambda) => Some(lambda.index()),
463 Self::ListComprehension(comp) => Some(comp.index()),
464 Self::SetComprehension(comp) => Some(comp.index()),
465 Self::DictComprehension(comp) => Some(comp.index()),
466 Self::GeneratorExpression(generator) => Some(generator.index()),
467 }
468 }
469}
470
471#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
472pub enum NodeWithScopeKey {
473 Module,
474 Class(NodeKey),
475 ClassTypeParameters(NodeKey),
476 Function(NodeKey),
477 FunctionTypeParameters(NodeKey),
478 TypeAlias(NodeKey),
479 TypeAliasTypeParameters(NodeKey),
480 Lambda(NodeKey),
481 ListComprehension(NodeKey),
482 SetComprehension(NodeKey),
483 DictComprehension(NodeKey),
484 GeneratorExpression(NodeKey),
485}