|
8 | 8 | from ...types import Fragment, FragmentId |
9 | 9 | from ..base import EdgeBuilder, EdgeDict |
10 | 10 |
|
11 | | -_RUST_USE_RE = re.compile(r"^\s*use\s+(?:crate::)?([a-zA-Z_]\w*(?:::[a-zA-Z_]\w*)*)", re.MULTILINE) |
12 | | -_RUST_USE_BRACED_RE = re.compile(r"use\s+(?:crate::)?([\w:]+)::\{([^}]+)\}", re.MULTILINE) |
| 11 | +_RUST_USE_STMT_RE = re.compile(r"^\s*use\s+(.+?)\s*;", re.MULTILINE) |
13 | 12 | _RUST_MOD_RE = re.compile(r"^\s*(?:pub(?:\([^)]*\))?\s+)?mod\s+([a-z_][a-z0-9_]*)\s*[;{]", re.MULTILINE) |
14 | 13 |
|
15 | 14 | _RUST_FN_RE = re.compile(r"^\s*(?:pub(?:\([^)]*\))?\s+)?(?:async\s+)?fn\s+([a-z_][a-z0-9_]*)", re.MULTILINE) |
@@ -124,24 +123,60 @@ def _is_rust_file(path: Path) -> bool: |
124 | 123 | return path.suffix.lower() == ".rs" |
125 | 124 |
|
126 | 125 |
|
| 126 | +_MAX_USE_TREE_DEPTH = 10 |
| 127 | + |
| 128 | + |
| 129 | +def _parse_use_tree(text: str, _depth: int = 0) -> list[str]: |
| 130 | + if _depth > _MAX_USE_TREE_DEPTH: |
| 131 | + return [] |
| 132 | + text = re.sub(r"^(?:crate|self|super)::", "", text.strip()) |
| 133 | + if "{" not in text: |
| 134 | + return [text] if text else [] |
| 135 | + brace_pos = text.index("{") |
| 136 | + prefix = text[:brace_pos].rstrip(":") |
| 137 | + inner = text[brace_pos + 1 :] |
| 138 | + depth = 1 |
| 139 | + end = 0 |
| 140 | + for i, ch in enumerate(inner): |
| 141 | + if ch == "{": |
| 142 | + depth += 1 |
| 143 | + elif ch == "}": |
| 144 | + depth -= 1 |
| 145 | + if depth == 0: |
| 146 | + end = i |
| 147 | + break |
| 148 | + items_str = inner[:end] |
| 149 | + results: list[str] = [] |
| 150 | + current: list[str] = [] |
| 151 | + d = 0 |
| 152 | + for ch in items_str: |
| 153 | + if ch == "{": |
| 154 | + d += 1 |
| 155 | + current.append(ch) |
| 156 | + elif ch == "}": |
| 157 | + d -= 1 |
| 158 | + current.append(ch) |
| 159 | + elif ch == "," and d == 0: |
| 160 | + item = "".join(current).strip() |
| 161 | + if item and item != "self": |
| 162 | + results.extend(_parse_use_tree(f"{prefix}::{item}" if prefix else item, _depth + 1)) |
| 163 | + current = [] |
| 164 | + else: |
| 165 | + current.append(ch) |
| 166 | + item = "".join(current).strip() |
| 167 | + if item and item != "self": |
| 168 | + results.extend(_parse_use_tree(f"{prefix}::{item}" if prefix else item, _depth + 1)) |
| 169 | + return results |
| 170 | + |
| 171 | + |
127 | 172 | def _extract_uses(content: str) -> set[str]: |
128 | 173 | uses: set[str] = set() |
129 | | - for match in _RUST_USE_RE.finditer(content): |
130 | | - path = match.group(1) |
131 | | - uses.add(path) |
132 | | - parts = path.split("::") |
133 | | - if len(parts) > 1: |
134 | | - uses.add(parts[0]) |
135 | | - for match in _RUST_USE_BRACED_RE.finditer(content): |
136 | | - base_path = match.group(1) |
137 | | - uses.add(base_path) |
138 | | - base_parts = base_path.split("::") |
139 | | - if len(base_parts) > 1: |
140 | | - uses.add(base_parts[0]) |
141 | | - for name in match.group(2).split(","): |
142 | | - name = name.strip() |
143 | | - if name: |
144 | | - uses.add(name) |
| 174 | + for match in _RUST_USE_STMT_RE.finditer(content): |
| 175 | + for path in _parse_use_tree(match.group(1)): |
| 176 | + uses.add(path) |
| 177 | + parts = path.split("::") |
| 178 | + if len(parts) > 1: |
| 179 | + uses.add(parts[0]) |
145 | 180 | return uses |
146 | 181 |
|
147 | 182 |
|
|
0 commit comments