Skip to content

Commit 0cd2420

Browse files
committed
Extract shared helpers for visibility-call processing
1 parent e17bd05 commit 0cd2420

2 files changed

Lines changed: 50 additions & 101 deletions

File tree

rust/rubydex/src/indexing/ruby_indexer.rs

Lines changed: 46 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,69 +1193,22 @@ impl<'a> RubyIndexer<'a> {
11931193
}
11941194

11951195
fn handle_constant_visibility(&mut self, node: &ruby_prism::CallNode, visibility: Visibility) {
1196-
let receiver = node.receiver();
1197-
1198-
let receiver_name_id = match receiver {
1199-
Some(ruby_prism::Node::ConstantPathNode { .. } | ruby_prism::Node::ConstantReadNode { .. }) => {
1200-
self.index_constant_reference(&receiver.unwrap(), true)
1201-
}
1202-
Some(ruby_prism::Node::SelfNode { .. }) | None => match self.nesting_stack.last() {
1203-
Some(Nesting::Method(_)) => {
1204-
// Dynamic private constant (called from a method), we ignore it but don't report an error since it's valid Ruby
1205-
// if being called from a singleton method.
1206-
1207-
return;
1208-
}
1209-
None => {
1210-
self.local_graph.add_diagnostic(
1211-
Rule::InvalidPrivateConstant,
1212-
Offset::from_prism_location(&node.location()),
1213-
"Private constant called at top level".to_string(),
1214-
);
1215-
1216-
return;
1217-
}
1218-
_ => None,
1219-
},
1220-
_ => {
1221-
self.local_graph.add_diagnostic(
1222-
Rule::InvalidPrivateConstant,
1223-
Offset::from_prism_location(&node.location()),
1224-
"Dynamic receiver for private constant".to_string(),
1225-
);
1226-
1227-
return;
1228-
}
1196+
let Some(receiver_name_id) = self.resolve_visibility_receiver(node, Rule::InvalidPrivateConstant) else {
1197+
return;
12291198
};
12301199

12311200
let Some(arguments) = node.arguments() else {
12321201
return;
12331202
};
12341203

12351204
for argument in &arguments.arguments() {
1236-
let (name, location) = match argument {
1237-
ruby_prism::Node::SymbolNode { .. } => {
1238-
let symbol = argument.as_symbol_node().unwrap();
1239-
if let Some(value_loc) = symbol.value_loc() {
1240-
(Self::location_to_string(&value_loc), value_loc)
1241-
} else {
1242-
continue;
1243-
}
1244-
}
1245-
ruby_prism::Node::StringNode { .. } => {
1246-
let string = argument.as_string_node().unwrap();
1247-
let name = String::from_utf8_lossy(string.unescaped()).to_string();
1248-
(name, argument.location())
1249-
}
1250-
_ => {
1251-
self.local_graph.add_diagnostic(
1252-
Rule::InvalidPrivateConstant,
1253-
Offset::from_prism_location(&argument.location()),
1254-
"Private constant called with non-symbol argument".to_string(),
1255-
);
1256-
1257-
return;
1258-
}
1205+
let Some((name, location)) = Self::extract_literal_name(&argument) else {
1206+
self.local_graph.add_diagnostic(
1207+
Rule::InvalidPrivateConstant,
1208+
Offset::from_prism_location(&argument.location()),
1209+
"Private constant called with non-symbol argument".to_string(),
1210+
);
1211+
return;
12591212
};
12601213

12611214
let str_id = self.local_graph.intern_string(name);
@@ -1284,32 +1237,9 @@ impl<'a> RubyIndexer<'a> {
12841237
visibility: Visibility,
12851238
call_name: &str,
12861239
) {
1287-
let receiver = node.receiver();
1288-
1289-
let receiver_name_id = match receiver {
1290-
Some(ruby_prism::Node::ConstantPathNode { .. } | ruby_prism::Node::ConstantReadNode { .. }) => {
1291-
self.index_constant_reference(&receiver.unwrap(), true)
1292-
}
1293-
Some(ruby_prism::Node::SelfNode { .. }) | None => match self.nesting_stack.last() {
1294-
Some(Nesting::Method(_)) => return,
1295-
None => {
1296-
self.local_graph.add_diagnostic(
1297-
Rule::InvalidSingletonMethodVisibility,
1298-
Offset::from_prism_location(&node.location()),
1299-
format!("`{call_name}` called at top level"),
1300-
);
1301-
return;
1302-
}
1303-
_ => None,
1304-
},
1305-
_ => {
1306-
self.local_graph.add_diagnostic(
1307-
Rule::InvalidSingletonMethodVisibility,
1308-
Offset::from_prism_location(&node.location()),
1309-
format!("Dynamic receiver for `{call_name}`"),
1310-
);
1311-
return;
1312-
}
1240+
let Some(receiver_name_id) = self.resolve_visibility_receiver(node, Rule::InvalidSingletonMethodVisibility)
1241+
else {
1242+
return;
13131243
};
13141244

13151245
let Some(arguments) = node.arguments() else {
@@ -1466,6 +1396,38 @@ impl<'a> RubyIndexer<'a> {
14661396
}
14671397
}
14681398

1399+
#[allow(clippy::option_option)]
1400+
fn resolve_visibility_receiver(&mut self, node: &ruby_prism::CallNode, rule: Rule) -> Option<Option<NameId>> {
1401+
let receiver = node.receiver();
1402+
match &receiver {
1403+
Some(ruby_prism::Node::ConstantPathNode { .. } | ruby_prism::Node::ConstantReadNode { .. }) => {
1404+
Some(self.index_constant_reference(receiver.as_ref().unwrap(), true))
1405+
}
1406+
Some(ruby_prism::Node::SelfNode { .. }) | None => match self.nesting_stack.last() {
1407+
Some(Nesting::Method(_)) => None,
1408+
None => {
1409+
let call_name = String::from_utf8_lossy(node.name().as_slice());
1410+
self.local_graph.add_diagnostic(
1411+
rule,
1412+
Offset::from_prism_location(&node.location()),
1413+
format!("`{call_name}` called at top level"),
1414+
);
1415+
None
1416+
}
1417+
_ => Some(None),
1418+
},
1419+
Some(other) => {
1420+
let call_name = String::from_utf8_lossy(node.name().as_slice());
1421+
self.local_graph.add_diagnostic(
1422+
rule,
1423+
Offset::from_prism_location(&other.location()),
1424+
format!("Dynamic receiver for `{call_name}`"),
1425+
);
1426+
None
1427+
}
1428+
}
1429+
}
1430+
14691431
fn is_attr_call(arg: &ruby_prism::Node) -> bool {
14701432
arg.as_call_node().is_some_and(|call| {
14711433
let receiver = call.receiver();
@@ -1520,21 +1482,8 @@ impl<'a> RubyIndexer<'a> {
15201482
}
15211483

15221484
fn create_method_visibility_definition(&mut self, arg: &ruby_prism::Node, visibility: Visibility) {
1523-
let (name, location) = match arg {
1524-
ruby_prism::Node::SymbolNode { .. } => {
1525-
let symbol = arg.as_symbol_node().unwrap();
1526-
if let Some(value_loc) = symbol.value_loc() {
1527-
(Self::location_to_string(&value_loc), value_loc)
1528-
} else {
1529-
return;
1530-
}
1531-
}
1532-
ruby_prism::Node::StringNode { .. } => {
1533-
let string = arg.as_string_node().unwrap();
1534-
let name = String::from_utf8_lossy(string.unescaped()).to_string();
1535-
(name, arg.location())
1536-
}
1537-
_ => return,
1485+
let Some((name, location)) = Self::extract_literal_name(arg) else {
1486+
return;
15381487
};
15391488

15401489
let str_id = self.local_graph.intern_string(format!("{name}()"));

rust/rubydex/src/indexing/ruby_indexer_tests.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3333,9 +3333,9 @@ mod visibility_tests {
33333333
assert_local_diagnostics_eq!(
33343334
&context,
33353335
vec![
3336-
"invalid-private-constant: Private constant called at top level (1:1-1:30)",
3337-
"invalid-private-constant: Private constant called at top level (2:1-2:35)",
3338-
"invalid-private-constant: Dynamic receiver for private constant (3:1-3:34)",
3336+
"invalid-private-constant: `private_constant` called at top level (1:1-1:30)",
3337+
"invalid-private-constant: `private_constant` called at top level (2:1-2:35)",
3338+
"invalid-private-constant: Dynamic receiver for `private_constant` (3:1-3:4)",
33393339
"invalid-private-constant: Private constant called with non-symbol argument (6:20-6:31)",
33403340
]
33413341
);
@@ -3823,7 +3823,7 @@ mod visibility_tests {
38233823
vec![
38243824
"invalid-singleton-method-visibility: `private_class_method` called at top level (1:1-1:34)",
38253825
"invalid-singleton-method-visibility: `private_class_method` called at top level (2:1-2:39)",
3826-
"invalid-singleton-method-visibility: Dynamic receiver for `private_class_method` (3:1-3:38)",
3826+
"invalid-singleton-method-visibility: Dynamic receiver for `private_class_method` (3:1-3:4)",
38273827
"invalid-singleton-method-visibility: `private_class_method` called with a non-literal argument (6:24-6:35)",
38283828
"invalid-singleton-method-visibility: `private_class_method` does not accept `attr_*` arguments (8:24-8:41)",
38293829
"invalid-singleton-method-visibility: `private_class_method` requires a singleton method definition (9:24-9:39)",

0 commit comments

Comments
 (0)