diff --git a/.gitignore b/.gitignore index 7eeb21406..b12a36573 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ .idea bad.R script.R + +*.Rcheck +lintr_*.tar.gz diff --git a/NEWS.md b/NEWS.md index c4b0e9939..9a7944f33 100644 --- a/NEWS.md +++ b/NEWS.md @@ -19,6 +19,7 @@ * `object_name_linter()` now excludes special R hook functions such as `.onLoad` (#500, #614, @AshesITR and @michaelchirico) * `equals_na_linter()` now lints `x != NA` and `NA == x`, and skips usages in comments (#545, @michaelchirico) * Malformed Rmd files now cause a lint instead of an error (#571, #575, @AshesITR) +* `object_name_linter()` gains a new default style, `"symbols"`, which won't lint all-symbol object names (in particular, that means operator names like `%+%` are skipped; #615, @michaelchirico) # lintr 2.0.1 diff --git a/R/actions.R b/R/actions.R index 7bc920ff1..9e0971998 100644 --- a/R/actions.R +++ b/R/actions.R @@ -5,9 +5,12 @@ in_github_actions <- function() { # Output logging commands for any lints found github_actions_log_lints <- function(lints) { for (x in lints) { - cat( - sprintf("::warning file=%s,line=%s,col=%s::%s\n", x$filename, x$line_number, x$column_number, x$message), - sep = "" + file_line_col <- sprintf( + "file=%s,line=%s,col=%s", x$filename, x$line_number, x$column_number ) + cat(sprintf( + "::warning %s::%s,%s\n", + file_line_col, file_line_col, x$message + ), sep = "") } } diff --git a/R/declared_functions.R b/R/declared_functions.R index 8ff135ee3..959f77ccf 100644 --- a/R/declared_functions.R +++ b/R/declared_functions.R @@ -1,22 +1,3 @@ -# Find all normal function declarations -# TODO: setMethod() calls not included -declared_functions <- function(x) { - - xpath <- paste0( - # Top level expression which - "/exprlist/expr", - - # Assigns to a symbol - "[./LEFT_ASSIGN|EQ_ASSIGN]", - "[./expr[FUNCTION]]", - "[./expr/SYMBOL]", - - # Retrieve assigned name of the function - "/expr/SYMBOL/text()") - - as.character(xml2::xml_find_all(x, xpath)) -} - declared_s3_generics <- function(x) { xpath <- paste0( # Top level expression which diff --git a/R/expect_lint.R b/R/expect_lint.R index 328701315..4fc134d10 100644 --- a/R/expect_lint.R +++ b/R/expect_lint.R @@ -45,7 +45,7 @@ expect_lint <- function(content, checks, ..., file = NULL, language = "en") { if (is.null(file)) { file <- tempfile() - on.exit(unlink(file)) + on.exit(unlink(file), add = TRUE) writeLines(content, con = file, sep = "\n") } diff --git a/R/lint.R b/R/lint.R index 08a540a0c..f777311ed 100644 --- a/R/lint.R +++ b/R/lint.R @@ -28,7 +28,7 @@ lint <- function(filename, linters = NULL, cache = FALSE, ..., parse_settings = if (inline_data) { content <- gsub("\n$", "", filename) filename <- tempfile() - on.exit(unlink(filename)) + on.exit(unlink(filename), add = TRUE) writeLines(text = content, con = filename, sep = "\n") } @@ -378,11 +378,23 @@ rstudio_source_markers <- function(lints) { }) # request source markers - rstudioapi::callFun("sourceMarkers", - name = "lintr", - markers = markers, - basePath = package_path, - autoSelect = "first") + out <- rstudioapi::callFun( + "sourceMarkers", + name = "lintr", + markers = markers, + basePath = package_path, + autoSelect = "first" + ) + + # workaround to avoid focusing an empty Markers pane + # when possible, better solution is to delete the "lintr" source marker list + # https://github.com/rstudio/rstudioapi/issues/209 + if (length(lints) == 0) { + Sys.sleep(0.1) + rstudioapi::executeCommand("activateConsole") + } + + out } #' Checkstyle Report for lint results diff --git a/R/methods.R b/R/methods.R index 3aacb81ee..4ae0977d1 100644 --- a/R/methods.R +++ b/R/methods.R @@ -52,9 +52,12 @@ markdown <- function(x, info, ...) { #' @export print.lints <- function(x, ...) { + rstudio_source_markers <- getOption("lintr.rstudio_source_markers", TRUE) && + rstudioapi::hasFun("sourceMarkers") + if (length(x)) { - if (getOption("lintr.rstudio_source_markers", TRUE) && - rstudioapi::hasFun("sourceMarkers")) { + inline_data <- x[[1]][["filename"]] == "" + if (!inline_data && rstudio_source_markers) { rstudio_source_markers(x) } else if (in_github_actions()) { github_actions_log_lints(x) @@ -78,8 +81,7 @@ print.lints <- function(x, ...) { if (isTRUE(settings$error_on_lint)) { quit("no", 31, FALSE) } - } else if (getOption("lintr.rstudio_source_markers", TRUE) && - rstudioapi::hasFun("sourceMarkers")) { + } else if (rstudio_source_markers) { # Empty lints: clear RStudio source markers rstudio_source_markers(x) } diff --git a/R/object_name_linters.R b/R/object_name_linters.R index 8c9bc9cf6..f2be63e72 100644 --- a/R/object_name_linters.R +++ b/R/object_name_linters.R @@ -3,7 +3,7 @@ #' \Sexpr[stage=render, results=rd]{lintr:::regexes_rd}. A name should #' match at least one of these styles. #' @export -object_name_linter <- function(styles = "snake_case") { +object_name_linter <- function(styles = c("snake_case", "symbols")) { .or_string <- function(xs) { # returns " or " @@ -13,7 +13,7 @@ object_name_linter <- function(styles = "snake_case") { if (len <= 1) { return(xs) } - comma_sepd_prefix <- paste(xs[-len], collapse = ", ") + comma_sepd_prefix <- toString(xs[-len]) paste(comma_sepd_prefix, "or", xs[len]) } @@ -24,10 +24,9 @@ object_name_linter <- function(styles = "snake_case") { ) function(source_file) { - x <- global_xml_parsed_content(source_file) - if (is.null(x)) { - return() - } + if (is.null(source_file$full_xml_parsed_content)) return(list()) + + xml <- source_file$full_xml_parsed_content xpath <- paste0( # Left hand assignments @@ -46,14 +45,14 @@ object_name_linter <- function(styles = "snake_case") { "//SYMBOL_FORMALS" ) - assignments <- xml2::xml_find_all(x, xpath) + assignments <- xml2::xml_find_all(xml, xpath) # Retrieve assigned name nms <- strip_names( as.character(xml2::xml_find_first(assignments, "./text()"))) generics <- strip_names(c( - declared_s3_generics(x), + declared_s3_generics(xml), namespace_imports()$fun, names(.knownS3Generics), .S3PrimitiveGenerics, ls(baseenv()))) @@ -105,7 +104,6 @@ strip_names <- function(x) { x } - object_lint2 <- function(expr, source_file, message, type) { symbol <- xml2::as_list(expr) Lint( @@ -281,53 +279,21 @@ object_lint <- function(source_file, token, message, type) { } -object_name_linter_old <- function(style = "snake_case") { - make_object_linter( - function(source_file, token) { - name <- unquote(token[["text"]]) - if (!any(matches_styles(name, style))) { - object_lint( - source_file, - token, - sprintf("Variable and function name style should be %s.", paste(style, collapse = " or ")), - "object_name_linter" - ) - } - } - ) -} - - loweralnum <- rex(one_of(lower, digit)) upperalnum <- rex(one_of(upper, digit)) style_regexes <- list( - "CamelCase" = rex(start, maybe("."), upper, zero_or_more(alnum), end), - "camelCase" = rex(start, maybe("."), lower, zero_or_more(alnum), end), - "snake_case" = rex(start, maybe("."), some_of(lower, digit), any_of("_", lower, digit), end), - "SNAKE_CASE" = rex(start, maybe("."), some_of(upper, digit), any_of("_", upper, digit), end), - "dotted.case" = rex(start, maybe("."), one_or_more(loweralnum), zero_or_more(dot, one_or_more(loweralnum)), end), + "symbols" = rex(start, maybe("."), zero_or_more(none_of(alnum)), end), + "CamelCase" = rex(start, maybe("."), upper, zero_or_more(alnum), end), + "camelCase" = rex(start, maybe("."), lower, zero_or_more(alnum), end), + "snake_case" = rex(start, maybe("."), some_of(lower, digit), any_of("_", lower, digit), end), + "SNAKE_CASE" = rex(start, maybe("."), some_of(upper, digit), any_of("_", upper, digit), end), + "dotted.case" = rex(start, maybe("."), one_or_more(loweralnum), zero_or_more(dot, one_or_more(loweralnum)), end), "lowercase" = rex(start, maybe("."), one_or_more(loweralnum), end), "UPPERCASE" = rex(start, maybe("."), one_or_more(upperalnum), end) ) -regexes_rd <- paste0(collapse = ", ", paste0("\\sQuote{", names(style_regexes), "}")) - -matches_styles <- function(name, styles=names(style_regexes)) { - invalids <- paste(styles[!styles %in% names(style_regexes)], collapse=", ") - if (nzchar(invalids)) { - valids <- paste(names(style_regexes), collapse=", ") - stop(sprintf("Invalid style(s) requested: %s\nValid styles are: %s\n", invalids, valids)) - } - name <- re_substitutes(name, rex(start, one_or_more(dot)), "") # remove leading dots - vapply( - style_regexes[styles], - re_matches, - logical(1L), - data=name - ) -} - +regexes_rd <- toString(paste0("\\sQuote{", names(style_regexes), "}")) #' @describeIn linters check that object names are not too long. #' @export diff --git a/R/tree-utils.R b/R/tree-utils.R index 312d2a54e..9349fea58 100644 --- a/R/tree-utils.R +++ b/R/tree-utils.R @@ -73,18 +73,6 @@ parents <- function(data, id, levels = Inf, simplify = TRUE) { } } -family <- function(data, id, parent_levels = 1L, child_levels = Inf) { - parents <- parents(data, id, parent_levels) - c(parents, - unlist(lapply( - parents, - children, - data = data, - levels = child_levels) - ) - ) -} - siblings <- function(data, id, child_levels = Inf) { parents <- parents(data, id, 1L) res <- unlist(lapply( diff --git a/R/utils.R b/R/utils.R index 607f5ce74..4754e92e8 100644 --- a/R/utils.R +++ b/R/utils.R @@ -74,16 +74,6 @@ auto_names <- function(x) { nms } -blank_text <- function(s, re, shift_start = 0, shift_end = 0) { - m <- gregexpr(re, s, perl = TRUE) - regmatches(s, m) <- lapply(regmatches(s, m), - quoted_blanks, - shift_start = shift_start, - shift_end = shift_end) - - s -} - quoted_blanks <- function(matches, shift_start = 0, shift_end = 0) { lengths <- nchar(matches) blanks <- vapply(Map(rep.int, @@ -101,15 +91,6 @@ names2 <- function(x) { names(x) %||% rep("", length(x)) } -recursive_ls <- function(env) { - if (parent.env(env) %!=% emptyenv()) { - c(ls(envir = env), recursive_ls(parent.env(env))) - } - else { - ls(envir = env) - } -} - safe_parse_to_xml <- function(parsed_content) { if (is.null(parsed_content)) return(NULL) tryCatch(xml2::read_xml(xmlparsedata::xml_parse_data(parsed_content)), error = function(e) NULL) @@ -172,7 +153,6 @@ try_silently <- function(expr) { } viapply <- function(x, ...) vapply(x, ..., FUN.VALUE = integer(1)) -vcapply <- function(x, ...) vapply(x, ..., FUN.VALUE = character(1)) # imitate sQuote(x, q) [requires R>=3.6] quote_wrap <- function(x, q) paste0(q, x, q) diff --git a/R/zzz.R b/R/zzz.R index e67844d31..9c687b621 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -1,11 +1,3 @@ -named_list <- function(...) { - nms <- re_substitutes(as.character(eval(substitute(alist(...)))), - rex("(", anything), "") - vals <- list(...) - names(vals) <- nms - vals[!vapply(vals, is.null, logical(1))] -} - #' Modify lintr defaults #' #' Make a new list based on \pkg{lintr}'s default linters, undesirable @@ -45,7 +37,7 @@ named_list <- function(...) { with_defaults <- function(..., default = default_linters) { vals <- list(...) nms <- names2(vals) - missing <- nms == "" + missing <- !nzchar(nms, keepNA = TRUE) if (any(missing)) { args <- as.character(eval(substitute(alist(...)[missing]))) # foo_linter(x=1) => "foo" @@ -53,16 +45,18 @@ with_defaults <- function(..., default = default_linters) { nms[missing] <- re_substitutes( re_substitutes( re_substitutes(args, rex("(", anything), ""), - rex(start, anything, "[\""), - ""), - rex("\"]", anything, end), - "") + rex(start, anything, '["'), + "" + ), + rex('"]', anything, end), + "" + ) } - vals[nms == vals] <- NA + is.na(vals) <- nms == vals default[nms] <- vals - res <- default[!vapply(default, is.null, logical(1))] + res <- default[!vapply(default, is.null, logical(1L))] res[] <- lapply(res, function(x) { prev_class <- class(x) @@ -73,11 +67,6 @@ with_defaults <- function(..., default = default_linters) { }) } -# this is just to make the auto documentation cleaner -str.lintr_function <- function(x, ...) { - cat("\n") -} - #' Default linters #' #' List of default linters for \code{\link{lint}}. Use \code{\link{with_defaults}} to customize it. @@ -182,6 +171,7 @@ default_settings <- NULL settings <- NULL +# nocov start .onLoad <- function(libname, pkgname) { op <- options() op.lintr <- list( @@ -211,3 +201,4 @@ settings <- NULL settings <<- list2env(default_settings, parent = emptyenv()) invisible() } +# nocov end diff --git a/man/get_source_expressions.Rd b/man/get_source_expressions.Rd index b940874f1..706119959 100644 --- a/man/get_source_expressions.Rd +++ b/man/get_source_expressions.Rd @@ -37,7 +37,7 @@ consisting of 6 elements: for .Rmd scripts, this is the extracted R source code (as text)} \item{\code{full_parsed_content} (\code{data.frame}) as given by \code{\link[utils:getParseData]{utils::getParseData()}} for the full content} -\item{\code{xml_parsed_content} (\code{xml_document}) the XML parse tree of all +\item{\code{full_xml_parsed_content} (\code{xml_document}) the XML parse tree of all expressions as given by \code{\link[xmlparsedata:xml_parse_data]{xmlparsedata::xml_parse_data()}}} \item{\code{terminal_newline} (\code{logical}) records whether \code{filename} has a terminal newline (as determined by \code{\link[=readLines]{readLines()}} producing a corresponding warning)} diff --git a/man/linters.Rd b/man/linters.Rd index 7fee6467f..802943d01 100644 --- a/man/linters.Rd +++ b/man/linters.Rd @@ -73,7 +73,7 @@ todo_comment_linter(todo = c("todo", "fixme")) cyclocomp_linter(complexity_limit = 25) -object_name_linter(styles = "snake_case") +object_name_linter(styles = c("snake_case", "symbols")) object_length_linter(length = 30L) diff --git a/tests/testthat/dummy_packages/clean/DESCRIPTION b/tests/testthat/dummy_packages/clean/DESCRIPTION new file mode 100644 index 000000000..a21dc2ae2 --- /dev/null +++ b/tests/testthat/dummy_packages/clean/DESCRIPTION @@ -0,0 +1,2 @@ +Package: clean +Version: 0.0.1 diff --git a/tests/testthat/dummy_packages/clean/R/default_linter_testcode.R b/tests/testthat/dummy_packages/clean/R/default_linter_testcode.R new file mode 100644 index 000000000..3dd7f9f81 --- /dev/null +++ b/tests/testthat/dummy_packages/clean/R/default_linter_testcode.R @@ -0,0 +1,3 @@ +f <- function(x, y = 1) x + y +message("hello") +y <- 2 + (1:10) diff --git a/tests/testthat/test-equals_na_linter.R b/tests/testthat/test-equals_na_linter.R index 75d018440..a9253d2b5 100644 --- a/tests/testthat/test-equals_na_linter.R +++ b/tests/testthat/test-equals_na_linter.R @@ -35,4 +35,11 @@ test_that("returns the correct linting", { NULL, equals_na_linter ) + + # correct line number for multiline code + expect_lint( + "x ==\nNA", + list(line_number = 1L, column_number = 3L, ranges = list(3:4)), + equals_na_linter + ) }) diff --git a/tests/testthat/test-expect_lint.R b/tests/testthat/test-expect_lint.R index e454f8ba7..fb03b329e 100644 --- a/tests/testthat/test-expect_lint.R +++ b/tests/testthat/test-expect_lint.R @@ -55,3 +55,7 @@ test_that("multiple checks", { ) }) + +test_that("expect_lint_free works", { + withr::with_envvar(c(NOT_CRAN = "true"), expect_lint_free("dummy_packages/clean")) +}) diff --git a/tests/testthat/test-methods.R b/tests/testthat/test-methods.R index aaf57d182..d58649c28 100644 --- a/tests/testthat/test-methods.R +++ b/tests/testthat/test-methods.R @@ -99,3 +99,20 @@ test_that("print.lint works", { ) expect_output(print(l), " 1:length(x)", fixed = TRUE) }) + +test_that("print.lint works for inline data, even in RStudio", { + l <- lint("x = 1\n") + + withr::with_options( + list("lintr.rstudio_source_markers" = FALSE), + expect_output(print(l), "not =") + ) + + withr::with_options( + list("lintr.rstudio_source_markers" = TRUE), + with_mock( + `rstudioapi::hasFun` = function(...) TRUE, + expect_output(print(l), "not =") + ) + ) +}) diff --git a/tests/testthat/test-object_name_linter.R b/tests/testthat/test-object_name_linter.R index 675310392..0f73fb3cc 100644 --- a/tests/testthat/test-object_name_linter.R +++ b/tests/testthat/test-object_name_linter.R @@ -3,40 +3,40 @@ context("object_name_linter") test_that("styles are correctly identified", { styles <- names(style_regexes) - do_style_check <- function(nms) lapply(styles, check_style, nms = nms) - # UpC lowC snake SNAKE dot alllow ALLUP - expect_equivalent(do_style_check("x" ), list(FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE)) - expect_equivalent(do_style_check(".x" ), list(FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE)) - expect_equivalent(do_style_check("X" ), list( TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE)) - expect_equivalent(do_style_check("x." ), list(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) - expect_equivalent(do_style_check("X." ), list(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) - expect_equivalent(do_style_check("x_" ), list(FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE)) - expect_equivalent(do_style_check("X_" ), list(FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE)) - expect_equivalent(do_style_check("xy" ), list(FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE)) - expect_equivalent(do_style_check("xY" ), list(FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE)) - expect_equivalent(do_style_check("Xy" ), list( TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) - expect_equivalent(do_style_check("XY" ), list( TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE)) - expect_equivalent(do_style_check("x1" ), list(FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE)) - expect_equivalent(do_style_check("X1" ), list( TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE)) - expect_equivalent(do_style_check("x_y"), list(FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE)) - expect_equivalent(do_style_check("X_Y"), list(FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE)) - expect_equivalent(do_style_check("X.Y"), list(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) - expect_equivalent(do_style_check("x_2"), list(FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE)) - expect_equivalent(do_style_check("X_2"), list(FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE)) - expect_equivalent(do_style_check("x.2"), list(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE)) - expect_equivalent(do_style_check("X.2"), list(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) - + # symbl UpC lowC snake SNAKE dot alllow ALLUP + expect_equivalent(do_style_check("x" ), list(FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE)) + expect_equivalent(do_style_check(".x" ), list(FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE)) + expect_equivalent(do_style_check("X" ), list(FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE)) + expect_equivalent(do_style_check("x." ), list(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("X." ), list(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("x_" ), list(FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("X_" ), list(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("xy" ), list(FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE)) + expect_equivalent(do_style_check("xY" ), list(FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("Xy" ), list(FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("XY" ), list(FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE)) + expect_equivalent(do_style_check("x1" ), list(FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE)) + expect_equivalent(do_style_check("X1" ), list(FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE)) + expect_equivalent(do_style_check("x_y"), list(FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("X_Y"), list(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("X.Y"), list(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("x_2"), list(FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("X_2"), list(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("x.2"), list(FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE)) + expect_equivalent(do_style_check("X.2"), list(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) - # UpC lowC snake SNAKE dot alllow ALLUP - expect_equivalent(do_style_check("IHave1Cat" ), c( TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) - expect_equivalent(do_style_check("iHave1Cat" ), c(FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE)) - expect_equivalent(do_style_check("i_have_1_cat" ), c(FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE)) - expect_equivalent(do_style_check("I_HAVE_1_CAT" ), c(FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE)) - expect_equivalent(do_style_check("i.have.1.cat" ), c(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE)) - expect_equivalent(do_style_check("ihave1cat" ), c(FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE)) - expect_equivalent(do_style_check("IHAVE1CAT" ), c( TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE)) - expect_equivalent(do_style_check("I.HAVE_ONECAT"), c(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) + # symbl UpC lowC snake SNAKE dot alllow ALLUP + expect_equivalent(do_style_check("IHave1Cat" ), c(FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("iHave1Cat" ), c(FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("i_have_1_cat" ), c(FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("I_HAVE_1_CAT" ), c(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("i.have.1.cat" ), c(FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE)) + expect_equivalent(do_style_check("ihave1cat" ), c(FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE)) + expect_equivalent(do_style_check("IHAVE1CAT" ), c(FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE)) + expect_equivalent(do_style_check("I.HAVE_ONECAT"), c(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("." ), c( TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) + expect_equivalent(do_style_check("%^%" ), c( TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) }) test_that("linter ignores some objects", { @@ -50,6 +50,7 @@ test_that("linter ignores some objects", { expect_lint("sapply(x,f,USE.NAMES=T)", NULL, object_name_linter("snake_case")) # defined elsewhere expect_lint(".onLoad <- function(...) TRUE", NULL, object_name_linter("snake_case")) # namespace hooks, #500 expect_lint(".First <- function(...) TRUE", NULL, object_name_linter("snake_case")) # namespace hooks + expect_lint("`%++%` <- `+`", NULL, object_name_linter("symbols")) # all-symbol operator }) test_that("linter returns correct linting", { diff --git a/tests/testthat/test-rstudio_markers.R b/tests/testthat/test-rstudio_markers.R index 765534720..07e0afcf7 100644 --- a/tests/testthat/test-rstudio_markers.R +++ b/tests/testthat/test-rstudio_markers.R @@ -1,93 +1,94 @@ context("rstudio_source_markers") test_that("it returns markers which match lints", { - with_mock(`rstudioapi::callFun` = function(...) return(list(...)), - lint1 <- structure( - list( - Lint(filename = "test_file", - line_number = 1, - column_number = 2, - type = "error", - line = "a line", - message = "hi") - ), - class = "lints" - ), - marker1 <- rstudio_source_markers(lint1), - - expect_equal(marker1$name, "lintr"), - expect_equal(marker1$markers[[1]]$type, lint1[[1]]$type), - expect_equal(marker1$markers[[1]]$file, lint1[[1]]$filename), - expect_equal(marker1$markers[[1]]$line, lint1[[1]]$line_number), - expect_equal(marker1$markers[[1]]$column, lint1[[1]]$column_number), - expect_equal(marker1$markers[[1]]$message, lint1[[1]]$message), - - lint2 <- structure( - list( - Lint(filename = "test_file", - line_number = 1, - column_number = 2, - type = "error", - line = "a line", - message = "hi"), - Lint(filename = "test_file2", - line_number = 10, - column_number = 5, - type = "warning", - message = "test a message") - ), - class = "lints" - ), - - marker2 <- rstudio_source_markers(lint2), - - expect_equal(marker2$name, "lintr"), - expect_equal(marker2$markers[[1]]$type, lint2[[1]]$type), - expect_equal(marker2$markers[[1]]$file, lint2[[1]]$filename), - expect_equal(marker2$markers[[1]]$line, lint2[[1]]$line_number), - expect_equal(marker2$markers[[1]]$column, lint2[[1]]$column_number), - expect_equal(marker2$markers[[1]]$message, lint2[[1]]$message), + lint1 <- structure( + list( + Lint(filename = "test_file", + line_number = 1, + column_number = 2, + type = "error", + line = "a line", + message = "hi") + ), + class = "lints" + ) + with_mock( + `rstudioapi::callFun` = function(...) list(...), + `rstudioapi::executeCommand` = function(...) NULL, + marker1 <- rstudio_source_markers(lint1) + ) + expect_equal(marker1$name, "lintr") + expect_equal(marker1$markers[[1]]$type, lint1[[1]]$type) + expect_equal(marker1$markers[[1]]$file, lint1[[1]]$filename) + expect_equal(marker1$markers[[1]]$line, lint1[[1]]$line_number) + expect_equal(marker1$markers[[1]]$column, lint1[[1]]$column_number) + expect_equal(marker1$markers[[1]]$message, lint1[[1]]$message) - expect_equal(marker2$name, "lintr"), - expect_equal(marker2$markers[[2]]$type, lint2[[2]]$type), - expect_equal(marker2$markers[[2]]$file, lint2[[2]]$filename), - expect_equal(marker2$markers[[2]]$line, lint2[[2]]$line_number), - expect_equal(marker2$markers[[2]]$column, lint2[[2]]$column_number), - expect_equal(marker2$markers[[2]]$message, lint2[[2]]$message) - )}) + lint2 <- structure( + list( + Lint(filename = "test_file", + line_number = 1, + column_number = 2, + type = "error", + line = "a line", + message = "hi"), + Lint(filename = "test_file2", + line_number = 10, + column_number = 5, + type = "warning", + message = "test a message") + ), + class = "lints" + ) + with_mock( + `rstudioapi::callFun` = function(...) list(...), + `rstudioapi::executeCommand` = function(...) NULL, + marker2 <- rstudio_source_markers(lint2) + ) + expect_equal(marker2$name, "lintr") + expect_equal(marker2$markers[[1]]$type, lint2[[1]]$type) + expect_equal(marker2$markers[[1]]$file, lint2[[1]]$filename) + expect_equal(marker2$markers[[1]]$line, lint2[[1]]$line_number) + expect_equal(marker2$markers[[1]]$column, lint2[[1]]$column_number) + expect_equal(marker2$markers[[1]]$message, lint2[[1]]$message) +}) test_that("it prepends the package path if it exists", { - with_mock(`rstudioapi::callFun` = function(...) return(list(...)), - lint3 <- structure( - list( - Lint(filename = "test_file", - line_number = 1, - column_number = 2, - type = "error", - line = "a line", - message = "hi") - ), - class = "lints", - path = "test" - ), - marker3 <- rstudio_source_markers(lint3), - expect_equal(marker3$name, "lintr"), - expect_equal(marker3$basePath, "test"), # nolint - expect_equal(marker3$markers[[1]]$type, lint3[[1]]$type), - expect_equal(marker3$markers[[1]]$file, file.path("test", lint3[[1]]$filename)), - expect_equal(marker3$markers[[1]]$line, lint3[[1]]$line_number), - expect_equal(marker3$markers[[1]]$column, lint3[[1]]$column_number), - expect_equal(marker3$markers[[1]]$message, lint3[[1]]$message) + lint3 <- structure( + list( + Lint(filename = "test_file", + line_number = 1, + column_number = 2, + type = "error", + line = "a line", + message = "hi") + ), + class = "lints", + path = "test" + ) + with_mock( + `rstudioapi::callFun` = function(...) list(...), + `rstudioapi::executeCommand` = function(...) NULL, + marker3 <- rstudio_source_markers(lint3) ) + expect_equal(marker3$name, "lintr") + expect_equal(marker3$basePath, "test") # nolint + expect_equal(marker3$markers[[1]]$type, lint3[[1]]$type) + expect_equal(marker3$markers[[1]]$file, file.path("test", lint3[[1]]$filename)) + expect_equal(marker3$markers[[1]]$line, lint3[[1]]$line_number) + expect_equal(marker3$markers[[1]]$column, lint3[[1]]$column_number) + expect_equal(marker3$markers[[1]]$message, lint3[[1]]$message) }) -test_that("it returns an empty list of markers if there are no lints", { - with_mock(`rstudioapi::callFun` = function(...) return(list(...)), - lint4 <- structure( - list(), - class = "lints" - ), - marker4 <- rstudio_source_markers(lint4), - expect_equal(marker4$name, "lintr"), - expect_equal(marker4$markers, list()) +test_that("it returns an empty list of markers if there are no lints", { + lint4 <- structure( + list(), + class = "lints" + ) + with_mock( + `rstudioapi::callFun` = function(...) list(...), + `rstudioapi::executeCommand` = function(...) NULL, + marker4 <- rstudio_source_markers(lint4) ) + expect_equal(marker4$name, "lintr") + expect_equal(marker4$markers, list()) }) diff --git a/tests/testthat/test-settings.R b/tests/testthat/test-settings.R index 5e731fa99..755125450 100644 --- a/tests/testthat/test-settings.R +++ b/tests/testthat/test-settings.R @@ -48,3 +48,15 @@ test_that("it errors if the config file does not end in a newline", { options("lintr.linter_file" = f) expect_error(read_settings("foo"), "Malformed config file") }) + +test_that("with_defaults works as expected", { + # test capturing unnamed args + defaults <- with_defaults(assignment_linter) + # assignment_linter is in defaults, so output doesn't change + expect_equal(names(defaults), names(with_defaults())) +}) + +test_that("rot utility works as intended", { + rot <- lintr:::rot + expect_equal(rot(letters), c(letters[14:26], LETTERS[1:13])) +})