Scan the source text for `/** @var Type $varName */` inline docblocks and return the set of variable names they declare.
(content: &str)
| 1208 | /// Scan the source text for `/** @var Type $varName */` inline |
| 1209 | /// docblocks and return the set of variable names they declare. |
| 1210 | fn collect_var_annotations(content: &str) -> HashSet<String> { |
| 1211 | let mut vars = HashSet::new(); |
| 1212 | // Look for patterns like: @var SomeType $varName |
| 1213 | // The regex-like scan: find `@var ` followed by a type, then `$name`. |
| 1214 | for line in content.lines() { |
| 1215 | let trimmed = line.trim(); |
| 1216 | // Must be inside a doc comment context. |
| 1217 | if !trimmed.contains("@var") { |
| 1218 | continue; |
| 1219 | } |
| 1220 | // Find `@var` and extract the variable name after the type. |
| 1221 | if let Some(var_pos) = trimmed.find("@var") { |
| 1222 | let after_var = &trimmed[var_pos + 4..]; |
| 1223 | let after_var = after_var.trim_start(); |
| 1224 | // Skip the type (everything before the $). |
| 1225 | if let Some(dollar_pos) = after_var.find('$') { |
| 1226 | let var_part = &after_var[dollar_pos..]; |
| 1227 | // Extract the variable name: $[a-zA-Z_][a-zA-Z0-9_]* |
| 1228 | let name_end = var_part |
| 1229 | .char_indices() |
| 1230 | .skip(1) // skip the $ |
| 1231 | .find(|(_, c)| !c.is_alphanumeric() && *c != '_') |
| 1232 | .map(|(i, _)| i) |
| 1233 | .unwrap_or(var_part.len()); |
| 1234 | let var_name = &var_part[..name_end]; |
| 1235 | // Trim trailing `*/` if present. |
| 1236 | let var_name = var_name.trim_end_matches("*/").trim(); |
| 1237 | if var_name.len() > 1 { |
| 1238 | vars.insert(var_name.to_string()); |
| 1239 | } |
| 1240 | } |
| 1241 | } |
| 1242 | } |
| 1243 | vars |
| 1244 | } |
| 1245 | |
| 1246 | // ─── Error suppression (@) offset collection ──────────────────────────────── |
| 1247 |
no test coverage detected