Skip to content

Commit 5ac2ec9

Browse files
committed
Enable Kotlin LSP fixture coverage
1 parent bcdcd42 commit 5ac2ec9

6 files changed

Lines changed: 82 additions & 19 deletions

File tree

ast/src/repo.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ impl Repo {
567567
Ok(())
568568
}
569569
fn start_lsp(root: &str, lang: &Lang, lsp: bool) -> Result<Option<CmdSender>> {
570-
Ok(if lsp {
570+
Ok(if lsp && lang.kind.has_lsp_support() {
571571
let (tx, rx) = tokio::sync::mpsc::channel(10000);
572572
spawn_analyzer(&root.into(), &lang.kind, rx)?;
573573
Some(tx)

ast/src/testing/annotations.rs

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -108,19 +108,34 @@ fn parse_meta_filter(s: &str) -> BTreeMap<String, String> {
108108
map
109109
}
110110

111+
fn strip_mode_annotation<'a>(
112+
trimmed: &'a str,
113+
prefix: &str,
114+
keyword: &str,
115+
use_lsp: bool,
116+
) -> Option<&'a str> {
117+
let common_prefix = format!("{}{}: ", prefix, keyword);
118+
if let Some(rest) = trimmed.strip_prefix(common_prefix.as_str()) {
119+
return Some(rest);
120+
}
121+
122+
let mode = if use_lsp { "lsp" } else { "no_lsp" };
123+
let mode_prefix = format!("{}{}_{}: ", prefix, keyword, mode);
124+
trimmed.strip_prefix(mode_prefix.as_str())
125+
}
126+
111127
#[derive(Debug, Clone)]
112128
struct AbsentAnnotation {
113129
node_type: NodeType,
114130
name: String,
115131
file_suffix: String,
116132
}
117133

118-
fn parse_absent_annotations(source: &str, prefix: &str) -> Vec<AbsentAnnotation> {
119-
let absent_prefix = format!("{}absent: ", prefix);
134+
fn parse_absent_annotations(source: &str, prefix: &str, use_lsp: bool) -> Vec<AbsentAnnotation> {
120135
let mut result = Vec::new();
121136
for line in source.lines() {
122137
let trimmed = line.trim();
123-
if let Some(rest) = trimmed.strip_prefix(absent_prefix.as_str()) {
138+
if let Some(rest) = strip_mode_annotation(trimmed, prefix, "absent", use_lsp) {
124139
let toks = parse_quoted_tokens(rest);
125140
if toks.len() >= 3 {
126141
if let Some(nt) = parse_node_type(&toks[0]) {
@@ -139,15 +154,16 @@ fn parse_absent_annotations(source: &str, prefix: &str) -> Vec<AbsentAnnotation>
139154
fn parse_file_annotations(
140155
source: &str,
141156
prefix: &str,
157+
use_lsp: bool,
142158
) -> Vec<(NodeType, String, BTreeMap<String, String>, Vec<EdgeAnnotation>)> {
143159
let mut result = Vec::new();
144160
let mut current: Option<(NodeType, String, BTreeMap<String, String>, Vec<EdgeAnnotation>)> =
145161
None;
146162

147163
for line in source.lines() {
148164
let trimmed = line.trim();
149-
if let Some(rest) = trimmed.strip_prefix(prefix) {
150-
if let Some(node_rest) = rest.strip_prefix("node: ") {
165+
if trimmed.strip_prefix(prefix).is_some() {
166+
if let Some(node_rest) = strip_mode_annotation(trimmed, prefix, "node", use_lsp) {
151167
if let Some(prev) = current.take() {
152168
result.push(prev);
153169
}
@@ -158,7 +174,7 @@ fn parse_file_annotations(
158174
current = Some((nt, toks[1].clone(), subject_meta, Vec::new()));
159175
}
160176
}
161-
} else if let Some(edge_rest) = rest.strip_prefix("edge: ") {
177+
} else if let Some(edge_rest) = strip_mode_annotation(trimmed, prefix, "edge", use_lsp) {
162178
if let Some((_, _, _, ref mut edges)) = current {
163179
let toks = parse_quoted_tokens(edge_rest);
164180
if toks.len() >= 5 {
@@ -199,9 +215,9 @@ fn annotation_prefix_for_ext(ext: &str, default: &'static str) -> &'static str {
199215
}
200216
}
201217

202-
pub fn verify_file(source: &str, file_suffix: &str, graph: &impl Graph, prefix: &str) -> (Vec<String>, BTreeMap<NodeType, usize>) {
203-
let groups = parse_file_annotations(source, prefix);
204-
let absent = parse_absent_annotations(source, prefix);
218+
pub fn verify_file(source: &str, file_suffix: &str, graph: &impl Graph, prefix: &str, use_lsp: bool) -> (Vec<String>, BTreeMap<NodeType, usize>) {
219+
let groups = parse_file_annotations(source, prefix, use_lsp);
220+
let absent = parse_absent_annotations(source, prefix, use_lsp);
205221
let mut failures: Vec<String> = Vec::new();
206222
let mut counts: BTreeMap<NodeType, usize> = BTreeMap::new();
207223

@@ -296,12 +312,12 @@ pub fn verify_file(source: &str, file_suffix: &str, graph: &impl Graph, prefix:
296312
(failures, counts)
297313
}
298314

299-
pub fn walk_and_verify(fixture_dir: &Path, root: &Path, graph: &impl Graph, lang: &Language) -> Vec<String> {
315+
pub fn walk_and_verify(fixture_dir: &Path, root: &Path, graph: &impl Graph, lang: &Language, use_lsp: bool) -> Vec<String> {
300316
let mut failures = Vec::new();
301317
let mut counts: BTreeMap<NodeType, usize> = BTreeMap::new();
302318
let exts: Vec<&str> = lang.exts();
303319
let skip_dirs: Vec<&str> = lang.skip_dirs();
304-
walk_impl(fixture_dir, root, graph, &mut failures, &mut counts, &exts, &skip_dirs, lang);
320+
walk_impl(fixture_dir, root, graph, &mut failures, &mut counts, &exts, &skip_dirs, lang, use_lsp);
305321
for (node_type, expected) in &counts {
306322
let actual = graph
307323
.find_nodes_by_type(node_type.clone())
@@ -327,6 +343,7 @@ fn walk_impl(
327343
exts: &[&str],
328344
skip_dirs: &[&str],
329345
lang: &Language,
346+
use_lsp: bool,
330347
) {
331348
let Ok(read) = std::fs::read_dir(dir) else {
332349
return;
@@ -341,7 +358,7 @@ fn walk_impl(
341358
if skip_dirs.contains(&dir_name) {
342359
continue;
343360
}
344-
walk_impl(&path, root, graph, failures, counts, exts, skip_dirs, lang);
361+
walk_impl(&path, root, graph, failures, counts, exts, skip_dirs, lang, use_lsp);
345362
} else {
346363
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
347364
if !exts.contains(&ext) {
@@ -358,7 +375,7 @@ fn walk_impl(
358375
.map(|p| p.to_string_lossy().to_string())
359376
.unwrap_or_else(|_| path.to_string_lossy().to_string());
360377
let file_prefix = annotation_prefix_for_ext(ext, lang.annotation_prefix());
361-
let (file_failures, file_counts) = verify_file(&src, &suffix, graph, file_prefix);
378+
let (file_failures, file_counts) = verify_file(&src, &suffix, graph, file_prefix, use_lsp);
362379
failures.extend(file_failures);
363380
for (nt, n) in file_counts {
364381
*counts.entry(nt).or_insert(0) += n;
@@ -371,11 +388,20 @@ pub async fn run_fixture_test<G: Graph + Sync>(
371388
subdir: &str,
372389
lang: &str,
373390
annotation_lang: Language,
391+
) -> Result<()> {
392+
run_fixture_test_with_lsp::<G>(subdir, lang, annotation_lang, false).await
393+
}
394+
395+
pub async fn run_fixture_test_with_lsp<G: Graph + Sync>(
396+
subdir: &str,
397+
lang: &str,
398+
annotation_lang: Language,
399+
use_lsp: bool,
374400
) -> Result<()> {
375401
let repo = Repo::new(
376402
subdir,
377403
Lang::from_str(lang).unwrap(),
378-
false,
404+
use_lsp,
379405
Vec::new(),
380406
Vec::new(),
381407
)
@@ -385,7 +411,7 @@ pub async fn run_fixture_test<G: Graph + Sync>(
385411
graph.analysis();
386412
let fixture_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join(subdir);
387413
let root = Path::new(env!("CARGO_MANIFEST_DIR"));
388-
let failures = walk_and_verify(&fixture_dir, root, &graph, &annotation_lang);
414+
let failures = walk_and_verify(&fixture_dir, root, &graph, &annotation_lang, use_lsp);
389415
if !failures.is_empty() {
390416
for f in &failures {
391417
eprintln!("{}", f);

ast/src/testing/kotlin/app/src/main/java/com/kotlintestapp/MainActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package com.kotlintestapp
66
// @ast node: Function "PersonItem"
77
// @ast node: Function "PersonList"
88
// @ast edge: Calls -> Function "PersonItem" "MainActivity.kt"
9+
// @ast edge_lsp: Calls -> Function "updatePerson" "PersonViewModel.kt"
910
// @ast node: Function "UpdateProfileDialog"
1011
// @ast node: Import "import-imports-srctestingkotlinappsrcmainjavacomkotlintestappmainactivitykt-0"
1112

@@ -150,4 +151,3 @@ fun UpdateProfileDialog(
150151
}
151152
)
152153
}
153-

ast/src/testing/kotlin/app/src/main/java/com/kotlintestapp/ui/viewmodels/HomeViewModel.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package com.kotlintestapp.ui.viewmodels
44
// @ast edge: Operand -> Function "onUserClicked" "HomeViewModel.kt"
55
// @ast node: DataModel "HomeViewModel"
66
// @ast node: Function "fetchUsers"
7+
// @ast edge_lsp: Calls -> Function "getUsers" "UserRepository.kt"
78
// @ast node: Function "onUserClicked"
89
// @ast node: Import "import-imports-srctestingkotlinappsrcmainjavacomkotlintestappuiviewmodelshomeviewmodelkt-0"
910

ast/src/testing/mod.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::str::FromStr;
66
pub mod annotations;
77
pub mod bash_toml;
88

9-
use annotations::run_fixture_test;
9+
use annotations::{run_fixture_test, run_fixture_test_with_lsp};
1010

1111
#[cfg(test)]
1212
pub mod builder;
@@ -220,6 +220,39 @@ async fn test_ruby() {
220220
}
221221
}
222222

223+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
224+
async fn test_kotlin() {
225+
let use_lsp = Language::Kotlin.default_do_lsp();
226+
227+
#[cfg(not(feature = "neo4j"))]
228+
{
229+
run_fixture_test_with_lsp::<ArrayGraph>(
230+
"src/testing/kotlin",
231+
"kotlin",
232+
Language::Kotlin,
233+
use_lsp,
234+
).await.unwrap();
235+
run_fixture_test_with_lsp::<BTreeMapGraph>(
236+
"src/testing/kotlin",
237+
"kotlin",
238+
Language::Kotlin,
239+
use_lsp,
240+
).await.unwrap();
241+
}
242+
#[cfg(feature = "neo4j")]
243+
{
244+
use crate::{lang::graphs::Neo4jGraph, testing::annotations::run_fixture_test_with_lsp};
245+
let graph = Neo4jGraph::default();
246+
graph.clear().await.unwrap();
247+
run_fixture_test_with_lsp::<Neo4jGraph>(
248+
"src/testing/kotlin",
249+
"kotlin",
250+
Language::Kotlin,
251+
use_lsp,
252+
).await.unwrap();
253+
}
254+
}
255+
223256
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
224257
async fn test_c() {
225258
#[cfg(not(feature = "neo4j"))]

lsp/src/language.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ impl Language {
168168
pub fn default_do_lsp(&self) -> bool {
169169
if let Ok(use_lsp) = std::env::var("USE_LSP") {
170170
if use_lsp == "true" || use_lsp == "1" {
171-
return matches!(self, Self::Rust | Self::Go | Self::Typescript | Self::Java);
171+
return matches!(self, Self::Rust | Self::Go | Self::Typescript | Self::Java | Self::Kotlin);
172172
}
173173
}
174174
false
@@ -445,6 +445,7 @@ mod tests {
445445
assert!(Language::Go.default_do_lsp());
446446
assert!(Language::Typescript.default_do_lsp());
447447
assert!(Language::Java.default_do_lsp());
448+
assert!(Language::Kotlin.default_do_lsp());
448449
assert!(!Language::Python.default_do_lsp());
449450
assert!(!Language::Ruby.default_do_lsp());
450451
std::env::remove_var("USE_LSP");
@@ -457,6 +458,7 @@ mod tests {
457458
assert!(Language::Go.default_do_lsp());
458459
assert!(Language::Typescript.default_do_lsp());
459460
assert!(Language::Java.default_do_lsp());
461+
assert!(Language::Kotlin.default_do_lsp());
460462
assert!(!Language::Python.default_do_lsp());
461463
std::env::remove_var("USE_LSP");
462464
}
@@ -468,6 +470,7 @@ mod tests {
468470
assert!(!Language::Go.default_do_lsp());
469471
assert!(!Language::Typescript.default_do_lsp());
470472
assert!(!Language::Java.default_do_lsp());
473+
assert!(!Language::Kotlin.default_do_lsp());
471474
assert!(!Language::Python.default_do_lsp());
472475
}
473476
}

0 commit comments

Comments
 (0)