what_breaks
Analyze code impact by identifying references to a specific symbol using structural search. Shows what might break when modifying functions or classes in Python, JavaScript, TypeScript, or Go projects.
Instructions
💥 STEP 3: See what code might break if you change this symbol.
USE THIS AFTER find_symbol() to understand the impact of changing a function/class.
IMPROVEMENTS:
Uses structural search (ast-grep) to find ACTUAL code references (ignoring comments/strings).
Returns 2 lines of context around each match.
INPUT:
exact_symbol: Pass THE ENTIRE SYMBOL OBJECT from find_symbol(), not just the name! Must be a dictionary with AT LEAST 'name' and 'path' keys.
EXAMPLE INPUT:
First, get a symbol from find_symbol():
symbols = find_symbol("/Users/john/project", "authenticate") symbol = symbols[0] # Pick the first result
Then pass THE WHOLE SYMBOL OBJECT:
what_breaks(symbol)
EXAMPLE OUTPUT: { "references": [ { "file": "/Users/john/project/src/api.py", "line": 23, "text": " # Authenticate the user user = authenticate_user(username, password) if not user:", "type": "code" } ], "total_count": 1, "strategy": "structural", "note": "Found 1 references using structural search." }
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| exact_symbol | Yes |
Implementation Reference
- src/xray/mcp_server.py:222-278 (handler)MCP tool handler and registration for 'what_breaks'. Extracts repository root, gets indexer, calls core what_breaks method.@mcp.tool def what_breaks(exact_symbol: Dict[str, Any]) -> Dict[str, Any]: """ 💥 STEP 3: See what code might break if you change this symbol. USE THIS AFTER find_symbol() to understand the impact of changing a function/class. IMPROVEMENTS: - Uses structural search (ast-grep) to find ACTUAL code references (ignoring comments/strings). - Returns 2 lines of context around each match. INPUT: - exact_symbol: Pass THE ENTIRE SYMBOL OBJECT from find_symbol(), not just the name! Must be a dictionary with AT LEAST 'name' and 'path' keys. EXAMPLE INPUT: # First, get a symbol from find_symbol(): symbols = find_symbol("/Users/john/project", "authenticate") symbol = symbols[0] # Pick the first result # Then pass THE WHOLE SYMBOL OBJECT: what_breaks(symbol) EXAMPLE OUTPUT: { "references": [ { "file": "/Users/john/project/src/api.py", "line": 23, "text": " # Authenticate the user\n user = authenticate_user(username, password)\n if not user:", "type": "code" } ], "total_count": 1, "strategy": "structural", "note": "Found 1 references using structural search." } """ try: # Extract root path from the symbol's path symbol_path = Path(exact_symbol['path']) root_path = str(symbol_path.parent) # Find a suitable root (go up until we find a git repo or reach root) while root_path != '/': if (Path(root_path) / '.git').exists(): break parent = Path(root_path).parent if parent == Path(root_path): break root_path = str(parent) indexer = get_indexer(root_path) return indexer.what_breaks(exact_symbol) except Exception as e: return {"error": f"Error finding references: {str(e)}"}
- src/xray/core/indexer.py:580-628 (helper)Core logic of what_breaks: structural search with ast-grep (prioritized), fallback to text search (ripgrep/python), filters definition file/line, returns references with context.def what_breaks(self, exact_symbol: Dict[str, Any], context_lines: int = 2) -> Dict[str, Any]: """ Find what uses a symbol (reverse dependencies) using structural search. Prioritizes ast-grep for code references, falls back to text search. """ symbol_name = exact_symbol['name'] definition_path = str(Path(exact_symbol['path']).resolve()) definition_start = exact_symbol.get('start_line', -1) references = [] strategy = "structural" # Try structural search first (ast-grep) struct_refs = self._ast_grep_search(symbol_name, context_lines) if struct_refs: # Filter out the definition itself for ref in struct_refs: ref_path = str(Path(ref['file']).resolve()) ref_line = ref['line'] # Simple collision check: same file and line is close to definition # (ast-grep definition match might be on definition line) if ref_path == definition_path and abs(ref_line - definition_start) <= 1: continue references.append(ref) else: # Fallback to text search if ast-grep found nothing (or failed) # Note: This might happen if the symbol is not in a supported language file # or if it's only used in comments/strings (which we might want to know about as fallback?) # For now, if structural search returns empty list, we trust it for code. # But we might want to run text search as a backup for non-code files? # Let's stick to the previous behavior's fallback logic: if ast-grep *fails to run*, we use grep. # If ast-grep runs and finds nothing, we return nothing (for code). # BUT, to be safe and "improve" without breaking, let's run text search # if structural search is empty, but mark them as "text matches". # Actually, let's just use the text search if structural returned nothing. strategy = "text" references = self._text_search(symbol_name, context_lines) return { "references": references, "total_count": len(references), "strategy": strategy, "note": f"Found {len(references)} references using {strategy} search." }