New rule: explicit package names
futurenew-rule
When shifting from an analysis script to package code, it is often helpful to turn:
```r
library(dplyr)
library(ggplot2)
iris |>
mutate(something = seq_len(nrow(iris)) |>
ggplot() +
geom_point(...)
```
into:
```r
library(dplyr)
library(ggplot2)
iris |>
dplyr::mutate(something = seq_len(nrow(iris)) |>
ggplot2::ggplot() +
ggplot2::geom_point(...)
```
to allow easy copy-pasting into the package.
It would be great to have a rule to make package names explicit in this way. I've given it a go, since prototypes are very cheap now. It implements this with a few specific choices to make it consistent:
1. If a function has multiple *package* sources, don't try to resolve it.
2. If a function has one package source, but is re-exported (e.g., `tibble()`), then resolve to the original (`tibble()` -> `tibble`, not to `dplyr` if both are loaded. Goes to `dplyr` if `tibble` is not loaded.)
3. If a function has a local source, treat that as the truth.
[Proof of concept](https://github.com/christopherkenny/jarl/commit/0c0dfc9d73a7f124f47d1ec3ea9a28236be7f2e2)
Input:
```
library(dplyr)
library(tibble)
library(rlang)
# Clear dplyr calls should be flagged and fixed.
x <- tibble(a = 1:5, g = c("a", "a", "b", "b", "b"))
y <- mutate(x, b = a + 1)
z <- summarise(group_by(y, g), total = sum(b))
# Re-exported calls should resolve to the provider package, not the re-exporter.
nested <- tibble(
id = 1:3,
label = as_label(expr(id))
)
# Already explicit calls should be left alone.
explicit <- dplyr::mutate(x, c = a * 2)
# Default R packages should be left alone.
base_calls <- list(mean(x$a), median(x$a), sum(x$a))
# This is ambiguous between stats and dplyr, so the rule should report without a fix.
maybe_ambiguous <- filter(x, a > 2)
# Local definitions should be left alone even if a package exports the same name.
tibble <- function(value) {
value
}
local_shadow <- tibble("not a package call")
# Calls with internal comments should preserve comments when fixed.
with_comment <- mutate(
x,
# keep this comment in place
d = a + 10
)
```
Output, with fixing:
```
library(dplyr)
library(tibble)
library(rlang)
# Clear dplyr calls should be flagged and fixed.
x <- tibble::tibble(a = 1:5, g = c("a", "a", "b", "b", "b"))
y <- dplyr::mutate(x, b = a + 1)
z <- dplyr::summarise(dplyr::group_by(y, g), total = sum(b))
# Re-exported calls should resolve to the provider package, not the re-exporter.
nested <- tibble::tibble(
id = 1:3,
label = rlang::as_label(rlang::expr(id))
)
# Already explicit calls should be left alone.
explicit <- dplyr::mutate(x, c = a * 2)
# Default R packages should be left alone.
base_calls <- list(mean(x$a), median(x$a), sum(x$a))
# This is ambiguous between stats and dplyr, so the rule should report without a fix.
maybe_ambiguous <- filter(x, a > 2)
# Local definitions should be left alone even if a package exports the same name.
tibble <- function(value) {
value
}
local_shadow <- tibble("not a package call")
# Calls with internal comments should preserve comments when fixed.
with_comment <- dplyr::mutate(
x,
# keep this comment in place
d = a + 10
)
```
Console notes:
```
warning: explicit_packages
--> explicit_packages_stretch.R:23:20
|
23 | maybe_ambiguous <- filter(x, a > 2)
| ------ Cannot choose an explicit package qualifier for `filter()`.
|
= help: `filter()` is exported by multiple loaded packages: stats, dplyr.
```
1 条评论