The semantic index (#1141) implements file-local resolution. When a symbol is unbound (or maybe unbound in a control flow path), we need to look up the "global" definition instead. The cross-file layer will be in charge of doing that resolution.
There are multiple things that feed into the resolution. For instance in packages:
- The collation order of files determines what a file sees (e.g. symbols in
aaa.R do not see those in zzz.R).
- Declared imports in NAMESPACE, Depends field in DESCRIPTION
- In test files, helpers are collated, as well as the parent test file (
tests/testthat.R)
And within R files:
library() and source() affect the search path and global env after they have been called.
We can model all of this using a chain of scopes:
enum BindingSource {
/// Bindings from a project file
FileExports { file: Url, exports: Vec<(String, TextRange)> },
/// importFrom(pkg, name)
ImportedNames(HashMap<String, PackageId>),
/// All exports of an attached package (`library()` or NAMESPACE `import()`)
PackageExports(PackageId),
/// Full namespace, e.g. for `load_all()` semantics in tests
PackageInternals(PackageId),
/// Base R, the last scope in the chain
Base,
}
FileExports would be resolved from a file's semantic index. We already keep track of top-level definitions (ones in the 0th top-level scope). The index should also keep track of calls like library() and require() called at top-level. Note that these may not necessarily be called in the top-level scope, they can be called in NSE subscopes as long as they are called eagerly, e.g. local(library(foo)).
The same BindingSource model works both for the cross-file chain and for within-file directive tracking. For resolution within a file, unbound symbols start with a default Vec<BindingSource> representing the initial search path state. Then for each library() or source() call, we push a layer on the stack. (We might want to use deque.)
The semantic index (#1141) implements file-local resolution. When a symbol is unbound (or maybe unbound in a control flow path), we need to look up the "global" definition instead. The cross-file layer will be in charge of doing that resolution.
There are multiple things that feed into the resolution. For instance in packages:
aaa.Rdo not see those inzzz.R).tests/testthat.R)And within R files:
library()andsource()affect the search path and global env after they have been called.We can model all of this using a chain of scopes:
FileExportswould be resolved from a file's semantic index. We already keep track of top-level definitions (ones in the 0th top-level scope). The index should also keep track of calls likelibrary()andrequire()called at top-level. Note that these may not necessarily be called in the top-level scope, they can be called in NSE subscopes as long as they are called eagerly, e.g.local(library(foo)).The same
BindingSourcemodel works both for the cross-file chain and for within-file directive tracking. For resolution within a file, unbound symbols start with a defaultVec<BindingSource>representing the initial search path state. Then for eachlibrary()orsource()call, we push a layer on the stack. (We might want to use deque.)library()andrequire()) and the external resolution routine. Add external symbol resolution infrastructure #1151R/(files intests/andtests/testthatlater on?). Implement package root layers with file collation support #1152source()directives imports top-level definitions and inner directives (e.g.library()side effects). Related issue: Workspace symbols should not affect local diagnostics and jump to definition positron#11112. PR: Add support forsource()directive in symbol resolution #1157