Skip to content

Index retroactive method visibility changes#695

Open
alexcrocha wants to merge 4 commits intomainfrom
03-25-index_retroactive_method_visibility_changes
Open

Index retroactive method visibility changes#695
alexcrocha wants to merge 4 commits intomainfrom
03-25-index_retroactive_method_visibility_changes

Conversation

@alexcrocha
Copy link
Copy Markdown
Contributor

@alexcrocha alexcrocha commented Mar 26, 2026

One more step towards #89

This PR extends the definition-based visibility path from constants to methods by indexing retroactive private/protected/public calls as MethodVisibilityDefinitions.

Why

Ruby supports retroactive visibility calls like private :foo, but the current codebase only models visibility from flag mode (private followed by later definitions) and scoped definitions (private def foo or protected attr_reader :bar). We already use a definition-based approach for constant visibility, so this applies the same model to methods and preserves those visibility changes in the graph.

This PR is intentionally limited to indexing. A follow-up PR will make MethodVisibilityDefinitions affect the resolved declaration graph.

What

  • add Definition::MethodVisibility and MethodVisibilityDefinition
  • expose the new definition kind through the Rust/C/Ruby definition APIs
  • index local retroactive private/protected/public calls with literal symbol and string targets
  • store method targets using existing member names like foo()
  • treat method visibility definitions as method-like in graph utilities
  • diagnose unsupported dynamic retroactive visibility calls
  • preserve existing behaviour for:
    • flag mode (private)
    • scoped definitions (private def foo)
    • scoped attr helpers (protected attr_reader :bar)

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@alexcrocha alexcrocha force-pushed the 03-25-index_retroactive_method_visibility_changes branch from e968172 to a6cc2ae Compare March 26, 2026 03:43
@alexcrocha alexcrocha self-assigned this Mar 26, 2026
@alexcrocha alexcrocha marked this pull request as ready for review March 26, 2026 03:49
@alexcrocha alexcrocha requested a review from a team as a code owner March 26, 2026 03:49
});

let is_scoped_definition =
message.as_str() == "module_function" || single_arg_is_def || single_arg_is_attr_helper;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It seems that module_function works like the other cases. It accepts retroactive definitions and multiple names. Could we not handle it the same? Does it require any different logic?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I added a new commit to handle retroactive module_function as well. It goes through the same logic with a extra guard to emit a diagnostic when used outside a module

Comment on lines +1936 to +1950
let has_single_arg = args.len() == 1;

let single_arg_is_def =
has_single_arg && matches!(args.iter().next().unwrap(), ruby_prism::Node::DefNode { .. });

let single_arg_is_attr_helper = has_single_arg
&& args.iter().next().unwrap().as_call_node().is_some_and(|call| {
let has_no_explicit_receiver = call.receiver().is_none()
|| call.receiver().as_ref().is_some_and(|r| r.as_self_node().is_some());
has_no_explicit_receiver
&& matches!(
call.name().as_slice(),
b"attr" | b"attr_reader" | b"attr_writer" | b"attr_accessor"
)
});
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There are a lot of checks in this section. Would it make it a bit easier to read if we extracted a helper method?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You'd be scared to see how it looked like before 😱 But I agree, there's still room for improvement

I added a new commit, let me know if you find it better

}

// Nested references still indexed
assert_constant_references_eq!(&context, ["SOME_CONST"]);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: this feels a bit redundant

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Since I folded the old index_retroactive_method_visibility_dynamic_visits_args test into this one as suggested below, I'll keep this and update the comment to make the intent more explicit that we are still indexing the constant reference despite the diagnostic

}

#[test]
fn index_retroactive_method_visibility() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we create a dedicated test mod for these tests?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

#672 is introducing a visibility tests mod. Depending on which one merges first I'll update the other

Index local 'private/protected/public :foo' calls as MethodVisibility
definitions so rubydex preserves retroactive method visibility changes
in the graph using the same definition based model as const visibility
@alexcrocha alexcrocha force-pushed the 03-25-index_retroactive_method_visibility_changes branch from a6cc2ae to d70417d Compare March 27, 2026 03:05
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.

3 participants