Rename SemanticModel::is_builtin to SemanticModel::has_builtin_binding (#10991)

This commit is contained in:
Alex Waygood 2024-04-18 11:11:42 +01:00 committed by GitHub
parent 2cc487eb22
commit e09180b1df
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
46 changed files with 74 additions and 67 deletions

View file

@ -1909,7 +1909,7 @@ impl<'a> Checker<'a> {
}
{
let (all_names, all_flags) =
extract_all_names(parent, |name| self.semantic.is_builtin(name));
extract_all_names(parent, |name| self.semantic.has_builtin_binding(name));
if all_flags.intersects(DunderAllFlags::INVALID_OBJECT) {
flags |= BindingFlags::INVALID_ALL_OBJECT;

View file

@ -246,7 +246,7 @@ impl<'a> Importer<'a> {
at: TextSize,
semantic: &SemanticModel,
) -> Result<(Option<Edit>, String), ResolutionError> {
if semantic.is_builtin(symbol) {
if semantic.has_builtin_binding(symbol) {
return Ok((None, symbol.to_string()));
}
let (import_edit, binding) =

View file

@ -464,7 +464,7 @@ fn find_shell_keyword<'a>(
semantic: &SemanticModel,
) -> Option<ShellKeyword<'a>> {
arguments.find_keyword("shell").map(|keyword| ShellKeyword {
truthiness: Truthiness::from_expr(&keyword.value, |id| semantic.is_builtin(id)),
truthiness: Truthiness::from_expr(&keyword.value, |id| semantic.has_builtin_binding(id)),
keyword,
})
}

View file

@ -148,7 +148,7 @@ pub(crate) fn boolean_type_hint_positional_argument(
}
// If `bool` isn't actually a reference to the `bool` built-in, return.
if !checker.semantic().is_builtin("bool") {
if !checker.semantic().has_builtin_binding("bool") {
return;
}

View file

@ -107,7 +107,7 @@ pub(crate) fn unused_loop_control_variable(checker: &mut Checker, stmt_for: &ast
// Avoid fixing any variables that _may_ be used, but undetectably so.
let certainty =
Certainty::from(!helpers::uses_magic_variable_access(&stmt_for.body, |id| {
checker.semantic().is_builtin(id)
checker.semantic().has_builtin_binding(id)
}));
// Attempt to rename the variable by prepending an underscore, but avoid

View file

@ -95,7 +95,7 @@ pub(crate) fn useless_expression(checker: &mut Checker, value: &Expr) {
}
// Ignore statements that have side effects.
if contains_effect(value, |id| checker.semantic().is_builtin(id)) {
if contains_effect(value, |id| checker.semantic().has_builtin_binding(id)) {
// Flag attributes as useless expressions, even if they're attached to calls or other
// expressions.
if value.is_attribute_expr() {

View file

@ -60,7 +60,7 @@ fn add_diagnostic(checker: &mut Checker, expr: &Expr) {
Expr::DictComp(_) => "dict",
_ => return,
};
if !checker.semantic().is_builtin(id) {
if !checker.semantic().has_builtin_binding(id) {
return;
}
let mut diagnostic = Diagnostic::new(

View file

@ -71,7 +71,7 @@ pub(crate) fn unnecessary_generator_list(checker: &mut Checker, call: &ast::Expr
) else {
return;
};
if !checker.semantic().is_builtin("list") {
if !checker.semantic().has_builtin_binding("list") {
return;
}

View file

@ -72,7 +72,7 @@ pub(crate) fn unnecessary_generator_set(checker: &mut Checker, call: &ast::ExprC
) else {
return;
};
if !checker.semantic().is_builtin("set") {
if !checker.semantic().has_builtin_binding("set") {
return;
}

View file

@ -53,7 +53,7 @@ pub(crate) fn unnecessary_list_call(
let Some(argument) = helpers::first_argument_with_matching_function("list", func, args) else {
return;
};
if !checker.semantic().is_builtin("list") {
if !checker.semantic().has_builtin_binding("list") {
return;
}
if !argument.is_list_comp_expr() {

View file

@ -56,7 +56,7 @@ pub(crate) fn unnecessary_list_comprehension_dict(
else {
return;
};
if !checker.semantic().is_builtin("dict") {
if !checker.semantic().has_builtin_binding("dict") {
return;
}
let Expr::ListComp(ast::ExprListComp { elt, .. }) = argument else {

View file

@ -52,7 +52,7 @@ pub(crate) fn unnecessary_list_comprehension_set(checker: &mut Checker, call: &a
) else {
return;
};
if !checker.semantic().is_builtin("set") {
if !checker.semantic().has_builtin_binding("set") {
return;
}
if argument.is_list_comp_expr() {

View file

@ -63,7 +63,7 @@ pub(crate) fn unnecessary_literal_dict(
else {
return;
};
if !checker.semantic().is_builtin("dict") {
if !checker.semantic().has_builtin_binding("dict") {
return;
}
let (kind, elts) = match argument {

View file

@ -60,7 +60,7 @@ pub(crate) fn unnecessary_literal_set(checker: &mut Checker, call: &ast::ExprCal
) else {
return;
};
if !checker.semantic().is_builtin("set") {
if !checker.semantic().has_builtin_binding("set") {
return;
}
let kind = match argument {

View file

@ -75,7 +75,7 @@ pub(crate) fn unnecessary_literal_within_dict_call(checker: &mut Checker, call:
else {
return;
};
if !checker.semantic().is_builtin("dict") {
if !checker.semantic().has_builtin_binding("dict") {
return;
}
let argument_kind = match argument {

View file

@ -77,7 +77,7 @@ pub(crate) fn unnecessary_literal_within_list_call(checker: &mut Checker, call:
else {
return;
};
if !checker.semantic().is_builtin("list") {
if !checker.semantic().has_builtin_binding("list") {
return;
}
let argument_kind = match argument {

View file

@ -80,7 +80,7 @@ pub(crate) fn unnecessary_literal_within_tuple_call(checker: &mut Checker, call:
) else {
return;
};
if !checker.semantic().is_builtin("tuple") {
if !checker.semantic().has_builtin_binding("tuple") {
return;
}
let argument_kind = match argument {

View file

@ -85,7 +85,8 @@ fn exc_info_arg_is_falsey(call: &ExprCall, checker: &mut Checker) -> bool {
.find_keyword("exc_info")
.map(|keyword| &keyword.value)
.is_some_and(|value| {
let truthiness = Truthiness::from_expr(value, |id| checker.semantic().is_builtin(id));
let truthiness =
Truthiness::from_expr(value, |id| checker.semantic().has_builtin_binding(id));
matches!(truthiness, Truthiness::False | Truthiness::Falsey)
})
}

View file

@ -71,7 +71,7 @@ pub(crate) fn invalid_get_logger_argument(checker: &mut Checker, call: &ast::Exp
return;
}
if !checker.semantic().is_builtin(id) {
if !checker.semantic().has_builtin_binding(id) {
return;
}
@ -84,7 +84,7 @@ pub(crate) fn invalid_get_logger_argument(checker: &mut Checker, call: &ast::Exp
}
let mut diagnostic = Diagnostic::new(InvalidGetLoggerArgument, expr.range());
if checker.semantic().is_builtin("__name__") {
if checker.semantic().has_builtin_binding("__name__") {
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
"__name__".to_string(),
expr.range(),

View file

@ -97,7 +97,7 @@ pub(crate) fn unnecessary_type_union<'a>(checker: &mut Checker, union: &'a Expr)
union.range(),
);
if semantic.is_builtin("type") {
if semantic.has_builtin_binding("type") {
let content = if let Some(subscript) = subscript {
let types = &Expr::Subscript(ast::ExprSubscript {
value: Box::new(Expr::Name(ast::ExprName {

View file

@ -488,7 +488,7 @@ fn to_pytest_raises_args<'a>(
/// PT015
pub(crate) fn assert_falsy(checker: &mut Checker, stmt: &Stmt, test: &Expr) {
let truthiness = Truthiness::from_expr(test, |id| checker.semantic().is_builtin(id));
let truthiness = Truthiness::from_expr(test, |id| checker.semantic().has_builtin_binding(id));
if matches!(truthiness, Truthiness::False | Truthiness::Falsey) {
checker
.diagnostics

View file

@ -385,7 +385,7 @@ pub(crate) fn duplicate_isinstance_call(checker: &mut Checker, expr: &Expr) {
},
expr.range(),
);
if !contains_effect(target, |id| checker.semantic().is_builtin(id)) {
if !contains_effect(target, |id| checker.semantic().has_builtin_binding(id)) {
// Grab the types used in each duplicate `isinstance` call (e.g., `int` and `str`
// in `isinstance(obj, int) or isinstance(obj, str)`).
let types: Vec<&Expr> = indices
@ -520,7 +520,7 @@ pub(crate) fn compare_with_tuple(checker: &mut Checker, expr: &Expr) {
// Avoid rewriting (e.g.) `a == "foo" or a == f()`.
if comparators
.iter()
.any(|expr| contains_effect(expr, |id| checker.semantic().is_builtin(id)))
.any(|expr| contains_effect(expr, |id| checker.semantic().has_builtin_binding(id)))
{
continue;
}
@ -614,7 +614,7 @@ pub(crate) fn expr_and_not_expr(checker: &mut Checker, expr: &Expr) {
return;
}
if contains_effect(expr, |id| checker.semantic().is_builtin(id)) {
if contains_effect(expr, |id| checker.semantic().has_builtin_binding(id)) {
return;
}
@ -671,7 +671,7 @@ pub(crate) fn expr_or_not_expr(checker: &mut Checker, expr: &Expr) {
return;
}
if contains_effect(expr, |id| checker.semantic().is_builtin(id)) {
if contains_effect(expr, |id| checker.semantic().has_builtin_binding(id)) {
return;
}
@ -748,14 +748,15 @@ fn is_short_circuit(
for (index, (value, next_value)) in values.iter().tuple_windows().enumerate() {
// Keep track of the location of the furthest-right, truthy or falsey expression.
let value_truthiness = Truthiness::from_expr(value, |id| checker.semantic().is_builtin(id));
let value_truthiness =
Truthiness::from_expr(value, |id| checker.semantic().has_builtin_binding(id));
let next_value_truthiness =
Truthiness::from_expr(next_value, |id| checker.semantic().is_builtin(id));
Truthiness::from_expr(next_value, |id| checker.semantic().has_builtin_binding(id));
// Keep track of the location of the furthest-right, non-effectful expression.
if value_truthiness.is_unknown()
&& (!checker.semantic().in_boolean_test()
|| contains_effect(value, |id| checker.semantic().is_builtin(id)))
|| contains_effect(value, |id| checker.semantic().has_builtin_binding(id)))
{
furthest = next_value;
continue;

View file

@ -172,7 +172,7 @@ pub(crate) fn if_expr_with_true_false(
.to_string(),
expr.range(),
)));
} else if checker.semantic().is_builtin("bool") {
} else if checker.semantic().has_builtin_binding("bool") {
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
checker.generator().expr(
&ast::ExprCall {

View file

@ -270,7 +270,7 @@ pub(crate) fn double_negation(checker: &mut Checker, expr: &Expr, op: UnaryOp, o
checker.locator().slice(operand.as_ref()).to_string(),
expr.range(),
)));
} else if checker.semantic().is_builtin("bool") {
} else if checker.semantic().has_builtin_binding("bool") {
let node = ast::ExprName {
id: "bool".into(),
ctx: ExprContext::Load,

View file

@ -161,7 +161,9 @@ pub(crate) fn if_else_block_instead_of_dict_get(checker: &mut Checker, stmt_if:
}
// Check that the default value is not "complex".
if contains_effect(default_value, |id| checker.semantic().is_builtin(id)) {
if contains_effect(default_value, |id| {
checker.semantic().has_builtin_binding(id)
}) {
return;
}
@ -261,7 +263,9 @@ pub(crate) fn if_exp_instead_of_dict_get(
}
// Check that the default value is not "complex".
if contains_effect(default_value, |id| checker.semantic().is_builtin(id)) {
if contains_effect(default_value, |id| {
checker.semantic().has_builtin_binding(id)
}) {
return;
}

View file

@ -77,10 +77,9 @@ pub(crate) fn if_else_block_instead_of_dict_lookup(checker: &mut Checker, stmt_i
return;
};
if value
.as_ref()
.is_some_and(|value| contains_effect(value, |id| checker.semantic().is_builtin(id)))
{
if value.as_ref().is_some_and(|value| {
contains_effect(value, |id| checker.semantic().has_builtin_binding(id))
}) {
return;
}
@ -112,7 +111,7 @@ pub(crate) fn if_else_block_instead_of_dict_lookup(checker: &mut Checker, stmt_i
return;
};
if value.as_ref().is_some_and(|value| {
contains_effect(value, |id| checker.semantic().is_builtin(id))
contains_effect(value, |id| checker.semantic().has_builtin_binding(id))
}) {
return;
};
@ -138,7 +137,7 @@ pub(crate) fn if_else_block_instead_of_dict_lookup(checker: &mut Checker, stmt_i
};
if value.as_ref().is_some_and(|value| {
contains_effect(value, |id| checker.semantic().is_builtin(id))
contains_effect(value, |id| checker.semantic().has_builtin_binding(id))
}) {
return;
};

View file

@ -207,7 +207,7 @@ pub(crate) fn needless_bool(checker: &mut Checker, stmt: &Stmt) {
// If the condition is a comparison, we can replace it with the condition, since we
// know it's a boolean.
Some(if_test.clone())
} else if checker.semantic().is_builtin("bool") {
} else if checker.semantic().has_builtin_binding("bool") {
// Otherwise, we need to wrap the condition in a call to `bool`.
let func_node = ast::ExprName {
id: "bool".into(),

View file

@ -113,7 +113,7 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt) {
},
TextRange::new(stmt.start(), terminal.stmt.end()),
);
if checker.semantic().is_builtin("any") {
if checker.semantic().has_builtin_binding("any") {
diagnostic.set_fix(Fix::unsafe_edit(Edit::replacement(
contents,
stmt.start(),
@ -199,7 +199,7 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt) {
},
TextRange::new(stmt.start(), terminal.stmt.end()),
);
if checker.semantic().is_builtin("all") {
if checker.semantic().has_builtin_binding("all") {
diagnostic.set_fix(Fix::unsafe_edit(Edit::replacement(
contents,
stmt.start(),

View file

@ -134,7 +134,7 @@ fn deprecated_type_comparison(checker: &mut Checker, compare: &ast::ExprCompare)
| "dict"
| "set"
| "memoryview"
) && semantic.is_builtin(id)
) && semantic.has_builtin_binding(id)
{
checker.diagnostics.push(Diagnostic::new(
TypeComparison {
@ -289,7 +289,7 @@ fn is_type(expr: &Expr, semantic: &SemanticModel) -> bool {
| "ValueError"
| "Warning"
| "ZeroDivisionError"
) && semantic.is_builtin(id)
) && semantic.has_builtin_binding(id)
}
_ => false,
}

View file

@ -219,7 +219,7 @@ fn remove_unused_variable(binding: &Binding, checker: &Checker) -> Option<Fix> {
{
if target.is_name_expr() {
return if targets.len() > 1
|| contains_effect(value, |id| checker.semantic().is_builtin(id))
|| contains_effect(value, |id| checker.semantic().has_builtin_binding(id))
{
// If the expression is complex (`x = foo()`), remove the assignment,
// but preserve the right-hand side.
@ -265,7 +265,7 @@ fn remove_unused_variable(binding: &Binding, checker: &Checker) -> Option<Fix> {
}) = statement
{
if target.is_name_expr() {
return if contains_effect(value, |id| checker.semantic().is_builtin(id)) {
return if contains_effect(value, |id| checker.semantic().has_builtin_binding(id)) {
// If the expression is complex (`x = foo()`), remove the assignment,
// but preserve the right-hand side.
let start = statement.start();

View file

@ -177,7 +177,7 @@ pub(crate) fn if_stmt_min_max(checker: &mut Checker, stmt_if: &ast::StmtIf) {
stmt_if.range(),
);
if checker.semantic().is_builtin(min_max.as_str()) {
if checker.semantic().has_builtin_binding(min_max.as_str()) {
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
replacement,
stmt_if.range(),

View file

@ -116,7 +116,7 @@ fn get_undecorated_methods(checker: &mut Checker, class_stmt: &Stmt, method_type
}) = value.as_ref()
{
if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
if id == method_name && checker.semantic().is_builtin(method_name) {
if id == method_name && checker.semantic().has_builtin_binding(method_name) {
if arguments.args.len() != 1 {
continue;
}
@ -159,7 +159,7 @@ fn get_undecorated_methods(checker: &mut Checker, class_stmt: &Stmt, method_type
// if we find the decorator we're looking for, skip
if decorator_list.iter().any(|decorator| {
if let Expr::Name(ast::ExprName { id, .. }) = &decorator.expression {
if id == method_name && checker.semantic().is_builtin(method_name) {
if id == method_name && checker.semantic().has_builtin_binding(method_name) {
return true;
}
}

View file

@ -72,7 +72,7 @@ pub(crate) fn super_without_brackets(checker: &mut Checker, func: &Expr) {
return;
}
if !checker.semantic().is_builtin(id.as_str()) {
if !checker.semantic().has_builtin_binding(id.as_str()) {
return;
}

View file

@ -93,7 +93,7 @@ pub(crate) fn unnecessary_dunder_call(checker: &mut Checker, call: &ast::ExprCal
// Ignore dunder methods used on `super`.
if let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() {
if checker.semantic().is_builtin("super") {
if checker.semantic().has_builtin_binding("super") {
if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
if id == "super" {
return;
@ -113,7 +113,7 @@ pub(crate) fn unnecessary_dunder_call(checker: &mut Checker, call: &ast::ExprCal
if let Some(dunder) = DunderReplacement::from_method(attr) {
match (&*call.arguments.args, dunder) {
([], DunderReplacement::Builtin(replacement, message)) => {
if !checker.semantic().is_builtin(replacement) {
if !checker.semantic().has_builtin_binding(replacement) {
return;
}
fixed = Some(format!(

View file

@ -95,7 +95,7 @@ fn atom_diagnostic(checker: &mut Checker, target: &Expr) {
fn tuple_diagnostic(checker: &mut Checker, tuple: &ast::ExprTuple, aliases: &[&Expr]) {
let mut diagnostic = Diagnostic::new(OSErrorAlias { name: None }, tuple.range());
let semantic = checker.semantic();
if semantic.is_builtin("OSError") {
if semantic.has_builtin_binding("OSError") {
// Filter out any `OSErrors` aliases.
let mut remaining: Vec<Expr> = tuple
.elts

View file

@ -107,7 +107,7 @@ fn atom_diagnostic(checker: &mut Checker, target: &Expr) {
fn tuple_diagnostic(checker: &mut Checker, tuple: &ast::ExprTuple, aliases: &[&Expr]) {
let mut diagnostic = Diagnostic::new(TimeoutErrorAlias { name: None }, tuple.range());
let semantic = checker.semantic();
if semantic.is_builtin("TimeoutError") {
if semantic.has_builtin_binding("TimeoutError") {
// Filter out any `TimeoutErrors` aliases.
let mut remaining: Vec<Expr> = tuple
.elts

View file

@ -67,7 +67,7 @@ pub(crate) fn type_of_primitive(checker: &mut Checker, expr: &Expr, func: &Expr,
}
let mut diagnostic = Diagnostic::new(TypeOfPrimitive { primitive }, expr.range());
let builtin = primitive.builtin();
if semantic.is_builtin(&builtin) {
if semantic.has_builtin_binding(&builtin) {
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
pad(primitive.builtin(), expr.range(), checker.locator()),
expr.range(),

View file

@ -89,7 +89,7 @@ pub(crate) fn check_and_remove_from_set(checker: &mut Checker, if_stmt: &ast::St
// `element` in the check should be the same as `element` in the body
|| !compare(&check_element.into(), &remove_element.into())
// `element` shouldn't have a side effect, otherwise we might change the semantics of the program.
|| contains_effect(check_element, |id| checker.semantic().is_builtin(id))
|| contains_effect(check_element, |id| checker.semantic().has_builtin_binding(id))
{
return;
}

View file

@ -90,7 +90,7 @@ pub(crate) fn if_exp_instead_of_or_operator(checker: &mut Checker, if_expr: &ast
),
if_expr.range(),
),
if contains_effect(body, |id| checker.semantic().is_builtin(id)) {
if contains_effect(body, |id| checker.semantic().has_builtin_binding(id)) {
Applicability::Unsafe
} else {
Applicability::Safe

View file

@ -139,7 +139,7 @@ pub(crate) fn if_expr_min_max(checker: &mut Checker, if_exp: &ast::ExprIf) {
if_exp.range(),
);
if checker.semantic().is_builtin(min_max.as_str()) {
if checker.semantic().has_builtin_binding(min_max.as_str()) {
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
replacement,
if_exp.range(),

View file

@ -275,7 +275,7 @@ fn try_construct_call(
) -> Result<String> {
// We can only do our fix if `builtin` identifier is still bound to
// the built-in type.
if !checker.semantic().is_builtin(builtin) {
if !checker.semantic().has_builtin_binding(builtin) {
bail!("Can't use built-in `{builtin}` constructor")
}

View file

@ -173,7 +173,9 @@ pub(crate) fn unnecessary_enumerate(checker: &mut Checker, stmt_for: &ast::StmtF
},
func.range(),
);
if checker.semantic().is_builtin("range") && checker.semantic().is_builtin("len") {
if checker.semantic().has_builtin_binding("range")
&& checker.semantic().has_builtin_binding("len")
{
// If the `start` argument is set to something other than the `range` default,
// there's no clear fix.
let start = arguments.find_argument("start", 1);

View file

@ -108,7 +108,7 @@ pub(crate) fn unnecessary_dict_comprehension_for_iterable(
dict_comp.range(),
);
if checker.semantic().is_builtin("dict") {
if checker.semantic().has_builtin_binding("dict") {
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
checker
.generator()

View file

@ -96,8 +96,8 @@ pub(crate) fn unnecessary_key_check(checker: &mut Checker, expr: &Expr) {
return;
}
if contains_effect(obj_left, |id| checker.semantic().is_builtin(id))
|| contains_effect(key_left, |id| checker.semantic().is_builtin(id))
if contains_effect(obj_left, |id| checker.semantic().has_builtin_binding(id))
|| contains_effect(key_left, |id| checker.semantic().has_builtin_binding(id))
{
return;
}

View file

@ -67,7 +67,7 @@ pub(crate) fn try_consider_else(
if let Some(stmt) = body.last() {
if let Stmt::Return(ast::StmtReturn { value, range: _ }) = stmt {
if let Some(value) = value {
if contains_effect(value, |id| checker.semantic().is_builtin(id)) {
if contains_effect(value, |id| checker.semantic().has_builtin_binding(id)) {
return;
}
}

View file

@ -255,7 +255,7 @@ impl<'a> SemanticModel<'a> {
/// Note that a "builtin binding" does *not* include explicit lookups via the `builtins`
/// module, e.g. `import builtins; builtins.open`. It *only* includes the bindings
/// that are pre-populated in Python's global scope before any imports have taken place.
pub fn is_builtin(&self, member: &str) -> bool {
pub fn has_builtin_binding(&self, member: &str) -> bool {
self.lookup_symbol(member)
.map(|binding_id| &self.bindings[binding_id])
.is_some_and(|binding| binding.kind.is_builtin())
@ -274,7 +274,7 @@ impl<'a> SemanticModel<'a> {
// Fast path: we only need to worry about name expressions
if !self.seen_module(Modules::BUILTINS) {
let name = &expr.as_name_expr()?.id;
return if self.is_builtin(name) {
return if self.has_builtin_binding(name) {
Some(name)
} else {
None
@ -299,7 +299,7 @@ impl<'a> SemanticModel<'a> {
let Expr::Name(ast::ExprName { id, .. }) = expr else {
return false;
};
return id == symbol && self.is_builtin(symbol);
return id == symbol && self.has_builtin_binding(symbol);
}
// slow path: we need to consider attribute accesses and aliased imports