Search code examples
rclangrcpp

Rcpp Rf_warningcall compiler warnings


In the C++ code of the R/terra package I using the below to handle warnings:

template <typename... Args>
inline void warningNoCall(const char* fmt, Args&&... args ) {
    Rf_warningcall(R_NilValue, tfm::format(fmt, std::forward<Args>(args)... ).c_str());
}

See https://www.r-bloggers.com/2018/03/suppressing-call-stack-info-in-rcpp-generated-errors-and-warnings/

That works fine, but CRAN now asks me to fix it because of CLANG compiler warnings.

Found the following significant warnings:
    RcppFunctions.cpp:298:32: warning: format string is not a string literal (potentially insecure) [-Wformat-security]
    ./geos_spat.h:80:32: warning: format string is not a string literal (potentially insecure) [-Wformat-security]
  See ‘/home/hornik/tmp/R.check/r-devel-clang/Work/PKGS/terra.Rcheck/00install.out’ for details.

This has been fixed Rcpp code generated by compileAttributes() (see https://github.com/RcppCore/Rcpp/issues/1287), but that is not the case here. (This is not generated by compileAttributes.)

How can I adjust the code above to avoid this warning?


Solution

  • As you may have seen first in the respective GitHub issue #1287 or second here in this SO question or then in my summary answer on the Rcpp list, these new warnings from R-devel and g++ / clang++ under -Wformat -Wformat-security have gotten to a few people.

    In most cases this is simple. In your case it is a little different per your code conventions but I think you want

    template <typename... Args>
    inline void warningNoCall(const char* fmt, Args&&... args ) {
        Rf_warningcall(R_NilValue, "%s", tfm::format(fmt, std::forward<Args>(args)... ).c_str());
    }
    

    matching what I now have in Rcpp itself with a format string as the second argument:

    inline void warningcall(SEXP call, const std::string & s) {
        Rf_warningcall(call, "%s", s.c_str());
    }
    

    (I think you could also try

    template <typename... Args>
    inline void warningNoCall(const char* fmt, Args&&... args ) {
        ::Rf_warning("%s", tfm::format(fmt, std::forward<Args>(args)... ).c_str());
    }
    

    if the caller is of no concern to you. But this is untested.)