Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Reference support

What the References mode (REFERENCES in the web UI, fcs refs, references=true, MCP mode: "references") finds, per language.

References are search-based, not compiler-based. While indexing, each file is parsed with tree-sitter, and the grammar’s tags query marks some names as references: call sites, type mentions, new expressions, base classes. A reference search returns the lines where the identifier appears in one of those positions. Two consequences hold for every language:

  • Names, not symbols. fcs refs parse returns every call of anything called parse, whichever class or module it belongs to. Nothing tells two functions with the same name apart.
  • Only the listed positions. A use in any other position (an import line; outside Python, a function passed as a value) is not a reference, even though a text search finds it. When references look incomplete, search the plain text as well.

A definition is never its own reference, and references are exact and case-sensitive on the identifier. Each reference is a call, type, value or attribute, and kind: filters on it (see Reference kinds).

If fcs misses a form listed as found, that is a bug; if it misses one listed as not found, that is a known gap.

At a glance

LanguageCallsMethod callsType mentionsnew / constructorsBase classesNot found (main gaps)
Rustyesyesyesn/aimpl Trait for Tanything inside a macro’s arguments, the Type in Type::new(), enum variants (Item::A), use lines, functions as values
Pythonyesyesyesyes (a call)yesimports, string annotations, a few value positions; also returns uses of other things with the same name (see below)
JavaScriptyesyesn/ayesnoextends, instanceof, functions as values, property reads, imports
TypeScriptyesyespartlyyesnoFoo[], Foo<T>, A | B, ns.Type, type arguments, type aliases, extends and implements, instanceof, imports
Goyesyesyescomposite literals (Item{})n/afunctions as values (Run: helper, cb := helper), field reads, imports
Javayesyespartlyyesyesfield, parameter, local and generic types, annotations, Foo.class, Foo::bar, the class in Util.f(), casts, imports
C#member calls onlyyespartlyyesyesplain calls Helper(), generic calls Make<T>(), parameter and generic types, attributes, typeof, casts, the class in Util.F(), using
Cnononon/an/aeverything: no references are recorded
C++nononononoeverything: no references are recorded
RubyyesyesconstantsItem.newyessee below: also returns local variables
PHPno (plain f())yesnonoimplements onlyplain function calls, new Item(), extends, parameter and return types, use
Bash and other languagesnonononononot parsed for references

“Partly” is spelled out in the language’s section below.

Rust (.rs)

FormFound
helper(x), self.helper(), crate::a::helper(), Type::new() (the new)yes
helper::<T>(), a::helper::<T>()yes
Types in fields, parameters, returns and generics (Result<Item, Error>), impl Trait for Syes
The Type in Type::new() or Item::default()no
Enum variants and paths in patterns (Item::A)no
Anything inside a macro call (println!("{}", helper(x)), assert!(…))no: macro arguments are not parsed
use crate::a::{Engine, helper};no
Functions as values (let cb = helper;, .map(helper))no
Field reads (self.prop)no
#[derive(Debug)]no

The macro gap matters most in tests: calls inside assert_eq!(…) are not references.

Python (.py, .pyi, .pyw)

Python is checked against Jedi, which resolves each name to its definition (see the end of the page).

FormFound
helper(x), self.helper(), pkg.util.do_thing(), Foo()yes
Decorators: @login_required, @app.route("/x"), @bp.before_app_request, @name.setteryes
Base classes: class Foo(Base, Mixin)yes
Type annotations: item: Item, -> Result, list[Item], Item | None, Callable[[A], B], t.Optional[Item]yes
Functions and classes as values: isinstance(x, Item), cb = helper, return Result, register(self.on_done), (a, b), {"k": helper}, x == helperyes
Any attribute that is read: self.prop, obj.CONSTANT, request.formyes
A class used through an attribute: Item.from_dict() (the Item, when it starts with a capital)yes
except Oops as e:, raise Failedyes
Assigning an attribute: self.prop = 1no, on purpose: it sets prop, it does not use it
A parameter or local variable with the same name as a function (def view(request, app): app.run())no, on purpose: Python makes a name bound anywhere in a function local to it, unless it is declared global or nonlocal
import, from … importno
Annotations written as strings: x: "Item"no
Other value positions: lambda bodies, yield x, *args unpacking, f-string {x}no

Each Python reference has a kind: decorators and calls are call; annotations, base classes and exceptions type; functions and classes used as values value; attribute reads attribute. -kind:attribute leaves out property reads (see Reference kinds).

Because references are names, fcs refs get also returns every .get(…) on a dictionary, and fcs refs status_code returns reads of every attribute called status_code, whichever class it belongs to. On the repositories below, between one Python reference in ten and one in five is such a use of something else with the same name.

JavaScript (.js, .jsx, .mjs, .cjs)

