Skip to content

Conversation

@lewis6991
Copy link
Collaborator

When setting a member owner for an index expression with a name prefix
(e.g. table.unpack), prefer the declared global type cache over the
inferred prefix type. This keeps stdlib members attached to their type
defs and avoids losing them after casts (like ---@cast table unknown).

Skip the override when the name is shadowed by a local.

When setting a member owner for an index expression with a name prefix
(e.g. `table.unpack`), prefer the declared global type cache over the
inferred prefix type. This keeps stdlib members attached to their type
defs and avoids losing them after casts (like `---@cast table unknown`).

Skip the override when the name is shadowed by a local.
@gemini-code-assist
Copy link

Summary of Changes

Hello @lewis6991, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the type-checking accuracy for Lua index expressions by adjusting how member ownership is resolved. It ensures that globally declared types are consistently used for name prefixes, thereby preserving type integrity for standard library functions even when type casts are applied, while also correctly handling cases where global names are shadowed by local variables.

Highlights

  • Type Checking Logic Refinement: Modified the set_index_expr_owner function to prioritize declared global type caches when determining the member owner for index expressions with name prefixes (e.g., table.unpack).
  • Preservation of Standard Library Types: This change ensures that standard library members remain correctly attached to their type definitions, preventing type information loss after explicit type casts like ---@cast table unknown.
  • Local Shadowing Consideration: Introduced a check to skip the preference for declared global types if the global name is shadowed by a local declaration, maintaining correct scope resolution.
  • New Test Case: Added a new test test_set_index_expr_owner_prefers_declared_global_type to validate the new behavior, specifically confirming that declared global types are respected even after type casting.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • crates/emmylua_code_analysis/src/compilation/analyzer/lua/stats.rs
    • Implemented logic to prefer declared global types for index expression member ownership when a name prefix is used.
    • Added a check to ensure this preference is skipped if the global name is shadowed by a local declaration.
  • crates/emmylua_code_analysis/src/semantic/type_check/test.rs
    • Imported LuaType for use in tests.
    • Added a new test case test_set_index_expr_owner_prefers_declared_global_type to verify that declared global types are preferred for member ownership, even after type casting.
Activity
  • No human activity (comments, reviews) has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@lewis6991
Copy link
Collaborator Author

Not sure if this is something we care about, buts it's something codex picked up.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a well-reasoned fix to prefer declared global types for index member ownership, which is particularly useful for maintaining type information for standard library members after casting. The logic is sound, and the new test case effectively validates the change. I have one suggestion to refactor a portion of the new logic to improve its readability and make it more idiomatic.

Comment on lines +213 to +239
let mut explicit_type = None;
if let Some(name) = name_expr.get_name_text() {
// Avoid attaching members to stdlib globals when a local shadows the name.
let is_shadowed = analyzer
.db
.get_decl_index()
.get_decl_tree(&file_id)
.and_then(|tree| tree.find_local_decl(&name, name_expr.get_position()))
.map(|decl| decl.is_local() || decl.is_implicit_self())
.unwrap_or(false);
if !is_shadowed
&& let Some(decl_ids) =
analyzer.db.get_global_index().get_global_decl_ids(&name)
{
// Pick the first resolvable global type cache as the owner type.
for decl_id in decl_ids {
if let Some(type_cache) = analyzer
.db
.get_type_index()
.get_type_cache(&(*decl_id).into())
{
explicit_type = Some(type_cache.as_type().clone());
break;
}
}
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This block of code for determining the explicit_type can be refactored to be more concise and idiomatic by using a more functional style with and_then and find_map. This change will reduce nesting, remove the need for a mutable variable, and improve the overall readability of the logic.

                let explicit_type = name_expr.get_name_text().and_then(|name| {
                    // Avoid attaching members to stdlib globals when a local shadows the name.
                    let is_shadowed = analyzer
                        .db
                        .get_decl_index()
                        .get_decl_tree(&file_id)
                        .and_then(|tree| tree.find_local_decl(&name, name_expr.get_position()))
                        .map(|decl| decl.is_local() || decl.is_implicit_self())
                        .unwrap_or(false);

                    if is_shadowed {
                        None
                    } else {
                        analyzer
                            .db
                            .get_global_index()
                            .get_global_decl_ids(&name)
                            .and_then(|decl_ids| {
                                // Pick the first resolvable global type cache as the owner type.
                                decl_ids.iter().find_map(|decl_id| {
                                    analyzer
                                        .db
                                        .get_type_index()
                                        .get_type_cache(&(*decl_id).into())
                                        .map(|type_cache| type_cache.as_type().clone())
                                })
                            })
                    }
                });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant