Bulk Fixes for Package Development
Robert Challen
2026-06-12
Source:vignettes/bulk-fixes.Rmd
bulk-fixes.RmdRunning R CMD check on a package often reveals the same
categories of issues: unqualified function calls, missing namespace
imports, undeclared global variables, non-ASCII characters, and
non-standard files. pkgtools provides automated fixes for
these common problems.
This vignette explains the fix functions, how they work, and best practices for using them in your development workflow.
The Automated Fix Pipeline
pkgtools::fix_check() is a convenience function that
runs all fixes in sequence:
pkgtools::fix_check()This calls, in order:
-
fix_unqualified_fns_bulk()— add namespace prefixes -
qcheck()— runR CMD check(without examples/tests) -
fix_non_standard_files()— update.Rbuildignore -
fix_global_variables()— updateglobals.R -
fix_utf8_encoding()— escape non-ASCII characters -
fix_dependencies()— fix Imports/Suggests inDESCRIPTION
Each step uses the output of the check to identify and fix issues.
Safety First: Git Backups
All bulk fix functions follow the same safety pattern:
- Commit any pending changes before modifying files
- Record the commit hash in an internal undo stack
- Ask for confirmation before making changes (in interactive mode)
This means you can always undo a bulk fix operation.
# Roll back to the state before the last pkgtools operation:
pkgtools::undo()
# To redo the changes:
gert::git_stash_pop()Fix Unqualified Function Names
The Problem
R package development requires fully qualified function calls when
referencing functions from imported packages. Without the prefix,
R CMD check reports “no visible binding for global
variable” warnings.
The Fix
pkgtools::fix_unqualified_fns_bulk(
pkg = ".",
rDirectories = c(here::here("R"), here::here("tests/testthat")),
prioritise = c("dplyr", "rlang", "stringr", "forcats", "ggplot2",
"purrr", "tidyr", "readr", "stats", "utils")
)This function:
- Scans files in the specified directories for unqualified calls
-
Resolves which package each function comes from
(using
package_map()which checks imports, loaded packages, and the current package) - Applies the namespace prefix via regex
- Reports which files were modified
Handling Priorities
When a function name exists in multiple packages (e.g.,
lag() is in both stats and
dplyr), the prioritise parameter controls
which package is used. Functions are matched against packages in this
order:
- Prioritised packages (specified in the argument)
- Current package
- Base and utils (never qualified)
-
Other imports in the order declared in
DESCRIPTION
Interactive vs Bulk Mode
The interactive version, fix_unqualified_fns(), operates
on the currently open file in RStudio and uses the
merge_code() gadget for visual review of changes. The bulk
version modifies files directly (after confirmation).
Fixing Dependencies
The Problem
R CMD check reports:
- “imports not declared from:
package” — a package is used but not listed inImportsorSuggests - “not imported from:
package” — a package is listed but not used
The Fix
# Run with automatic check:
pkgtools::fix_dependencies()
# Or with existing check output:
check <- qcheck()
pkgtools::fix_dependencies(check = check)This function:
- Parses the check output for import-related messages
- Extracts the package names from warning/note text
-
Removes unnecessary packages from
DESCRIPTION -
Adds missing packages to
Imports -
Writes the updated
DESCRIPTIONfile
Fixing Global Variables
The Problem
R CMD check reports:
for global variable 'myvar'
This means a variable is used in package code but not declared in a
globals.R file or via
utils::globalVariables().
The Fix
pkgtools::fix_global_variables()This function:
- Parses the check output for “global variable” messages
-
Reads the existing
R/globals.Rfile (or creates one) -
Adds missing variable names to the
globalVariables()call -
Writes the updated
globals.Rfile
Example globals.R:
utils::globalVariables(c(
"myvar",
"other_var",
"data$column"
))The function handles both simple variable names and
$-referenced columns.
Fixing Non-Standard Files
The Problem
R CMD check reports:
found at top level: `mydir`
hidden files and directories: `.svn`
These are files or directories that R CMD build would
try to include in the package but shouldn’t be.
The Fix
pkgtools::fix_non_standard_files()This function:
- Parses the check output for “found at top level” and “hidden files” messages
-
Escapes special characters in file names for
.Rbuildignore -
Adds entries to
.Rbuildignore - Preserves existing entries (no duplicates)
Example .Rbuildignore:
^mydir$
^\.svn$
^\.git$
Fixing UTF-8 Encoding
The Problem
R CMD check reports:
non-ASCII characters: 'file.R'
This means the file contains characters outside the ASCII range, which can cause portability issues.
The Fix
pkgtools::fix_utf8_encoding()This function:
- Parses the check output for “non-ASCII characters” messages
-
Escapes non-ASCII characters in the affected files
using
\uXXXXUnicode escapes - Writes the modified files
The escaping uses a three-step process:
- Escape all Unicode with
stringi::stri_escape_unicode() - Mark UTF-8 characters with unique delimiters
- Unescape and replace markers with
\uXXXXsequences
This preserves the file’s encoding while making it ASCII-compatible.
Interactive Fixing
Per-File Fixing
For individual files, use the interactive version:
pkgtools::fix_unqualified_fns()This function:
- Reads the current file from the RStudio source editor
- Proposes changes (adding namespace prefixes)
-
Launches the
merge_code()gadget for visual review - Applies the merged result back to the editor
Merge Code Gadget
The merge_code() function provides an interactive
three-panel diff viewer:
- Left panel: the original code
- Right panel: the suggested changes
- Middle panel: the merged result
Buttons: - Save: accept the current middle-panel content - Accept all: accept the right-panel content - Cancel: revert to the original
This is used by several pkgtools functions: -
fix_unqualified_fns() - merge_code()
(directly) - what_has_changed() -
use_template() (when overwriting existing files)
Customising the Gadget
merged <- pkgtools::merge_code(
old = "original code",
new = "suggested code",
old = "original code",
value = "current content",
lhs = "Original",
rhs = "Suggested",
accept = "Save",
rhs_accept = "Accept all",
mode = "r" # or "markdown", "yaml"
)Quick Check Without Running Code
qcheck() runs R CMD check with
--no-examples --no-tests --ignore-vignettes, which catches
structural issues without executing code:
check <- pkgtools::qcheck()This is useful for:
- Fast feedback during development (no test execution)
- CI pipelines where tests are run separately
- Debugging import/namespace issues before running tests
The function also optionally runs:
-
spelling::spell_check_package()(if spelling is installed) -
urlchecker::url_check()(if urlchecker is installed)
Workflow Example
A typical fix workflow might look like:
# 1. Make some changes to your package
# 2. Run automated fixes
pkgtools::fix_check()
# 3. If something went wrong, undo
pkgtools::undo()
gert::git_stash_pop()
# 4. Or continue with targeted fixes
pkgtools::fix_unqualified_fns_bulk()
pkgtools::fix_dependencies()
pkgtools::fix_global_variables()
# 5. Verify with qcheck
pkgtools::qcheck()
# 6. If all good, commit and push
gert::git_add(".")
gert::git_commit("fix package linting issues")Comparing with devtools::document()
devtools::document() handles roxygen2-based
documentation but does not fix code-level issues.
pkgtools’s fix functions are complementary:
| Tool | Fixes | Documentation |
|---|---|---|
devtools::document() |
No | Yes (Rd files) |
pkgtools::fix_check() |
Yes | No |
pkgtools::fix_unqualified_fns_bulk() |
Yes | No |
Use both in your workflow:
Common Pitfalls
Multiple Packages with Same Function Name
If lag() exists in both stats and
dplyr, the prioritise parameter controls which
is used. Adjust this list for your project’s dependencies:
pkgtools::fix_unqualified_fns_bulk(
prioritise = c("dplyr", "tidyr", "purrr", "stats", "utils")
)Comments and Strings
The unqualified function finder uses regex, not parsing, so it may occasionally match function names in comments or strings. Review the proposed changes carefully.
Non-ASCII in Different Encodings
The UTF-8 fixer assumes files are encoded as UTF-8. If your files use a different encoding, the escaping may not produce correct results. Convert files to UTF-8 first.
Globals in S3 Methods
fix_global_variables() adds variables to
globals.R, but S3 method dispatch uses different rules.
Check the R CMD check output manually if you have S3 methods with
undeclared variables.
Related Functions
| Function | Purpose |
|---|---|
fix_check() |
Run all fixes in sequence |
fix_unqualified_fns_bulk() |
Add namespace prefixes |
fix_unqualified_fns() |
Interactive per-file fixing |
fix_dependencies() |
Fix Imports/Suggests |
fix_global_variables() |
Update globals.R |
fix_utf8_encoding() |
Escape non-ASCII characters |
fix_non_standard_files() |
Update .Rbuildignore |
qcheck() |
Quick check without running code |
undo() |
Roll back last pkgtools operation |
Funding
The authors gratefully acknowledge the support of the UK Research and Innovation AI programme of the Engineering and Physical Sciences Research Council EPSRC grant EP/Y028392/1.