Skip to content

Cross-file symbol resolution #1148

@lionel-

Description

@lionel-

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.)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions