From eabdb9a0374ec41d8193eedbe6e8f948168b16c0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ji=C5=99=C3=AD=20Moravec?= <jiri.c.moravec@gmail.com>
Date: Thu, 22 Feb 2024 09:50:18 +1300
Subject: [PATCH] Allows the use of = instead of <-

---
 R/assignment_linter.R                   | 14 +++++++++++---
 man/assignment_linter.Rd                |  4 ++++
 tests/testthat/test-assignment_linter.R |  8 ++++++++
 3 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/R/assignment_linter.R b/R/assignment_linter.R
index da42b5119..c2839b2fc 100644
--- a/R/assignment_linter.R
+++ b/R/assignment_linter.R
@@ -2,6 +2,8 @@
 #'
 #' Check that `<-` is always used for assignment.
 #'
+#' @param allow_equal_assignment Logical, default `FALSE`.
+#'   If `TRUE`, `=` instead of `<-` is used for assignment.
 #' @param allow_cascading_assign Logical, default `TRUE`.
 #'   If `FALSE`, [`<<-`][base::assignOps] and `->>` are not allowed.
 #' @param allow_right_assign Logical, default `FALSE`. If `TRUE`, `->` and `->>` are allowed.
@@ -70,7 +72,8 @@
 #' - <https://style.tidyverse.org/syntax.html#assignment-1>
 #' - <https://style.tidyverse.org/pipes.html#assignment-2>
 #' @export
-assignment_linter <- function(allow_cascading_assign = TRUE,
+assignment_linter <- function(allow_equal_assignment = FALSE,
+                              allow_cascading_assign = TRUE,
                               allow_right_assign = FALSE,
                               allow_trailing = TRUE,
                               allow_pipe_assign = FALSE) {
@@ -88,7 +91,7 @@ assignment_linter <- function(allow_cascading_assign = TRUE,
 
   xpath <- paste(collapse = " | ", c(
     # always block = (NB: the parser differentiates EQ_ASSIGN, EQ_SUB, and EQ_FORMALS)
-    "//EQ_ASSIGN",
+    if (allow_equal_assignment) "//LEFT_ASSIGN" else "//EQ_ASSIGN",
     # -> and ->> are both 'RIGHT_ASSIGN'
     if (!allow_right_assign) "//RIGHT_ASSIGN" else if (!allow_cascading_assign) "//RIGHT_ASSIGN[text() = '->>']",
     # <-, :=, and <<- are all 'LEFT_ASSIGN'; check the text if blocking <<-.
@@ -108,7 +111,12 @@ assignment_linter <- function(allow_cascading_assign = TRUE,
     }
 
     operator <- xml_text(bad_expr)
-    lint_message_fmt <- rep("Use <-, not %s, for assignment.", length(operator))
+    lint_message_fmt <- rep(
+      paste0("Use ",
+             if (allow_equal_assignment) "=" else "<-",
+             ", not %s, for assignment."),
+      length(operator)
+    )
     lint_message_fmt[operator %in% c("<<-", "->>")] <-
       "Replace %s by assigning to a specific environment (with assign() or <-) to avoid hard-to-predict behavior."
     lint_message_fmt[operator == "%<>%"] <-
diff --git a/man/assignment_linter.Rd b/man/assignment_linter.Rd
index 291343fb2..2a925d169 100644
--- a/man/assignment_linter.Rd
+++ b/man/assignment_linter.Rd
@@ -5,6 +5,7 @@
 \title{Assignment linter}
 \usage{
 assignment_linter(
+  allow_equal_assignment = FALSE,
   allow_cascading_assign = TRUE,
   allow_right_assign = FALSE,
   allow_trailing = TRUE,
@@ -12,6 +13,9 @@ assignment_linter(
 )
 }
 \arguments{
+\item{allow_equal_assignment}{Logical, default \code{FALSE}.
+If \code{TRUE}, \code{=} instead of \verb{<-} is used for assignment.}
+
 \item{allow_cascading_assign}{Logical, default \code{TRUE}.
 If \code{FALSE}, \code{\link[base:assignOps]{<<-}} and \verb{->>} are not allowed.}
 
diff --git a/tests/testthat/test-assignment_linter.R b/tests/testthat/test-assignment_linter.R
index bae8a048e..71d008d06 100644
--- a/tests/testthat/test-assignment_linter.R
+++ b/tests/testthat/test-assignment_linter.R
@@ -192,3 +192,11 @@ test_that("multiple lints throw correct messages", {
     assignment_linter(allow_cascading_assign = FALSE)
   )
 })
+
+test_that("equal = instead of <- can be used for assignment", {
+  linter <- assignment_linter(allow_equal_assignment = TRUE)
+  lint_msg <- rex::rex("Use =, not <-, for assignment.")
+
+  expect_lint("blah = 1", NULL, linter)
+  expect_lint("blah <- 1", lint_msg, linter)
+})