FormFound
helper(x), this.helper(), z.string()yes
new Foo()yes
require("x")no, on purpose
class Foo extends Baseno
x instanceof Itemno
Functions as values (const cb = helper, arr.map(helper))no
Property reads (this.prop) and object keysno
import { helper } from "./x"no

TypeScript (.ts, .mts, .cts, .tsx)

Everything JavaScript finds, plus some type mentions:

FormFound
A plain type in an annotation: item: Item, ): Resultyes
Arrays, generics and unions: Item[], Map<string, Item>, Item | nullno
Qualified types: z.ZodType, ns.Itemno
Type arguments: generic<Item>(x), Promise<Item>no
type T = Item & Other, interface A extends Bno
class Foo extends Base implements Ifaceno
Decoratorsno

Go (.go)

FormFound
helper(x), s.helper(), pkg.DoThing()yes
Types anywhere (e *Engine, []Item, map[string]Item, Item{})yes, built-in types such as string and error too
Functions as values: cb := helper, Command{Run: helper}, sort.Slice(x, less)no
Field reads (s.prop)no
The package in pkg.DoThing(), import linesno

Java (.java)

FormFound
helper(x), this.helper(), Util.doThing() (the doThing)yes
new Item()yes
extends Base, implements Ifaceyes
Field, parameter, return, local and generic types: private Engine e;, run(Engine e, List<Item> xs), Item xno
The class in Util.doThing()no
Item.class, S::helper, casts (Result) xno
Annotations (@Annot)no
import linesno

C# (.cs)

FormFound
this.Helper(), Util.DoThing() (the DoThing)yes
new Item(), Item x = …, field typesyes
class S : Base, IFaceyes
Plain calls: Helper(e)no
Generic calls and generic types: Make<Item>(), List<Item>no
Parameter and return typesno
The class in Util.DoThing()no
Attributes ([Serializable]), typeof(Item), castsno
using linesno

C and C++

No references are recorded. The C and C++ grammars’ tags queries define symbols only, so fcs refs finds nothing in .c, .h, .cpp, .cc, .hpp and the other C and C++ extensions. Use a plain or whole-word text search instead.

Ruby (.rb, .rake, .gemspec)

Calls, method calls, constants (Item.new, Util.do_thing), base classes and include Mixin are found. Ruby’s tags query is meant to skip local variables, but fcs does not apply that filter, so local variables and parameters are returned as references too (x in x = Item.new).

PHP (.php, .phtml)

FormFound
$this->helper(), Util::doThing() (the doThing)yes
implements IFaceyes
Plain calls: helper($e)no
new Item()no
extends Base, parameter and return types, use linesno

How complete this is

Python, against Jedi

examples/ref_oracle.rs compares the references fcs records in a repository with the ones Jedi 0.20 finds (src/check/oracle/py_refs.py): every name Jedi resolves to a function, class or property defined in the repository, other than the definition itself and import lines. A reference Jedi lists and fcs does not is missing. A reference fcs records, under the name of one of those functions or classes, where Jedi resolves the name to something else, is extra: almost always a use of something else with the same name. Where Jedi cannot resolve a name at all (an untyped receiver), neither side is counted.

RepositoryFoundPrecisionWith -kind:attributeBefore October 2026
pallets/flask98.1%87.3%88.4% found, 93.8% precision57.4% found, 90.9% precision
psf/requests100%90.2%93.6%, 90.9%74.4%, 88.9%
pallets/click97.1%91.3%92.7%, 95.6%65.5%, 93.6%
encode/httpx99.5%80.5%77.9%, 90.1%59.0%, 86.6%

CI runs it on Flask and Click (.github/workflows/ref-oracle.yml) and fails if a change misses or adds more references than that.

pip install jedi==0.20.1
python3 src/check/oracle/py_refs.py bench-corpus/flask > flask.json
cargo run --release --example ref_oracle -- --oracle flask.json
cargo run --release --example ref_oracle -- --oracle flask.json --drop-kind attribute

The report groups misses and extras by the syntax around the name, which says which row of the Python table each one is.

Other languages

There is no oracle for the other languages yet. Each form above is checked with a small file per language. To give a sense of scale, a one-off count in October 2026 took every function, method and type defined in a public repository and counted how many of its uses in code fall on a reference. The count is rough: some of those uses are not references at all (a local variable that shares a type’s name, an import line).

LanguageRepositoryUses found as references
Gospf13/cobra90%
Rustfast_code_search src/53%
Javagoogle/gson52%
C#JamesNK/Newtonsoft.Json50%
TypeScriptcolinhacks/zod40%
TypeScriptsindresorhus/ky38%
CDaveGamble/cJSON0%
C++fmtlib/fmt0%

Closing these gaps is on the roadmap.