diff --git a/.dev/ast_fuzz_test.R b/.dev/ast_fuzz_test.R index 501a5b2cc..c08976976 100644 --- a/.dev/ast_fuzz_test.R +++ b/.dev/ast_fuzz_test.R @@ -54,7 +54,9 @@ pkgload::load_all() # of getting top-level exclusions done for 'nofuzz start|end' ranges, except # maybe if it enabled us to reuse lintr's own exclude() system. # therefore we take this approach: pass over the test suite first and comment out -# any tests/units that have been marked 'nofuzz'. restore later. +# any tests/units that have been marked 'nofuzz'. restore later. one consequence +# is there's no support for fuzzer-specific exclusion, e.g. we fully disable +# the unnecessary_placeholder_linter() tests because |> and _ placeholders differ. test_restorations <- list() for (test_file in list.files("tests/testthat", pattern = "^test-", full.names = TRUE)) { xml <- read_xml(xmlparsedata::xml_parse_data(parse(test_file, keep.source = TRUE))) @@ -114,15 +116,15 @@ failures <- reporter$failures$as_list() valid_failure <- vapply( failures, function(failure) { - if (grepl('(column_number|ranges|line) .* did not match', failure$message)) { + if (grepl("(column_number|ranges|line) .* did not match", failure$message)) { return(TRUE) } FALSE }, logical(1L) ) -if (!all(valid_failure)) { - failures <- failures[!valid_failure] +failures <- failures[!valid_failure] +if (length(failures) > 0L) { names(failures) <- vapply(failures, `[[`, "test", FUN.VALUE = character(1L)) cat("Some fuzzed tests failed unexpectedly!\n") print(failures) diff --git a/.dev/maybe_fuzz_content.R b/.dev/maybe_fuzz_content.R index 3db32d7c1..b76edca00 100644 --- a/.dev/maybe_fuzz_content.R +++ b/.dev/maybe_fuzz_content.R @@ -14,48 +14,66 @@ maybe_fuzz_content <- function(file, lines) { new_file } -function_lambda_fuzzer <- function(pd, lines) { - fun_tokens <- c(`'\\\\'` = "\\", `FUNCTION` = "function") - fun_idx <- which(pd$token %in% names(fun_tokens)) - n_fun <- length(fun_idx) +# skip errors for e.g. Rmd files, and ignore warnings. +# We could use get_source_expressions(), but with little benefit & much slower. +# also avoid over-use of 'nofuzz' induced by some incompatible swaps, e.g. not all '%>%' can be +# swapped to '|>' (if '.' is used, or if RHS is not an allowed simple call) +error_or_parse_data <- function(f) { + tryCatch(getParseData(suppressWarnings(parse(f, keep.source = TRUE))), error = identity) +} - if (n_fun == 0L) { - return(invisible()) - } +simple_swap_fuzzer <- function(pd_filter, replacements) { + function(pd, lines) { + idx <- which(pd_filter(pd)) + n <- length(idx) + + if (n == 0L) { + return(invisible()) + } - pd$new_text <- NA_character_ - pd$new_text[fun_idx] <- sample(fun_tokens, n_fun, replace = TRUE) + pd$new_text <- NA_character_ + pd$new_text[idx] <- sample(replacements, n, replace = TRUE) - for (ii in rev(fun_idx)) { - if (pd$text[ii] == pd$new_text[ii]) next - # Tried, with all rex(), hit a bug: https://github.com/r-lib/rex/issues/96 - ptn = paste0("^(.{", pd$col1[ii] - 1L, "})", rex::rex(pd$text[ii])) - lines[pd$line1[ii]] <- rex::re_substitutes(lines[pd$line1[ii]], ptn, paste0("\\1", rex::rex(pd$new_text[ii]))) + for (ii in rev(idx)) { + if (pd$text[ii] == pd$new_text[ii]) next + # Tried, with all rex(), hit a bug: https://github.com/r-lib/rex/issues/96 + ptn = paste0("^(.{", pd$col1[ii] - 1L, "})", rex::rex(pd$text[ii])) + lines[pd$line1[ii]] <- rex::re_substitutes(lines[pd$line1[ii]], ptn, paste0("\\1", rex::rex(pd$new_text[ii]))) + } + lines } - lines } +function_lambda_fuzzer <- simple_swap_fuzzer( + \(pd) pd$token %in% c("'\\\\'", "FUNCTION"), + replacements = c("\\", "function") +) + +pipe_fuzzer <- simple_swap_fuzzer( + \(pd) (pd$token == "SPECIAL" & pd$text == "%>%") | pd$token == "PIPE", + replacements = c("%>%", "|>") +) + # we could also consider just passing any test where no fuzzing takes place, # i.e. letting the other GHA handle whether unfuzzed tests pass as expected. apply_fuzzers <- function(f) { - # skip errors for e.g. Rmd files, and ignore warnings. - # We could use get_source_expressions(), but with little benefit & much slower. - pd <- tryCatch(getParseData(suppressWarnings(parse(f, keep.source = TRUE))), error = identity) + pd <- error_or_parse_data(f) if (inherits(pd, "error")) { return(invisible()) } - reparse <- FALSE - lines <- readLines(f) - for (fuzzer in list(function_lambda_fuzzer)) { - if (reparse) { - pd <- getParseData(parse(f, keep.source = TRUE)) - lines <- readLines(f) - } + unedited <- lines <- readLines(f) + for (fuzzer in list(function_lambda_fuzzer, pipe_fuzzer)) { updated_lines <- fuzzer(pd, lines) - reparse <- !is.null(updated_lines) - if (!reparse) next # skip some I/O if we can + if (is.null(updated_lines)) next # skip some I/O if we can writeLines(updated_lines, f) + # check if our attempted edit introduced some error + pd <- error_or_parse_data(f) + if (inherits(pd, "error")) { + writeLines(unedited, f) + return(invisible()) + } + lines <- readLines(f) } invisible() diff --git a/tests/testthat/test-brace_linter.R b/tests/testthat/test-brace_linter.R index 5bc8eb3dd..0d0532f8e 100644 --- a/tests/testthat/test-brace_linter.R +++ b/tests/testthat/test-brace_linter.R @@ -8,13 +8,13 @@ test_that("brace_linter lints braces correctly", { ) linter <- brace_linter() - expect_lint("blah", NULL, linter) - expect_lint("a <- function() {\n}", NULL, linter) - expect_lint("a <- function() { \n}", NULL, linter) + expect_no_lint("blah", linter) + expect_no_lint("a <- function() {\n}", linter) + expect_no_lint("a <- function() { \n}", linter) expect_lint("a <- function() { 1 }", list(open_curly_msg, closed_curly_msg), linter) # allowed by allow_single_line - expect_lint("a <- function() { 1 }", NULL, brace_linter(allow_single_line = TRUE)) + expect_no_lint("a <- function() { 1 }", brace_linter(allow_single_line = TRUE)) expect_lint( trim_some(" @@ -51,30 +51,28 @@ test_that("brace_linter lints braces correctly", { ) # }) is allowed - expect_lint("eval(bquote({\n...\n}))", NULL, linter) + expect_no_lint("eval(bquote({\n...\n}))", linter) # }] is too - expect_lint("df[, {\n...\n}]", NULL, linter) + expect_no_lint("df[, {\n...\n}]", linter) # }, is allowed - expect_lint( + expect_no_lint( trim_some(" fun({ statements }, param)"), - NULL, linter ) - expect_lint( + expect_no_lint( trim_some(" fun(function(a) { statements }, param)"), - NULL, linter ) # ,<\n>{ is allowed - expect_lint( + expect_no_lint( trim_some(" switch( x, @@ -86,12 +84,11 @@ test_that("brace_linter lints braces correctly", { } ) "), - NULL, linter ) # a comment before ,<\n>{ is allowed - expect_lint( + expect_no_lint( trim_some(" switch( x, @@ -103,12 +100,11 @@ test_that("brace_linter lints braces correctly", { } ) "), - NULL, linter ) # a comment before <\n>{ is allowed - expect_lint( + expect_no_lint( trim_some(" switch(stat, o = { @@ -120,11 +116,10 @@ test_that("brace_linter lints braces correctly", { } ) "), - NULL, linter ) - expect_lint( + expect_no_lint( trim_some(" fun( 'This is very very very long text.', @@ -134,12 +129,11 @@ test_that("brace_linter lints braces correctly", { } ) "), - NULL, linter ) # (\n{ is allowed optionally - expect_lint( + expect_no_lint( trim_some(" tryCatch( { @@ -149,14 +143,13 @@ test_that("brace_linter lints braces correctly", { } ) "), - NULL, linter ) # {{ }} is allowed - expect_lint("{{ x }}", NULL, linter) + expect_no_lint("{{ x }}", linter) - expect_lint( + expect_no_lint( trim_some(" pkg_name <- function(path = find_package()) { if (is.null(path)) { @@ -166,7 +159,6 @@ test_that("brace_linter lints braces correctly", { } } "), - NULL, linter ) @@ -185,13 +177,12 @@ test_that("brace_linter lints braces correctly", { expect_lint("a <- function()\n\t{\n 1 \n}", open_curly_msg, linter) # trailing comments are allowed - expect_lint( + expect_no_lint( trim_some(' if ("P" != "NP") { # what most people expect print("Cryptomania is possible") } '), - NULL, linter ) }) @@ -236,10 +227,9 @@ test_that("brace_linter lints spaces before open braces", { ) # should ignore strings and comments, as in regexes: - expect_lint("grepl('(iss){2}', 'Mississippi')", NULL, linter) - expect_lint( + expect_no_lint("grepl('(iss){2}', 'Mississippi')", linter) + expect_no_lint( "x <- 123 # don't flag (paren){brace} if inside a comment", - NULL, linter ) # should not be thrown when the brace lies on subsequent line @@ -258,8 +248,8 @@ test_that("brace_linter lints spaces before open braces", { test_that("brace_linter lints else correctly", { linter <- brace_linter() - expect_lint("if (TRUE) 1 else 2", NULL, linter) - expect_lint("if (TRUE) 1", NULL, linter) + expect_no_lint("if (TRUE) 1 else 2", linter) + expect_no_lint("if (TRUE) 1", linter) lines_brace <- trim_some(" if (TRUE) { @@ -268,7 +258,7 @@ test_that("brace_linter lints else correctly", { 2 } ") - expect_lint(lines_brace, NULL, linter) + expect_no_lint(lines_brace, linter) # such usage is also not allowed by the style guide, but test anyway lines_unbrace <- trim_some(" @@ -279,7 +269,7 @@ test_that("brace_linter lints else correctly", { 2 } ") - expect_lint(lines_unbrace, NULL, linter) + expect_no_lint(lines_unbrace, linter) lines <- trim_some(" foo <- function(x) { @@ -380,8 +370,8 @@ test_that("brace_linter lints function expressions correctly", { test_that("brace_linter lints if/else matching braces correctly", { linter <- brace_linter() - expect_lint("if (TRUE) 1 else 2", NULL, linter) - expect_lint("if (TRUE) 1", NULL, linter) + expect_no_lint("if (TRUE) 1 else 2", linter) + expect_no_lint("if (TRUE) 1", linter) lines_brace <- trim_some(" if (TRUE) { @@ -390,7 +380,7 @@ test_that("brace_linter lints if/else matching braces correctly", { 2 } ") - expect_lint(lines_brace, NULL, linter) + expect_no_lint(lines_brace, linter) # such usage is also not allowed by the style guide, but test anyway lines_unbrace <- trim_some(" @@ -401,7 +391,7 @@ test_that("brace_linter lints if/else matching braces correctly", { 2 } ") - expect_lint(lines_unbrace, NULL, linter) + expect_no_lint(lines_unbrace, linter) # else if is OK lines_else_if <- trim_some(" @@ -413,7 +403,7 @@ test_that("brace_linter lints if/else matching braces correctly", { 3 } ") - expect_lint(lines_else_if, NULL, linter) + expect_no_lint(lines_else_if, linter) lines_if <- trim_some(" foo <- function(x) { @@ -444,13 +434,17 @@ test_that("brace_linter lints if/else matching braces correctly", { # Keep up to date with https://github.com/tidyverse/style/issues/191 test_that("empty brace expressions are always allowed inline", { - expect_lint("while (FALSE) {}", NULL, brace_linter()) - expect_lint("while (FALSE) { }", NULL, brace_linter()) + linter <- brace_linter() + linter_allow <- brace_linter(allow_single_line = TRUE) + lint_msg <- rex::rex("Opening curly braces") + + expect_no_lint("while (FALSE) {}", linter) + expect_no_lint("while (FALSE) { }", linter) # only applies when `{` is "attached" to the preceding token on the same line - expect_lint("while (FALSE)\n{}", rex::rex("Opening curly braces"), brace_linter()) - expect_lint("while (FALSE)\n{ }", rex::rex("Opening curly braces"), brace_linter()) - expect_lint("while (FALSE) {}", NULL, brace_linter(allow_single_line = TRUE)) - expect_lint("while (FALSE) { }", NULL, brace_linter(allow_single_line = TRUE)) + expect_lint("while (FALSE)\n{}", lint_msg, linter) + expect_lint("while (FALSE)\n{ }", lint_msg, linter) + expect_no_lint("while (FALSE) {}", linter_allow) + expect_no_lint("while (FALSE) { }", linter_allow) }) test_that("formula syntax is linted properly", { @@ -458,7 +452,7 @@ test_that("formula syntax is linted properly", { lint_msg_open <- rex::rex("Opening curly braces should never go on their own line") lint_msg_closed <- rex::rex("Closing curly-braces should always be on their own line") - expect_lint( + expect_no_lint( trim_some(" map( .x = 1:4, @@ -466,7 +460,6 @@ test_that("formula syntax is linted properly", { .x + 1 } )"), - NULL, linter ) @@ -515,35 +508,32 @@ test_that("code with pipes is handled correctly", { lint_msg_open <- rex::rex("Opening curly braces should never go on their own line") lint_msg_closed <- rex::rex("Closing curly-braces should always be on their own line") - expect_lint( + expect_no_lint( trim_some(" out <- lapply(stuff, function(i) { do_something(i) - }) %>% unlist + }) %>% unlist() "), - NULL, linter ) - expect_lint( + expect_no_lint( trim_some(" 1:4 %!>% { sum(.) } "), - NULL, linter ) # %>%\n{ is allowed - expect_lint( + expect_no_lint( trim_some(" 1:4 %T>% { sum(.) } "), - NULL, linter ) @@ -592,13 +582,12 @@ test_that("code with pipes is handled correctly", { skip_if_not_r_version("4.1.0") - expect_lint( + expect_no_lint( trim_some(" out <- lapply(stuff, function(i) { do_something(i) }) |> unlist() "), - NULL, linter ) @@ -615,7 +604,7 @@ test_that("function shorthand is treated like 'full' function", { skip_if_not_r_version("4.1.0") linter <- brace_linter() - expect_lint("a <- \\() { \n}", NULL, linter) + expect_no_lint("a <- \\() { \n}", linter) expect_lint( trim_some(" x <- \\() diff --git a/tests/testthat/test-commented_code_linter.R b/tests/testthat/test-commented_code_linter.R index 5154a5a94..8809335de 100644 --- a/tests/testthat/test-commented_code_linter.R +++ b/tests/testthat/test-commented_code_linter.R @@ -1,21 +1,21 @@ test_that("commented_code_linter skips allowed usages", { linter <- commented_code_linter() - expect_lint("blah", NULL, linter) - expect_lint("#' blah <- 1", NULL, linter) - expect_lint("a <- 1\n# comment without code", NULL, linter) - expect_lint("a <- 1\n## whatever", NULL, linter) - - expect_lint("TRUE", NULL, linter) - expect_lint("#' @examples", NULL, linter) - expect_lint("#' foo(1) # list(1)", NULL, linter) # comment in roxygen block ignored - expect_lint("1+1 # gives 2", NULL, linter) - expect_lint("# Non-existent:", NULL, linter) - expect_lint("# 1-a", NULL, linter) # "-" removed from code operators - expect_lint('1+1 # for example cat("123")', NULL, linter) + expect_no_lint("blah", linter) + expect_no_lint("#' blah <- 1", linter) + expect_no_lint("a <- 1\n# comment without code", linter) + expect_no_lint("a <- 1\n## whatever", linter) + + expect_no_lint("TRUE", linter) + expect_no_lint("#' @examples", linter) + expect_no_lint("#' foo(1) # list(1)", linter) # comment in roxygen block ignored + expect_no_lint("1+1 # gives 2", linter) + expect_no_lint("# Non-existent:", linter) + expect_no_lint("# 1-a", linter) # "-" removed from code operators + expect_no_lint('1+1 # for example cat("123")', linter) # regression test for #451 - expect_lint("c('#a#' = 1)", NULL, linter) + expect_no_lint("c('#a#' = 1)", linter) }) test_that("commented_code_linter blocks disallowed usages", { @@ -88,19 +88,17 @@ test_that("commented_code_linter can detect operators in comments and lint corre ) for (op in test_ops) { - expect_lint(paste("i", op, "1", collapse = ""), NULL, linter) - expect_lint(paste("# something like i", op, "1", collapse = ""), NULL, linter) + expect_no_lint(paste("i", op, "1", collapse = ""), linter) + expect_no_lint(paste("# something like i", op, "1", collapse = ""), linter) expect_lint(paste("# i", op, "1", collapse = ""), lint_msg, linter) } -}) -test_that("commented_code_linter can detect operators in comments and lint correctly", { skip_if_not_r_version("4.1.0") expect_lint( "# 1:3 |> sum()", rex::rex("Remove commented code."), - commented_code_linter() + linter ) }) diff --git a/tests/testthat/test-implicit_assignment_linter.R b/tests/testthat/test-implicit_assignment_linter.R index 5457cb441..0681b2ecd 100644 --- a/tests/testthat/test-implicit_assignment_linter.R +++ b/tests/testthat/test-implicit_assignment_linter.R @@ -1,83 +1,76 @@ test_that("implicit_assignment_linter skips allowed usages", { linter <- implicit_assignment_linter() - expect_lint("x <- 1L", NULL, linter) - expect_lint("1L -> x", NULL, linter) - expect_lint("x <<- 1L", NULL, linter) - expect_lint("1L ->> x", NULL, linter) - expect_lint("y <- if (is.null(x)) z else x", NULL, linter) - expect_lint("for (x in 1:10) x <- x + 1", NULL, linter) + expect_no_lint("x <- 1L", linter) + expect_no_lint("1L -> x", linter) + expect_no_lint("x <<- 1L", linter) + expect_no_lint("1L ->> x", linter) + expect_no_lint("y <- if (is.null(x)) z else x", linter) + expect_no_lint("for (x in 1:10) x <- x + 1", linter) - expect_lint("abc <- mean(1:4)", NULL, linter) - expect_lint("mean(1:4) -> abc", NULL, linter) + expect_no_lint("abc <- mean(1:4)", linter) + expect_no_lint("mean(1:4) -> abc", linter) - expect_lint( + expect_no_lint( trim_some(" x <- 1:4 mean(x)"), - NULL, linter ) - expect_lint( + expect_no_lint( trim_some(" x <- 1L if (x) TRUE"), - NULL, linter ) - expect_lint( + expect_no_lint( trim_some(" 0L -> abc while (abc) { FALSE }"), - NULL, linter ) - expect_lint( + expect_no_lint( trim_some(" if (x > 20L) { x <- x / 2.0 }"), - NULL, linter ) - expect_lint( + expect_no_lint( trim_some(" i <- 1 while (i < 6L) { print(i) i <- i + 1 }"), - NULL, linter ) - expect_lint( + expect_no_lint( trim_some(" foo <- function(x) { x <- x + 1 return(x) }"), - NULL, linter ) - expect_lint( + expect_no_lint( trim_some(" f <- function() { p <- g() p <- if (is.null(p)) x else p }"), - NULL, linter ) - expect_lint( + expect_no_lint( trim_some(" map( .x = 1:4, @@ -86,42 +79,37 @@ test_that("implicit_assignment_linter skips allowed usages", { x } )"), - NULL, linter ) - expect_lint( + expect_no_lint( trim_some(" lapply(1:4, function(x) { x <- x + 1 x })"), - NULL, linter ) skip_if_not_r_version("4.1.0") - expect_lint( + expect_no_lint( trim_some(" map(1:4, \\(x) { x <- x + 1 x })"), - NULL, linter ) }) test_that("implicit_assignment_linter respects except argument", { - expect_lint( + expect_no_lint( "local({ a <- 1L })", - NULL, implicit_assignment_linter(except = NULL) ) - expect_lint( + expect_no_lint( "local({ a <- 1L })", - NULL, implicit_assignment_linter(except = character(0L)) ) @@ -137,9 +125,8 @@ test_that("implicit_assignment_linter respects except argument", { implicit_assignment_linter(except = NULL) ) - expect_lint( + expect_no_lint( "local(a <- 1L)", - NULL, implicit_assignment_linter(except = "local") ) }) @@ -147,58 +134,52 @@ test_that("implicit_assignment_linter respects except argument", { test_that("implicit_assignment_linter skips allowed usages with braces", { linter <- implicit_assignment_linter(except = character(0L)) - expect_lint( + expect_no_lint( trim_some(" foo({ a <- 1L }) "), - NULL, linter ) - expect_lint( + expect_no_lint( trim_some(" output <- capture.output({ x <- f() }) "), - NULL, linter ) - expect_lint( + expect_no_lint( trim_some(" quote({ a <- 1L }) "), - NULL, linter ) - expect_lint( + expect_no_lint( trim_some(" bquote({ a <- 1L }) "), - NULL, linter ) - expect_lint( + expect_no_lint( trim_some(" expression({ a <- 1L }) "), - NULL, linter ) - expect_lint( + expect_no_lint( trim_some(" local({ a <- 1L }) "), - NULL, linter ) }) @@ -206,20 +187,19 @@ test_that("implicit_assignment_linter skips allowed usages with braces", { test_that("implicit_assignment_linter makes exceptions for functions that capture side-effects", { linter <- implicit_assignment_linter() - expect_lint( + expect_no_lint( trim_some(" test_that('my test', { a <- 1L expect_equal(a, 1L) })"), - NULL, linter ) # rlang - expect_lint("expr(a <- 1L)", NULL, linter) - expect_lint("quo(a <- 1L)", NULL, linter) - expect_lint("quos(a <- 1L)", NULL, linter) + expect_no_lint("expr(a <- 1L)", linter) + expect_no_lint("quo(a <- 1L)", linter) + expect_no_lint("quos(a <- 1L)", linter) }) test_that("implicit_assignment_linter blocks disallowed usages in simple conditional statements", { @@ -335,15 +315,18 @@ test_that("implicit_assignment_linter blocks disallowed usages in function calls test_that("implicit_assignment_linter works as expected with pipes and walrus operator", { linter <- implicit_assignment_linter() - expect_lint("data %>% mutate(a := b)", NULL, linter) - expect_lint("dt %>% .[, z := x + y]", NULL, linter) - expect_lint("data %<>% mutate(a := b)", NULL, linter) + expect_no_lint("data %>% mutate(a := b)", linter) + expect_no_lint( + "dt %>% .[, z := x + y]", + linter + ) + expect_no_lint("data %<>% mutate(a := b)", linter) - expect_lint("DT[i, x := i]", NULL, linter) + expect_no_lint("DT[i, x := i]", linter) skip_if_not_r_version("4.1.0") - expect_lint("data |> mutate(a := b)", NULL, linter) + expect_no_lint("data |> mutate(a := b)", linter) }) test_that("parenthetical assignments are caught", { @@ -358,21 +341,21 @@ test_that("allow_lazy lets lazy assignments through", { linter <- implicit_assignment_linter(allow_lazy = TRUE) lint_message <- rex::rex("Avoid implicit assignments in function calls.") - expect_lint("A && (B <- foo(A))", NULL, linter) + expect_no_lint("A && (B <- foo(A))", linter) # || also admits laziness - expect_lint("A || (B <- foo(A))", NULL, linter) + expect_no_lint("A || (B <- foo(A))", linter) # & and |, however, do not expect_lint("A & (B <- foo(A))", lint_message, linter) expect_lint("A | (B <- foo(A))", lint_message, linter) - expect_lint("A && foo(bar(idx <- baz()))", NULL, linter) + expect_no_lint("A && foo(bar(idx <- baz()))", linter) # LHS _is_ linted expect_lint("(A <- foo()) && B", lint_message, linter) # however we skip on _any_ RHS (even if it's later an LHS) # test on all &&/|| combinations to stress test operator precedence - expect_lint("A && (B <- foo(A)) && C", NULL, linter) - expect_lint("A && (B <- foo(A)) || C", NULL, linter) - expect_lint("A || (B <- foo(A)) && C", NULL, linter) - expect_lint("A || (B <- foo(A)) || C", NULL, linter) + expect_no_lint("A && (B <- foo(A)) && C", linter) + expect_no_lint("A && (B <- foo(A)) || C", linter) + expect_no_lint("A || (B <- foo(A)) && C", linter) + expect_no_lint("A || (B <- foo(A)) || C", linter) # &&/|| elsewhere in the tree don't matter expect_lint( trim_some(" @@ -388,13 +371,12 @@ test_that("allow_scoped skips scoped assignments", { linter <- implicit_assignment_linter(allow_scoped = TRUE) lint_message <- rex::rex("Avoid implicit assignments in function calls.") - expect_lint( + expect_no_lint( trim_some(" if (any(idx <- x < 0)) { stop('negative elements: ', toString(which(idx))) } "), - NULL, linter ) expect_lint( @@ -418,12 +400,11 @@ test_that("allow_scoped skips scoped assignments", { linter ) - expect_lint( + expect_no_lint( trim_some(" obj <- letters while ((n <- length(obj)) > 0) obj <- obj[-n] "), - NULL, linter ) expect_lint( @@ -446,13 +427,12 @@ test_that("allow_scoped skips scoped assignments", { test_that("interaction of allow_lazy and allow_scoped", { linter <- implicit_assignment_linter(allow_scoped = TRUE, allow_lazy = TRUE) - expect_lint( + expect_no_lint( trim_some(" if (any(idx <- foo()) && BB) { stop('Invalid foo() output: ', toString(idx)) } "), - NULL, linter ) expect_lint( @@ -465,14 +445,13 @@ test_that("interaction of allow_lazy and allow_scoped", { rex::rex("Avoid implicit assignments in function calls."), linter ) - expect_lint( + expect_no_lint( trim_some(" if (AA && any(idx <- foo())) { stop('Invalid foo() output: ', toString(idx)) } print(format(idx)) # NB: bad code! idx may not exist. "), - NULL, linter ) }) diff --git a/tests/testthat/test-infix_spaces_linter.R b/tests/testthat/test-infix_spaces_linter.R index ade7969c0..245ac8a4e 100644 --- a/tests/testthat/test-infix_spaces_linter.R +++ b/tests/testthat/test-infix_spaces_linter.R @@ -31,12 +31,12 @@ test_that("returns the correct linting", { linter <- infix_spaces_linter() lint_msg <- rex::rex("Put spaces around all infix operators.") - expect_lint("blah", NULL, linter) + expect_no_lint("blah", linter) for (op in ops) { - expect_lint(paste0("1 ", op, " 2"), NULL, linter) - expect_lint(paste0("1 ", op, "\n2"), NULL, linter) - expect_lint(paste0("1 ", op, "\n 2"), NULL, linter) + expect_no_lint(paste0("1 ", op, " 2"), linter) + expect_no_lint(paste0("1 ", op, "\n2"), linter) + expect_no_lint(paste0("1 ", op, "\n 2"), linter) expect_lint(paste0("1", op, "2"), lint_msg, linter) @@ -48,11 +48,11 @@ test_that("returns the correct linting", { expect_lint(paste0("1", op, " 2"), lint_msg, linter) } - expect_lint("b <- 2E+4", NULL, linter) - expect_lint("a <- 1e-3", NULL, linter) - expect_lint("a[-1]", NULL, linter) - expect_lint("a[-1 + 1]", NULL, linter) - expect_lint("a[1 + -1]", NULL, linter) + expect_no_lint("b <- 2E+4", linter) + expect_no_lint("a <- 1e-3", linter) + expect_no_lint("a[-1]", linter) + expect_no_lint("a[-1 + 1]", linter) + expect_no_lint("a[1 + -1]", linter) expect_lint("fun(a=1)", lint_msg, linter) }) @@ -72,26 +72,25 @@ test_that("The three `=` are all linted", { test_that("exclude_operators works", { lint_msg <- rex::rex("Put spaces around all infix operators.") - expect_lint("a+b", NULL, infix_spaces_linter(exclude_operators = "+")) - expect_lint( + expect_no_lint("a+b", infix_spaces_linter(exclude_operators = "+")) + expect_no_lint( trim_some(" a+b a-b "), - NULL, infix_spaces_linter(exclude_operators = c("+", "-")) ) # operators match on text, not hidden node expect_lint("a<<-1", lint_msg, infix_spaces_linter(exclude_operators = "<-")) - expect_lint("a<<-1", NULL, infix_spaces_linter(exclude_operators = "<<-")) + expect_no_lint("a<<-1", infix_spaces_linter(exclude_operators = "<<-")) expect_lint("a:=1", lint_msg, infix_spaces_linter(exclude_operators = "<-")) - expect_lint("a:=1", NULL, infix_spaces_linter(exclude_operators = ":=")) + expect_no_lint("a:=1", infix_spaces_linter(exclude_operators = ":=")) expect_lint("a->>1", lint_msg, infix_spaces_linter(exclude_operators = "->")) - expect_lint("a->>1", NULL, infix_spaces_linter(exclude_operators = "->>")) - expect_lint("a%any%1", NULL, infix_spaces_linter(exclude_operators = "%%")) - expect_lint("function(a=1) { }", NULL, infix_spaces_linter(exclude_operators = "=")) - expect_lint("foo(a=1)", NULL, infix_spaces_linter(exclude_operators = "=")) + expect_no_lint("a->>1", infix_spaces_linter(exclude_operators = "->>")) + expect_no_lint("a%any%1", infix_spaces_linter(exclude_operators = "%%")) + expect_no_lint("function(a=1) { }", infix_spaces_linter(exclude_operators = "=")) + expect_no_lint("foo(a=1)", infix_spaces_linter(exclude_operators = "=")) }) # more tests specifically for assignment @@ -99,23 +98,22 @@ test_that("assignment cases return the correct linting", { linter <- infix_spaces_linter() lint_msg <- rex::rex("Put spaces around all infix operators.") - expect_lint("fun(blah = 1)", NULL, linter) + expect_no_lint("fun(blah = 1)", linter) - expect_lint("blah <- 1", NULL, linter) - expect_lint("blah = 1", NULL, linter) + expect_no_lint("blah <- 1", linter) + expect_no_lint("blah = 1", linter) - expect_lint("\"my = variable\" <- 42.0", NULL, linter) + expect_no_lint("\"my = variable\" <- 42.0", linter) - expect_lint("if (0 < 1) x <- 42L", NULL, linter) - expect_lint( + expect_no_lint("if (0 < 1) x <- 42L", linter) + expect_no_lint( trim_some(" if (0 < 1) { x <- 42L }"), - NULL, linter ) - expect_lint("my = bad = variable = name <- 2.0", NULL, linter) + expect_no_lint("my = bad = variable = name <- 2.0", linter) expect_lint("blah<- 1", lint_msg, linter) expect_lint("blah <-1", lint_msg, linter) @@ -135,9 +133,9 @@ test_that("infix_spaces_linter can allow >1 spaces optionally", { test_that("exception for box::use()", { linter <- infix_spaces_linter() - expect_lint("box::use(a/b)", NULL, linter) - expect_lint("box::use(./a/b)", NULL, linter) - expect_lint( + expect_no_lint("box::use(a/b)", linter) + expect_no_lint("box::use(./a/b)", linter) + expect_no_lint( trim_some(" box::use( a, @@ -146,7 +144,6 @@ test_that("exception for box::use()", { alias = a/b/c[xyz = abc, ...], ) "), - NULL, linter ) }) @@ -167,8 +164,8 @@ test_that("Rules around missing arguments are respected", { linter <- infix_spaces_linter() lint_msg <- rex::rex("Put spaces around all infix operators.") - expect_lint("switch(a = , b = 2)", NULL, linter) - expect_lint("alist(missing_arg = )", NULL, linter) + expect_no_lint("switch(a = , b = 2)", linter) + expect_no_lint("alist(missing_arg = )", linter) expect_lint("switch(a =, b = 2)", lint_msg, linter) expect_lint("alist(missing_arg =)", lint_msg, linter) @@ -178,7 +175,7 @@ test_that("native pipe is supported", { skip_if_not_r_version("4.1.0") linter <- infix_spaces_linter() - expect_lint("a |> foo()", NULL, linter) + expect_no_lint("a |> foo()", linter) expect_lint("a|>foo()", rex::rex("Put spaces around all infix operators."), linter) }) @@ -194,19 +191,19 @@ test_that("mixed unary & binary operators aren't mis-lint", { }) test_that("parse tags are accepted by exclude_operators", { - expect_lint("sum(x, na.rm=TRUE)", NULL, infix_spaces_linter(exclude_operators = "EQ_SUB")) - expect_lint("function(x, na.rm=TRUE) { }", NULL, infix_spaces_linter(exclude_operators = "EQ_FORMALS")) - expect_lint("x=1", NULL, infix_spaces_linter(exclude_operators = "EQ_ASSIGN")) + expect_no_lint("sum(x, na.rm=TRUE)", infix_spaces_linter(exclude_operators = "EQ_SUB")) + expect_no_lint("function(x, na.rm=TRUE) { }", infix_spaces_linter(exclude_operators = "EQ_FORMALS")) + expect_no_lint("x=1", infix_spaces_linter(exclude_operators = "EQ_ASSIGN")) # uses parse_tag - expect_lint("1+1", NULL, infix_spaces_linter(exclude_operators = "'+'")) + expect_no_lint("1+1", infix_spaces_linter(exclude_operators = "'+'")) # mixing text <- "x=function(a=foo(bar=1)) { }" col_assign <- list(column_number = 2L) col_formals <- list(column_number = 13L) col_sub <- list(column_number = 21L) - expect_lint(text, NULL, infix_spaces_linter(exclude_operators = c("EQ_SUB", "EQ_FORMALS", "EQ_ASSIGN"))) + expect_no_lint(text, infix_spaces_linter(exclude_operators = c("EQ_SUB", "EQ_FORMALS", "EQ_ASSIGN"))) expect_lint(text, col_assign, infix_spaces_linter(exclude_operators = c("EQ_SUB", "EQ_FORMALS"))) expect_lint(text, col_formals, infix_spaces_linter(exclude_operators = c("EQ_SUB", "EQ_ASSIGN"))) expect_lint(text, col_sub, infix_spaces_linter(exclude_operators = c("EQ_FORMALS", "EQ_ASSIGN"))) diff --git a/tests/testthat/test-one_call_pipe_linter.R b/tests/testthat/test-one_call_pipe_linter.R index 9a1a84975..9cf7a60e9 100644 --- a/tests/testthat/test-one_call_pipe_linter.R +++ b/tests/testthat/test-one_call_pipe_linter.R @@ -2,16 +2,17 @@ test_that("one_call_pipe_linter skips allowed usages", { linter <- one_call_pipe_linter() # two pipe steps is OK - expect_lint("x %>% foo() %>% bar()", NULL, linter) + expect_no_lint("x %>% foo() %>% bar()", linter) # call in first step --> OK - expect_lint("foo(x) %>% bar()", NULL, linter) + expect_no_lint("foo(x) %>% bar()", linter) # both calls in second step --> OK - expect_lint("x %>% foo(bar(.))", NULL, linter) + expect_no_lint("x %>% foo(bar(.))", linter) # assignment pipe is exempted - expect_lint("x %<>% as.character()", NULL, linter) + expect_no_lint("x %<>% as.character()", linter) }) +# nofuzz start test_that("one_call_pipe_linter blocks simple disallowed usages", { linter <- one_call_pipe_linter() lint_msg <- rex::rex("Avoid pipe %>% for expressions with only a single call.") @@ -24,18 +25,19 @@ test_that("one_call_pipe_linter blocks simple disallowed usages", { # nested case expect_lint("x %>% inner_join(y %>% filter(is_treatment))", lint_msg, linter) }) +# nofuzz end test_that("one_call_pipe_linter skips data.table chains", { linter <- one_call_pipe_linter() lint_msg <- rex::rex("Avoid pipe %>% for expressions with only a single call.") - expect_lint("DT[x > 5, sum(y), by = keys] %>% .[, .SD[1], by = key1]", NULL, linter) + expect_no_lint("DT[x > 5, sum(y), by = keys] %>% .[, .SD[1], by = key1]", linter) # lint here: instead of a pipe, use DT[x > 5, sum(y), by = keys] expect_lint("DT %>% .[x > 5, sum(y), by = keys]", lint_msg, linter) # ditto for [[ - expect_lint("DT %>% rowSums() %>% .[[idx]]", NULL, linter) + expect_no_lint("DT %>% rowSums() %>% .[[idx]]", linter) expect_lint("DT %>% .[[idx]]", lint_msg, linter) }) @@ -44,17 +46,13 @@ test_that("one_call_pipe_linter treats all pipes equally", { linter <- one_call_pipe_linter() lint_msg_part <- " for expressions with only a single call." - expect_lint("foo %>% bar() %$% col", NULL, linter) + expect_no_lint("foo %>% bar() %$% col", linter) expect_lint("x %T>% foo()", rex::rex("%T>%", lint_msg_part), linter) expect_lint("x %$%\n foo", rex::rex("%$%", lint_msg_part), linter) - expect_lint( - 'data %>% filter(type == "console") %$% obscured_id %>% unique()', - NULL, - linter - ) + expect_no_lint('data %>% filter(type == "console") %$% obscured_id %>% unique()', linter) }) -test_that("multiple lints are generated correctly", { +test_that("multiple lints are generated correctly", { # nofuzz expect_lint( trim_some("{ a %>% b() @@ -76,17 +74,17 @@ test_that("Native pipes are handled as well", { linter <- one_call_pipe_linter() - expect_lint( + expect_lint( # nofuzz "x |> foo()", rex::rex("Avoid pipe |> for expressions with only a single call."), linter ) # mixed pipes - expect_lint("x |> foo() %>% bar()", NULL, linter) - expect_lint("x %>% foo() |> bar()", NULL, linter) + expect_no_lint("x |> foo() %>% bar()", linter) + expect_no_lint("x %>% foo() |> bar()", linter) - expect_lint( + expect_lint( # nofuzz trim_some("{ a %>% b() c |> d() @@ -105,13 +103,13 @@ test_that("one_call_pipe_linter skips data.table chains with native pipe", { linter <- one_call_pipe_linter() lint_msg <- rex::rex("Avoid pipe |> for expressions with only a single call.") - expect_lint("DT[x > 5, sum(y), by = keys] |> _[, .SD[1], by = key1]", NULL, linter) + expect_no_lint("DT[x > 5, sum(y), by = keys] |> _[, .SD[1], by = key1]", linter) # lint here: instead of a pipe, use DT[x > 5, sum(y), by = keys] expect_lint("DT |> _[x > 5, sum(y), by = keys]", lint_msg, linter) # ditto for [[ - expect_lint("DT |> rowSums() |> _[[idx]]", NULL, linter) + expect_no_lint("DT |> rowSums() |> _[[idx]]", linter) expect_lint("DT |> _[[idx]]", lint_msg, linter) }) diff --git a/tests/testthat/test-pipe_call_linter.R b/tests/testthat/test-pipe_call_linter.R index 96cf2036e..e15226de3 100644 --- a/tests/testthat/test-pipe_call_linter.R +++ b/tests/testthat/test-pipe_call_linter.R @@ -1,10 +1,10 @@ test_that("pipe_call_linter skips allowed usages", { linter <- pipe_call_linter() - expect_lint("a %>% foo()", NULL, linter) - expect_lint("a %>% foo(x)", NULL, linter) - expect_lint("b %>% { foo(., ., .) }", NULL, linter) - expect_lint("a %>% foo() %>% bar()", NULL, linter) + expect_no_lint("a %>% foo()", linter) + expect_no_lint("a %>% foo(x)", linter) + expect_no_lint("b %>% { foo(., ., .) }", linter) + expect_no_lint("a %>% foo() %>% bar()", linter) # ensure it works across lines too lines <- trim_some(" @@ -12,10 +12,10 @@ test_that("pipe_call_linter skips allowed usages", { foo() %>% bar() ") - expect_lint(lines, NULL, linter) + expect_no_lint(lines, linter) # symbol extraction is OK (don't force extract2(), e.g.) - expect_lint("a %>% .$y %>% mean()", NULL, linter) + expect_no_lint("a %>% .$y %>% mean()", linter) # more complicated expressions don't pick up on nested symbols lines <- trim_some(" @@ -25,10 +25,10 @@ test_that("pipe_call_linter skips allowed usages", { my_combination_fun(tmp, bla) } ") - expect_lint(lines, NULL, linter) + expect_no_lint(lines, linter) # extraction pipe uses RHS symbols - expect_lint("a %$% b", NULL, linter) + expect_no_lint("a %$% b", linter) }) test_that("pipe_call_linter blocks simple disallowed usages", { @@ -68,7 +68,7 @@ local({ patrick::with_parameters_test_that( "All pipe operators are caught", { - expect_lint(sprintf("a %s foo()", pipe), NULL, linter) + expect_no_lint(sprintf("a %s foo()", pipe), linter) expect_lint(sprintf("a %s foo", pipe), sprintf("`a %s foo`", pipe), linter) }, pipe = pipes, diff --git a/tests/testthat/test-pipe_consistency_linter.R b/tests/testthat/test-pipe_consistency_linter.R index 4a236b156..57c6df83b 100644 --- a/tests/testthat/test-pipe_consistency_linter.R +++ b/tests/testthat/test-pipe_consistency_linter.R @@ -1,19 +1,19 @@ +# nofuzz start test_that("pipe_consistency skips allowed usage", { skip_if_not_r_version("4.1.0") linter <- pipe_consistency_linter() - expect_lint("1:3 %>% mean() %>% as.character()", NULL, linter) - expect_lint("1:3 |> mean() |> as.character()", NULL, linter) + expect_no_lint("1:3 %>% mean() %>% as.character()", linter) + expect_no_lint("1:3 |> mean() |> as.character()", linter) # With no pipes - expect_lint("x <- 1:5", NULL, linter) + expect_no_lint("x <- 1:5", linter) # Across multiple lines - expect_lint( + expect_no_lint( trim_some(" 1:3 %>% mean() %>% as.character() "), - NULL, linter ) }) @@ -96,9 +96,8 @@ test_that("pipe_consistency_linter works with |> argument", { linter ) - expect_lint( + expect_no_lint( "1:3 |> mean() |> as.character()", - NULL, linter ) @@ -134,11 +133,7 @@ test_that("pipe_consistency_linter works with %>% argument", { linter ) - expect_lint( - "1:3 %>% mean() %>% as.character()", - NULL, - linter - ) + expect_no_lint("1:3 %>% mean() %>% as.character()", linter) expect_lint( trim_some(" @@ -156,7 +151,7 @@ test_that("pipe_consistency_linter works with other magrittr pipes", { linter <- pipe_consistency_linter() expected_message <- rex::rex("Stick to one pipe operator; found 1 instances of %>% and 1 instances of |>.") - expect_lint("1:3 %>% mean() %T% print()", NULL, linter) + expect_no_lint("1:3 %>% mean() %T% print()", linter) expect_lint( "1:3 |> mean() %T>% print()", list( @@ -166,3 +161,4 @@ test_that("pipe_consistency_linter works with other magrittr pipes", { linter ) }) +# nofuzz end diff --git a/tests/testthat/test-pipe_continuation_linter.R b/tests/testthat/test-pipe_continuation_linter.R index 0d3350bca..566848505 100644 --- a/tests/testthat/test-pipe_continuation_linter.R +++ b/tests/testthat/test-pipe_continuation_linter.R @@ -3,19 +3,18 @@ test_that("pipe-continuation correctly handles stand-alone expressions", { lint_msg <- rex::rex("Put a space before `%>%` and a new line after it,") # Expressions without pipes are ignored - expect_lint("blah", NULL, linter) + expect_no_lint("blah", linter) # Pipe expressions on a single line are ignored - expect_lint("foo %>% bar() %>% baz()", NULL, linter) + expect_no_lint("foo %>% bar() %>% baz()", linter) # Pipe expressions spanning multiple lines with each expression on a line are ignored - expect_lint( + expect_no_lint( trim_some(" foo %>% bar() %>% baz() "), - NULL, linter ) @@ -66,13 +65,12 @@ test_that("pipe-continuation linter correctly handles nesting", { ) # but no lints here - expect_lint( + expect_no_lint( trim_some(" 1:4 %>% { (.) %>% sum() } "), - NULL, linter ) }) @@ -84,14 +82,13 @@ test_that("pipe-continuation linter handles native pipe", { lint_msg_native <- rex::rex("Put a space before `|>` and a new line after it,") lint_msg_magrittr <- rex::rex("Put a space before `%>%` and a new line after it,") - expect_lint("foo |> bar() |> baz()", NULL, linter) - expect_lint( + expect_no_lint("foo |> bar() |> baz()", linter) + expect_no_lint( trim_some(" foo |> bar() |> baz() "), - NULL, linter ) expect_lint( @@ -161,12 +158,14 @@ local({ test_data <- diamonds %>% head(10) %>% tail(5) }) "), "three inside test_that()", - trim_some(" - { - x <- a %>% b %>% c - y <- c %>% b %>% a - } - "), "two different single-line pipelines", + trim_some( + " + { + x <- a %>% b %>% c + y <- c %>% b %>% a + } + " + ), "two different single-line pipelines", trim_some(" my_fun <- function() { a %>% @@ -179,7 +178,7 @@ local({ "valid nesting is handled", # nolint next: unnecessary_nesting_linter. TODO(#2334): Remove this nolint. { - expect_lint(code_string, NULL, linter) + expect_no_lint(code_string, linter) }, .cases = .cases ) diff --git a/tests/testthat/test-pipe_return_linter.R b/tests/testthat/test-pipe_return_linter.R index 1d7af5f6c..f235edbd1 100644 --- a/tests/testthat/test-pipe_return_linter.R +++ b/tests/testthat/test-pipe_return_linter.R @@ -6,7 +6,7 @@ test_that("pipe_return_linter skips allowed usages", { filter(str > 5) %>% summarize(str = sum(str)) ") - expect_lint(normal_pipe_lines, NULL, linter) + expect_no_lint(normal_pipe_lines, linter) normal_function_lines <- trim_some(" pipeline <- function(x) { @@ -16,7 +16,7 @@ test_that("pipe_return_linter skips allowed usages", { return(out) } ") - expect_lint(normal_function_lines, NULL, linter) + expect_no_lint(normal_function_lines, linter) nested_return_lines <- trim_some(" pipeline <- function(x) { @@ -27,7 +27,7 @@ test_that("pipe_return_linter skips allowed usages", { return(x_squared) } ") - expect_lint(nested_return_lines, NULL, linter) + expect_no_lint(nested_return_lines, linter) }) test_that("pipe_return_linter blocks simple disallowed usages", { diff --git a/tests/testthat/test-unnecessary_placeholder_linter.R b/tests/testthat/test-unnecessary_placeholder_linter.R index d8a1e677a..8ee413a4b 100644 --- a/tests/testthat/test-unnecessary_placeholder_linter.R +++ b/tests/testthat/test-unnecessary_placeholder_linter.R @@ -5,21 +5,21 @@ patrick::with_parameters_test_that( "unnecessary_placeholder_linter skips allowed usages", { # . used in position other than first --> ok - expect_lint(sprintf("x %s foo(y, .)", pipe), NULL, linter) + expect_no_lint(sprintf("x %s foo(y, .)", pipe), linter) # ditto for nested usage - expect_lint(sprintf("x %s foo(y, bar(.))", pipe), NULL, linter) + expect_no_lint(sprintf("x %s foo(y, bar(.))", pipe), linter) # . used twice --> ok - expect_lint(sprintf("x %s foo(., .)", pipe), NULL, linter) + expect_no_lint(sprintf("x %s foo(., .)", pipe), linter) # . used as a keyword argument --> ok - expect_lint(sprintf("x %s foo(arg = .)", pipe), NULL, linter) + expect_no_lint(sprintf("x %s foo(arg = .)", pipe), linter) # . used inside a scope --> ok - expect_lint(sprintf("x %s { foo(arg = .) }", pipe), NULL, linter) + expect_no_lint(sprintf("x %s { foo(arg = .) }", pipe), linter) }, .test_name = names(pipes), pipe = pipes ) -patrick::with_parameters_test_that( +patrick::with_parameters_test_that( # nofuzz "unnecessary_placeholder_linter blocks simple disallowed usages", { expect_lint( @@ -38,7 +38,7 @@ patrick::with_parameters_test_that( pipe = pipes ) -test_that("lints vectorize", { +test_that("lints vectorize", { # nofuzz lint_msg <- rex::rex("Don't use the placeholder (`.`) when it's not needed") expect_lint( diff --git a/tests/testthat/test-unused_import_linter.R b/tests/testthat/test-unused_import_linter.R index cb6f89872..4c2bac2ff 100644 --- a/tests/testthat/test-unused_import_linter.R +++ b/tests/testthat/test-unused_import_linter.R @@ -1,21 +1,27 @@ test_that("unused_import_linter lints as expected", { linter <- unused_import_linter() - expect_lint("library(dplyr)\ntibble(a = 1)", NULL, linter) + expect_no_lint("library(dplyr)\ntibble(a = 1)", linter) # SYMBOL_FUNCTION_CALL usage is detected - expect_lint("library(tidyverse)\ntibble(a = 1)", NULL, linter) + expect_no_lint("library(tidyverse)\ntibble(a = 1)", linter) # SYMBOL usage is detected - expect_lint("library(dplyr)\ndo.call(tibble, args = list(a = 1))", NULL, linter) + expect_no_lint("library(dplyr)\ndo.call(tibble, args = list(a = 1))", linter) # SPECIAL usage is detected - expect_lint("library(magrittr)\n1:3 %>% mean()", NULL, linter) + expect_no_lint( # nofuzz + trim_some(" + library(magrittr) + 1:3 %>% mean() + "), + linter + ) # dataset is detected - expect_lint("library(dplyr)\nstarwars", NULL, linter) - expect_lint("library(datasets)\nstate.center", NULL, linter) + expect_no_lint("library(dplyr)\nstarwars", linter) + expect_no_lint("library(datasets)\nstate.center", linter) # Missing packages are ignored - expect_lint("library(not.a.package)\ntibble(a = 1)", NULL, linter) + expect_no_lint("library(not.a.package)\ntibble(a = 1)", linter) # SYMBOL calls with character.only = TRUE are ignored, even if the argument is a package name - expect_lint("library(dplyr, character.only = TRUE)\n1 + 1", NULL, linter) + expect_no_lint("library(dplyr, character.only = TRUE)\n1 + 1", linter) lint_msg <- rex::rex("Package 'dplyr' is attached but never used") msg_ns <- rex::rex("Don't attach package 'dplyr', which is only used by namespace.") @@ -26,11 +32,11 @@ test_that("unused_import_linter lints as expected", { expect_lint("library('dplyr', character.only = TRUE)\n1 + 1", lint_msg, linter) # ignore namespaced usages by default, but provide custom lint message expect_lint("library(dplyr)\ndplyr::tibble(a = 1)", msg_ns, linter) - expect_lint("library(dplyr)\ndplyr::tibble(a = 1)", NULL, unused_import_linter(allow_ns_usage = TRUE)) + expect_no_lint("library(dplyr)\ndplyr::tibble(a = 1)", unused_import_linter(allow_ns_usage = TRUE)) # ignore packages in except_packages - expect_lint("library(data.table)\n1 + 1", NULL, linter) - expect_lint("library(dplyr)\n1 + 1", NULL, unused_import_linter(except_packages = "dplyr")) + expect_no_lint("library(data.table)\n1 + 1", linter) + expect_no_lint("library(dplyr)\n1 + 1", unused_import_linter(except_packages = "dplyr")) }) test_that("unused_import_linter handles message vectorization", { @@ -70,6 +76,6 @@ test_that("glue usages are seen", { glue('{ xml_parse_data() }') ") - expect_lint(lines, NULL, unused_import_linter()) + expect_no_lint(lines, unused_import_linter()) expect_lint(lines, lint_msg, unused_import_linter(interpret_glue = FALSE)) })