From fa5d6d3d47e269f979b15f3f8078469c9d0a516e Mon Sep 17 00:00:00 2001 From: Vadim Lazuk Date: Fri, 6 Nov 2026 20:04:59 +0300 Subject: [PATCH] fix(mybatis): skip method-level annotations when parsing method declarations (#1759) (#1793) Signed-off-by: Inf1mum Co-authored-by: arookieofc <2128194521@qq.com> --- plugins/official/mybatis/dist/plugin.wasm | Bin 168367 -> 168222 bytes .../official/mybatis/dist/plugin.wasm.sources | 2 +- plugins/official/mybatis/src/lib.rs | 285 ++++++++++++++---- 3 files changed, 228 insertions(+), 59 deletions(-) diff --git a/plugins/official/mybatis/dist/plugin.wasm b/plugins/official/mybatis/dist/plugin.wasm index 0966aff50dbfdb900342bbdbf04843663b5ece0d..8d1bf5357a6a71262e48e1605fc1c0cdf69fdc47 100644 GIT binary patch delta 28070 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 diff --git a/plugins/official/mybatis/dist/plugin.wasm.sources b/plugins/official/mybatis/dist/plugin.wasm.sources index f336bc551..6d6a5ce8a 100644 --- a/plugins/official/mybatis/dist/plugin.wasm.sources +++ b/plugins/official/mybatis/dist/plugin.wasm.sources @@ -1 +1 @@ -b3c715c1d3489db1c9f6067f66aa31623ffd23ac6ad9d9fb9cc3b5d548088473 +a6d1331afb12c8b215d8dc4450b8c4a424a6951b359dadb925da5ef0c52ecedc diff --git a/plugins/official/mybatis/src/lib.rs b/plugins/official/mybatis/src/lib.rs index f84cd0188..6b99bb69b 100644 --- a/plugins/official/mybatis/src/lib.rs +++ b/plugins/official/mybatis/src/lib.rs @@ -70,51 +70,156 @@ fn ident(c: char) -> bool { c.is_alphanumeric() || c == '_' || c == '$' } -/// Index of the `(` opening a method's parameter list inside a declaration -/// fragment. `(`s that belong to annotation arguments — `@Anno(…)` — are -/// skipped so `@DataPermission(column = "id") int count()` finds the paren -/// after `count`, not the one after `DataPermission`. -fn method_paren(stmt: &str) -> Option { - let mut rest = stmt; - let mut base = 0; - while let Some(i) = rest.find('(') { - // Is the token right before this `(` an annotation name (`@Anno`, `@a.b.Anno`)? - let before = rest[..i].trim_end(); - let annotation = before.rfind('@').is_some_and(|a| before[a + 1..].chars().all(|c| ident(c) || c == '.')); - if !annotation { - return Some(base + i); +/// The index just past the string or char literal opening at `bytes[i]`. +fn skip_string(bytes: &[u8], mut i: usize) -> usize { + let quote = bytes[i]; + i += 1; + while i < bytes.len() { + match bytes[i] { + b'\\' => i += 2, + b if b == quote => return i + 1, + _ => i += 1, } - // Skip the annotation's argument list (it may nest and hold literals). - let mut depth = 1usize; - let mut end = None; - let mut chars = rest[i + 1..].char_indices().peekable(); - while let Some((j, c)) = chars.next() { - match c { - '"' | '\'' => { - for (_, d) in chars.by_ref() { - if d == c { - break; - } - } + } + i +} + +/// The index just past the `//…` or `/*…*/` comment opening at `bytes[i]`. +fn skip_comment(bytes: &[u8], mut i: usize) -> usize { + if bytes.get(i + 1) == Some(&b'/') { + bytes[i + 2..].iter().position(|&b| b == b'\n').map_or(bytes.len(), |n| i + 2 + n) + } else { + i += 2; + while i + 1 < bytes.len() && (bytes[i] != b'*' || bytes[i + 1] != b'/') { + i += 1; + } + (i + 2).min(bytes.len()) + } +} + +/// A method declaration in a fragment of an interface's body: the name's byte +/// range and its parameter list's `(` and `)` (offsets in the fragment). +#[derive(Debug, PartialEq, Eq)] +struct Declaration { + name: (usize, usize), + parens: (usize, usize), +} + +/// The method declaration in `stmt`, skipping comments, strings and annotations +/// on the way: an annotation's arguments nest parentheses and may hold strings +/// and comments, so the name is the identifier before the first `(` left over — +/// `@Select("…") User find(…)` names `find`, not `Select`. Anything past that +/// `(` (where `@Param(…)` lives) only matters for its matching `)`, so it never +/// confuses the scan. +fn scan_declaration(stmt: &str) -> Option { + let bytes = stmt.as_bytes(); + let mut i = 0; + let mut nested = 0usize; // paren depth inside an annotation's arguments + while i < bytes.len() { + match bytes[i] { + b' ' | b'\t' | b'\r' | b'\n' => i += 1, + b'/' if matches!(bytes.get(i + 1), Some(b'/' | b'*')) => i = skip_comment(bytes, i), + b'"' | b'\'' => i = skip_string(bytes, i), + b'@' if nested == 0 => { + // The annotation's qualified name, then (optionally) its arguments. + i += 1; + while i < bytes.len() && (ident(bytes[i] as char) || bytes[i] == b'.') { + i += 1; } - '(' => depth += 1, - ')' => { - depth -= 1; - if depth == 0 { - end = Some(i + 1 + j + 1); - break; - } + let mut j = i; + while j < bytes.len() && bytes[j].is_ascii_whitespace() { + j += 1; + } + if bytes.get(j) == Some(&b'(') { + nested = 1; + i = j + 1; } - _ => {} } + b'(' if nested > 0 => { + nested += 1; + i += 1; + } + b')' if nested > 0 => { + nested -= 1; + i += 1; + } + b'(' => { + let before = stmt[..i].trim_end(); + let start = before.rfind(|c: char| !ident(c)).map_or(0, |p| p + 1); + if start == before.len() { + return None; + } + // The matching `)`, minding strings and nested parens. + let (mut close, mut depth) = (i + 1, 1usize); + while close < bytes.len() { + match bytes[close] { + b'"' | b'\'' => close = skip_string(bytes, close), + b'/' if matches!(bytes.get(close + 1), Some(b'/' | b'*')) => close = skip_comment(bytes, close), + b'(' => { + depth += 1; + close += 1; + } + b')' => { + depth -= 1; + if depth == 0 { + return Some(Declaration { name: (start, before.len()), parens: (i, close) }); + } + close += 1; + } + _ => close += 1, + } + } + return None; // an unterminated parameter list is no declaration + } + _ => i += 1, } - let skip = end?; - base += skip; - rest = &rest[skip..]; } None } +/// The parameter names in a declaration's parameter list: `@Param("…")` wins, +/// otherwise the parameter's last identifier. Split at commas outside nested +/// parens, braces, brackets and strings, so `@Size(min = 1, max = 5) String n` +/// stays one parameter and a `@Param("a,b")` keeps its whole name. +fn params_of(list: &str) -> Vec { + let bytes = list.as_bytes(); + let mut parts = Vec::new(); + let (mut depth, mut start) = (0usize, 0usize); + let mut i = 0; + while i < bytes.len() { + match bytes[i] { + b'"' | b'\'' => i = skip_string(bytes, i), + b'/' if matches!(bytes.get(i + 1), Some(b'/' | b'*')) => i = skip_comment(bytes, i), + b'(' | b'{' | b'[' => { + depth += 1; + i += 1; + } + b')' | b'}' | b']' => { + depth = depth.saturating_sub(1); + i += 1; + } + b',' if depth == 0 => { + parts.push(&list[start..i]); + start = i + 1; + i += 1; + } + _ => i += 1, + } + } + parts.push(&list[start..]); + parts + .into_iter() + .filter_map(|p| { + // `@Param("id") long id` → the @Param name wins. + if let Some(q) = p.find("@Param(\"") { + return p[q + 8..].split('"').next().map(str::to_owned); + } + p.split_whitespace().last().map(|n| n.trim_matches(|c: char| !ident(c)).to_owned()) + }) + .filter(|n| !n.is_empty()) + .collect() +} + /// The interface's name and methods (with their parameter names). pub fn parse_java(text: &str) -> MapperJava { let package = @@ -140,28 +245,14 @@ pub fn parse_java(text: &str) -> MapperJava { } ';' if depth == 0 => { let stmt = &text[stmt_start..k]; - if let Some(paren) = method_paren(stmt) { - let before = stmt[..paren].trim_end(); - let name_start = before.rfind(|c: char| !ident(c)).map_or(0, |p| p + 1); - let method = &before[name_start..]; - if !method.is_empty() && !before.contains(" default ") { - let s = stmt_start + name_start; - let params = stmt[paren + 1..stmt.rfind(')').unwrap_or(stmt.len())] - .split(',') - .filter_map(|p| { - // `@Param("id") long id` → the @Param name wins. - if let Some(q) = p.find("@Param(\"") { - return p[q + 8..].split('"').next().map(str::to_owned); - } - p.split_whitespace().last().map(|n| n.trim_matches(|c: char| !ident(c)).to_owned()) - }) - .filter(|n| !n.is_empty()) - .collect(); - methods.push(( - Named { name: method.to_owned(), range: range_of(text, s, s + method.len()) }, - params, - )); - } + if let Some(decl) = scan_declaration(stmt) + && !stmt[..decl.parens.0].contains(" default ") + { + let (s, e) = (stmt_start + decl.name.0, stmt_start + decl.name.1); + methods.push(( + Named { name: stmt[decl.name.0..decl.name.1].to_owned(), range: range_of(text, s, e) }, + params_of(&stmt[decl.parens.0 + 1..decl.parens.1]), + )); } stmt_start = k + 1; } @@ -372,6 +463,22 @@ mod tests { assert_eq!(xml.statements[0].range, Range::new(Position::new(2, 14), Position::new(2, 22))); } + const ANNOTATED: &str = concat!( + "package com.acme;\n\n", + "import org.apache.ibatis.annotations.Param;\n\n", + "public interface UserMapper {\n", + " @Select(\"SELECT (a, b) FROM users WHERE c = ')' OR d = 'x, y'\")\n", + " List selectAnnotated(Long id);\n\n", + " @Cacheable(key = @Key(value = \"x(y)\", of = {@Part(\"a)b\"), @Part(\"c,d\")}))\n", + " @Override\n", + " // a comment (with parens) before the name\n", + " User findAnnotated(@Param(\"id\") Long id, @Size(min = 1, max = 5) String name);\n\n", + " List selectPlain(Long id);\n", + "}\n" + ); + + const ANNOTATED_XML: &str = "\n\n \n \n\n"; + fn setup() -> MockHost { let host = MockHost::install(); host.add_file("src/main/java/com/acme/UserMapper.java", JAVA); @@ -380,6 +487,68 @@ mod tests { host } + fn annotated_setup() -> MockHost { + let host = MockHost::install(); + host.add_file("src/main/java/com/acme/UserMapper.java", ANNOTATED); + host.add_file("src/main/resources/mapper/UserMapper.xml", ANNOTATED_XML); + host.add_file("pom.xml", ""); + host + } + + /// Methods behind method-level annotations keep their names, parameters and + /// positions: the scanner skips comments, strings and annotation arguments + /// (nested annotations, `(` `)` `{` `}` `,` in strings) before the name. + #[test] + fn parses_methods_with_annotations() { + let java = parse_java(ANNOTATED); + let names: Vec<_> = java.methods.iter().map(|(m, p)| (m.name.as_str(), p.clone())).collect(); + assert_eq!( + names, + [ + ("selectAnnotated", vec!["id".to_owned()]), + ("findAnnotated", vec!["id".to_owned(), "name".to_owned()],), + ("selectPlain", vec!["id".to_owned()]) + ], + "annotation names must not become method names, @Param wins, argument commas must not split a parameter" + ); + // ` List ` before the name on its line (UTF-16 columns). + assert_eq!(java.methods[0].0.range, Range::new(Position::new(6, 15), Position::new(6, 30))); + assert_eq!(java.methods[1].0.range, Range::new(Position::new(11, 9), Position::new(11, 22))); + } + + /// A multibyte annotation string on the name's line keeps UTF-16 columns: + /// `中文` is one unit each and `😀` two, while bytes would run ahead. + #[test] + fn keeps_utf16_columns_around_multibyte_annotations() { + let text = "package com.acme;\n\npublic interface UserMapper {\n @Select(\"中文 😀 注释\") List find(Long id);\n}\n"; + let java = parse_java(text); + assert_eq!(java.methods.len(), 1); + assert_eq!(java.methods[0].0.name, "find"); + // `"中文 😀 注释"` costs 10 UTF-16 units (😀 two); `find` starts at 4 + 8 + 1 + 10 + 1 + 11 = 35. + assert_eq!(java.methods[0].0.range, Range::new(Position::new(3, 35), Position::new(3, 39))); + assert_eq!(java.methods[0].1, ["id".to_owned()]); + } + + /// Annotated methods still get both directions of navigation. + #[test] + fn navigates_annotated_methods() { + let _host = annotated_setup(); + let mut n = Navigator::default(); + let java = doc("/project/src/main/java/com/acme/UserMapper.java", "java", ANNOTATED); + let markers = n.line_markers(&java); + assert_eq!(markers.iter().map(|m| m.line).collect::>(), [6, 11], "selectPlain has no statement"); + assert_eq!(markers[1].tooltip, "Go to UserMapper.xml: findAnnotated"); + + let xml = doc("/project/src/main/resources/mapper/UserMapper.xml", "xml", ANNOTATED_XML); + let to_java = n.definition(&xml, Position::new(2, 20)); + assert_eq!(to_java.len(), 1); + assert_eq!( + (to_java[0].path.ends_with("UserMapper.java"), to_java[0].range.start), + (true, Position::new(6, 15)), + "the XML statement jumps back to the annotated method" + ); + } + fn doc(path: &str, language: &str, text: &str) -> Document { Document { path: path.into(), language: language.into(), version: 1, text: text.into() } }