use crate::index::engine::AstGrepEngine;
use crate::index::symbol::ReferenceKind;
fn extract_refs(
engine: &AstGrepEngine,
ext: &str,
source: &str,
) -> Vec<crate::index::symbol::Reference> {
let lang = engine.find_language(ext).expect("unsupported ext");
engine.extract_references(lang, source, &format!("test.{ext}"))
}
#[test]
fn rust_use_import_refs() {
let engine = AstGrepEngine::new();
let source = r#"
use std::collections::HashMap;
use crate::config::Config;
fn main() {}
"#;
let refs = extract_refs(&engine, "rs", source);
let imports: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Import)
.map(|r| r.target_name.as_str())
.collect();
assert!(
imports.iter().any(|i| i.contains("HashMap")),
"missing HashMap import, got: {imports:?}"
);
assert!(
imports.iter().any(|i| i.contains("Config")),
"missing Config import, got: {imports:?}"
);
}
#[test]
fn rust_impl_trait_refs() {
let engine = AstGrepEngine::new();
let source = r#"
struct MyStruct;
impl Display for MyStruct {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "MyStruct")
}
}
"#;
let refs = extract_refs(&engine, "rs", source);
let impls: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Implements)
.map(|r| r.target_name.as_str())
.collect();
assert!(
impls.contains(&"Display"),
"missing Display impl, got: {impls:?}"
);
}
#[test]
fn python_import_refs() {
let engine = AstGrepEngine::new();
let source = r#"
import os
import json
from pathlib import Path
"#;
let refs = extract_refs(&engine, "py", source);
let imports: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Import)
.map(|r| r.target_name.as_str())
.collect();
assert!(
imports.contains(&"os"),
"missing os import, got: {imports:?}"
);
}
#[test]
fn python_class_inheritance_refs() {
let engine = AstGrepEngine::new();
let source = r#"
class Animal:
pass
class Dog(Animal):
def bark(self):
pass
"#;
let refs = extract_refs(&engine, "py", source);
let inherits: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Inherits)
.map(|r| r.target_name.as_str())
.collect();
assert!(
inherits.contains(&"Animal"),
"missing Animal inheritance, got: {inherits:?}"
);
}
#[test]
fn go_import_refs() {
let engine = AstGrepEngine::new();
let source = r#"
package main
import (
"fmt"
"net/http"
)
func main() {}
"#;
let refs = extract_refs(&engine, "go", source);
let imports: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Import)
.map(|r| r.target_name.as_str())
.collect();
assert!(
imports.contains(&"fmt"),
"missing fmt import, got: {imports:?}"
);
assert!(
imports.contains(&"net/http"),
"missing net/http import, got: {imports:?}"
);
}
#[test]
fn ts_class_extends_refs() {
let engine = AstGrepEngine::new();
let source = r#"
class Animal {
name: string;
}
class Dog extends Animal {
bark() {}
}
"#;
let refs = extract_refs(&engine, "ts", source);
let inherits: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Inherits)
.map(|r| r.target_name.as_str())
.collect();
assert!(
inherits.contains(&"Animal"),
"missing Animal extends ref, got: {inherits:?}"
);
}
#[test]
fn ts_class_implements_refs() {
let engine = AstGrepEngine::new();
let source = r#"
interface Serializable {
serialize(): string;
}
class User implements Serializable {
serialize() { return "{}"; }
}
"#;
let refs = extract_refs(&engine, "ts", source);
let impls: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Implements)
.map(|r| r.target_name.as_str())
.collect();
assert!(
impls.contains(&"Serializable"),
"missing Serializable implements ref, got: {impls:?}"
);
}
#[test]
fn java_import_refs() {
let engine = AstGrepEngine::new();
let source = r#"
import java.util.HashMap;
import java.io.File;
public class Main {
public static void main(String[] args) {}
}
"#;
let refs = extract_refs(&engine, "java", source);
let imports: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Import)
.map(|r| r.target_name.as_str())
.collect();
assert!(
imports.iter().any(|i| i.contains("HashMap")),
"missing HashMap import, got: {imports:?}"
);
assert!(
imports.iter().any(|i| i.contains("File")),
"missing File import, got: {imports:?}"
);
}
#[test]
fn java_class_extends_refs() {
let engine = AstGrepEngine::new();
let source = r#"
public class Animal {
public void speak() {}
}
public class Dog extends Animal {
public void bark() {}
}
"#;
let refs = extract_refs(&engine, "java", source);
let inherits: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Inherits)
.map(|r| r.target_name.as_str())
.collect();
assert!(
inherits.contains(&"Animal"),
"missing Animal extends ref, got: {inherits:?}"
);
}
#[test]
fn java_class_implements_refs() {
let engine = AstGrepEngine::new();
let source = r#"
public class MyService implements Runnable {
public void run() {}
}
"#;
let refs = extract_refs(&engine, "java", source);
let heritage: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Implements || r.kind == ReferenceKind::Inherits)
.map(|r| r.target_name.as_str())
.collect();
if !heritage.is_empty() {
assert!(
heritage.contains(&"Runnable"),
"expected Runnable in heritage refs, got: {heritage:?}"
);
}
}
#[test]
fn cpp_include_refs() {
let engine = AstGrepEngine::new();
let source = r#"
#include <iostream>
#include "myheader.h"
int main() { return 0; }
"#;
let refs = extract_refs(&engine, "cpp", source);
let imports: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Import)
.map(|r| r.target_name.as_str())
.collect();
assert!(
imports.contains(&"iostream"),
"missing iostream include, got: {imports:?}"
);
assert!(
imports.contains(&"myheader.h"),
"missing myheader.h include, got: {imports:?}"
);
}
#[test]
fn csharp_using_refs() {
let engine = AstGrepEngine::new();
let source = r#"
using System;
using System.Collections.Generic;
namespace MyApp {
class Program {
static void Main() {}
}
}
"#;
let refs = extract_refs(&engine, "cs", source);
let imports: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Import)
.map(|r| r.target_name.as_str())
.collect();
assert!(
imports.contains(&"System"),
"missing System using, got: {imports:?}"
);
assert!(
imports.iter().any(|i| i.contains("Collections")),
"missing System.Collections.Generic using, got: {imports:?}"
);
}
#[test]
fn ruby_require_refs() {
let engine = AstGrepEngine::new();
let source = r#"
require "json"
require_relative "helpers"
class MyClass
end
"#;
let refs = extract_refs(&engine, "rb", source);
let imports: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Import)
.map(|r| r.target_name.as_str())
.collect();
assert!(
imports.contains(&"json"),
"missing json require, got: {imports:?}"
);
assert!(
imports.contains(&"helpers"),
"missing helpers require_relative, got: {imports:?}"
);
}
#[test]
fn ruby_superclass_refs() {
let engine = AstGrepEngine::new();
let source = r#"
class Base
end
class Child < Base
end
"#;
let refs = extract_refs(&engine, "rb", source);
let inherits: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Inherits)
.map(|r| r.target_name.as_str())
.collect();
assert!(
inherits.iter().any(|i| i.contains("Base")),
"missing Base superclass ref, got: {inherits:?}"
);
}
#[test]
fn ruby_include_extend_refs() {
let engine = AstGrepEngine::new();
let source = r#"
class MyClass
include Comparable
extend ActiveModel::Naming
end
"#;
let refs = extract_refs(&engine, "rb", source);
let imports: Vec<&str> = refs
.iter()
.filter(|r| r.kind == crate::index::symbol::ReferenceKind::Import)
.map(|r| r.target_name.as_str())
.collect();
assert!(
imports.contains(&"Comparable"),
"missing Comparable import, got: {imports:?}"
);
}
#[test]
fn kotlin_import_refs() {
let engine = AstGrepEngine::new();
let source = r#"
import kotlin.collections.HashMap
import java.io.File
class Main {
fun run() {}
}
"#;
let refs = extract_refs(&engine, "kt", source);
let imports: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Import)
.map(|r| r.target_name.as_str())
.collect();
assert!(
imports.iter().any(|i| i.contains("HashMap")),
"missing HashMap import, got: {imports:?}"
);
assert!(
imports.iter().any(|i| i.contains("File")),
"missing File import, got: {imports:?}"
);
}
#[test]
fn swift_import_refs() {
let engine = AstGrepEngine::new();
let source = r#"
import Foundation
import UIKit
class ViewController {
func viewDidLoad() {}
}
"#;
let refs = extract_refs(&engine, "swift", source);
let imports: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Import)
.map(|r| r.target_name.as_str())
.collect();
assert!(
imports.contains(&"Foundation"),
"missing Foundation import, got: {imports:?}"
);
assert!(
imports.contains(&"UIKit"),
"missing UIKit import, got: {imports:?}"
);
}
#[test]
fn php_use_refs() {
let engine = AstGrepEngine::new();
let source = r#"<?php
use App\Models\User;
use Illuminate\Http\Request;
class Controller {
public function index() {}
}
"#;
let refs = extract_refs(&engine, "php", source);
let imports: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Import)
.map(|r| r.target_name.as_str())
.collect();
assert!(
imports.iter().any(|i| i.contains("User")),
"missing User use, got: {imports:?}"
);
}
#[test]
fn scala_import_refs() {
let engine = AstGrepEngine::new();
let source = r#"
import scala.collection.mutable
import java.io.File
class MyClass {
def run(): Unit = {}
}
"#;
let refs = extract_refs(&engine, "scala", source);
let imports: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Import)
.map(|r| r.target_name.as_str())
.collect();
assert!(
imports.iter().any(|i| i.contains("mutable")),
"missing mutable import, got: {imports:?}"
);
}
#[test]
fn scala_extends_refs() {
let engine = AstGrepEngine::new();
let source = r#"
trait Animal {
def speak(): String
}
class Dog extends Animal {
def speak(): String = "Woof"
}
"#;
let refs = extract_refs(&engine, "scala", source);
let inherits: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Inherits)
.map(|r| r.target_name.as_str())
.collect();
if !inherits.is_empty() {
assert!(
inherits.contains(&"Animal"),
"expected Animal in extends refs, got: {inherits:?}"
);
}
}
#[test]
fn hcl_function_call_refs() {
let engine = AstGrepEngine::new();
let source = r#"
output "result" {
value = join(",", var.list)
}
"#;
let refs = extract_refs(&engine, "tf", source);
let calls: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Call)
.map(|r| r.target_name.as_str())
.collect();
if !calls.is_empty() {
assert!(
calls.contains(&"join"),
"expected join in calls, got: {calls:?}"
);
}
}
#[test]
fn python_callback_args_extracted() {
let engine = AstGrepEngine::new();
let source = r#"
def transform(x):
return x * 2
def is_valid(x):
return x > 0
def main():
result = map(transform, items)
filtered = filter(is_valid, data)
"#;
let refs = extract_refs(&engine, "py", source);
let callbacks: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Callback)
.map(|r| r.target_name.as_str())
.collect();
assert!(
callbacks.contains(&"transform"),
"missing transform callback, got: {callbacks:?}"
);
assert!(
callbacks.contains(&"is_valid"),
"missing is_valid callback, got: {callbacks:?}"
);
assert!(
!callbacks.contains(&"items"),
"items should be filtered out, got: {callbacks:?}"
);
assert!(
!callbacks.contains(&"data"),
"data should be filtered out, got: {callbacks:?}"
);
}
#[test]
fn typescript_callback_args_extracted() {
let engine = AstGrepEngine::new();
let source = r#"
function processItem(item: any) { return item; }
function validateAuth(req: any, res: any, next: any) { next(); }
function handleRequest(req: any, res: any) { res.send("ok"); }
const items = [1, 2, 3];
const mapped = items.map(processItem);
app.get("/api", validateAuth, handleRequest);
"#;
let refs = extract_refs(&engine, "ts", source);
let callbacks: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Callback)
.map(|r| r.target_name.as_str())
.collect();
assert!(
callbacks.contains(&"processItem"),
"missing processItem callback, got: {callbacks:?}"
);
assert!(
callbacks.contains(&"validateAuth"),
"missing validateAuth callback, got: {callbacks:?}"
);
assert!(
callbacks.contains(&"handleRequest"),
"missing handleRequest callback, got: {callbacks:?}"
);
}
#[test]
fn python_builtin_calls_filtered() {
let engine = AstGrepEngine::new();
let source = r#"
def process():
print("hello")
n = len(items)
data = get_data()
result = transform(data)
"#;
let refs = extract_refs(&engine, "py", source);
let call_names: Vec<&str> = refs
.iter()
.filter(|r| r.kind == ReferenceKind::Call)
.map(|r| r.target_name.as_str())
.collect();
assert!(
!call_names.contains(&"print"),
"print should be filtered, got: {call_names:?}"
);
assert!(
!call_names.contains(&"len"),
"len should be filtered, got: {call_names:?}"
);
assert!(
call_names.contains(&"get_data"),
"get_data should be kept, got: {call_names:?}"
);
assert!(
call_names.contains(&"transform"),
"transform should be kept, got: {call_names:?}"
);
}