use super::*;
use crate::index::{Symbol, SymbolKind};
use std::path::PathBuf;
use std::sync::Once;
static INIT: Once = Once::new();
fn init_grammar_cache() {
INIT.call_once(|| {
let config = tree_sitter_language_pack::PackConfig {
cache_dir: Some(crate::lang::grammar_cache_dir()),
..Default::default()
};
tree_sitter_language_pack::configure(&config).expect("failed to configure grammar cache");
});
}
fn extract(lang: &str, src: &str, file: &str) -> Vec<Symbol> {
init_grammar_cache();
parse_and_extract(lang, src.as_bytes(), &PathBuf::from(file)).unwrap()
}
#[test]
fn markdown_headings_are_sections() {
let src = "# Title\nintro\n\n## Usage\nbody\n\n### Details\nmore\n\n## Install\nsteps\n";
let syms = extract("markdown", src, "README.md");
assert_eq!(syms.len(), 4);
assert_eq!(syms[0].name, "Title");
assert_eq!(syms[0].kind, SymbolKind::Heading);
assert_eq!(syms[0].signature, "# Title");
assert_eq!(&src.as_bytes()[syms[1].byte_range.0..syms[1].byte_range.1], b"## Usage\nbody\n\n### Details\nmore\n\n");
}
#[test]
fn markdown_ignores_headings_in_fenced_code() {
let src = "# Title\n\n```md\n# Not a heading\n```\n\n## Real\n";
let syms = extract("markdown", src, "README.md");
let names: Vec<&str> = syms.iter().map(|s| s.name.as_str()).collect();
assert_eq!(names, vec!["Title", "Real"]);
}
#[test]
fn objc_class_protocol_methods_and_selectors() {
let src = r#"
@interface Greeter : NSObject
@property (nonatomic, copy) NSString *name;
- (void)sayHello:(NSString *)target;
- (void)configureWithName:(NSString *)name age:(NSUInteger)age;
@end
@protocol GreeterDelegate
- (void)greeterDidGreet:(Greeter *)greeter;
@end
@implementation Greeter
- (void)sayHello:(NSString *)target {
}
- (void)configureWithName:(NSString *)name age:(NSUInteger)age {
}
+ (instancetype)sharedGreeter {
return nil;
}
@end
"#;
let syms = extract("objc", src, "Greeter.m");
assert!(
syms.iter()
.any(|s| s.name == "Greeter" && s.kind == SymbolKind::Class)
);
assert!(
syms.iter()
.any(|s| s.name == "GreeterDelegate" && s.kind == SymbolKind::Interface)
);
assert!(
syms.iter()
.any(|s| s.name == "sayHello:" && s.kind == SymbolKind::Fn)
);
assert!(
syms.iter()
.any(|s| s.name == "configureWithName:age:" && s.kind == SymbolKind::Fn)
);
assert!(
syms.iter()
.any(|s| s.name == "sharedGreeter" && s.kind == SymbolKind::Fn)
);
}
#[test]
fn objc_categories_extensions_and_protocol_inheritance() {
let src = r#"
@interface Greeter ()
- (void)prepare;
@end
@interface Greeter (Formatting)
- (NSString *)formattedNameForLocale:(NSString *)locale;
@end
@protocol GreeterDelegate <NSObject>
- (void)greeter:(Greeter *)greeter didGreet:(NSString *)name;
@end
@implementation Greeter (Formatting)
- (NSString *)formattedNameForLocale:(NSString *)locale {
return locale;
}
@end
"#;
let syms = extract("objc", src, "Greeter.m");
assert!(
syms.iter()
.any(|s| s.name == "Greeter()" && s.kind == SymbolKind::Class)
);
assert!(
syms.iter()
.any(|s| s.name == "Greeter(Formatting)" && s.kind == SymbolKind::Class)
);
assert!(
syms.iter()
.any(|s| s.name == "GreeterDelegate" && s.kind == SymbolKind::Interface)
);
assert!(
syms.iter()
.any(|s| s.name == "formattedNameForLocale:" && s.kind == SymbolKind::Fn)
);
assert!(
syms.iter()
.any(|s| s.name == "greeter:didGreet:" && s.kind == SymbolKind::Fn)
);
}
#[test]
fn objc_c_functions_and_mm_extension() {
let src = r#"
static int add(int a, int b) {
return a + b;
}
void run(void);
"#;
let syms = extract("objc", src, "Greeter.mm");
assert!(
syms.iter()
.any(|s| s.name == "add" && s.kind == SymbolKind::Fn)
);
assert!(
syms.iter()
.any(|s| s.name == "run" && s.kind == SymbolKind::Fn)
);
}
#[test]
fn rust_function() {
let src = "pub fn calculate_fee(amount: u64) -> u64 {\n amount * 3 / 1000\n}";
let syms = extract("rust", src, "test.rs");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "calculate_fee");
assert_eq!(syms[0].kind, SymbolKind::Fn);
assert!(!syms[0].signature.contains('{'));
assert!(syms[0].signature.contains("pub fn"));
}
#[test]
fn rust_struct() {
let src = "pub struct FeeConfig {\n pub rate: u64,\n}";
let syms = extract("rust", src, "test.rs");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "FeeConfig");
assert_eq!(syms[0].kind, SymbolKind::Struct);
}
#[test]
fn rust_enum() {
let src = "pub enum FeeTier {\n Low,\n High,\n}";
let syms = extract("rust", src, "test.rs");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "FeeTier");
assert_eq!(syms[0].kind, SymbolKind::Enum);
}
#[test]
fn rust_trait() {
let src = "pub trait Configurable {\n fn configure(&self);\n}";
let syms = extract("rust", src, "test.rs");
let trait_sym = syms.iter().find(|s| s.name == "Configurable").unwrap();
assert_eq!(trait_sym.kind, SymbolKind::Trait);
}
#[test]
fn rust_multiple_symbols() {
let src = "pub fn foo() {}\nfn bar() {}\npub struct Baz;";
let syms = extract("rust", src, "test.rs");
assert!(syms.len() >= 3);
let names: Vec<&str> = syms.iter().map(|s| s.name.as_str()).collect();
assert!(names.contains(&"foo"));
assert!(names.contains(&"bar"));
assert!(names.contains(&"Baz"));
}
#[test]
fn rust_byte_range() {
let src = "pub fn test_func() -> u32 { 42 }";
let syms = extract("rust", src, "test.rs");
assert_eq!(syms.len(), 1);
let (start, end) = syms[0].byte_range;
assert!(start < end);
assert!(end <= src.len());
assert!(src[start..end].contains("test_func"));
}
#[test]
fn ts_function() {
let src = "function greet(name: string): string { return name; }";
let syms = extract("typescript", src, "test.ts");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "greet");
assert_eq!(syms[0].kind, SymbolKind::Fn);
}
#[test]
fn ts_class() {
let src = "export class UserService {\n getName() { return 'test'; }\n}";
let syms = extract("typescript", src, "test.ts");
let class = syms.iter().find(|s| s.name == "UserService").unwrap();
assert_eq!(class.kind, SymbolKind::Class);
let method = syms.iter().find(|s| s.name == "getName").unwrap();
assert_eq!(method.kind, SymbolKind::Fn);
}
#[test]
fn ts_interface() {
let src = "export interface Config {\n host: string;\n port: number;\n}";
let syms = extract("typescript", src, "test.ts");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "Config");
assert_eq!(syms[0].kind, SymbolKind::Interface);
}
#[test]
fn ts_arrow_function() {
let src = "const add = (a: number, b: number) => a + b;";
let syms = extract("typescript", src, "test.ts");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "add");
assert_eq!(syms[0].kind, SymbolKind::Fn);
assert!(
syms[0].signature.contains("const add"),
"should include const: {}",
syms[0].signature
);
assert!(
!syms[0].signature.contains("a + b"),
"should not include body: {}",
syms[0].signature
);
}
#[test]
fn ts_tsx() {
let src = "export function App() { return <div />; }";
let syms = extract("typescript", src, "test.tsx");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "App");
}
#[test]
fn py_function() {
let src = "def greet(name: str) -> str:\n return f'Hello, {name}'";
let syms = extract("python", src, "test.py");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "greet");
assert_eq!(syms[0].kind, SymbolKind::Fn);
assert!(
syms[0].signature.contains("-> str"),
"should preserve return type: {}",
syms[0].signature
);
assert!(
syms[0].signature.contains("name: str"),
"should preserve param types: {}",
syms[0].signature
);
}
#[test]
fn py_class() {
let src = "class UserService:\n def get_name(self):\n return 'test'";
let syms = extract("python", src, "test.py");
let class = syms.iter().find(|s| s.name == "UserService").unwrap();
assert_eq!(class.kind, SymbolKind::Class);
}
#[test]
fn py_constant() {
let src = "MAX_SIZE = 100";
let syms = extract("python", src, "test.py");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "MAX_SIZE");
assert_eq!(syms[0].kind, SymbolKind::Const);
}
#[test]
fn py_multiple_symbols() {
let src = "def foo():\n pass\n\ndef bar():\n pass\n\nclass Baz:\n pass";
let syms = extract("python", src, "test.py");
assert!(syms.len() >= 3);
let names: Vec<&str> = syms.iter().map(|s| s.name.as_str()).collect();
assert!(names.contains(&"foo"));
assert!(names.contains(&"bar"));
assert!(names.contains(&"Baz"));
}
#[test]
fn py_type_annotation_preserved() {
let src = "def foo(x: int, y: list[str]) -> bool:\n return True";
let syms = extract("python", src, "test.py");
assert_eq!(syms.len(), 1);
assert!(
syms[0].signature.contains("int"),
"sig: {}",
syms[0].signature
);
assert!(
syms[0].signature.contains("bool"),
"sig: {}",
syms[0].signature
);
}
#[test]
fn py_method_in_class() {
let src = "class Foo:\n def bar(self):\n pass";
let syms = extract("python", src, "test.py");
let cls = syms.iter().find(|s| s.name == "Foo").unwrap();
assert_eq!(cls.kind, SymbolKind::Class);
let bar = syms.iter().find(|s| s.name == "bar").unwrap();
assert_eq!(bar.kind, SymbolKind::Fn);
}
#[test]
fn py_decorated_method() {
let src = "class Foo:\n @property\n def name(self):\n return self._name";
let syms = extract("python", src, "test.py");
let name = syms.iter().find(|s| s.name == "name").unwrap();
assert_eq!(name.kind, SymbolKind::Fn);
}
#[test]
fn py_classmethod() {
let src = "class Foo:\n @classmethod\n def create(cls):\n return cls()";
let syms = extract("python", src, "test.py");
let create = syms.iter().find(|s| s.name == "create").unwrap();
assert_eq!(create.kind, SymbolKind::Fn);
}
#[test]
fn py_staticmethod() {
let src = "class Foo:\n @staticmethod\n def helper():\n pass";
let syms = extract("python", src, "test.py");
let helper = syms.iter().find(|s| s.name == "helper").unwrap();
assert_eq!(helper.kind, SymbolKind::Fn);
}
#[test]
fn py_decorated_top_level_fn() {
let src = "@timer\ndef run():\n pass";
let syms = extract("python", src, "test.py");
let run = syms.iter().find(|s| s.name == "run").unwrap();
assert_eq!(run.kind, SymbolKind::Fn);
}
#[test]
fn py_decorated_class() {
let src = "@dataclass\nclass Point:\n x: int\n y: int";
let syms = extract("python", src, "test.py");
let point = syms.iter().find(|s| s.name == "Point").unwrap();
assert_eq!(point.kind, SymbolKind::Class);
}
#[test]
fn py_async_method() {
let src = "class Server:\n async def handle(self, req):\n pass";
let syms = extract("python", src, "test.py");
let handle = syms.iter().find(|s| s.name == "handle").unwrap();
assert_eq!(handle.kind, SymbolKind::Fn);
}
#[test]
fn py_nested_class() {
let src = "class Outer:\n class Inner:\n pass";
let syms = extract("python", src, "test.py");
let outer = syms.iter().find(|s| s.name == "Outer").unwrap();
assert_eq!(outer.kind, SymbolKind::Class);
let inner = syms.iter().find(|s| s.name == "Inner").unwrap();
assert_eq!(inner.kind, SymbolKind::Class);
}
#[test]
fn py_dataclass_fields() {
let src = "@dataclass\nclass Point:\n x: int\n y: int\n z: float = 0.0";
let syms = extract("python", src, "test.py");
let point = syms.iter().find(|s| s.name == "Point").unwrap();
assert_eq!(point.kind, SymbolKind::Class);
let x = syms.iter().find(|s| s.name == "x").unwrap();
assert_eq!(x.kind, SymbolKind::Field);
let y = syms.iter().find(|s| s.name == "y").unwrap();
assert_eq!(y.kind, SymbolKind::Field);
let z = syms.iter().find(|s| s.name == "z").unwrap();
assert_eq!(z.kind, SymbolKind::Field);
}
#[test]
fn py_typed_class_variable() {
let src = "class Config:\n count: int = 0\n name: str = 'default'\n x = 5";
let syms = extract("python", src, "test.py");
let config = syms.iter().find(|s| s.name == "Config").unwrap();
assert_eq!(config.kind, SymbolKind::Class);
let count = syms.iter().find(|s| s.name == "count").unwrap();
assert_eq!(count.kind, SymbolKind::Field);
let name = syms.iter().find(|s| s.name == "name").unwrap();
assert_eq!(name.kind, SymbolKind::Field);
assert!(
!syms.iter().any(|s| s.name == "x"),
"plain class body assignment without type annotation should not be captured"
);
}
#[test]
fn go_function() {
let src = "func Calculate(amount int) int {\n\treturn amount * 3\n}";
let syms = extract("go", src, "test.go");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "Calculate");
assert_eq!(syms[0].kind, SymbolKind::Fn);
assert!(
syms[0].signature.contains("func"),
"sig: {}",
syms[0].signature
);
}
#[test]
fn go_method() {
let src = "func (s *Server) Start() error {\n\treturn nil\n}";
let syms = extract("go", src, "test.go");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "Start");
assert_eq!(syms[0].kind, SymbolKind::Fn);
}
#[test]
fn go_type() {
let src = "type Config struct {\n\tHost string\n}";
let syms = extract("go", src, "test.go");
assert_eq!(syms.len(), 2);
let config = syms.iter().find(|s| s.name == "Config").unwrap();
assert_eq!(config.kind, SymbolKind::Struct);
let host = syms.iter().find(|s| s.name == "Host").unwrap();
assert_eq!(host.kind, SymbolKind::Field);
}
#[test]
fn c_function() {
let src = "int calculate(int amount) {\n return amount * 3;\n}";
let syms = extract("c", src, "test.c");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "calculate");
assert_eq!(syms[0].kind, SymbolKind::Fn);
}
#[test]
fn c_pointer_returning_function() {
let src = "char *strdup(const char *s) {\n return NULL;\n}";
let syms = extract("c", src, "test.c");
let f = syms.iter().find(|s| s.name == "strdup");
assert!(f.is_some(), "should find pointer-returning fn: {syms:?}");
assert_eq!(f.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn c_struct() {
let src = "struct Config {\n int rate;\n};";
let syms = extract("c", src, "test.c");
let s = syms.iter().find(|s| s.name == "Config");
assert!(s.is_some(), "should find struct: {syms:?}");
assert_eq!(s.unwrap().kind, SymbolKind::Struct);
}
#[test]
fn cpp_class() {
let src = "class Server {\npublic:\n void start();\n};";
let syms = extract("cpp", src, "test.cpp");
let class = syms.iter().find(|s| s.name == "Server");
assert!(class.is_some(), "should find class: {syms:?}");
assert_eq!(class.unwrap().kind, SymbolKind::Class);
}
#[test]
fn java_class_and_method() {
let src = "public class UserService {\n public String getName() {\n return \"test\";\n }\n}";
let syms = extract("java", src, "Test.java");
let class = syms.iter().find(|s| s.name == "UserService");
assert!(class.is_some(), "should find class: {syms:?}");
assert_eq!(class.unwrap().kind, SymbolKind::Class);
}
#[test]
fn ruby_class_and_method() {
let src = "class UserService\n def get_name\n 'test'\n end\nend";
let syms = extract("ruby", src, "test.rb");
let class = syms.iter().find(|s| s.name == "UserService");
assert!(class.is_some(), "should find class: {syms:?}");
assert_eq!(class.unwrap().kind, SymbolKind::Class);
let method = syms.iter().find(|s| s.name == "get_name");
assert!(method.is_some(), "should find method: {syms:?}");
}
#[test]
fn lua_function() {
let src = "function greet(name)\n return 'Hello, ' .. name\nend";
let syms = extract("lua", src, "test.lua");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "greet");
assert_eq!(syms[0].kind, SymbolKind::Fn);
}
#[test]
fn zig_function() {
let src = "pub fn calculate(amount: u64) u64 {\n return amount * 3;\n}";
let syms = extract("zig", src, "test.zig");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "calculate");
assert_eq!(syms[0].kind, SymbolKind::Fn);
}
#[test]
fn zig_struct() {
let src = "const Point = struct {\n x: f32,\n y: f32,\n};";
let syms = extract("zig", src, "test.zig");
let point = syms.iter().find(|s| s.name == "Point").unwrap();
assert_eq!(point.kind, SymbolKind::Struct);
let x = syms.iter().find(|s| s.name == "x").unwrap();
assert_eq!(x.kind, SymbolKind::Field);
let y = syms.iter().find(|s| s.name == "y").unwrap();
assert_eq!(y.kind, SymbolKind::Field);
}
#[test]
fn zig_enum() {
let src = "const Color = enum {\n red,\n green,\n blue,\n};";
let syms = extract("zig", src, "test.zig");
let color = syms.iter().find(|s| s.name == "Color").unwrap();
assert_eq!(color.kind, SymbolKind::Enum);
}
#[test]
fn zig_union() {
let src = "const Msg = union {\n int: i32,\n float: f64,\n};";
let syms = extract("zig", src, "test.zig");
let msg = syms.iter().find(|s| s.name == "Msg").unwrap();
assert_eq!(msg.kind, SymbolKind::Struct);
assert!(
syms.iter()
.any(|s| s.name == "int" && s.kind == SymbolKind::Field)
);
}
#[test]
fn zig_pub_struct() {
let src = "pub const Point = struct {\n x: f32,\n y: f32,\n};";
let syms = extract("zig", src, "test.zig");
let point = syms.iter().find(|s| s.name == "Point").unwrap();
assert_eq!(point.kind, SymbolKind::Struct);
assert_eq!(
syms.iter().filter(|s| s.kind == SymbolKind::Field).count(),
2
);
}
#[test]
fn zig_error_set() {
let src = "const MyError = error {\n OutOfMemory,\n InvalidInput,\n};";
let syms = extract("zig", src, "test.zig");
let err = syms.iter().find(|s| s.name == "MyError").unwrap();
assert_eq!(err.kind, SymbolKind::Enum);
}
#[test]
fn zig_const_and_test() {
let src = "const std = @import(\"std\");\npub const max_size: usize = 1024;\ntest \"basic\" {}";
let syms = extract("zig", src, "test.zig");
let ms = syms.iter().find(|s| s.name == "max_size").unwrap();
assert_eq!(ms.kind, SymbolKind::Const);
let t = syms.iter().find(|s| s.name.contains("basic")).unwrap();
assert_eq!(t.kind, SymbolKind::Fn);
}
#[test]
fn bash_function() {
let src = "function greet() {\n echo \"Hello\"\n}";
let syms = extract("bash", src, "test.sh");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "greet");
assert_eq!(syms[0].kind, SymbolKind::Fn);
}
#[test]
fn solidity_contract_and_function() {
let src = "contract Token {\n function transfer(address to, uint amount) public {\n }\n}";
let syms = extract("solidity", src, "test.sol");
let contract = syms.iter().find(|s| s.name == "Token");
assert!(contract.is_some(), "should find contract: {syms:?}");
let func = syms.iter().find(|s| s.name == "transfer");
assert!(func.is_some(), "should find function: {syms:?}");
}
#[test]
fn solidity_event() {
let src = "contract Token {\n event Transfer(address indexed from, address indexed to, uint256 value);\n}";
let syms = extract("solidity", src, "test.sol");
let event = syms.iter().find(|s| s.name == "Transfer");
assert!(event.is_some(), "should find event: {syms:?}");
assert_eq!(event.unwrap().kind, SymbolKind::Event);
}
#[test]
fn elixir_module_and_function() {
let src = "defmodule MyApp.Users do\n def get_user(id) do\n id\n end\nend";
let syms = extract("elixir", src, "test.ex");
let module = syms.iter().find(|s| s.name == "MyApp.Users");
assert!(module.is_some(), "should find module: {syms:?}");
assert_eq!(module.unwrap().kind, SymbolKind::Module);
let func = syms.iter().find(|s| s.name == "get_user");
assert!(func.is_some(), "should find function: {syms:?}");
assert_eq!(func.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn elixir_type_definitions() {
let src = "defmodule MyApp do\n @type status :: :active | :inactive\n @typep internal :: map()\n @opaque token :: binary()\nend";
let syms = extract("elixir", src, "test.ex");
let status = syms.iter().find(|s| s.name == "status");
assert!(status.is_some(), "should find @type: {syms:?}");
assert_eq!(status.unwrap().kind, SymbolKind::Type);
let internal = syms.iter().find(|s| s.name == "internal");
assert!(internal.is_some(), "should find @typep: {syms:?}");
assert_eq!(internal.unwrap().kind, SymbolKind::Type);
let token = syms.iter().find(|s| s.name == "token");
assert!(token.is_some(), "should find @opaque: {syms:?}");
assert_eq!(token.unwrap().kind, SymbolKind::Type);
}
#[test]
fn elixir_callback() {
let src = "defmodule MyBehaviour do\n @callback validate(term()) :: :ok | {:error, term()}\n @callback format(term()) :: String.t()\nend";
let syms = extract("elixir", src, "test.ex");
let validate = syms.iter().find(|s| s.name == "validate");
assert!(
validate.is_some(),
"should find @callback validate: {syms:?}"
);
assert_eq!(validate.unwrap().kind, SymbolKind::Fn);
let format = syms.iter().find(|s| s.name == "format");
assert!(format.is_some(), "should find @callback format: {syms:?}");
assert_eq!(format.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn elixir_defimpl() {
let src = "defimpl String.Chars, for: MyApp.User do\n def to_string(user), do: user.name\nend";
let syms = extract("elixir", src, "test.ex");
let impl_sym = syms.iter().find(|s| s.name == "String.Chars");
assert!(impl_sym.is_some(), "should find defimpl: {syms:?}");
assert_eq!(impl_sym.unwrap().kind, SymbolKind::Module);
let func = syms.iter().find(|s| s.name == "to_string");
assert!(func.is_some(), "should find function in impl: {syms:?}");
}
#[test]
fn elixir_protocol() {
let src =
"defprotocol Renderable do\n @spec render(t()) :: String.t()\n def render(data)\nend";
let syms = extract("elixir", src, "test.ex");
let proto = syms.iter().find(|s| s.name == "Renderable");
assert!(proto.is_some(), "should find defprotocol: {syms:?}");
assert_eq!(proto.unwrap().kind, SymbolKind::Module);
}
#[test]
fn swift_function() {
let src = "func greet(name: String) -> String {\n return \"Hello, \\(name)\"\n}";
let syms = extract("swift", src, "test.swift");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "greet");
assert_eq!(syms[0].kind, SymbolKind::Fn);
assert!(syms[0].signature.contains("func greet"));
}
#[test]
fn swift_class_and_method() {
let src = "class Animal {\n func speak() -> String {\n return \"...\"\n }\n}";
let syms = extract("swift", src, "test.swift");
let cls = syms.iter().find(|s| s.name == "Animal");
assert!(cls.is_some(), "should find class: {syms:?}");
assert_eq!(cls.unwrap().kind, SymbolKind::Class);
let method = syms.iter().find(|s| s.name == "speak");
assert!(method.is_some(), "should find method: {syms:?}");
assert_eq!(method.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn swift_struct() {
let src = "struct Point {\n var x: Double\n var y: Double\n}";
let syms = extract("swift", src, "test.swift");
let s = syms.iter().find(|s| s.name == "Point");
assert!(s.is_some(), "should find struct: {syms:?}");
assert_eq!(s.unwrap().kind, SymbolKind::Struct);
let x = syms.iter().find(|s| s.name == "x");
assert!(x.is_some(), "should find property x: {syms:?}");
assert_eq!(x.unwrap().kind, SymbolKind::Const);
let y = syms.iter().find(|s| s.name == "y");
assert!(y.is_some(), "should find property y: {syms:?}");
assert_eq!(y.unwrap().kind, SymbolKind::Const);
}
#[test]
fn swift_enum() {
let src = "enum Direction {\n case north, south, east, west\n}";
let syms = extract("swift", src, "test.swift");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "Direction");
assert_eq!(syms[0].kind, SymbolKind::Enum);
}
#[test]
fn swift_enum_methods() {
let src = "enum Direction {\n case north, south\n func opposite() -> Direction {\n switch self {\n case .north: return .south\n default: return .north\n }\n }\n init?(rawValue: String) {\n switch rawValue {\n case \"n\": self = .north\n default: return nil\n }\n }\n}";
let syms = extract("swift", src, "test.swift");
let e = syms.iter().find(|s| s.name == "Direction");
assert!(e.is_some(), "should find enum: {syms:?}");
assert_eq!(e.unwrap().kind, SymbolKind::Enum);
let method = syms.iter().find(|s| s.name == "opposite");
assert!(method.is_some(), "should find method in enum: {syms:?}");
assert_eq!(method.unwrap().kind, SymbolKind::Fn);
let init_sym = syms.iter().find(|s| s.name == "init");
assert!(init_sym.is_some(), "should find init in enum: {syms:?}");
assert_eq!(init_sym.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn swift_protocol() {
let src = "protocol Drawable {\n func draw()\n}";
let syms = extract("swift", src, "test.swift");
let proto = syms.iter().find(|s| s.name == "Drawable");
assert!(proto.is_some(), "should find protocol: {syms:?}");
assert_eq!(proto.unwrap().kind, SymbolKind::Interface);
let draw = syms.iter().find(|s| s.name == "draw");
assert!(draw.is_some(), "should find protocol method: {syms:?}");
assert_eq!(draw.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn swift_typealias() {
let src = "typealias Callback = (Int) -> Void";
let syms = extract("swift", src, "test.swift");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "Callback");
assert_eq!(syms[0].kind, SymbolKind::Type);
}
#[test]
fn swift_init() {
let src = "class Foo {\n init(x: Int) {\n self.x = x\n }\n}";
let syms = extract("swift", src, "test.swift");
let init_sym = syms.iter().find(|s| s.name == "init");
assert!(init_sym.is_some(), "should find init: {syms:?}");
assert_eq!(init_sym.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn swift_deinit() {
let src = "class Foo {\n deinit {\n print(\"bye\")\n }\n}";
let syms = extract("swift", src, "test.swift");
let deinit_sym = syms.iter().find(|s| s.name == "deinit");
assert!(deinit_sym.is_some(), "should find deinit: {syms:?}");
assert_eq!(deinit_sym.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn swift_actor() {
let src = "actor BankAccount {\n var balance: Double\n func deposit(_ amount: Double) {\n balance += amount\n }\n}";
let syms = extract("swift", src, "test.swift");
let actor = syms.iter().find(|s| s.name == "BankAccount");
assert!(actor.is_some(), "should find actor: {syms:?}");
assert_eq!(actor.unwrap().kind, SymbolKind::Class);
let method = syms.iter().find(|s| s.name == "deposit");
assert!(method.is_some(), "should find actor method: {syms:?}");
assert_eq!(method.unwrap().kind, SymbolKind::Fn);
let prop = syms.iter().find(|s| s.name == "balance");
assert!(prop.is_some(), "should find actor property: {syms:?}");
assert_eq!(prop.unwrap().kind, SymbolKind::Const);
}
#[test]
fn swift_extension() {
let src = "extension String {\n func reversed() -> String {\n return String(self.reversed())\n }\n}";
let syms = extract("swift", src, "test.swift");
let ext = syms.iter().find(|s| s.name == "String");
assert!(ext.is_some(), "should find extension: {syms:?}");
assert_eq!(ext.unwrap().kind, SymbolKind::Module);
let method = syms.iter().find(|s| s.name == "reversed");
assert!(method.is_some(), "should find extension method: {syms:?}");
assert_eq!(method.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn swift_extension_constrained() {
let src = "extension Array where Element: Comparable {\n func sorted() -> [Element] {\n return []\n }\n}";
let syms = extract("swift", src, "test.swift");
let ext = syms.iter().find(|s| s.kind == SymbolKind::Module);
assert!(ext.is_some(), "should find constrained extension: {syms:?}");
let method = syms.iter().find(|s| s.name == "sorted");
assert!(
method.is_some(),
"should find method in constrained extension: {syms:?}"
);
assert_eq!(method.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn swift_subscript() {
let src = "struct Matrix {\n subscript(row: Int, col: Int) -> Double {\n return 0.0\n }\n}";
let syms = extract("swift", src, "test.swift");
let sub = syms.iter().find(|s| s.name == "subscript");
assert!(sub.is_some(), "should find subscript: {syms:?}");
assert_eq!(sub.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn swift_protocol_property() {
let src = "protocol Named {\n var name: String { get }\n func greet() -> String\n}";
let syms = extract("swift", src, "test.swift");
let proto = syms.iter().find(|s| s.name == "Named");
assert!(proto.is_some(), "should find protocol: {syms:?}");
assert_eq!(proto.unwrap().kind, SymbolKind::Interface);
let prop = syms.iter().find(|s| s.name == "name");
assert!(prop.is_some(), "should find protocol property: {syms:?}");
assert_eq!(prop.unwrap().kind, SymbolKind::Const);
let method = syms.iter().find(|s| s.name == "greet");
assert!(method.is_some(), "should find protocol method: {syms:?}");
assert_eq!(method.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn dart_class_and_method() {
let src = "class Animal {\n String speak() { return \"...\"; }\n}";
let syms = extract("dart", src, "test.dart");
assert_eq!(syms.len(), 2, "class + method: {syms:?}");
let cls = syms.iter().find(|s| s.name == "Animal");
assert!(cls.is_some(), "should find class: {syms:?}");
assert_eq!(cls.unwrap().kind, SymbolKind::Class);
let method = syms.iter().find(|s| s.name == "speak");
assert!(method.is_some(), "should find method: {syms:?}");
assert_eq!(method.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn dart_mixin() {
let src = "mixin Swimming {\n void swim() { print(\"swimming\"); }\n}";
let syms = extract("dart", src, "test.dart");
assert_eq!(syms.len(), 2, "mixin + method: {syms:?}");
let mixin = syms.iter().find(|s| s.name == "Swimming");
assert!(mixin.is_some(), "should find mixin: {syms:?}");
assert_eq!(mixin.unwrap().kind, SymbolKind::Class);
let method = syms.iter().find(|s| s.name == "swim");
assert!(method.is_some(), "should find mixin method: {syms:?}");
assert_eq!(method.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn dart_extension() {
let src =
"extension StringExt on String {\n String reversed() => split('').reversed.join('');\n}";
let syms = extract("dart", src, "test.dart");
assert_eq!(syms.len(), 2, "extension + method: {syms:?}");
let ext = syms.iter().find(|s| s.name == "StringExt");
assert!(ext.is_some(), "should find extension: {syms:?}");
assert_eq!(ext.unwrap().kind, SymbolKind::Module);
}
#[test]
fn dart_enum() {
let src = "enum Color { red, green, blue }";
let syms = extract("dart", src, "test.dart");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "Color");
assert_eq!(syms[0].kind, SymbolKind::Enum);
}
#[test]
fn dart_top_level_function() {
let src = "void greet(String name) {\n print(\"Hello $name\");\n}";
let syms = extract("dart", src, "test.dart");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "greet");
assert_eq!(syms[0].kind, SymbolKind::Fn);
assert!(
syms[0].signature.contains("greet"),
"sig: {}",
syms[0].signature
);
}
#[test]
fn dart_typedef() {
let src = "typedef Callback = void Function(int);";
let syms = extract("dart", src, "test.dart");
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "Callback");
assert_eq!(syms[0].kind, SymbolKind::Type);
}
#[test]
fn dart_getter_setter() {
let src =
"class Foo {\n String get displayName => \"\";\n set displayName(String value) {}\n}";
let syms = extract("dart", src, "test.dart");
assert_eq!(syms.len(), 3, "class + getter + setter: {syms:?}");
let getters_setters: Vec<_> = syms.iter().filter(|s| s.name == "displayName").collect();
assert_eq!(
getters_setters.len(),
2,
"should find getter and setter: {syms:?}"
);
assert!(getters_setters.iter().all(|s| s.kind == SymbolKind::Fn));
}
#[test]
fn dart_constructor() {
let src = "class Animal {\n Animal(this.name);\n}";
let syms = extract("dart", src, "test.dart");
assert_eq!(syms.len(), 2, "class + constructor: {syms:?}");
let ctor = syms
.iter()
.find(|s| s.name == "Animal" && s.kind == SymbolKind::Fn);
assert!(ctor.is_some(), "should find constructor: {syms:?}");
}
#[test]
fn dart_named_constructor() {
let src = "class Point {\n final int x;\n Point(this.x);\n Point.origin() : x = 0;\n}";
let syms = extract("dart", src, "test.dart");
let named = syms
.iter()
.find(|s| s.name == "origin" && s.kind == SymbolKind::Fn);
assert!(
named.is_some(),
"should find named constructor 'origin': {syms:?}"
);
let unnamed = syms
.iter()
.find(|s| s.name == "Point" && s.kind == SymbolKind::Fn);
assert!(
unnamed.is_some(),
"should find unnamed constructor: {syms:?}"
);
}
#[test]
fn dart_factory_constructor() {
let src = "class Animal {\n factory Animal.create(String name) => Animal(name);\n}";
let syms = extract("dart", src, "test.dart");
let factory = syms
.iter()
.find(|s| s.kind == SymbolKind::Fn && s.name == "create");
assert!(
factory.is_some(),
"should find named factory 'create': {syms:?}"
);
}
#[test]
fn dart_abstract_method() {
let src =
"abstract class Repo {\n Future<int> getById(int id);\n Future<void> save(int item);\n}";
let syms = extract("dart", src, "test.dart");
assert_eq!(syms.len(), 3, "class + 2 abstract methods: {syms:?}");
let get_by_id = syms.iter().find(|s| s.name == "getById");
assert!(get_by_id.is_some(), "should find abstract method: {syms:?}");
assert_eq!(get_by_id.unwrap().kind, SymbolKind::Fn);
let save = syms.iter().find(|s| s.name == "save");
assert!(save.is_some(), "should find abstract method save: {syms:?}");
assert_eq!(save.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn dart_static_method() {
let src = "class Utils {\n static void doStuff() {}\n}";
let syms = extract("dart", src, "test.dart");
assert_eq!(
syms.len(),
2,
"class + static method (no duplicates): {syms:?}"
);
let method = syms.iter().find(|s| s.name == "doStuff");
assert!(method.is_some(), "should find static method: {syms:?}");
assert_eq!(method.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn dart_operator_overload() {
let src = "class Vector {\n Vector operator +(Vector other) => Vector();\n}";
let syms = extract("dart", src, "test.dart");
assert_eq!(syms.len(), 2, "class + operator: {syms:?}");
let op = syms
.iter()
.find(|s| s.name == "operator" && s.kind == SymbolKind::Fn);
assert!(op.is_some(), "should find operator overload: {syms:?}");
assert!(
op.unwrap().signature.contains("operator +"),
"sig should show operator: {}",
op.unwrap().signature
);
}
#[test]
fn dart_extension_getter() {
let src = "extension StringUtils on String {\n bool get isBlank => trim().isEmpty;\n String capitalize() => this[0] + substring(1);\n}";
let syms = extract("dart", src, "test.dart");
assert_eq!(syms.len(), 3, "extension + getter + method: {syms:?}");
let getter = syms.iter().find(|s| s.name == "isBlank");
assert!(getter.is_some(), "should find extension getter: {syms:?}");
assert_eq!(getter.unwrap().kind, SymbolKind::Fn);
let method = syms.iter().find(|s| s.name == "capitalize");
assert!(method.is_some(), "should find extension method: {syms:?}");
assert_eq!(method.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn dart_enum_getter() {
let src = "enum Status {\n active;\n String get label => name.toUpperCase();\n}";
let syms = extract("dart", src, "test.dart");
assert_eq!(syms.len(), 2, "enum + getter: {syms:?}");
let e = syms.iter().find(|s| s.name == "Status");
assert!(e.is_some(), "should find enum: {syms:?}");
assert_eq!(e.unwrap().kind, SymbolKind::Enum);
let getter = syms.iter().find(|s| s.name == "label");
assert!(getter.is_some(), "should find enum getter: {syms:?}");
assert_eq!(getter.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn dart_sealed_class() {
let src = "sealed class Shape {}\nbase class Circle extends Shape {}\ninterface class Printable {}\nmixin class Both {}";
let syms = extract("dart", src, "test.dart");
assert_eq!(syms.len(), 4, "4 class variants: {syms:?}");
let names: Vec<&str> = syms.iter().map(|s| s.name.as_str()).collect();
assert!(
names.contains(&"Shape"),
"should find sealed class: {syms:?}"
);
assert!(
names.contains(&"Circle"),
"should find base class: {syms:?}"
);
assert!(
names.contains(&"Printable"),
"should find interface class: {syms:?}"
);
assert!(names.contains(&"Both"), "should find mixin class: {syms:?}");
assert!(syms.iter().all(|s| s.kind == SymbolKind::Class));
}
#[test]
fn dart_extension_type() {
let src = "extension type Wrapper(int value) {\n int get doubled => value * 2;\n}";
let syms = extract("dart", src, "test.dart");
assert_eq!(syms.len(), 2, "extension type + getter: {syms:?}");
let ext = syms.iter().find(|s| s.name == "Wrapper");
assert!(ext.is_some(), "should find extension type: {syms:?}");
assert_eq!(ext.unwrap().kind, SymbolKind::Class);
let getter = syms.iter().find(|s| s.name == "doubled");
assert!(
getter.is_some(),
"should find extension type getter: {syms:?}"
);
assert_eq!(getter.unwrap().kind, SymbolKind::Fn);
}
#[test]
fn refs_rust_finds_all_usages() {
init_grammar_cache();
let src = "struct Foo { x: i32 }\nfn bar(f: Foo) -> Foo { f }";
let refs = find_references("rust", src.as_bytes(), &PathBuf::from("test.rs"), "Foo").unwrap();
assert_eq!(
refs.len(),
3,
"should find struct def + 2 usages: {:?}",
refs.iter().map(|r| r.line).collect::<Vec<_>>()
);
}
#[test]
fn refs_rust_no_match() {
init_grammar_cache();
let src = "fn main() {}";
let refs = find_references(
"rust",
src.as_bytes(),
&PathBuf::from("test.rs"),
"nonexistent",
)
.unwrap();
assert!(refs.is_empty());
}
#[test]
fn refs_line_column_correct() {
init_grammar_cache();
let src = "let x = 1;\nlet y = x + x;";
let refs = find_references("rust", src.as_bytes(), &PathBuf::from("test.rs"), "x").unwrap();
assert_eq!(refs.len(), 3);
assert_eq!(refs[0].line, 1);
assert_eq!(refs[1].line, 2);
assert_eq!(refs[2].line, 2);
}
#[test]
fn refs_typescript_identifier() {
init_grammar_cache();
let src = "const foo = 1;\nconsole.log(foo);";
let refs = find_references(
"typescript",
src.as_bytes(),
&PathBuf::from("test.ts"),
"foo",
)
.unwrap();
assert_eq!(refs.len(), 2);
}
#[test]
fn refs_python_identifier() {
init_grammar_cache();
let src = "def greet(name):\n return name";
let refs =
find_references("python", src.as_bytes(), &PathBuf::from("test.py"), "name").unwrap();
assert_eq!(refs.len(), 2);
}
#[test]
fn rust_test_attribute_detected() {
let syms = extract(
"rust",
"#[test]\nfn test_foo() { assert!(true); }",
"test.rs",
);
let sym = syms.iter().find(|s| s.name == "test_foo").unwrap();
assert!(
sym.is_test,
"function with #[test] should be marked as test"
);
}
#[test]
fn rust_cfg_test_mod_detected() {
let src = r"
fn main() {}
#[cfg(test)]
mod tests {
fn helper() {}
}
";
let syms = extract("rust", src, "lib.rs");
let main_sym = syms.iter().find(|s| s.name == "main").unwrap();
assert!(!main_sym.is_test, "main should not be marked as test");
let tests_mod = syms.iter().find(|s| s.name == "tests").unwrap();
assert!(
tests_mod.is_test,
"#[cfg(test)] mod should be marked as test"
);
if let Some(helper) = syms.iter().find(|s| s.name == "helper") {
assert!(
helper.is_test,
"function inside #[cfg(test)] mod should be marked as test"
);
}
}
#[test]
fn rust_normal_fn_not_test() {
let syms = extract(
"rust",
"pub fn add(a: i32, b: i32) -> i32 { a + b }",
"lib.rs",
);
let sym = syms.iter().find(|s| s.name == "add").unwrap();
assert!(!sym.is_test, "normal function should not be marked as test");
}
#[test]
fn rust_test_and_normal_mixed() {
let src = r"
pub fn real_fn() {}
#[test]
fn test_real_fn() {}
pub struct MyStruct;
";
let syms = extract("rust", src, "lib.rs");
let real_fn = syms.iter().find(|s| s.name == "real_fn").unwrap();
assert!(!real_fn.is_test);
let test_fn = syms.iter().find(|s| s.name == "test_real_fn").unwrap();
assert!(test_fn.is_test);
let my_struct = syms.iter().find(|s| s.name == "MyStruct").unwrap();
assert!(!my_struct.is_test);
}
#[test]
fn go_struct_interface_const_field() {
let src = "package main\ntype MyStruct struct {\n Name string\n}\ntype MyInterface interface {\n DoSomething() error\n}\ntype Duration int64\nconst MaxRetries = 3\n";
let syms = extract("go", src, "test.go");
let ms = syms.iter().find(|s| s.name == "MyStruct").unwrap();
assert_eq!(ms.kind, SymbolKind::Struct);
let mi = syms.iter().find(|s| s.name == "MyInterface").unwrap();
assert_eq!(mi.kind, SymbolKind::Interface);
let name = syms.iter().find(|s| s.name == "Name").unwrap();
assert_eq!(name.kind, SymbolKind::Field);
let ds = syms.iter().find(|s| s.name == "DoSomething").unwrap();
assert_eq!(ds.kind, SymbolKind::Fn);
let dur = syms.iter().find(|s| s.name == "Duration").unwrap();
assert_eq!(dur.kind, SymbolKind::Type);
let mr = syms.iter().find(|s| s.name == "MaxRetries").unwrap();
assert_eq!(mr.kind, SymbolKind::Const);
}
#[test]
fn rust_const_and_static() {
let src = "pub const MAX_SIZE: usize = 1024;\nstatic GLOBAL: &str = \"hello\";";
let syms = extract("rust", src, "test.rs");
let c = syms.iter().find(|s| s.name == "MAX_SIZE").unwrap();
assert_eq!(c.kind, SymbolKind::Const);
let s = syms.iter().find(|s| s.name == "GLOBAL").unwrap();
assert_eq!(s.kind, SymbolKind::Const);
}
#[test]
fn go_type_alias() {
let src = "package main\ntype Byte = uint8\n";
let syms = extract("go", src, "test.go");
let b = syms.iter().find(|s| s.name == "Byte").unwrap();
assert_eq!(b.kind, SymbolKind::Type);
}
#[test]
fn java_constructor_and_record() {
let src = "public class Foo {\n public Foo(int x) {}\n}\n";
let syms = extract("java", src, "Test.java");
let ctor = syms
.iter()
.find(|s| s.name == "Foo" && s.kind == SymbolKind::Fn);
assert!(ctor.is_some(), "should find constructor: {syms:?}");
}
#[test]
fn java_field_and_enum_constant() {
let src = "public class Config {\n private int port;\n}\nenum Color { RED, GREEN, BLUE }";
let syms = extract("java", src, "Test.java");
let field = syms.iter().find(|s| s.name == "port").unwrap();
assert_eq!(field.kind, SymbolKind::Field);
let red = syms.iter().find(|s| s.name == "RED").unwrap();
assert_eq!(red.kind, SymbolKind::Const);
}
#[test]
fn cpp_namespace() {
let src = "namespace utils {\n void helper() {}\n}";
let syms = extract("cpp", src, "test.cpp");
let ns = syms.iter().find(|s| s.name == "utils").unwrap();
assert_eq!(ns.kind, SymbolKind::Module);
let fn_ = syms.iter().find(|s| s.name == "helper").unwrap();
assert_eq!(fn_.kind, SymbolKind::Fn);
}
#[test]
fn cpp_template_class() {
let src = "template<typename T>\nclass Container {\npublic:\n T value;\n};";
let syms = extract("cpp", src, "test.cpp");
let c = syms.iter().find(|s| s.name == "Container").unwrap();
assert_eq!(c.kind, SymbolKind::Class);
}
#[test]
fn cpp_using_alias() {
let src = "using MyInt = int;";
let syms = extract("cpp", src, "test.cpp");
let t = syms.iter().find(|s| s.name == "MyInt").unwrap();
assert_eq!(t.kind, SymbolKind::Type);
}
#[test]
fn cpp_header_declarations_with_bom() {
let src = "\u{feff}#pragma once\nclass Guide2DLineDemoAlgorithm {\npublic:\n QString AlgorithmId() const;\n bool Validate(const GuideAlgorithmDataView& ctx, QString& errorMsg) const;\n};";
let syms = extract("cpp", src, "Guide2DLineDemoAlgorithm.h");
let class = syms
.iter()
.find(|s| s.name == "Guide2DLineDemoAlgorithm")
.unwrap();
assert_eq!(class.kind, SymbolKind::Class);
let algorithm_id = syms
.iter()
.find(|s| s.name == "AlgorithmId")
.unwrap_or_else(|| panic!("should find AlgorithmId declaration: {syms:?}"));
assert_eq!(algorithm_id.kind, SymbolKind::Fn);
let validate = syms
.iter()
.find(|s| s.name == "Validate")
.unwrap_or_else(|| panic!("should find Validate declaration: {syms:?}"));
assert_eq!(validate.kind, SymbolKind::Fn);
}
#[test]
fn c_union() {
let src = "union Data {\n int i;\n float f;\n};";
let syms = extract("c", src, "test.c");
let u = syms.iter().find(|s| s.name == "Data").unwrap();
assert_eq!(u.kind, SymbolKind::Struct);
}
#[test]
fn c_function_prototype() {
let src = "void foo(int x);\nint bar(void);";
let syms = extract("c", src, "test.c");
assert_eq!(syms.len(), 2);
assert!(syms.iter().all(|s| s.kind == SymbolKind::Fn));
}
#[test]
fn solidity_modifier_and_state_var() {
let src = "contract Vault {\n uint256 public balance;\n modifier onlyOwner() { _; }\n}";
let syms = extract("solidity", src, "test.sol");
let bal = syms.iter().find(|s| s.name == "balance").unwrap();
assert_eq!(bal.kind, SymbolKind::Field);
let m = syms.iter().find(|s| s.name == "onlyOwner").unwrap();
assert_eq!(m.kind, SymbolKind::Fn);
}
#[test]
fn solidity_error_declaration() {
let src = "error Unauthorized(address caller);";
let syms = extract("solidity", src, "test.sol");
let e = syms.iter().find(|s| s.name == "Unauthorized").unwrap();
assert_eq!(e.kind, SymbolKind::Type);
}
#[test]
fn ruby_constant() {
let src = "MAX_SIZE = 100\nclass Foo\nend";
let syms = extract("ruby", src, "test.rb");
let c = syms.iter().find(|s| s.name == "MAX_SIZE").unwrap();
assert_eq!(c.kind, SymbolKind::Const);
}
#[test]
fn lua_top_level_local() {
let src = "local M = {}\nfunction M.setup()\nend\n";
let syms = extract("lua", src, "test.lua");
let m = syms.iter().find(|s| s.name == "M").unwrap();
assert_eq!(m.kind, SymbolKind::Const);
let setup = syms.iter().find(|s| s.name == "setup").unwrap();
assert_eq!(setup.kind, SymbolKind::Fn);
}
#[test]
fn bash_top_level_var() {
let src = "#!/bin/bash\nVERSION=\"1.0\"\nfoo() {\n local x=1\n}\n";
let syms = extract("bash", src, "test.sh");
let v = syms.iter().find(|s| s.name == "VERSION").unwrap();
assert_eq!(v.kind, SymbolKind::Const);
let f = syms.iter().find(|s| s.name == "foo").unwrap();
assert_eq!(f.kind, SymbolKind::Fn);
assert!(
!syms.iter().any(|s| s.name == "x"),
"local vars should not be captured"
);
}