fix/audit-findings #5
2 changed files with 74 additions and 2 deletions
fix(dart,cpp): generic type arguments are not base classes
The base walker descended into type-argument lists, so `class MyBloc extends Bloc<Ev, St>` emitted inherits edges to Bloc, Ev and St; on pipe, 39 of 145 Dart inherits edges (27%) pointed at type arguments (e.g. `Command<int>` -> `int`). C++ `: public Base<Arg>` shared the walker through `template_argument_list`. The walker stops at `type_arguments` / `template_argument_list` / `type_parameters`, and handles tree-sitter-dart's shape, where the arguments are sibling `type` fields of `superclass` (only the first names the base) and nested `type` children of a `type`. Test: languages::generic_type_arguments_are_not_base_classes fails before this change (["Bloc", "Ev", "St"]).
commit
46bd3f50f1
|
|
@ -933,7 +933,24 @@ fn bases(node: Node<'_>, source: &str) -> Vec<String> {
|
||||||
child.kind(),
|
child.kind(),
|
||||||
"base_class_clause" | "class_heritage" | "superclass" | "extends_type_clause"
|
"base_class_clause" | "class_heritage" | "superclass" | "extends_type_clause"
|
||||||
) {
|
) {
|
||||||
let mut stack = vec![child];
|
let mut stack = Vec::new();
|
||||||
|
if child.kind() == "superclass" {
|
||||||
|
// tree-sitter-dart gives `extends Bloc<Ev, St>` as sibling `type`
|
||||||
|
// fields: the first is the base, later ones are its type arguments.
|
||||||
|
let mut first_type = true;
|
||||||
|
let mut c = child.walk();
|
||||||
|
for part in child.named_children(&mut c) {
|
||||||
|
if part.kind() == "type" {
|
||||||
|
if !first_type {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
first_type = false;
|
||||||
|
}
|
||||||
|
stack.push(part);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stack.push(child);
|
||||||
|
}
|
||||||
while let Some(base) = stack.pop() {
|
while let Some(base) = stack.pop() {
|
||||||
if matches!(
|
if matches!(
|
||||||
base.kind(),
|
base.kind(),
|
||||||
|
|
@ -946,8 +963,20 @@ fn bases(node: Node<'_>, source: &str) -> Vec<String> {
|
||||||
result.push(base_type(base, source));
|
result.push(base_type(base, source));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// Type arguments name parameters of the base, not further bases:
|
||||||
|
// `extends Bloc<Ev, St>` / `: public Base<Arg>` inherit only Bloc/Base.
|
||||||
|
if matches!(
|
||||||
|
base.kind(),
|
||||||
|
"type_arguments" | "template_argument_list" | "type_parameters"
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let mut c = base.walk();
|
let mut c = base.walk();
|
||||||
stack.extend(base.named_children(&mut c));
|
let nested_type_arguments = base.kind() == "type";
|
||||||
|
stack.extend(
|
||||||
|
base.named_children(&mut c)
|
||||||
|
.filter(|part| !(nested_type_arguments && part.kind() == "type")),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -539,3 +539,46 @@ fn cpp_parent_relative_includes_resolve_without_basename_fallback() {
|
||||||
.any(|n| n.kind == NodeKind::External && n.name == "../missing/base.h")
|
.any(|n| n.kind == NodeKind::External && n.name == "../missing/base.h")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generic_type_arguments_are_not_base_classes() {
|
||||||
|
// `extends Bloc<Ev, St>` produced inherits edges to Ev and St: the base walker
|
||||||
|
// descended into the type-argument list. On pipe 39 of 145 Dart inherits edges
|
||||||
|
// (27%) pointed at type arguments. C++ `: public Base<Arg>` shared the walker.
|
||||||
|
let (_, model) = project(&[
|
||||||
|
(
|
||||||
|
"lib/bloc.dart",
|
||||||
|
"class Ev {}\nclass St {}\nclass Bloc<E, S> {}\nclass MyBloc extends Bloc<Ev, St> {}\n",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"src/base.cpp",
|
||||||
|
"template <class T> class Base {};\nclass Arg {};\nclass Derived : public Base<Arg> {};\n",
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
let inherits = |source: &Node| {
|
||||||
|
let mut names: Vec<String> = model
|
||||||
|
.edges
|
||||||
|
.iter()
|
||||||
|
.filter(|e| e.kind == EdgeKind::Inherits && e.source == source.id)
|
||||||
|
.map(|e| {
|
||||||
|
model
|
||||||
|
.nodes
|
||||||
|
.iter()
|
||||||
|
.find(|n| n.id == e.target)
|
||||||
|
.unwrap()
|
||||||
|
.name
|
||||||
|
.clone()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
names.sort();
|
||||||
|
names
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
inherits(symbol(&model, "lib/bloc.dart", "MyBloc")),
|
||||||
|
["Bloc"]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
inherits(symbol(&model, "src/base.cpp", "Derived")),
|
||||||
|
["Base"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue