I want to create a function myfun that can only be used inside another function, in my case dplyrs mutate or summarise. I further do not want to rely on dplyrs internals (for example mask$...).
I came up with a quick and dirty workaround: A function search_calling_fn that checks all function names in the call stack and looks for a specific pattern in the calling functions.
search_calling_fn <- function(pattern) {
  
  call_st <- lapply(sys.calls(), `[[`, 1)
  
  res <- any(unlist(lapply(call_st, function(x) grepl(pattern, x, perl = TRUE))))
  
  if (!res) {
    stop("`myfun()` must only be used inside dplyr::mutate or dplyr::summarise")
  } else {
    return()
  }
}
This works as expected as the two examples below show (dplyr = 1.0.0)
library(dplyr)
myfun <- function() {
  search_calling_fn("^mutate|^summarise")
  NULL
}
# throws as expected no error
mtcars %>% 
  mutate(myfun())
myfun2 <- function() {
  search_calling_fn("^select")
  NULL
}
# throws as expected an error
mtcars %>% 
  mutate(myfun2())
This approach has one loophole: myfun could be called from a function with a similar name which is not a dplyr function. I wonder how I can check from which namespace a function on my call stack is coming. rlang has a function call_ns but this will only work, if the function is explicitly called with package::.... Further, when using mutate there is mutate_cols an internal function and mutate.data.frame an S3 method on the call stack - both seem to make getting the namespace even more complicated.
On a second thought I wonder whether there is a better, more official approach to achieve the same outcome: only allow myfun to be called within dplyrs mutate or summarise.
The approach should work no matter how the function is called:
- mutate
- dplyr::mutate
Additional note
After discussing @r2evans answer, I realize that a solution should pass the following test:
library(dplyr)
myfun <- function() {
  search_calling_fn("^mutate|^summarise")
  NULL
}
# an example for a function masking dplyr's mutate
mutate <- function(df, x) {
  NULL
}
# should throw an error but doesn't
mtcars %>% 
  mutate(myfun())
So the checking function should not only look at the callstack, but also try to see which package a function on the callstack is coming from. Interestingly, RStudios debugger shows the namespace for each function on the callstack, even for internal functions. I wonder how it does this, since environment(fun)) is only working on exported functions.
 
    