Roslyn QueryTwo Visual Studio tool windows over Roslyn. Roslyn Query runs a C# predicate you write over every Reference Graph roots a lazily-expandable tree on the member or type at the caret, showing what
references it and what it references, recursively.
ContentsUsing itThe window has two tabs, Search and Replace, sharing one Find box and one set of Target/Scope/Cap/Generated settings between them. Search browses and navigates; Replace, described below, additionally writes matches back. Pick a Target, pick a Scope, type a predicate, press Enter (or Run). The signature line above the box tells you what is in scope:
Despite the
The returned node/token/operation must actually come from the tree being searched - one of its own
descendants, ancestors, or siblings is fine, but a node built with Different matches that end up reporting the same location - three statements in one method that all report that method, say - collapse to a single result rather than repeating it once per match. Write either a single boolean expression or a full statement body ending in a
Predicates are compiled
Awaiting is worth it only for something a predicate cannot get synchronously - it runs once per
node, so an Scopes: containing member, containing type, current document, current project, solution. The three narrow ones are resolved from the caret in the last active code window. Examples
Keys
The predicate box is a real editor view, not a The commit characters are Roslyn's C# set without the space, so Every run is explicit: Ctrl+Enter or the Run button. There is no run-as-you-type mode, on purpose. Each distinct expression leaks a small assembly for the session (see below), and a debounced re-run turns that into one leak per pause in your typing. Cap stops the run once that many matches are found and marks the results as capped. Generated includes generated documents. A document counts as generated if its name matches the
usual conventions ( The filter only applies to project and solution scope. A document you have pointed the caret at is always scanned, generated or not. Query historyThe History button toggles a resizable sidebar listing every predicate still in the compile cache, newest first. Double-click one to put it back in the box and run it: the target is restored with it, the scope is left on whatever you currently have selected. Entries are shown re-formatted. The cache is keyed on a minified form of the text, so two spellings of the same predicate are one entry and one compiled assembly. That cache is also the leak referred to above. Every distinct predicate emits an assembly, and .NET Framework has no way to unload one, so it stays for the life of the VS process; the status line reports the running count and total size. The cache is capped at 512 entries, which bounds the list, not the underlying leak. ReplaceThe Replace tab sits next to Search and shares its Find box, Target, Scope and Cap - there is one query, and Replace runs it itself rather than requiring a prior Search run. Type a replacement below the Find box and press Generate Previews (or Ctrl+Enter in the replacement box, the same way Ctrl+Enter in the Find box runs Search). Generate Previews re-runs the Find query itself first, so there is no need to switch to Search and run it separately - and switching tabs to run Search directly clears any previews still on screen, so they never go stale against a changed query. It returns one of:
The replacement signature mirrors the predicate's:
Replace has no Each previewed match gets a checkbox, checked by default. A match that can't actually apply - two overlapping spans, a null result, a span that's gone stale - has its box unchecked and disabled instead, with a warning explaining why; there's no re-checking your way past it. All / None toggle every checkbox that isn't disabled. Apply Selected writes the checked replacements back to the workspace in one pass. Spans are re-resolved against the live solution at apply time, so an edit made between preview and apply doesn't silently land in the wrong place; anything that no longer resolves is skipped and reported rather than applied somewhere wrong. The apply is wrapped in VS's linked undo transaction API, and every changed document is explicitly enrolled in it before the edit lands, so a change spanning several files is one Ctrl+Z. Structural ( Reference GraphA second window, styled after the built-in Call Hierarchy but generalized to every kind of reference rather than just calls. Right-click a method, constructor, property, field, event or type in the editor and choose
View Reference Graph, or open the window empty from Each invocation adds a root at the top of the list rather than replacing what is there, so the window keeps a history; Clear empties it, and Del removes just the selected root. Every root has two branches:
Every row expands the same way, recursively, staying in the direction its branch started in. A row
is one declaration, not one call site: its second line reads Double-click a row, or select it and press Enter, to jump to it - the individual location rows go to
that exact occurrence, and a row with only one occurrence goes straight there. A row whose symbol
already appears above it in the tree is marked ScopeThe scope combo - current document, current project, my solution - narrows Usage kindsThe Filter button opens a checkbox flyout, one box per kind of reference:
Invocations, reads, writes and constructions start on; type references and doc comments start off, since a cref is documentation rather than a real code path and a type root's type references otherwise swamp everything else. Ticking or unticking a box re-reads every expanded row immediately, as does Refresh and changing the scope. Stop cancels whatever is in flight and clears every expanded row, since a cancelled fetch may have left them stale; expand a row again to re-read it. A compound assignment is both a read and a write, and is counted under each. Any single branch stops
at 200 rows, with the remainder collapsed into one BuildingRequires the Visual Studio SDK workload.
The Targets VS 2022 and VS 2026 from one artifact. Tests are xUnit.v3 on Microsoft.Testing.Platform, which builds a self-contained runner:
|