From a3c12d00e8b7d83546a9afabebb904ca54c4b7cf Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Tue, 30 Aug 2022 00:26:56 +0000 Subject: [PATCH 1/3] Export get_r_string --- NAMESPACE | 1 + NEWS.md | 7 +++++-- R/utils.R | 18 +++++++++++++++--- man/get_r_string.Rd | 24 ++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 man/get_r_string.Rd diff --git a/NAMESPACE b/NAMESPACE index 1c2b28c47..991b17c78 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -58,6 +58,7 @@ export(extraction_operator_linter) export(fixed_regex_linter) export(function_argument_linter) export(function_left_parentheses_linter) +export(get_r_string) export(get_source_expressions) export(ids_with_token) export(ifelse_censor_linter) diff --git a/NEWS.md b/NEWS.md index f1ebf8f4b..d9413452e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,7 +10,7 @@ * `brace_linter()` allows opening curly braces on a new line when there is a comment ending the preceding line (#1433 and #1434, @IndrajeetPatil). - + * `seq_linter()` produces lint for `seq(...)`, since it also cannot properly handle empty edge cases (#1468, @IndrajeetPatil). @@ -33,9 +33,12 @@ * New `function_argument_linter()` to enforce that arguments with defaults appear last in function declarations, see the [Tidyverse design guide](https://design.tidyverse.org/args-data-details.html) (#450, @AshesITR). - + * New `allow_trailing` argument added to `assignment_linter()` to check when assignment operators are at the end of a line, and the value is on the following line (#1491, @ashbaldry) + +* New `get_r_string()` helper to get the R-equivalent value of a string, especially useful for R-4-style raw strings. + Previously an internal `lintr` helper, now exported to facilitate writing custom linters (#1493, @MichaelChirico). ## New features diff --git a/R/utils.R b/R/utils.R index 27e32e5c5..c4358eafb 100644 --- a/R/utils.R +++ b/R/utils.R @@ -210,9 +210,21 @@ release_bullets <- function() { platform_independent_order <- function(x) order(tolower(gsub("_", "0", x, fixed = TRUE))) platform_independent_sort <- function(x) x[platform_independent_order(x)] -# convert STR_CONST text() values into R strings. mainly to account for arbitrary -# character literals valid since R 4.0, e.g. R"------[ hello ]------". -# NB: this is also properly vectorized. +#' Extract text from `STR_CONST` nodes +#' +#' Convert `STR_CONST` `text()` values into R strings. This is useful to account for arbitrary +#' character literals valid since R 4.0, e.g. `R"------[hello]------"`, which is parsed in +#' R as `"hello"`. It is quite cumbersome to write XPaths allowing for strings like this, +#' so whenever your linter logic requires testing a `STR_CONST` node's value, use this +#' function. +#' NB: this is also properly vectorized on `s`, and accepts a variety of inputs. Empty inputs +#' will become `NA` outputs, which helps ensure that `length(get_r_string(s)) == length(s)`. +#' +#' @param s An input string or strings. If `s` is an `xml_node` or `xml_nodeset` and `xpath` is `NULL`, +#' extract its string value with [xml2::xml_text()]. If `s` is an `xml_node` or `xml_nodeset` +#' and `xpath` is specified, it is extracted with [xml2::xml_find_chr()]. +#' @param xpath An XPath, passed on to [xml2::xml_find_chr()] after wrapping with `string()`. +#' @export get_r_string <- function(s, xpath = NULL) { if (inherits(s, c("xml_node", "xml_nodeset"))) { if (is.null(xpath)) { diff --git a/man/get_r_string.Rd b/man/get_r_string.Rd new file mode 100644 index 000000000..18a62941e --- /dev/null +++ b/man/get_r_string.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{get_r_string} +\alias{get_r_string} +\title{Extract text from \code{STR_CONST} nodes} +\usage{ +get_r_string(s, xpath = NULL) +} +\arguments{ +\item{s}{An input string or strings. If \code{s} is an \code{xml_node} or \code{xml_nodeset} and \code{xpath} is \code{NULL}, +extract its string value with \code{\link[xml2:xml_text]{xml2::xml_text()}}. If \code{s} is an \code{xml_node} or \code{xml_nodeset} +and \code{xpath} is specified, it is extracted with \code{\link[xml2:xml_find_all]{xml2::xml_find_chr()}}.} + +\item{xpath}{An XPath, passed on to \code{\link[xml2:xml_find_all]{xml2::xml_find_chr()}} after wrapping with \code{string()}.} +} +\description{ +Convert \code{STR_CONST} \code{text()} values into R strings. This is useful to account for arbitrary +character literals valid since R 4.0, e.g. \verb{R"------[hello]------"}, which is parsed in +R as \code{"hello"}. It is quite cumbersome to write XPaths allowing for strings like this, +so whenever your linter logic requires testing a \code{STR_CONST} node's value, use this +function. +NB: this is also properly vectorized on \code{s}, and accepts a variety of inputs. Empty inputs +will become \code{NA} outputs, which helps ensure that \code{length(get_r_string(s)) == length(s)}. +} From 66959e612ff588570045d53181a929c5e744be8f Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Tue, 30 Aug 2022 00:31:46 +0000 Subject: [PATCH 2/3] mention in vignette --- vignettes/creating_linters.Rmd | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/vignettes/creating_linters.Rmd b/vignettes/creating_linters.Rmd index 6a34da89b..9bc32adac 100644 --- a/vignettes/creating_linters.Rmd +++ b/vignettes/creating_linters.Rmd @@ -254,6 +254,18 @@ expect_lint("blah=1; blah=2", It is always better to write too many tests rather than too few. +## Other utilities for writing custom linters + +Besides `is_lint_level()`, `{lintr}` also exports some other helpers generally useful +for writing custom linters; these are used a lot in the internals of our own helpers, +and so they've been tested and demonstrated their utility already. + + * `get_r_string()`: Whenever your linter needs to examine the value of a character + literal (e.g., whether an argument value is set to some string), use this to + extract the string exactly as R will see it. This is especially important to make + your logic robust to R-4-style raw strings like `R"-(hello)-"`, which is otherwise + difficult to express, for example as an XPath. + ## Contributing to `{lintr}` ### More details about writing tests for new `{lintr}` linters From 22ba2c5353493bdccd60f7f24b0b7b25ceeafb29 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Mon, 19 Sep 2022 00:53:52 -0700 Subject: [PATCH 3/3] add to pkgdown site --- _pkgdown.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/_pkgdown.yml b/_pkgdown.yml index c4b301556..3619f80b5 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -38,6 +38,7 @@ reference: - expect_lint_free - ids_with_token - is_lint_level + - get_r_string - use_lintr - xml_nodes_to_lints