", fixed = TRUE)
16 |
17 | xml <- xml2::read_xml("")
18 | expect_error(xp_call_name(xml, depth = -1L), "depth >= 0", fixed = TRUE)
19 | expect_error(xp_call_name(xml, depth = "1"), "is.numeric(depth)", fixed = TRUE)
20 | expect_error(xp_call_name(xml, condition = 1L), "is.character(condition)", fixed = TRUE)
21 | })
22 |
--------------------------------------------------------------------------------
/tests/testthat/knitr_formats/test.Rtex:
--------------------------------------------------------------------------------
1 | \documentclass{article}
2 | \usepackage{graphicx}
3 | \title{Test}
4 |
5 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
6 | tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
7 | vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
8 | no sea takimata sanctus est Lorem ipsum dolor sit amet.
9 |
10 | %% begin.rcode
11 | % a = 1
12 | %% end.rcode
13 |
14 | \subtitle{Test}
15 |
16 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
17 | tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
18 | vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
19 | no sea takimata sanctus est Lorem ipsum dolor sit amet.
20 |
21 | %% begin.rcode test_chunk
22 | % b <- function(x) {
23 | % d = 1
24 | % }
25 | %
26 | %% end.rcode
27 |
28 | %% begin.rcode engine="python"
29 | % a=[]
30 | %
31 | % a[0]=1
32 | %% end.rcode
33 |
--------------------------------------------------------------------------------
/man/trailing_blank_lines_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/trailing_blank_lines_linter.R
3 | \name{trailing_blank_lines_linter}
4 | \alias{trailing_blank_lines_linter}
5 | \title{Trailing blank lines linter}
6 | \usage{
7 | trailing_blank_lines_linter()
8 | }
9 | \description{
10 | Check that there are no trailing blank lines in source code.
11 | }
12 | \examples{
13 | # will produce lints
14 | f <- tempfile()
15 | cat("x <- 1\n\n", file = f)
16 | writeLines(readChar(f, file.size(f)))
17 | lint(
18 | filename = f,
19 | linters = trailing_blank_lines_linter()
20 | )
21 | unlink(f)
22 |
23 | # okay
24 | cat("x <- 1\n", file = f)
25 | writeLines(readChar(f, file.size(f)))
26 | lint(
27 | filename = f,
28 | linters = trailing_blank_lines_linter()
29 | )
30 | unlink(f)
31 |
32 | }
33 | \seealso{
34 | \link{linters} for a complete list of linters available in lintr.
35 | }
36 | \section{Tags}{
37 | \link[=default_linters]{default}, \link[=style_linters]{style}
38 | }
39 |
--------------------------------------------------------------------------------
/tests/testthat/dummy_packages/clean/R/clean_generics.R:
--------------------------------------------------------------------------------
1 | # Regression test for #737 via expecting clean to be lint-free
2 |
3 | #' drink_me
4 | #' @description empty
5 | #'
6 | #' @export
7 | drink_me <- function(x, ...) {
8 | UseMethod("drink_me")
9 | }
10 |
11 | #' drink_me for most things
12 | #' @export
13 | drink_me.default <- function(x, ...) {
14 | 1
15 | }
16 |
17 | #' drink_me for lists
18 | #' @export
19 | drink_me.list <- function(x, ...) {
20 | NULL
21 | }
22 |
23 | #' drink_me for data.frames
24 | #' @export
25 | drink_me.data.frame <- function(x, ...) {
26 | NULL
27 | }
28 |
29 | #' head on my_s3_object
30 | #' @importFrom utils head
31 | #' @export
32 | head.my_s3_object <- function(x, ...) {
33 | NULL
34 | }
35 |
36 | #' assign names for my_custom_class
37 | #' @export
38 | `names<-.my_custom_class` <- function(x, value) {
39 | NULL
40 | }
41 |
42 | #' Defined S3 generic in R/eat_me.R
43 | #' Tests #1808
44 | #' @export
45 | eat_me.liiiiiiiiiiiiiiiiiiiiiiiiiiist <- function(x, ...) {
46 | NULL
47 | }
48 |
--------------------------------------------------------------------------------
/tests/testthat/dummy_packages/package/vignettes/test.Rtex:
--------------------------------------------------------------------------------
1 | \documentclass{article}
2 | \usepackage{graphicx}
3 | \title{Test}
4 |
5 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
6 | tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
7 | vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
8 | no sea takimata sanctus est Lorem ipsum dolor sit amet.
9 |
10 | %% begin.rcode
11 | % a = 1
12 | %% end.rcode
13 |
14 | \subtitle{Test}
15 |
16 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
17 | tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
18 | vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
19 | no sea takimata sanctus est Lorem ipsum dolor sit amet.
20 |
21 | %% begin.rcode test_chunk
22 | % b <- function(x) {
23 | % d = 1
24 | % }
25 | %
26 | %% end.rcode
27 |
28 | %% begin.rcode engine="python"
29 | % a=[]
30 | %
31 | % a[0]=1
32 | %% end.rcode
33 |
--------------------------------------------------------------------------------
/tests/testthat/test-stopifnot_all_linter.R:
--------------------------------------------------------------------------------
1 | test_that("stopifnot_all_linter skips allowed usages", {
2 | expect_lint("stopifnot(all(x) || any(y))", NULL, stopifnot_all_linter())
3 | })
4 |
5 | test_that("stopifnot_all_linter blocks simple disallowed usages", {
6 | linter <- stopifnot_all_linter()
7 | lint_msg <- rex::rex("stopifnot(x) runs all() 'under the hood'")
8 |
9 | expect_lint("stopifnot(all(A))", list(lint_msg, column_number = 11L), linter)
10 | expect_lint("stopifnot(x, y, all(z))", list(lint_msg, column_number = 17L), linter)
11 |
12 | expect_lint(
13 | trim_some("{
14 | stopifnot(all(x), all(y),
15 | all(z)
16 | )
17 | stopifnot(a > 0, b < 0, all(c == 0))
18 | }"),
19 | list(
20 | list(lint_msg, line_number = 2L, column_number = 13L),
21 | list(lint_msg, line_number = 2L, column_number = 21L),
22 | list(lint_msg, line_number = 3L, column_number = 5L),
23 | list(lint_msg, line_number = 5L, column_number = 27L)
24 | ),
25 | linter
26 | )
27 | })
28 |
--------------------------------------------------------------------------------
/tests/testthat/test-which_grepl_linter.R:
--------------------------------------------------------------------------------
1 | test_that("which_grepl_linter skips allowed usages", {
2 | # this _could_ be combined as p1|p2, but often it's cleaner to read this way
3 | expect_no_lint("which(grepl(p1, x) | grepl(p2, x))", which_grepl_linter())
4 | })
5 |
6 | test_that("which_grepl_linter blocks simple disallowed usages", {
7 | linter <- which_grepl_linter()
8 | lint_msg <- rex::rex("grep(pattern, x) is better than which(grepl(pattern, x)).")
9 |
10 | expect_lint("which(grepl('^a', x))", lint_msg, linter)
11 | # options also don't matter (grep has more arguments: value, invert)
12 | expect_lint("which(grepl('^a', x, perl = TRUE, fixed = TRUE))", lint_msg, linter)
13 |
14 | expect_lint(
15 | trim_some('{
16 | which(x)
17 | grepl(y)
18 | which(grepl("pt1", x))
19 | which(grepl("pt2", y))
20 | }'),
21 | list(
22 | list(lint_msg, line_number = 4L, column_number = 3L),
23 | list(lint_msg, line_number = 5L, column_number = 3L)
24 | ),
25 | linter
26 | )
27 | })
28 |
--------------------------------------------------------------------------------
/man/list_comparison_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/list_comparison_linter.R
3 | \name{list_comparison_linter}
4 | \alias{list_comparison_linter}
5 | \title{Block usage of comparison operators with known-list() functions like lapply}
6 | \usage{
7 | list_comparison_linter()
8 | }
9 | \description{
10 | Usage like \code{lapply(x, sum) > 10} is awkward because the list must first
11 | be coerced to a vector for comparison. A function like \code{\link[=vapply]{vapply()}}
12 | should be preferred.
13 | }
14 | \examples{
15 | # will produce lints
16 | lint(
17 | text = "lapply(x, sum) > 10",
18 | linters = list_comparison_linter()
19 | )
20 |
21 | # okay
22 | lint(
23 | text = "unlist(lapply(x, sum)) > 10",
24 | linters = list_comparison_linter()
25 | )
26 |
27 | }
28 | \seealso{
29 | \link{linters} for a complete list of linters available in lintr.
30 | }
31 | \section{Tags}{
32 | \link[=best_practices_linters]{best_practices}, \link[=common_mistakes_linters]{common_mistakes}
33 | }
34 |
--------------------------------------------------------------------------------
/tests/testthat/dummy_projects/project/default_linter_testcode.R:
--------------------------------------------------------------------------------
1 | # Each of the default linters should throw at least one lint on this file
2 |
3 | # assignment
4 | # function_left_parentheses
5 | # closed_curly
6 | # commas
7 | # paren_brace
8 | f = function (x,y = 1){}
9 |
10 | # commented_code
11 | # some <- commented("out code")
12 |
13 | # equals_na
14 | # infix_spaces
15 | # line_length
16 | # object_length
17 | # object_name
18 | # object_usage
19 | # open_curly
20 | # T_and_F_symbol
21 | someComplicatedFunctionWithALongCamelCaseName <- function(x)
22 | {
23 | y <- 1
24 | if (1 > 2 && 2 > 3 && 3 > 4 && 4 > 5 && 5*10 > 6 && 5 > 6 && 6 > 7 && x == NA) {T} else {F}
25 | }
26 |
27 | # no_tab
28 | # pipe_continuation
29 | # seq_linter
30 | # spaces_inside
31 | x <- 1:10
32 | x[ 2]
33 | 1:length(x) %>% lapply(function(x) x*2) %>%
34 | head()
35 |
36 | # single_quotes
37 | message('single_quotes')
38 |
39 | # spaces_left_parentheses
40 | # trailing_whitespace
41 | # semicolon
42 | x <- 42; y <- 2 +(1:10)
43 |
44 | # trailing_blank_lines
45 |
46 |
--------------------------------------------------------------------------------
/man/rep_len_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/rep_len_linter.R
3 | \name{rep_len_linter}
4 | \alias{rep_len_linter}
5 | \title{Require usage of rep_len(x, n) over rep(x, length.out = n)}
6 | \usage{
7 | rep_len_linter()
8 | }
9 | \description{
10 | \code{rep(x, length.out = n)} calls \code{rep_len(x, n)} "under the hood". The latter
11 | is thus more direct and equally readable.
12 | }
13 | \examples{
14 | # will produce lints
15 | lint(
16 | text = "rep(1:3, length.out = 10)",
17 | linters = rep_len_linter()
18 | )
19 |
20 | # okay
21 | lint(
22 | text = "rep_len(1:3, 10)",
23 | linters = rep_len_linter()
24 | )
25 |
26 | lint(
27 | text = "rep(1:3, each = 2L, length.out = 10L)",
28 | linters = rep_len_linter()
29 | )
30 |
31 | }
32 | \seealso{
33 | \link{linters} for a complete list of linters available in lintr.
34 | }
35 | \section{Tags}{
36 | \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=readability_linters]{readability}
37 | }
38 |
--------------------------------------------------------------------------------
/tests/testthat/knitr_formats/test.Rhtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Test
5 |
6 |
7 | Test
8 |
9 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
10 | tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
11 | vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
12 | no sea takimata sanctus est Lorem ipsum dolor sit amet.
13 |
14 |
17 |
18 | Test
19 |
20 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
21 | tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
22 | vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
23 | no sea takimata sanctus est Lorem ipsum dolor sit amet.
24 |
25 |
31 |
32 |
37 |
--------------------------------------------------------------------------------
/R/nonportable_path_linter.R:
--------------------------------------------------------------------------------
1 | #' Non-portable path linter
2 | #'
3 | #' Check that [file.path()] is used to construct safe and portable paths.
4 | #'
5 | #' @examples
6 | #' # will produce lints
7 | #' lint(
8 | #' text = "'abcdefg/hijklmnop/qrst/uv/wxyz'",
9 | #' linters = nonportable_path_linter()
10 | #' )
11 | #'
12 | #' # okay
13 | #' lint(
14 | #' text = "file.path('abcdefg', 'hijklmnop', 'qrst', 'uv', 'wxyz')",
15 | #' linters = nonportable_path_linter()
16 | #' )
17 | #'
18 | #' @inheritParams absolute_path_linter
19 | #' @evalRd rd_tags("nonportable_path_linter")
20 | #' @seealso
21 | #' - [linters] for a complete list of linters available in lintr.
22 | #' - [absolute_path_linter()]
23 | #' @export
24 | nonportable_path_linter <- function(lax = TRUE) {
25 | path_linter_factory(
26 | path_function = function(path) {
27 | is_path(path) && is_valid_long_path(path, lax) && path != "/" &&
28 | re_matches(path, rex(one_of("/", "\\")))
29 | },
30 | message = "Use file.path() to construct portable file paths."
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/tests/testthat/test-repeat_linter.R:
--------------------------------------------------------------------------------
1 | test_that("test repeat_linter", {
2 | linter <- repeat_linter()
3 | msg <- rex::rex("Use 'repeat' instead of 'while (TRUE)' for infinite loops.")
4 |
5 | expect_lint("repeat { }", NULL, linter)
6 | expect_lint("while (FALSE) { }", NULL, linter)
7 | expect_lint("while (i < 5) { }", NULL, linter)
8 | expect_lint("while (j < 5) TRUE", NULL, linter)
9 | expect_lint("while (TRUE && j < 5) { ... }", NULL, linter)
10 |
11 | expect_lint("while (TRUE) { }", msg, linter)
12 | expect_lint("for (i in 1:10) { while (TRUE) { if (i == 5) { break } } }", msg, linter)
13 | expect_lint("while (TRUE) { while (TRUE) { } }", list(msg, msg), linter)
14 | expect_lint(
15 | trim_some("{
16 | while (TRUE) {
17 | }
18 | while (TRUE) {
19 | }
20 | }"),
21 | list(
22 | list(message = msg, line_number = 2L, column_number = 3L, ranges = list(c(3L, 14L))),
23 | list(message = msg, line_number = 4L, column_number = 3L, ranges = list(c(3L, 14L)))
24 | ),
25 | linter
26 | )
27 | })
28 |
--------------------------------------------------------------------------------
/tests/testthat/dummy_packages/package/R/default_linter_testcode.R:
--------------------------------------------------------------------------------
1 | # Each of the default linters should throw at least one lint on this file
2 |
3 | # assignment
4 | # function_left_parentheses
5 | # closed_curly
6 | # commas
7 | # paren_brace
8 | f = function (x,y = 1){}
9 |
10 | # commented_code
11 | # some <- commented("out code")
12 |
13 | # cyclocomp
14 | # equals_na
15 | # infix_spaces
16 | # line_length
17 | # object_length
18 | # object_name
19 | # object_usage
20 | # open_curly
21 | # T_and_F_symbol
22 | someComplicatedFunctionWithALongCamelCaseName <- function(x)
23 | {
24 | y <- 1
25 | if (1 > 2 && 2 > 3 && 3 > 4 && 4 > 5 && 5*10 > 6 && 5 > 6 && 6 > 7 && x == NA) {T} else {F}
26 | }
27 |
28 | # no_tab
29 | # pipe_continuation
30 | # seq_linter
31 | # spaces_inside
32 | x <- 1:10
33 | x[ 2]
34 | 1:length(x) %>% lapply(function(x) x*2) %>%
35 | head()
36 |
37 | # single_quotes
38 | message('single_quotes')
39 |
40 | # spaces_left_parentheses
41 | # trailing_whitespace
42 | # semicolon
43 | x <- 42; y <- 2 +(1:10)
44 |
45 | # trailing_blank_lines
46 |
47 |
--------------------------------------------------------------------------------
/tests/testthat/dummy_packages/package/vignettes/test.Rhtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Test
5 |
6 |
7 | Test
8 |
9 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
10 | tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
11 | vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
12 | no sea takimata sanctus est Lorem ipsum dolor sit amet.
13 |
14 |
17 |
18 | Test
19 |
20 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
21 | tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
22 | vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
23 | no sea takimata sanctus est Lorem ipsum dolor sit amet.
24 |
25 |
31 |
32 |
37 |
--------------------------------------------------------------------------------
/tests/testthat/dummy_packages/package/data-raw/default_linter_testcode.R:
--------------------------------------------------------------------------------
1 | # Each of the default linters should throw at least one lint on this file
2 |
3 | # assignment
4 | # function_left_parentheses
5 | # closed_curly
6 | # commas
7 | # paren_brace
8 | f = function (x,y = 1){}
9 |
10 | # commented_code
11 | # some <- commented("out code")
12 |
13 | # cyclocomp
14 | # equals_na
15 | # infix_spaces
16 | # line_length
17 | # object_length
18 | # object_name
19 | # object_usage
20 | # open_curly
21 | # T_and_F_symbol
22 | someComplicatedFunctionWithALongCamelCaseName <- function(x)
23 | {
24 | y <- 1
25 | if (1 > 2 && 2 > 3 && 3 > 4 && 4 > 5 && 5*10 > 6 && 5 > 6 && 6 > 7 && x == NA) {T} else {F}
26 | }
27 |
28 | # no_tab
29 | # pipe_continuation
30 | # seq_linter
31 | # spaces_inside
32 | x <- 1:10
33 | x[ 2]
34 | 1:length(x) %>% lapply(function(x) x*2) %>%
35 | head()
36 |
37 | # single_quotes
38 | message('single_quotes')
39 |
40 | # spaces_left_parentheses
41 | # trailing_whitespace
42 | # semicolon
43 | x <- 42; y <- 2 +(1:10)
44 |
45 | # trailing_blank_lines
46 |
47 |
--------------------------------------------------------------------------------
/man/Linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{Linter}
4 | \alias{Linter}
5 | \title{Create a \code{linter} closure}
6 | \usage{
7 | Linter(
8 | fun,
9 | name = linter_auto_name(),
10 | linter_level = c(NA_character_, "file", "expression")
11 | )
12 | }
13 | \arguments{
14 | \item{fun}{A function that takes a source file and returns \code{lint} objects.}
15 |
16 | \item{name}{Default name of the Linter.
17 | Lints produced by the linter will be labelled with \code{name} by default.}
18 |
19 | \item{linter_level}{Which level of expression is the linter working with?
20 | \code{"expression"} means an individual expression in \code{xml_parsed_content}, while \code{"file"} means all expressions
21 | in the current file are available in \code{full_xml_parsed_content}.
22 | \code{NA} means the linter will be run with both, expression-level and file-level source expressions.}
23 | }
24 | \value{
25 | The same function with its class set to 'linter'.
26 | }
27 | \description{
28 | Create a \code{linter} closure
29 | }
30 |
--------------------------------------------------------------------------------
/tests/testthat/dummy_packages/package/inst/data-raw/default_linter_testcode.R:
--------------------------------------------------------------------------------
1 | # Each of the default linters should throw at least one lint on this file
2 |
3 | # assignment
4 | # function_left_parentheses
5 | # closed_curly
6 | # commas
7 | # paren_brace
8 | f = function (x,y = 1){}
9 |
10 | # commented_code
11 | # some <- commented("out code")
12 |
13 | # cyclocomp
14 | # equals_na
15 | # infix_spaces
16 | # line_length
17 | # object_length
18 | # object_name
19 | # object_usage
20 | # open_curly
21 | # T_and_F_symbol
22 | someComplicatedFunctionWithALongCamelCaseName <- function(x)
23 | {
24 | y <- 1
25 | if (1 > 2 && 2 > 3 && 3 > 4 && 4 > 5 && 5*10 > 6 && 5 > 6 && 6 > 7 && x == NA) {T} else {F}
26 | }
27 |
28 | # no_tab
29 | # pipe_continuation
30 | # seq_linter
31 | # spaces_inside
32 | x <- 1:10
33 | x[ 2]
34 | 1:length(x) %>% lapply(function(x) x*2) %>%
35 | head()
36 |
37 | # single_quotes
38 | message('single_quotes')
39 |
40 | # spaces_left_parentheses
41 | # trailing_whitespace
42 | # semicolon
43 | x <- 42; y <- 2 +(1:10)
44 |
45 | # trailing_blank_lines
46 |
47 |
--------------------------------------------------------------------------------
/tests/testthat/test-checkstyle_output.R:
--------------------------------------------------------------------------------
1 | test_that("return lint report as checkstyle xml", {
2 | lints <- list(
3 | Lint(
4 | filename = "test_file",
5 | line_number = 1L,
6 | column_number = 2L,
7 | type = "error",
8 | line = "a line",
9 | message = "foo"
10 | ),
11 | Lint(
12 | filename = "test_file",
13 | line_number = 2L,
14 | column_number = 1L,
15 | type = "style",
16 | line = "another line",
17 | message = "bar"
18 | ),
19 | Lint(
20 | filename = "test_file2",
21 | line_number = 1L,
22 | column_number = 1L,
23 | type = "warning",
24 | line = "yet another line",
25 | message = "baz"
26 | )
27 | )
28 | class(lints) <- "lints"
29 | tmp <- withr::local_tempfile()
30 | checkstyle_output(lints, tmp)
31 |
32 | # The second line is the checkstyle version, so we ignore it during the
33 | # check, so we don't have to update the version every release.
34 | expect_identical(readLines(tmp)[-2L], readLines(test_path("checkstyle.xml"))[-2L])
35 | })
36 |
--------------------------------------------------------------------------------
/R/whitespace_linter.R:
--------------------------------------------------------------------------------
1 | #' Whitespace linter
2 | #'
3 | #' Check that the correct character is used for indentation.
4 | #'
5 | #' Currently, only supports linting in the presence of tabs.
6 | #'
7 | #' Much ink has been spilled on this topic, and we encourage you to check
8 | #' out references for more information.
9 | #'
10 | #' @include make_linter_from_regex.R
11 | #'
12 | #' @examples
13 | #' # will produce lints
14 | #' lint(
15 | #' text = "\tx",
16 | #' linters = whitespace_linter()
17 | #' )
18 | #'
19 | #' # okay
20 | #' lint(
21 | #' text = " x",
22 | #' linters = whitespace_linter()
23 | #' )
24 | #'
25 | #' @evalRd rd_tags("whitespace_linter")
26 | #' @seealso [linters] for a complete list of linters available in lintr.
27 | #'
28 | #' @references
29 | #' - https://www.jwz.org/doc/tabs-vs-spaces.html
30 | #' - https://blog.codinghorror.com/death-to-the-space-infidels/
31 | #' @export
32 | whitespace_linter <- make_linter_from_regex(
33 | regex = rex(start, zero_or_more(regex("\\s")), one_or_more("\t")),
34 | lint_type = "style",
35 | lint_msg = "Use spaces to indent, not tabs."
36 | )
37 |
--------------------------------------------------------------------------------
/R/AAA.R:
--------------------------------------------------------------------------------
1 | #' @include utils.R
2 | #' @include xp_utils.R
3 | #' @include make_linter_from_xpath.R
4 | NULL
5 |
6 | #' Available linters
7 | #' @name linters
8 | #'
9 | #' @description A variety of linters are available in \pkg{lintr}. The most popular ones are readily
10 | #' accessible through [default_linters()].
11 | #'
12 | #' Within a [lint()] function call, the linters in use are initialized with the provided
13 | #' arguments and fed with the source file (provided by [get_source_expressions()]).
14 | #'
15 | #' A data frame of all available linters can be retrieved using [available_linters()].
16 | #' Documentation for linters is structured into tags to allow for easier discovery;
17 | #' see also [available_tags()].
18 | #'
19 | #' @evalRd rd_taglist()
20 | #' @evalRd rd_linterlist()
21 | NULL
22 |
23 | # need to register rex shortcuts as globals to avoid CRAN check errors
24 | rex::register_shortcuts("lintr")
25 |
26 | globalVariables(
27 | c(
28 | "line1", "col1", "line2", "col2", # columns of parsed_content
29 | "id", "parent", "token", "terminal", "text" # ditto
30 | ),
31 | "lintr"
32 | )
33 |
--------------------------------------------------------------------------------
/man/executing_linters.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/linter_tag_docs.R
3 | \name{executing_linters}
4 | \alias{executing_linters}
5 | \title{Code executing linters}
6 | \description{
7 | Linters that evaluate parts of the linted code, such as loading referenced packages.
8 | These linters should not be used with untrusted code, and may need dependencies of the linted package or project to
9 | be available in order to function correctly. For package authors, note that this includes loading the package itself,
10 | e.g. with \code{pkgload::load_all()} or installing and attaching the package.
11 | }
12 | \seealso{
13 | \link{linters} for a complete list of linters available in lintr.
14 | }
15 | \section{Linters}{
16 | The following linters are tagged with 'executing':
17 | \itemize{
18 | \item{\code{\link{namespace_linter}}}
19 | \item{\code{\link{object_length_linter}}}
20 | \item{\code{\link{object_name_linter}}}
21 | \item{\code{\link{object_overwrite_linter}}}
22 | \item{\code{\link{object_usage_linter}}}
23 | \item{\code{\link{unused_import_linter}}}
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/man/common_mistakes_linters.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/linter_tag_docs.R
3 | \name{common_mistakes_linters}
4 | \alias{common_mistakes_linters}
5 | \title{Common mistake linters}
6 | \description{
7 | Linters highlighting common mistakes, such as duplicate arguments.
8 | }
9 | \seealso{
10 | \link{linters} for a complete list of linters available in lintr.
11 | }
12 | \section{Linters}{
13 | The following linters are tagged with 'common_mistakes':
14 | \itemize{
15 | \item{\code{\link{all_equal_linter}}}
16 | \item{\code{\link{download_file_linter}}}
17 | \item{\code{\link{duplicate_argument_linter}}}
18 | \item{\code{\link{equals_na_linter}}}
19 | \item{\code{\link{length_test_linter}}}
20 | \item{\code{\link{list_comparison_linter}}}
21 | \item{\code{\link{missing_argument_linter}}}
22 | \item{\code{\link{missing_package_linter}}}
23 | \item{\code{\link{pipe_return_linter}}}
24 | \item{\code{\link{redundant_equals_linter}}}
25 | \item{\code{\link{sprintf_linter}}}
26 | \item{\code{\link{unused_import_linter}}}
27 | \item{\code{\link{vector_logic_linter}}}
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/man/lintr-package.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/lintr-package.R
3 | \docType{package}
4 | \name{lintr-package}
5 | \alias{lintr}
6 | \alias{lintr-package}
7 | \title{Lintr}
8 | \description{
9 | Checks adherence to a given style, syntax errors, and possible semantic issues.
10 | Supports on the fly checking of R code edited with Emacs, Vim, and Sublime Text.
11 | }
12 | \seealso{
13 | \code{\link[=lint]{lint()}}, \code{\link[=lint_package]{lint_package()}}, \code{\link[=lint_dir]{lint_dir()}}, \link{linters}
14 | }
15 | \author{
16 | \strong{Maintainer}: Michael Chirico \email{michaelchirico4@gmail.com} (\href{https://orcid.org/0000-0003-0787-087X}{ORCID})
17 |
18 | Authors:
19 | \itemize{
20 | \item Jim Hester
21 | \item Florent Angly (fangly)
22 | \item Russ Hyde
23 | \item Kun Ren
24 | \item Alexander Rosenstock (AshesITR)
25 | \item Indrajeet Patil \email{patilindrajeet.science@gmail.com} (\href{https://orcid.org/0000-0003-1995-6531}{ORCID})
26 | \item Hugo Gruson (\href{https://orcid.org/0000-0002-4094-1476}{ORCID})
27 | }
28 |
29 | }
30 | \keyword{internal}
31 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014-2024, James Hester
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/man/spaces_left_parentheses_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/spaces_left_parentheses_linter.R
3 | \name{spaces_left_parentheses_linter}
4 | \alias{spaces_left_parentheses_linter}
5 | \title{Spaces before parentheses linter}
6 | \usage{
7 | spaces_left_parentheses_linter()
8 | }
9 | \description{
10 | Check that all left parentheses have a space before them unless they are in a function call.
11 | }
12 | \examples{
13 | # will produce lints
14 | lint(
15 | text = "if(TRUE) x else y",
16 | linters = spaces_left_parentheses_linter()
17 | )
18 |
19 | # okay
20 | lint(
21 | text = "if (TRUE) x else y",
22 | linters = spaces_left_parentheses_linter()
23 | )
24 |
25 | }
26 | \seealso{
27 | \itemize{
28 | \item \link{linters} for a complete list of linters available in lintr.
29 | \item \url{https://style.tidyverse.org/syntax.html#parentheses}
30 | \item \code{\link[=function_left_parentheses_linter]{function_left_parentheses_linter()}}
31 | }
32 | }
33 | \section{Tags}{
34 | \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style}
35 | }
36 |
--------------------------------------------------------------------------------
/tests/testthat/test-system_file_linter.R:
--------------------------------------------------------------------------------
1 | test_that("system_file_linter skips allowed usages", {
2 | linter <- system_file_linter()
3 |
4 | expect_lint("system.file('a', 'b', 'c')", NULL, linter)
5 | expect_lint("file.path('a', 'b', 'c')", NULL, linter)
6 | })
7 |
8 | test_that("system_file_linter blocks simple disallowed usages", {
9 | linter <- system_file_linter()
10 | lint_msg <- rex::rex("Use the `...` argument of system.file() to expand paths")
11 |
12 | expect_lint("system.file(file.path('path', 'to', 'data'), package = 'foo')", lint_msg, linter)
13 | expect_lint("file.path(system.file(package = 'foo'), 'path', 'to', 'data')", lint_msg, linter)
14 | })
15 |
16 | test_that("lints vectorize", {
17 | lint_msg <- rex::rex("Use the `...` argument of system.file() to expand paths")
18 |
19 | expect_lint(
20 | trim_some("{
21 | file.path(system.file(package = 'foo'), 'bar')
22 | system.file(file.path('bar', 'data'), package = 'foo')
23 | }"),
24 | list(
25 | list(lint_msg, line_number = 2L),
26 | list(lint_msg, line_number = 3L)
27 | ),
28 | system_file_linter()
29 | )
30 | })
31 |
--------------------------------------------------------------------------------
/R/expect_not_linter.R:
--------------------------------------------------------------------------------
1 | #' Require usage of `expect_false(x)` over `expect_true(!x)`
2 | #'
3 | #' [testthat::expect_false()] exists specifically for testing that an output is
4 | #' `FALSE`. [testthat::expect_true()] can also be used for such tests by
5 | #' negating the output, but it is better to use the tailored function instead.
6 | #' The reverse is also true -- use `expect_false(A)` instead of
7 | #' `expect_true(!A)`.
8 | #'
9 | #' @examples
10 | #' # will produce lints
11 | #' lint(
12 | #' text = "expect_true(!x)",
13 | #' linters = expect_not_linter()
14 | #' )
15 | #'
16 | #' # okay
17 | #' lint(
18 | #' text = "expect_false(x)",
19 | #' linters = expect_not_linter()
20 | #' )
21 | #'
22 | #' @evalRd rd_tags("expect_not_linter")
23 | #' @seealso [linters] for a complete list of linters available in lintr.
24 | #' @export
25 | expect_not_linter <- make_linter_from_function_xpath(
26 | function_names = c("expect_true", "expect_false"),
27 | xpath = "
28 | following-sibling::expr[OP-EXCLAMATION]
29 | /parent::expr
30 | ",
31 | lint_message = "expect_false(x) is better than expect_true(!x), and vice versa."
32 | )
33 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # MIT License
2 |
3 | Copyright (c) 2025 lintr authors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/man/numeric_leading_zero_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/numeric_leading_zero_linter.R
3 | \name{numeric_leading_zero_linter}
4 | \alias{numeric_leading_zero_linter}
5 | \title{Require usage of a leading zero in all fractional numerics}
6 | \usage{
7 | numeric_leading_zero_linter()
8 | }
9 | \description{
10 | While .1 and 0.1 mean the same thing, the latter is easier to read due
11 | to the small size of the '.' glyph.
12 | }
13 | \examples{
14 | # will produce lints
15 | lint(
16 | text = "x <- .1",
17 | linters = numeric_leading_zero_linter()
18 | )
19 |
20 | lint(
21 | text = "x <- -.1",
22 | linters = numeric_leading_zero_linter()
23 | )
24 |
25 | # okay
26 | lint(
27 | text = "x <- 0.1",
28 | linters = numeric_leading_zero_linter()
29 | )
30 |
31 | lint(
32 | text = "x <- -0.1",
33 | linters = numeric_leading_zero_linter()
34 | )
35 |
36 | }
37 | \seealso{
38 | \link{linters} for a complete list of linters available in lintr.
39 | }
40 | \section{Tags}{
41 | \link[=consistency_linters]{consistency}, \link[=readability_linters]{readability}, \link[=style_linters]{style}
42 | }
43 |
--------------------------------------------------------------------------------
/man/whitespace_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/whitespace_linter.R
3 | \name{whitespace_linter}
4 | \alias{whitespace_linter}
5 | \title{Whitespace linter}
6 | \usage{
7 | whitespace_linter()
8 | }
9 | \description{
10 | Check that the correct character is used for indentation.
11 | }
12 | \details{
13 | Currently, only supports linting in the presence of tabs.
14 |
15 | Much ink has been spilled on this topic, and we encourage you to check
16 | out references for more information.
17 | }
18 | \examples{
19 | # will produce lints
20 | lint(
21 | text = "\tx",
22 | linters = whitespace_linter()
23 | )
24 |
25 | # okay
26 | lint(
27 | text = " x",
28 | linters = whitespace_linter()
29 | )
30 |
31 | }
32 | \references{
33 | \itemize{
34 | \item https://www.jwz.org/doc/tabs-vs-spaces.html
35 | \item https://blog.codinghorror.com/death-to-the-space-infidels/
36 | }
37 | }
38 | \seealso{
39 | \link{linters} for a complete list of linters available in lintr.
40 | }
41 | \section{Tags}{
42 | \link[=consistency_linters]{consistency}, \link[=default_linters]{default}, \link[=style_linters]{style}
43 | }
44 |
--------------------------------------------------------------------------------
/man/commented_code_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/commented_code_linter.R
3 | \name{commented_code_linter}
4 | \alias{commented_code_linter}
5 | \title{Commented code linter}
6 | \usage{
7 | commented_code_linter()
8 | }
9 | \description{
10 | Check that there is no commented code outside roxygen blocks.
11 | }
12 | \examples{
13 | # will produce lints
14 | lint(
15 | text = "# x <- 1",
16 | linters = commented_code_linter()
17 | )
18 |
19 | lint(
20 | text = "x <- f() # g()",
21 | linters = commented_code_linter()
22 | )
23 |
24 | lint(
25 | text = "x + y # + z[1, 2]",
26 | linters = commented_code_linter()
27 | )
28 |
29 | # okay
30 | lint(
31 | text = "x <- 1; x <- f(); x + y",
32 | linters = commented_code_linter()
33 | )
34 |
35 | lint(
36 | text = "#' x <- 1",
37 | linters = commented_code_linter()
38 | )
39 |
40 | }
41 | \seealso{
42 | \link{linters} for a complete list of linters available in lintr.
43 | }
44 | \section{Tags}{
45 | \link[=best_practices_linters]{best_practices}, \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style}
46 | }
47 |
--------------------------------------------------------------------------------
/man/stopifnot_all_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/stopifnot_all_linter.R
3 | \name{stopifnot_all_linter}
4 | \alias{stopifnot_all_linter}
5 | \title{Block usage of all() within stopifnot()}
6 | \usage{
7 | stopifnot_all_linter()
8 | }
9 | \description{
10 | \code{stopifnot(A)} actually checks \code{all(A)} "under the hood" if \code{A} is a vector,
11 | and produces a better error message than \code{stopifnot(all(A))} does.
12 | }
13 | \examples{
14 | # will produce lints
15 | lint(
16 | text = "stopifnot(all(x > 0))",
17 | linters = stopifnot_all_linter()
18 | )
19 |
20 | lint(
21 | text = "stopifnot(y > 3, all(x < 0))",
22 | linters = stopifnot_all_linter()
23 | )
24 |
25 | # okay
26 | lint(
27 | text = "stopifnot(is.null(x) || all(x > 0))",
28 | linters = stopifnot_all_linter()
29 | )
30 |
31 | lint(
32 | text = "assert_that(all(x > 0))",
33 | linters = stopifnot_all_linter()
34 | )
35 |
36 | }
37 | \seealso{
38 | \link{linters} for a complete list of linters available in lintr.
39 | }
40 | \section{Tags}{
41 | \link[=best_practices_linters]{best_practices}, \link[=readability_linters]{readability}
42 | }
43 |
--------------------------------------------------------------------------------
/man/pkg_testthat_linters.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/linter_tag_docs.R
3 | \name{pkg_testthat_linters}
4 | \alias{pkg_testthat_linters}
5 | \title{Testthat linters}
6 | \description{
7 | Linters encouraging best practices within testthat suites.
8 | }
9 | \seealso{
10 | \itemize{
11 | \item \link{linters} for a complete list of linters available in lintr.
12 | \item \url{https://testthat.r-lib.org}
13 | \item \url{https://r-pkgs.org/testing-basics.html}
14 | }
15 | }
16 | \section{Linters}{
17 | The following linters are tagged with 'pkg_testthat':
18 | \itemize{
19 | \item{\code{\link{conjunct_test_linter}}}
20 | \item{\code{\link{expect_comparison_linter}}}
21 | \item{\code{\link{expect_identical_linter}}}
22 | \item{\code{\link{expect_length_linter}}}
23 | \item{\code{\link{expect_named_linter}}}
24 | \item{\code{\link{expect_not_linter}}}
25 | \item{\code{\link{expect_null_linter}}}
26 | \item{\code{\link{expect_s3_class_linter}}}
27 | \item{\code{\link{expect_s4_class_linter}}}
28 | \item{\code{\link{expect_true_false_linter}}}
29 | \item{\code{\link{expect_type_linter}}}
30 | \item{\code{\link{yoda_test_linter}}}
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/R/absolute_path_linter.R:
--------------------------------------------------------------------------------
1 | #' Absolute path linter
2 | #'
3 | #' Check that no absolute paths are used (e.g. "/var", "C:\\System", "~/docs").
4 | #'
5 | #' @param lax Less stringent linting, leading to fewer false positives.
6 | #' If `TRUE`, only lint path strings, which
7 | #'
8 | #' * contain at least two path elements, with one having at least two characters and
9 | #' * contain only alphanumeric chars (including UTF-8), spaces, and win32-allowed punctuation
10 | #'
11 | #' @examples
12 | #' # will produce lints
13 | #' lint(
14 | #' text = 'R"(/blah/file.txt)"',
15 | #' linters = absolute_path_linter()
16 | #' )
17 | #'
18 | #' # okay
19 | #' lint(
20 | #' text = 'R"(./blah)"',
21 | #' linters = absolute_path_linter()
22 | #' )
23 | #'
24 | #' @evalRd rd_tags("absolute_path_linter")
25 | #' @seealso
26 | #' - [linters] for a complete list of linters available in lintr.
27 | #' - [nonportable_path_linter()]
28 | #' @export
29 | absolute_path_linter <- function(lax = TRUE) {
30 | path_linter_factory(
31 | path_function = function(path) {
32 | is_absolute_path(path) && is_valid_long_path(path, lax)
33 | },
34 | message = "Do not use absolute paths."
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/R/lengths_linter.R:
--------------------------------------------------------------------------------
1 | #' Require usage of `lengths()` where possible
2 | #'
3 | #' [base::lengths()] is a function that was added to base R in version 3.2.0 to
4 | #' get the length of each element of a list. It is equivalent to
5 | #' `sapply(x, length)`, but faster and more readable.
6 | #'
7 | #' @examples
8 | #' # will produce lints
9 | #' lint(
10 | #' text = "sapply(x, length)",
11 | #' linters = lengths_linter()
12 | #' )
13 | #'
14 | #' lint(
15 | #' text = "vapply(x, length, integer(1L))",
16 | #' linters = lengths_linter()
17 | #' )
18 | #'
19 | #' lint(
20 | #' text = "purrr::map_int(x, length)",
21 | #' linters = lengths_linter()
22 | #' )
23 | #'
24 | #' # okay
25 | #' lint(
26 | #' text = "lengths(x)",
27 | #' linters = lengths_linter()
28 | #' )
29 | #'
30 | #' @evalRd rd_tags("lengths_linter")
31 | #' @seealso [linters] for a complete list of linters available in lintr.
32 | #' @export
33 | lengths_linter <- make_linter_from_function_xpath(
34 | function_names = c("sapply", "vapply", "map_int", "map_dbl"),
35 | xpath = "parent::expr[expr/SYMBOL[text() = 'length']]",
36 | lint_message = "Use lengths() to find the length of each element in a list."
37 | )
38 |
--------------------------------------------------------------------------------
/man/empty_assignment_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/empty_assignment_linter.R
3 | \name{empty_assignment_linter}
4 | \alias{empty_assignment_linter}
5 | \title{Block assignment of \code{{}}}
6 | \usage{
7 | empty_assignment_linter()
8 | }
9 | \description{
10 | Assignment of \code{{}} is the same as assignment of \code{NULL}; use the latter
11 | for clarity. Closely related: \code{\link[=unnecessary_concatenation_linter]{unnecessary_concatenation_linter()}}.
12 | }
13 | \examples{
14 | # will produce lints
15 | lint(
16 | text = "x <- {}",
17 | linters = empty_assignment_linter()
18 | )
19 |
20 | writeLines("x = {\n}")
21 | lint(
22 | text = "x = {\n}",
23 | linters = empty_assignment_linter()
24 | )
25 |
26 | # okay
27 | lint(
28 | text = "x <- { 3 + 4 }",
29 | linters = empty_assignment_linter()
30 | )
31 |
32 | lint(
33 | text = "x <- NULL",
34 | linters = empty_assignment_linter()
35 | )
36 |
37 | }
38 | \seealso{
39 | \link{linters} for a complete list of linters available in lintr.
40 | }
41 | \section{Tags}{
42 | \link[=best_practices_linters]{best_practices}, \link[=readability_linters]{readability}
43 | }
44 |
--------------------------------------------------------------------------------
/tests/testthat/test-print_linter.R:
--------------------------------------------------------------------------------
1 | test_that("print_linter skips allowed usages", {
2 | linter <- print_linter()
3 |
4 | expect_lint("print(x)", NULL, linter)
5 | expect_lint("print(foo(x))", NULL, linter)
6 | })
7 |
8 | test_that("print_linter blocks disallowed usages", {
9 | linter <- print_linter()
10 | lint_msg <-
11 | rex::rex("Use cat() instead of print() logging messages. Use message() in cases calling for a signalled condition.")
12 |
13 | expect_lint('print("hi")', lint_msg, linter)
14 |
15 | # basic known-character functions
16 | expect_lint('print(paste(x, "b", y))', lint_msg, linter)
17 | expect_lint('print(paste0(x, "c", y))', lint_msg, linter)
18 | expect_lint('print(sprintf("a %s", x))', lint_msg, linter)
19 |
20 | # vectorization, metadata
21 | expect_lint(
22 | trim_some("{
23 | print('a')
24 | print(paste('x', y))
25 | print(z)
26 | print(sprintf('%s', b))
27 | }"),
28 | list(
29 | list(lint_msg, line_number = 2L, column_number = 3L),
30 | list(lint_msg, line_number = 3L, column_number = 3L),
31 | list(lint_msg, line_number = 5L, column_number = 3L)
32 | ),
33 | linter
34 | )
35 | })
36 |
--------------------------------------------------------------------------------
/R/repeat_linter.R:
--------------------------------------------------------------------------------
1 | #' Repeat linter
2 | #'
3 | #' Check that `while (TRUE)` is not used for infinite loops. While this is valid
4 | #' R code, using `repeat {}` is more explicit.
5 | #'
6 | #' @examples
7 | #' # will produce lints
8 | #' lint(
9 | #' text = "while (TRUE) { }",
10 | #' linters = repeat_linter()
11 | #' )
12 | #'
13 | #'
14 | #' # okay
15 | #' lint(
16 | #' text = "repeat { }",
17 | #' linters = repeat_linter()
18 | #' )
19 | #'
20 | #' @evalRd rd_tags("repeat_linter")
21 | #' @seealso [linters] for a complete list of linters available in lintr.
22 | #' @export
23 | repeat_linter <- function() {
24 | xpath <- "//WHILE[following-sibling::expr[1]/NUM_CONST[text() = 'TRUE']]"
25 |
26 | Linter(linter_level = "expression", function(source_expression) {
27 | xml <- source_expression$xml_parsed_content
28 |
29 | lints <- xml_find_all(xml, xpath)
30 |
31 | xml_nodes_to_lints(
32 | lints,
33 | source_expression = source_expression,
34 | lint_message = "Use 'repeat' instead of 'while (TRUE)' for infinite loops.",
35 | range_start_xpath = "number(./@col1)",
36 | range_end_xpath = "number(./following-sibling::*[3]/@col2)"
37 | )
38 | })
39 | }
40 |
--------------------------------------------------------------------------------
/man/all_linters.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/with.R
3 | \name{all_linters}
4 | \alias{all_linters}
5 | \title{Create a linter configuration based on all available linters}
6 | \usage{
7 | all_linters(..., packages = "lintr")
8 | }
9 | \arguments{
10 | \item{...}{Arguments of elements to change. If unnamed, the argument is automatically named.
11 | If the named argument already exists in the list of linters, it is replaced by the new element.
12 | If it does not exist, it is added. If the value is \code{NULL}, the linter is removed.}
13 |
14 | \item{packages}{A character vector of packages to search for linters.}
15 | }
16 | \description{
17 | Create a linter configuration based on all available linters
18 | }
19 | \examples{
20 | names(all_linters())
21 |
22 | }
23 | \seealso{
24 | \itemize{
25 | \item \link{linters_with_defaults} for basing off lintr's set of default linters.
26 | \item \link{linters_with_tags} for basing off tags attached to linters, possibly across multiple packages.
27 | \item \link{available_linters} to get a data frame of available linters.
28 | \item \link{linters} for a complete list of linters available in lintr.
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/man/package_development_linters.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/linter_tag_docs.R
3 | \name{package_development_linters}
4 | \alias{package_development_linters}
5 | \title{Package development linters}
6 | \description{
7 | Linters useful to package developers, for example for writing consistent tests.
8 | }
9 | \seealso{
10 | \link{linters} for a complete list of linters available in lintr.
11 | }
12 | \section{Linters}{
13 | The following linters are tagged with 'package_development':
14 | \itemize{
15 | \item{\code{\link{backport_linter}}}
16 | \item{\code{\link{conjunct_test_linter}}}
17 | \item{\code{\link{expect_comparison_linter}}}
18 | \item{\code{\link{expect_identical_linter}}}
19 | \item{\code{\link{expect_length_linter}}}
20 | \item{\code{\link{expect_named_linter}}}
21 | \item{\code{\link{expect_not_linter}}}
22 | \item{\code{\link{expect_null_linter}}}
23 | \item{\code{\link{expect_s3_class_linter}}}
24 | \item{\code{\link{expect_s4_class_linter}}}
25 | \item{\code{\link{expect_true_false_linter}}}
26 | \item{\code{\link{expect_type_linter}}}
27 | \item{\code{\link{package_hooks_linter}}}
28 | \item{\code{\link{yoda_test_linter}}}
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/man/lengths_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/lengths_linter.R
3 | \name{lengths_linter}
4 | \alias{lengths_linter}
5 | \title{Require usage of \code{lengths()} where possible}
6 | \usage{
7 | lengths_linter()
8 | }
9 | \description{
10 | \code{\link[base:lengths]{base::lengths()}} is a function that was added to base R in version 3.2.0 to
11 | get the length of each element of a list. It is equivalent to
12 | \code{sapply(x, length)}, but faster and more readable.
13 | }
14 | \examples{
15 | # will produce lints
16 | lint(
17 | text = "sapply(x, length)",
18 | linters = lengths_linter()
19 | )
20 |
21 | lint(
22 | text = "vapply(x, length, integer(1L))",
23 | linters = lengths_linter()
24 | )
25 |
26 | lint(
27 | text = "purrr::map_int(x, length)",
28 | linters = lengths_linter()
29 | )
30 |
31 | # okay
32 | lint(
33 | text = "lengths(x)",
34 | linters = lengths_linter()
35 | )
36 |
37 | }
38 | \seealso{
39 | \link{linters} for a complete list of linters available in lintr.
40 | }
41 | \section{Tags}{
42 | \link[=best_practices_linters]{best_practices}, \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability}
43 | }
44 |
--------------------------------------------------------------------------------
/man/for_loop_index_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/for_loop_index_linter.R
3 | \name{for_loop_index_linter}
4 | \alias{for_loop_index_linter}
5 | \title{Block usage of for loops directly overwriting the indexing variable}
6 | \usage{
7 | for_loop_index_linter()
8 | }
9 | \description{
10 | \verb{for (x in x)} is a poor choice of indexing variable. This overwrites
11 | \code{x} in the calling scope and is confusing to read.
12 | }
13 | \examples{
14 | # will produce lints
15 | lint(
16 | text = "for (x in x) { TRUE }",
17 | linters = for_loop_index_linter()
18 | )
19 |
20 | lint(
21 | text = "for (x in foo(x, y)) { TRUE }",
22 | linters = for_loop_index_linter()
23 | )
24 |
25 | # okay
26 | lint(
27 | text = "for (xi in x) { TRUE }",
28 | linters = for_loop_index_linter()
29 | )
30 |
31 | lint(
32 | text = "for (col in DF$col) { TRUE }",
33 | linters = for_loop_index_linter()
34 | )
35 |
36 | }
37 | \seealso{
38 | \link{linters} for a complete list of linters available in lintr.
39 | }
40 | \section{Tags}{
41 | \link[=best_practices_linters]{best_practices}, \link[=readability_linters]{readability}, \link[=robustness_linters]{robustness}
42 | }
43 |
--------------------------------------------------------------------------------
/man/spaces_inside_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/spaces_inside_linter.R
3 | \name{spaces_inside_linter}
4 | \alias{spaces_inside_linter}
5 | \title{Spaces inside linter}
6 | \usage{
7 | spaces_inside_linter()
8 | }
9 | \description{
10 | Check that parentheses and square brackets do not have spaces directly
11 | inside them, i.e., directly following an opening delimiter or directly
12 | preceding a closing delimiter.
13 | }
14 | \examples{
15 | # will produce lints
16 | lint(
17 | text = "c( TRUE, FALSE )",
18 | linters = spaces_inside_linter()
19 | )
20 |
21 | lint(
22 | text = "x[ 1L ]",
23 | linters = spaces_inside_linter()
24 | )
25 |
26 | # okay
27 | lint(
28 | text = "c(TRUE, FALSE)",
29 | linters = spaces_inside_linter()
30 | )
31 |
32 | lint(
33 | text = "x[1L]",
34 | linters = spaces_inside_linter()
35 | )
36 |
37 | }
38 | \seealso{
39 | \itemize{
40 | \item \link{linters} for a complete list of linters available in lintr.
41 | \item \url{https://style.tidyverse.org/syntax.html#parentheses}
42 | }
43 | }
44 | \section{Tags}{
45 | \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style}
46 | }
47 |
--------------------------------------------------------------------------------
/tests/testthat/test-list_comparison_linter.R:
--------------------------------------------------------------------------------
1 | test_that("list_comparison_linter skips allowed usages", {
2 | expect_lint("sapply(x, sum) > 10", NULL, list_comparison_linter())
3 | })
4 |
5 | local({
6 | linter <- list_comparison_linter()
7 | lint_msg <- rex::rex("a list(), is being coerced for comparison")
8 |
9 | cases <- expand.grid(
10 | list_mapper = c("lapply", "map", "Map", ".mapply"),
11 | comparator = c("==", "!=", ">=", "<=", ">", "<")
12 | )
13 | cases$.test_name <- with(cases, paste(list_mapper, comparator))
14 | patrick::with_parameters_test_that(
15 | "list_comparison_linter blocks simple disallowed usages",
16 | expect_lint(sprintf("%s(x, sum) %s 10", list_mapper, comparator), lint_msg, linter),
17 | .cases = cases
18 | )
19 | })
20 |
21 | test_that("list_comparison_linter vectorizes", {
22 | expect_lint(
23 | trim_some("{
24 | sapply(x, sum) > 10
25 | .mapply(`+`, list(1:10, 1:10), NULL) == 2
26 | lapply(x, sum) < 5
27 | }"),
28 | list(
29 | list(rex::rex(".mapply()", anything, "`==`"), line_number = 3L),
30 | list(rex::rex("lapply()", anything, "`<`"), line_number = 4L)
31 | ),
32 | list_comparison_linter()
33 | )
34 | })
35 |
--------------------------------------------------------------------------------
/man/boolean_arithmetic_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/boolean_arithmetic_linter.R
3 | \name{boolean_arithmetic_linter}
4 | \alias{boolean_arithmetic_linter}
5 | \title{Require usage of boolean operators over equivalent arithmetic}
6 | \usage{
7 | boolean_arithmetic_linter()
8 | }
9 | \description{
10 | \code{length(which(x == y)) == 0} is the same as \code{!any(x == y)}, but the latter
11 | is more readable and more efficient.
12 | }
13 | \examples{
14 | # will produce lints
15 | lint(
16 | text = "length(which(x == y)) == 0L",
17 | linters = boolean_arithmetic_linter()
18 | )
19 |
20 | lint(
21 | text = "sum(grepl(pattern, x)) == 0",
22 | linters = boolean_arithmetic_linter()
23 | )
24 |
25 | # okay
26 | lint(
27 | text = "!any(x == y)",
28 | linters = boolean_arithmetic_linter()
29 | )
30 |
31 | lint(
32 | text = "!any(grepl(pattern, x))",
33 | linters = boolean_arithmetic_linter()
34 | )
35 |
36 | }
37 | \seealso{
38 | \link{linters} for a complete list of linters available in lintr.
39 | }
40 | \section{Tags}{
41 | \link[=best_practices_linters]{best_practices}, \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability}
42 | }
43 |
--------------------------------------------------------------------------------
/man/system_file_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/system_file_linter.R
3 | \name{system_file_linter}
4 | \alias{system_file_linter}
5 | \title{Block usage of \code{file.path()} with \code{system.file()}}
6 | \usage{
7 | system_file_linter()
8 | }
9 | \description{
10 | \code{\link[=system.file]{system.file()}} has a \code{...} argument which, internally, is passed to
11 | \code{\link[=file.path]{file.path()}}, so including it in user code is repetitive.
12 | }
13 | \examples{
14 | # will produce lints
15 | lint(
16 | text = 'system.file(file.path("path", "to", "data"), package = "foo")',
17 | linters = system_file_linter()
18 | )
19 |
20 | lint(
21 | text = 'file.path(system.file(package = "foo"), "path", "to", "data")',
22 | linters = system_file_linter()
23 | )
24 |
25 | # okay
26 | lint(
27 | text = 'system.file("path", "to", "data", package = "foo")',
28 | linters = system_file_linter()
29 | )
30 |
31 | }
32 | \seealso{
33 | \link{linters} for a complete list of linters available in lintr.
34 | }
35 | \section{Tags}{
36 | \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=readability_linters]{readability}
37 | }
38 |
--------------------------------------------------------------------------------
/R/rep_len_linter.R:
--------------------------------------------------------------------------------
1 | #' Require usage of rep_len(x, n) over rep(x, length.out = n)
2 | #'
3 | #' `rep(x, length.out = n)` calls `rep_len(x, n)` "under the hood". The latter
4 | #' is thus more direct and equally readable.
5 | #'
6 | #' @examples
7 | #' # will produce lints
8 | #' lint(
9 | #' text = "rep(1:3, length.out = 10)",
10 | #' linters = rep_len_linter()
11 | #' )
12 | #'
13 | #' # okay
14 | #' lint(
15 | #' text = "rep_len(1:3, 10)",
16 | #' linters = rep_len_linter()
17 | #' )
18 | #'
19 | #' lint(
20 | #' text = "rep(1:3, each = 2L, length.out = 10L)",
21 | #' linters = rep_len_linter()
22 | #' )
23 | #'
24 | #' @evalRd rd_tags("rep_len_linter")
25 | #' @seealso [linters] for a complete list of linters available in lintr.
26 | #' @export
27 | rep_len_linter <- make_linter_from_function_xpath(
28 | function_names = "rep",
29 | # count(expr) is for cases using positional matching; see ?rep.
30 | xpath = "
31 | parent::expr[
32 | (
33 | SYMBOL_SUB[text() = 'length.out']
34 | or (not(SYMBOL_SUB) and count(expr) = 4)
35 | )
36 | and not(SYMBOL_SUB[text() = 'each'] or count(expr) = 5)
37 | ]
38 | ",
39 | lint_message = "Use rep_len(x, n) instead of rep(x, length.out = n)."
40 | )
41 |
--------------------------------------------------------------------------------
/man/equals_na_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/equals_na_linter.R
3 | \name{equals_na_linter}
4 | \alias{equals_na_linter}
5 | \title{Equality check with NA linter}
6 | \usage{
7 | equals_na_linter()
8 | }
9 | \description{
10 | Check for \code{x == NA}, \code{x != NA} and \code{x \%in\% NA}. Such usage is almost surely incorrect --
11 | checks for missing values should be done with \code{\link[=is.na]{is.na()}}.
12 | }
13 | \examples{
14 | # will produce lints
15 | lint(
16 | text = "x == NA",
17 | linters = equals_na_linter()
18 | )
19 |
20 | lint(
21 | text = "x != NA",
22 | linters = equals_na_linter()
23 | )
24 |
25 | lint(
26 | text = "x \%in\% NA",
27 | linters = equals_na_linter()
28 | )
29 |
30 | # okay
31 | lint(
32 | text = "is.na(x)",
33 | linters = equals_na_linter()
34 | )
35 |
36 | lint(
37 | text = "!is.na(x)",
38 | linters = equals_na_linter()
39 | )
40 |
41 | }
42 | \seealso{
43 | \link{linters} for a complete list of linters available in lintr.
44 | }
45 | \section{Tags}{
46 | \link[=common_mistakes_linters]{common_mistakes}, \link[=correctness_linters]{correctness}, \link[=default_linters]{default}, \link[=robustness_linters]{robustness}
47 | }
48 |
--------------------------------------------------------------------------------
/man/expect_length_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/expect_length_linter.R
3 | \name{expect_length_linter}
4 | \alias{expect_length_linter}
5 | \title{Require usage of \code{expect_length(x, n)} over \code{expect_equal(length(x), n)}}
6 | \usage{
7 | expect_length_linter()
8 | }
9 | \description{
10 | \code{\link[testthat:expect_length]{testthat::expect_length()}} exists specifically for testing the \code{\link[=length]{length()}} of
11 | an object. \code{\link[testthat:equality-expectations]{testthat::expect_equal()}} can also be used for such tests,
12 | but it is better to use the tailored function instead.
13 | }
14 | \examples{
15 | # will produce lints
16 | lint(
17 | text = "expect_equal(length(x), 2L)",
18 | linters = expect_length_linter()
19 | )
20 |
21 | # okay
22 | lint(
23 | text = "expect_length(x, 2L)",
24 | linters = expect_length_linter()
25 | )
26 |
27 | }
28 | \seealso{
29 | \link{linters} for a complete list of linters available in lintr.
30 | }
31 | \section{Tags}{
32 | \link[=best_practices_linters]{best_practices}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat}, \link[=readability_linters]{readability}
33 | }
34 |
--------------------------------------------------------------------------------
/man/cyclocomp_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/cyclocomp_linter.R
3 | \name{cyclocomp_linter}
4 | \alias{cyclocomp_linter}
5 | \title{Cyclomatic complexity linter}
6 | \usage{
7 | cyclocomp_linter(complexity_limit = 15L)
8 | }
9 | \arguments{
10 | \item{complexity_limit}{Maximum cyclomatic complexity, default \code{15}. Expressions more complex
11 | than this are linted.}
12 | }
13 | \description{
14 | Check for overly complicated expressions. See \code{cyclocomp()} function from \code{{cyclocomp}}.
15 | }
16 | \examples{
17 | \dontshow{if (requireNamespace("cyclocomp", quietly = TRUE)) withAutoprint(\{ # examplesIf}
18 | # will produce lints
19 | lint(
20 | text = "if (TRUE) 1 else 2",
21 | linters = cyclocomp_linter(complexity_limit = 1L)
22 | )
23 |
24 | # okay
25 | lint(
26 | text = "if (TRUE) 1 else 2",
27 | linters = cyclocomp_linter(complexity_limit = 2L)
28 | )
29 | \dontshow{\}) # examplesIf}
30 | }
31 | \seealso{
32 | \link{linters} for a complete list of linters available in lintr.
33 | }
34 | \section{Tags}{
35 | \link[=best_practices_linters]{best_practices}, \link[=configurable_linters]{configurable}, \link[=readability_linters]{readability}, \link[=style_linters]{style}
36 | }
37 |
--------------------------------------------------------------------------------
/R/lintr-package.R:
--------------------------------------------------------------------------------
1 | #' Lintr
2 | #'
3 | #' Checks adherence to a given style, syntax errors, and possible semantic issues.
4 | #' Supports on the fly checking of R code edited with Emacs, Vim, and Sublime Text.
5 | #'
6 | #' @seealso [lint()], [lint_package()], [lint_dir()], [linters]
7 | #' @keywords internal
8 | "_PACKAGE"
9 |
10 | ## lintr namespace: start
11 | #' @importFrom cli cli_inform cli_abort cli_warn qty
12 | #' @importFrom glue glue glue_collapse
13 | #' @importFrom rex rex regex re_matches re_substitutes character_class
14 | #' @importFrom stats complete.cases na.omit
15 | #' @importFrom tools R_user_dir
16 | #' @importFrom utils capture.output getParseData globalVariables head relist tail
17 | #' @importFrom xml2 as_list
18 | #' xml_attr xml_children xml_find_all xml_find_chr xml_find_lgl xml_find_num
19 | #' xml_find_first xml_name xml_parent xml_text
20 | ## lintr namespace: end
21 | NULL
22 |
23 | # make binding available for mock testing
24 | # ref: https://testthat.r-lib.org/dev/reference/local_mocked_bindings.html#base-functions
25 | # nolint start: object_name_linter. These will be copied from base style.
26 | requireNamespace <- NULL
27 | system.file <- NULL
28 | unlink <- NULL
29 | quit <- NULL
30 | # nolint end: object_name_linter.
31 |
--------------------------------------------------------------------------------
/man/outer_negation_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/outer_negation_linter.R
3 | \name{outer_negation_linter}
4 | \alias{outer_negation_linter}
5 | \title{Require usage of \code{!any(x)} over \code{all(!x)}, \code{!all(x)} over \code{any(!x)}}
6 | \usage{
7 | outer_negation_linter()
8 | }
9 | \description{
10 | \code{any(!x)} is logically equivalent to \code{!all(x)}; ditto for the equivalence of
11 | \code{all(!x)} and \code{!any(x)}. Negating after aggregation only requires inverting
12 | one logical value, and is typically more readable.
13 | }
14 | \examples{
15 | # will produce lints
16 | lint(
17 | text = "all(!x)",
18 | linters = outer_negation_linter()
19 | )
20 |
21 | lint(
22 | text = "any(!x)",
23 | linters = outer_negation_linter()
24 | )
25 |
26 | # okay
27 | lint(
28 | text = "!any(x)",
29 | linters = outer_negation_linter()
30 | )
31 |
32 | lint(
33 | text = "!all(x)",
34 | linters = outer_negation_linter()
35 | )
36 |
37 | }
38 | \seealso{
39 | \link{linters} for a complete list of linters available in lintr.
40 | }
41 | \section{Tags}{
42 | \link[=best_practices_linters]{best_practices}, \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability}
43 | }
44 |
--------------------------------------------------------------------------------
/tests/testthat/test-list2df_linter.R:
--------------------------------------------------------------------------------
1 | test_that("list2df_linter skips allowed usages", {
2 | linter <- list2df_linter()
3 |
4 | expect_no_lint("cbind.data.frame(x, x)", linter)
5 | expect_no_lint("do.call(mean, x)", linter)
6 | expect_no_lint("do.call('c', x)", linter)
7 |
8 | # Other cbind methods
9 | expect_no_lint("do.call(cbind, x)", linter)
10 |
11 | # Anonymous function
12 | expect_no_lint("do.call(function(x) x, l)", linter)
13 | })
14 |
15 | test_that("list2df_linter blocks simple disallowed usages", {
16 | linter <- list2df_linter()
17 | lint_message <- rex::rex("use `data.frame(lst)`")
18 |
19 | expect_lint("do.call(cbind.data.frame, x)", lint_message, linter)
20 | expect_lint("do.call('cbind.data.frame', x)", lint_message, linter)
21 | })
22 |
23 | test_that("lints vectorize", {
24 | lint_message <- rex::rex("use `data.frame(lst)`")
25 |
26 | expect_lint(
27 | trim_some("{
28 | cbind(a, b)
29 | do.call(cbind.data.frame, x)
30 | do.call(function(x) x, l)
31 | do.call('cbind.data.frame', y)
32 | }"),
33 | list(
34 | list(lint_message, line_number = 3L, column_number = 3L),
35 | list(lint_message, line_number = 5L, column_number = 3L)
36 | ),
37 | list2df_linter()
38 | )
39 | })
40 |
--------------------------------------------------------------------------------
/R/numeric_leading_zero_linter.R:
--------------------------------------------------------------------------------
1 | #' Require usage of a leading zero in all fractional numerics
2 | #'
3 | #' While .1 and 0.1 mean the same thing, the latter is easier to read due
4 | #' to the small size of the '.' glyph.
5 | #'
6 | #' @examples
7 | #' # will produce lints
8 | #' lint(
9 | #' text = "x <- .1",
10 | #' linters = numeric_leading_zero_linter()
11 | #' )
12 | #'
13 | #' lint(
14 | #' text = "x <- -.1",
15 | #' linters = numeric_leading_zero_linter()
16 | #' )
17 | #'
18 | #' # okay
19 | #' lint(
20 | #' text = "x <- 0.1",
21 | #' linters = numeric_leading_zero_linter()
22 | #' )
23 | #'
24 | #' lint(
25 | #' text = "x <- -0.1",
26 | #' linters = numeric_leading_zero_linter()
27 | #' )
28 | #'
29 | #' @evalRd rd_tags("numeric_leading_zero_linter")
30 | #' @seealso [linters] for a complete list of linters available in lintr.
31 | #' @export
32 | numeric_leading_zero_linter <- make_linter_from_xpath(
33 | # NB:
34 | # 1. negative constants are split to two components:
35 | # OP-MINUS, NUM_CONST
36 | # 2. complex constants are split to three components:
37 | # NUM_CONST, OP-PLUS/OP-MINUS, NUM_CONST
38 | xpath = "//NUM_CONST[starts-with(text(), '.')]",
39 | lint_message = "Include the leading zero for fractional numeric constants."
40 | )
41 |
--------------------------------------------------------------------------------
/.github/workflows/test-package-vigilant.yaml:
--------------------------------------------------------------------------------
1 | # based on test-coverage, running testthat with options(warn = 2) to fail on test warnings
2 | on:
3 | push:
4 | branches: [main, master]
5 | pull_request:
6 | branches: [main, master]
7 |
8 | name: test-package
9 |
10 | jobs:
11 | test-package:
12 | runs-on: ubuntu-latest
13 | env:
14 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
15 |
16 | steps:
17 | - uses: actions/checkout@v6
18 |
19 | - uses: r-lib/actions/setup-r@v2
20 | with:
21 | use-public-rspm: true
22 |
23 | - uses: r-lib/actions/setup-r-dependencies@v2
24 | with:
25 | install-quarto: false
26 | extra-packages: local::.
27 |
28 | - name: Run Tests
29 | run: |
30 | ## --------------------------------------------------------------------
31 | options(
32 | crayon.enabled = TRUE,
33 | warn = 2L,
34 | warnPartialMatchArgs = TRUE,
35 | warnPartialMatchAttr = TRUE,
36 | warnPartialMatchDollar = TRUE
37 | )
38 | if (Sys.getenv("_R_CHECK_FORCE_SUGGESTS_", "") == "") Sys.setenv("_R_CHECK_FORCE_SUGGESTS_" = "false")
39 | testthat::test_dir("tests")
40 | shell: Rscript {0}
41 |
--------------------------------------------------------------------------------
/man/sprintf_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/sprintf_linter.R
3 | \name{sprintf_linter}
4 | \alias{sprintf_linter}
5 | \title{Require correct \code{sprintf()} calls}
6 | \usage{
7 | sprintf_linter()
8 | }
9 | \description{
10 | Check for an inconsistent number of arguments or arguments with incompatible types (for literal arguments) in
11 | \code{\link[=sprintf]{sprintf()}} calls.
12 | }
13 | \details{
14 | \code{\link[=gettextf]{gettextf()}} calls are also included, since \code{gettextf()} is a thin wrapper around \code{sprintf()}.
15 | }
16 | \examples{
17 | # will produce lints
18 | lint(
19 | text = 'sprintf("hello \%s \%s \%d", x, y)',
20 | linters = sprintf_linter()
21 | )
22 |
23 | lint(
24 | text = 'sprintf("hello")',
25 | linters = sprintf_linter()
26 | )
27 |
28 | # okay
29 | lint(
30 | text = 'sprintf("hello \%s \%s \%d", x, y, z)',
31 | linters = sprintf_linter()
32 | )
33 |
34 | lint(
35 | text = 'sprintf("hello \%s \%s \%d", x, y, ...)',
36 | linters = sprintf_linter()
37 | )
38 |
39 | }
40 | \seealso{
41 | \link{linters} for a complete list of linters available in lintr.
42 | }
43 | \section{Tags}{
44 | \link[=common_mistakes_linters]{common_mistakes}, \link[=correctness_linters]{correctness}
45 | }
46 |
--------------------------------------------------------------------------------
/man/comparison_negation_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/comparison_negation_linter.R
3 | \name{comparison_negation_linter}
4 | \alias{comparison_negation_linter}
5 | \title{Block usages like !(x == y) where a direct relational operator is appropriate}
6 | \usage{
7 | comparison_negation_linter()
8 | }
9 | \description{
10 | \code{!(x == y)} is more readably expressed as \code{x != y}. The same is true of
11 | other negations of simple comparisons like \code{!(x > y)} and \code{!(x <= y)}.
12 | }
13 | \examples{
14 | # will produce lints
15 | lint(
16 | text = "!x == 2",
17 | linters = comparison_negation_linter()
18 | )
19 |
20 | lint(
21 | text = "!(x > 2)",
22 | linters = comparison_negation_linter()
23 | )
24 |
25 | # okay
26 | lint(
27 | text = "!(x == 2 & y > 2)",
28 | linters = comparison_negation_linter()
29 | )
30 |
31 | lint(
32 | text = "!(x & y)",
33 | linters = comparison_negation_linter()
34 | )
35 |
36 | lint(
37 | text = "x != 2",
38 | linters = comparison_negation_linter()
39 | )
40 |
41 | }
42 | \seealso{
43 | \link{linters} for a complete list of linters available in lintr.
44 | }
45 | \section{Tags}{
46 | \link[=consistency_linters]{consistency}, \link[=readability_linters]{readability}
47 | }
48 |
--------------------------------------------------------------------------------
/R/for_loop_index_linter.R:
--------------------------------------------------------------------------------
1 | #' Block usage of for loops directly overwriting the indexing variable
2 | #'
3 | #' `for (x in x)` is a poor choice of indexing variable. This overwrites
4 | #' `x` in the calling scope and is confusing to read.
5 | #'
6 | #' @examples
7 | #' # will produce lints
8 | #' lint(
9 | #' text = "for (x in x) { TRUE }",
10 | #' linters = for_loop_index_linter()
11 | #' )
12 | #'
13 | #' lint(
14 | #' text = "for (x in foo(x, y)) { TRUE }",
15 | #' linters = for_loop_index_linter()
16 | #' )
17 | #'
18 | #' # okay
19 | #' lint(
20 | #' text = "for (xi in x) { TRUE }",
21 | #' linters = for_loop_index_linter()
22 | #' )
23 | #'
24 | #' lint(
25 | #' text = "for (col in DF$col) { TRUE }",
26 | #' linters = for_loop_index_linter()
27 | #' )
28 | #'
29 | #' @evalRd rd_tags("for_loop_index_linter")
30 | #' @seealso [linters] for a complete list of linters available in lintr.
31 | #' @export
32 | for_loop_index_linter <- make_linter_from_xpath(
33 | xpath = "
34 | //forcond
35 | /SYMBOL[text() =
36 | following-sibling::expr
37 | //SYMBOL[not(parent::expr[OP-DOLLAR or OP-AT or preceding-sibling::OP-LEFT-BRACKET])]
38 | /text()
39 | ]
40 | ",
41 | lint_message = "Don't re-use any sequence symbols as the index symbol in a for loop."
42 | )
43 |
--------------------------------------------------------------------------------
/inst/CITATION:
--------------------------------------------------------------------------------
1 | bibentry(
2 | "Article",
3 | doi = "10.21105/joss.07240",
4 | year = 2025,
5 | publisher = "{Open Journals}",
6 | volume = 10,
7 | number = 108,
8 | pages = "7240",
9 | author = c(
10 | person(given = "Jim", family = "Hester", role = c("aut"), email = NULL, comment = c(ORCID = "0000-0002-2739-7082")),
11 | person(given = "Florent", family = "Angly", role = c("aut"), email = NULL, comment = c(ORCID = "0000-0002-8999-0738")),
12 | person(given = "Michael", family = "Chirico", role = c("aut"), email = NULL, comment = c(ORCID = "0000-0003-0787-087X")),
13 | person(given = "Russ", family = "Hyde", role = c("aut")),
14 | person(given = "Ren", family = "Kun", role = c("aut")),
15 | person(given = "Indrajeet", family = "Patil", role = c("aut"), email = NULL, comment = c(ORCID = "0000-0003-1995-6531")),
16 | person(given = "Alexander", family = "Rosenstock", role = c("aut"))
17 | ),
18 | title = "Static Code Analysis for R",
19 | journal = "{Journal of Open Source Software}",
20 | textVersion = paste(
21 | "Hester, J., Angly, F., Chirico, M., Hyde, R., Kun, R., Patil, I., & Rosenstock, A.",
22 | "(2025). Static Code Analysis for R. Journal of Open Source Software, 10(108), 7240.",
23 | "https://doi.org/10.21105/joss.07240"
24 | )
25 | )
26 |
--------------------------------------------------------------------------------
/man/any_is_na_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/any_is_na_linter.R
3 | \name{any_is_na_linter}
4 | \alias{any_is_na_linter}
5 | \title{Require usage of \code{anyNA(x)} over \code{any(is.na(x))}}
6 | \usage{
7 | any_is_na_linter()
8 | }
9 | \description{
10 | \code{\link[base:NA]{base::anyNA()}} exists as a replacement for \code{any(is.na(x))} which is more efficient
11 | for simple objects, and is at worst equally efficient.
12 | Therefore, it should be used in all situations instead of the latter.
13 | }
14 | \examples{
15 | # will produce lints
16 | lint(
17 | text = "any(is.na(x), na.rm = TRUE)",
18 | linters = any_is_na_linter()
19 | )
20 |
21 | lint(
22 | text = "any(is.na(foo(x)))",
23 | linters = any_is_na_linter()
24 | )
25 |
26 | # okay
27 | lint(
28 | text = "anyNA(x)",
29 | linters = any_is_na_linter()
30 | )
31 |
32 | lint(
33 | text = "anyNA(foo(x))",
34 | linters = any_is_na_linter()
35 | )
36 |
37 | lint(
38 | text = "any(!is.na(x), na.rm = TRUE)",
39 | linters = any_is_na_linter()
40 | )
41 |
42 | }
43 | \seealso{
44 | \link{linters} for a complete list of linters available in lintr.
45 | }
46 | \section{Tags}{
47 | \link[=best_practices_linters]{best_practices}, \link[=efficiency_linters]{efficiency}
48 | }
49 |
--------------------------------------------------------------------------------
/man/expect_not_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/expect_not_linter.R
3 | \name{expect_not_linter}
4 | \alias{expect_not_linter}
5 | \title{Require usage of \code{expect_false(x)} over \code{expect_true(!x)}}
6 | \usage{
7 | expect_not_linter()
8 | }
9 | \description{
10 | \code{\link[testthat:logical-expectations]{testthat::expect_false()}} exists specifically for testing that an output is
11 | \code{FALSE}. \code{\link[testthat:logical-expectations]{testthat::expect_true()}} can also be used for such tests by
12 | negating the output, but it is better to use the tailored function instead.
13 | The reverse is also true -- use \code{expect_false(A)} instead of
14 | \code{expect_true(!A)}.
15 | }
16 | \examples{
17 | # will produce lints
18 | lint(
19 | text = "expect_true(!x)",
20 | linters = expect_not_linter()
21 | )
22 |
23 | # okay
24 | lint(
25 | text = "expect_false(x)",
26 | linters = expect_not_linter()
27 | )
28 |
29 | }
30 | \seealso{
31 | \link{linters} for a complete list of linters available in lintr.
32 | }
33 | \section{Tags}{
34 | \link[=best_practices_linters]{best_practices}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat}, \link[=readability_linters]{readability}
35 | }
36 |
--------------------------------------------------------------------------------
/man/sample_int_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/sample_int_linter.R
3 | \name{sample_int_linter}
4 | \alias{sample_int_linter}
5 | \title{Require usage of sample.int(n, m, ...) over sample(1:n, m, ...)}
6 | \usage{
7 | sample_int_linter()
8 | }
9 | \description{
10 | \code{\link[=sample.int]{sample.int()}} is preferable to \code{sample()} for the case of sampling numbers
11 | between 1 and \code{n}. \code{sample} calls \code{sample.int()} "under the hood".
12 | }
13 | \examples{
14 | # will produce lints
15 | lint(
16 | text = "sample(1:10, 2)",
17 | linters = sample_int_linter()
18 | )
19 |
20 | lint(
21 | text = "sample(seq(4), 2)",
22 | linters = sample_int_linter()
23 | )
24 |
25 | lint(
26 | text = "sample(seq_len(8), 2)",
27 | linters = sample_int_linter()
28 | )
29 |
30 | # okay
31 | lint(
32 | text = "sample(seq(1, 5, by = 2), 2)",
33 | linters = sample_int_linter()
34 | )
35 |
36 | lint(
37 | text = "sample(letters, 2)",
38 | linters = sample_int_linter()
39 | )
40 |
41 | }
42 | \seealso{
43 | \link{linters} for a complete list of linters available in lintr.
44 | }
45 | \section{Tags}{
46 | \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability}, \link[=robustness_linters]{robustness}
47 | }
48 |
--------------------------------------------------------------------------------
/man/expect_s4_class_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/expect_s4_class_linter.R
3 | \name{expect_s4_class_linter}
4 | \alias{expect_s4_class_linter}
5 | \title{Require usage of \code{expect_s4_class(x, k)} over \code{expect_true(is(x, k))}}
6 | \usage{
7 | expect_s4_class_linter()
8 | }
9 | \description{
10 | \code{\link[testthat:inheritance-expectations]{testthat::expect_s4_class()}} exists specifically for testing the class
11 | of S4 objects. \code{\link[testthat:logical-expectations]{testthat::expect_true()}} can also be used for such tests,
12 | but it is better to use the tailored function instead.
13 | }
14 | \examples{
15 | # will produce lints
16 | lint(
17 | text = 'expect_true(is(x, "Matrix"))',
18 | linters = expect_s4_class_linter()
19 | )
20 |
21 | # okay
22 | lint(
23 | text = 'expect_s4_class(x, "Matrix")',
24 | linters = expect_s4_class_linter()
25 | )
26 |
27 | }
28 | \seealso{
29 | \itemize{
30 | \item \link{linters} for a complete list of linters available in lintr.
31 | \item \code{\link[=expect_s3_class_linter]{expect_s3_class_linter()}}
32 | }
33 | }
34 | \section{Tags}{
35 | \link[=best_practices_linters]{best_practices}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat}
36 | }
37 |
--------------------------------------------------------------------------------
/man/routine_registration_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/routine_registration_linter.R
3 | \name{routine_registration_linter}
4 | \alias{routine_registration_linter}
5 | \title{Identify unregistered native routines}
6 | \usage{
7 | routine_registration_linter()
8 | }
9 | \description{
10 | It is preferable to register routines for efficiency and safety.
11 | }
12 | \examples{
13 | # will produce lints
14 | lint(
15 | text = '.Call("cpp_routine", PACKAGE = "mypkg")',
16 | linters = routine_registration_linter()
17 | )
18 |
19 | lint(
20 | text = '.Fortran("f_routine", PACKAGE = "mypkg")',
21 | linters = routine_registration_linter()
22 | )
23 |
24 | # okay
25 | lint(
26 | text = ".Call(cpp_routine)",
27 | linters = routine_registration_linter()
28 | )
29 |
30 | lint(
31 | text = ".Fortran(f_routine)",
32 | linters = routine_registration_linter()
33 | )
34 |
35 | }
36 | \seealso{
37 | \itemize{
38 | \item \link{linters} for a complete list of linters available in lintr.
39 | \item \url{https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Registering-native-routines}
40 | }
41 | }
42 | \section{Tags}{
43 | \link[=best_practices_linters]{best_practices}, \link[=efficiency_linters]{efficiency}, \link[=robustness_linters]{robustness}
44 | }
45 |
--------------------------------------------------------------------------------
/R/routine_registration_linter.R:
--------------------------------------------------------------------------------
1 | #' Identify unregistered native routines
2 | #'
3 | #' It is preferable to register routines for efficiency and safety.
4 | #'
5 | #' @examples
6 | #' # will produce lints
7 | #' lint(
8 | #' text = '.Call("cpp_routine", PACKAGE = "mypkg")',
9 | #' linters = routine_registration_linter()
10 | #' )
11 | #'
12 | #' lint(
13 | #' text = '.Fortran("f_routine", PACKAGE = "mypkg")',
14 | #' linters = routine_registration_linter()
15 | #' )
16 | #'
17 | #' # okay
18 | #' lint(
19 | #' text = ".Call(cpp_routine)",
20 | #' linters = routine_registration_linter()
21 | #' )
22 | #'
23 | #' lint(
24 | #' text = ".Fortran(f_routine)",
25 | #' linters = routine_registration_linter()
26 | #' )
27 | #'
28 | #' @evalRd rd_tags("routine_registration_linter")
29 | #' @seealso
30 | #' - [linters] for a complete list of linters available in lintr.
31 | #' -
32 | #'
33 | #' @export
34 | routine_registration_linter <- make_linter_from_function_xpath(
35 | function_names = c(".C", ".Call", ".Fortran", ".External"),
36 | xpath = "
37 | following-sibling::expr[1]/STR_CONST
38 | /parent::expr
39 | ",
40 | lint_message = "Register your native code routines with useDynLib and R_registerRoutines()."
41 | )
42 |
--------------------------------------------------------------------------------
/R/stopifnot_all_linter.R:
--------------------------------------------------------------------------------
1 | #' Block usage of all() within stopifnot()
2 | #'
3 | #' `stopifnot(A)` actually checks `all(A)` "under the hood" if `A` is a vector,
4 | #' and produces a better error message than `stopifnot(all(A))` does.
5 | #'
6 | #' @examples
7 | #' # will produce lints
8 | #' lint(
9 | #' text = "stopifnot(all(x > 0))",
10 | #' linters = stopifnot_all_linter()
11 | #' )
12 | #'
13 | #' lint(
14 | #' text = "stopifnot(y > 3, all(x < 0))",
15 | #' linters = stopifnot_all_linter()
16 | #' )
17 | #'
18 | #' # okay
19 | #' lint(
20 | #' text = "stopifnot(is.null(x) || all(x > 0))",
21 | #' linters = stopifnot_all_linter()
22 | #' )
23 | #'
24 | #' lint(
25 | #' text = "assert_that(all(x > 0))",
26 | #' linters = stopifnot_all_linter()
27 | #' )
28 | #'
29 | #' @evalRd rd_tags("stopifnot_all_linter")
30 | #' @seealso [linters] for a complete list of linters available in lintr.
31 | #' @export
32 | stopifnot_all_linter <- make_linter_from_function_xpath(
33 | function_names = "stopifnot",
34 | xpath = "
35 | parent::expr
36 | /expr[expr/SYMBOL_FUNCTION_CALL[text() = 'all']]
37 | ",
38 | lint_message = paste(
39 | "Use stopifnot(x) instead of stopifnot(all(x)).",
40 | "stopifnot(x) runs all() 'under the hood' and provides a better error message in case of failure."
41 | )
42 | )
43 |
--------------------------------------------------------------------------------
/tests/testthat/default_linter_testcode.R:
--------------------------------------------------------------------------------
1 | # Each of the default linters should throw at least one lint on this file
2 |
3 | # assignment
4 | # function_left_parentheses
5 | # brace_linter
6 | # commas
7 | # paren_brace
8 | f = function (x,y = 1){}
9 |
10 | # return_linter
11 | g <- function(x) {
12 | return(x + 1)
13 | }
14 |
15 | # commented_code
16 | # some <- commented("out code")
17 |
18 | # equals_na
19 | # brace_linter
20 | # indentation
21 | # infix_spaces
22 | # line_length
23 | # object_length
24 | # object_name
25 | # object_usage
26 | # open_curly
27 | # T_and_F_symbol
28 | someComplicatedFunctionWithALongCamelCaseName <- function(x)
29 | {
30 | y <- 1
31 | if (1 > 2 && 2 > 3 && 3 > 4 && 4 > 5 && 5*10 > 6 && 5 > 6 && 6 > 7 && x == NA) {T} else F
32 | }
33 |
34 | # vector_logic
35 | if (1 & 2) FALSE else TRUE
36 |
37 | # function_brace
38 | my_metric <- function(x)
39 | sum(x) + prod(x)
40 |
41 | # no_tab
42 | # pipe_consistency
43 | # pipe_continuation
44 | # seq_linter
45 | # spaces_inside
46 | # indentation
47 | x <- 1:10
48 | x[ 2]
49 | 1:length(x) %>% lapply(function(x) x*2) %>%
50 | head()
51 |
52 | # single_quotes
53 | message('single_quotes')
54 |
55 | # spaces_left_parentheses
56 | # trailing_whitespace
57 | # semicolon
58 | x <- 42; y <- 2 +(1:10)
59 |
60 | # trailing_blank_lines
61 |
62 |
--------------------------------------------------------------------------------
/man/is_lint_level.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/is_lint_level.R
3 | \name{is_lint_level}
4 | \alias{is_lint_level}
5 | \title{Is this an expression- or a file-level source object?}
6 | \usage{
7 | is_lint_level(source_expression, level = c("expression", "file"))
8 | }
9 | \arguments{
10 | \item{source_expression}{A parsed expression object, i.e., an element
11 | of the object returned by \code{\link[=get_source_expressions]{get_source_expressions()}}.}
12 |
13 | \item{level}{Which level of expression is being tested? \code{"expression"}
14 | means an individual expression, while \code{"file"} means all expressions
15 | in the current file are available.}
16 | }
17 | \description{
18 | Helper for determining whether the current \code{source_expression} contains
19 | all expressions in the current file, or just a single expression.
20 | }
21 | \examples{
22 | tmp <- tempfile()
23 | writeLines(c("x <- 1", "y <- x + 1"), tmp)
24 | source_exprs <- get_source_expressions(tmp)
25 | is_lint_level(source_exprs$expressions[[1L]], level = "expression")
26 | is_lint_level(source_exprs$expressions[[1L]], level = "file")
27 | is_lint_level(source_exprs$expressions[[3L]], level = "expression")
28 | is_lint_level(source_exprs$expressions[[3L]], level = "file")
29 | unlink(tmp)
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/man/list2df_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/list2df_linter.R
3 | \name{list2df_linter}
4 | \alias{list2df_linter}
5 | \title{Recommend direct usage of \code{data.frame()} to create a data.frame from a list}
6 | \usage{
7 | list2df_linter()
8 | }
9 | \description{
10 | \code{\link[base:list2DF]{base::list2DF()}} is the preferred way to turn a list of columns into a data.frame.
11 | Note that it doesn't support recycling; if that's required, use \code{\link[=data.frame]{data.frame()}}.
12 | }
13 | \examples{
14 | # will produce lints
15 | lint(
16 | text = "do.call(cbind.data.frame, x)",
17 | linters = list2df_linter()
18 | )
19 |
20 | lint(
21 | text = "do.call('cbind.data.frame', x)",
22 | linters = list2df_linter()
23 | )
24 |
25 | lint(
26 | text = "do.call(cbind.data.frame, list(a = 1, b = 1:10))",
27 | linters = list2df_linter()
28 | )
29 |
30 | # okay
31 | lint(
32 | text = "list2df(x)",
33 | linters = list2df_linter()
34 | )
35 |
36 | lint(
37 | text = "data.frame(list(a = 1, b = 1:10))",
38 | linters = list2df_linter()
39 | )
40 |
41 | }
42 | \seealso{
43 | \link{linters} for a complete list of linters available in lintr.
44 | }
45 | \section{Tags}{
46 | \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability}
47 | }
48 |
--------------------------------------------------------------------------------
/man/terminal_close_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/terminal_close_linter.R
3 | \name{terminal_close_linter}
4 | \alias{terminal_close_linter}
5 | \title{Prohibit close() from terminating a function definition}
6 | \usage{
7 | terminal_close_linter()
8 | }
9 | \description{
10 | Functions that end in \code{close(x)} are almost always better written by using
11 | \code{on.exit(close(x))} close to where \code{x} is defined and/or opened.
12 | }
13 | \examples{
14 | # will produce lints
15 | code <- paste(
16 | "f <- function(fl) {",
17 | " conn <- file(fl, open = 'r')",
18 | " readLines(conn)",
19 | " close(conn)",
20 | "}",
21 | sep = "\n"
22 | )
23 | writeLines(code)
24 | lint(
25 | text = code,
26 | linters = terminal_close_linter()
27 | )
28 |
29 | # okay
30 | code <- paste(
31 | "f <- function(fl) {",
32 | " conn <- file(fl, open = 'r')",
33 | " on.exit(close(conn))",
34 | " readLines(conn)",
35 | "}",
36 | sep = "\n"
37 | )
38 | writeLines(code)
39 | lint(
40 | text = code,
41 | linters = terminal_close_linter()
42 | )
43 |
44 | }
45 | \seealso{
46 | \link{linters} for a complete list of linters available in lintr.
47 | }
48 | \section{Tags}{
49 | \link[=best_practices_linters]{best_practices}, \link[=robustness_linters]{robustness}
50 | }
51 |
--------------------------------------------------------------------------------
/man/absolute_path_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/absolute_path_linter.R
3 | \name{absolute_path_linter}
4 | \alias{absolute_path_linter}
5 | \title{Absolute path linter}
6 | \usage{
7 | absolute_path_linter(lax = TRUE)
8 | }
9 | \arguments{
10 | \item{lax}{Less stringent linting, leading to fewer false positives.
11 | If \code{TRUE}, only lint path strings, which
12 | \itemize{
13 | \item contain at least two path elements, with one having at least two characters and
14 | \item contain only alphanumeric chars (including UTF-8), spaces, and win32-allowed punctuation
15 | }}
16 | }
17 | \description{
18 | Check that no absolute paths are used (e.g. "/var", "C:\\System", "~/docs").
19 | }
20 | \examples{
21 | # will produce lints
22 | lint(
23 | text = 'R"(/blah/file.txt)"',
24 | linters = absolute_path_linter()
25 | )
26 |
27 | # okay
28 | lint(
29 | text = 'R"(./blah)"',
30 | linters = absolute_path_linter()
31 | )
32 |
33 | }
34 | \seealso{
35 | \itemize{
36 | \item \link{linters} for a complete list of linters available in lintr.
37 | \item \code{\link[=nonportable_path_linter]{nonportable_path_linter()}}
38 | }
39 | }
40 | \section{Tags}{
41 | \link[=best_practices_linters]{best_practices}, \link[=configurable_linters]{configurable}, \link[=robustness_linters]{robustness}
42 | }
43 |
--------------------------------------------------------------------------------
/tests/testthat/dummy_packages/package/vignettes/test.Rmd:
--------------------------------------------------------------------------------
1 | # Test #
2 |
3 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
4 | tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
5 | vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
6 | no sea takimata sanctus est Lorem ipsum dolor sit amet.
7 |
8 | ```{r}
9 | a = 1
10 | ```
11 |
12 | Test
13 | ====
14 |
15 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
16 | tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
17 | vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
18 | no sea takimata sanctus est Lorem ipsum dolor sit amet.
19 |
20 | ```{r}
21 | b <- function(x) {
22 | d = 1
23 | }
24 |
25 | ```
26 |
27 | ```{r engine="python"}
28 | a=[]
29 |
30 | a[0]=1
31 | ```
32 |
33 | ```
34 | Plain code blocks can be written after three or more backticks
35 | - R Markdown: The Definitive Guide. Xie, Allaire and Grolemund (2.5.2)
36 | ```
37 |
38 | Calls to a non-R knitr-engine using {engine_name} syntax.
39 |
40 | ```{python}
41 | # Python that looks like R
42 | a = list()
43 | b = {2}
44 | print(a)
45 | ```
46 |
47 | ```{python}
48 | # Python that's definitely not R
49 | a = []
50 | a.append(2)
51 | print(a)
52 | ```
53 |
--------------------------------------------------------------------------------
/man/consecutive_assertion_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/consecutive_assertion_linter.R
3 | \name{consecutive_assertion_linter}
4 | \alias{consecutive_assertion_linter}
5 | \title{Force consecutive calls to assertions into just one when possible}
6 | \usage{
7 | consecutive_assertion_linter()
8 | }
9 | \description{
10 | \code{\link[base:stopifnot]{base::stopifnot()}} accepts any number of tests, so sequences like
11 | \verb{stopifnot(x); stopifnot(y)} are redundant. Ditto for tests using
12 | \code{assertthat::assert_that()} without specifying \verb{msg=}.
13 | }
14 | \examples{
15 | # will produce lints
16 | lint(
17 | text = "stopifnot(x); stopifnot(y)",
18 | linters = consecutive_assertion_linter()
19 | )
20 |
21 | lint(
22 | text = "assert_that(x); assert_that(y)",
23 | linters = consecutive_assertion_linter()
24 | )
25 |
26 | # okay
27 | lint(
28 | text = "stopifnot(x, y)",
29 | linters = consecutive_assertion_linter()
30 | )
31 |
32 | lint(
33 | text = 'assert_that(x, msg = "Bad x!"); assert_that(y)',
34 | linters = consecutive_assertion_linter()
35 | )
36 |
37 | }
38 | \seealso{
39 | \link{linters} for a complete list of linters available in lintr.
40 | }
41 | \section{Tags}{
42 | \link[=consistency_linters]{consistency}, \link[=readability_linters]{readability}, \link[=style_linters]{style}
43 | }
44 |
--------------------------------------------------------------------------------
/man/missing_argument_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/missing_argument_linter.R
3 | \name{missing_argument_linter}
4 | \alias{missing_argument_linter}
5 | \title{Missing argument linter}
6 | \usage{
7 | missing_argument_linter(
8 | except = c("alist", "quote", "switch"),
9 | allow_trailing = FALSE
10 | )
11 | }
12 | \arguments{
13 | \item{except}{a character vector of function names as exceptions.}
14 |
15 | \item{allow_trailing}{always allow trailing empty arguments?}
16 | }
17 | \description{
18 | Check for missing arguments in function calls (e.g. \code{stats::median(1:10, )}).
19 | }
20 | \examples{
21 | # will produce lints
22 | lint(
23 | text = 'tibble(x = "a", )',
24 | linters = missing_argument_linter()
25 | )
26 |
27 | # okay
28 | lint(
29 | text = 'tibble(x = "a")',
30 | linters = missing_argument_linter()
31 | )
32 |
33 | lint(
34 | text = 'tibble(x = "a", )',
35 | linters = missing_argument_linter(except = "tibble")
36 | )
37 |
38 | lint(
39 | text = 'tibble(x = "a", )',
40 | linters = missing_argument_linter(allow_trailing = TRUE)
41 | )
42 |
43 | }
44 | \seealso{
45 | \link{linters} for a complete list of linters available in lintr.
46 | }
47 | \section{Tags}{
48 | \link[=common_mistakes_linters]{common_mistakes}, \link[=configurable_linters]{configurable}, \link[=correctness_linters]{correctness}
49 | }
50 |
--------------------------------------------------------------------------------
/man/use_lintr.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/use_lintr.R
3 | \name{use_lintr}
4 | \alias{use_lintr}
5 | \title{Use lintr in your project}
6 | \usage{
7 | use_lintr(path = ".", type = c("tidyverse", "full"))
8 | }
9 | \arguments{
10 | \item{path}{Path to project root, where a \code{.lintr} file should be created.
11 | If the \code{.lintr} file already exists, an error will be thrown.}
12 |
13 | \item{type}{What kind of configuration to create?
14 | \itemize{
15 | \item \code{tidyverse} creates a minimal lintr config, based on the default linters (\code{\link[=linters_with_defaults]{linters_with_defaults()}}).
16 | These are suitable for following \href{https://style.tidyverse.org/}{the tidyverse style guide}.
17 | \item \code{full} creates a lintr config using all available linters via \code{\link[=all_linters]{all_linters()}}.
18 | }}
19 | }
20 | \value{
21 | Path to the generated configuration, invisibly.
22 | }
23 | \description{
24 | Create a minimal lintr config file as a starting point for customization
25 | }
26 | \examples{
27 | if (FALSE) {
28 | # use the default set of linters
29 | lintr::use_lintr()
30 | # or try all linters
31 | lintr::use_lintr(type = "full")
32 |
33 | # then
34 | lintr::lint_dir()
35 | }
36 | }
37 | \seealso{
38 | \code{vignette("lintr")} for detailed introduction to using and configuring lintr.
39 | }
40 |
--------------------------------------------------------------------------------
/man/ifelse_censor_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/ifelse_censor_linter.R
3 | \name{ifelse_censor_linter}
4 | \alias{ifelse_censor_linter}
5 | \title{Block usage of \code{ifelse()} where \code{pmin()} or \code{pmax()} is more appropriate}
6 | \usage{
7 | ifelse_censor_linter()
8 | }
9 | \description{
10 | \code{ifelse(x > M, M, x)} is the same as \code{pmin(x, M)}, but harder
11 | to read and requires several passes over the vector.
12 | }
13 | \details{
14 | The same goes for other similar ways to censor a vector, e.g.
15 | \code{ifelse(x <= M, x, M)} is \code{pmin(x, M)},
16 | \code{ifelse(x < m, m, x)} is \code{pmax(x, m)}, and
17 | \code{ifelse(x >= m, x, m)} is \code{pmax(x, m)}.
18 | }
19 | \examples{
20 | # will produce lints
21 | lint(
22 | text = "ifelse(5:1 < pi, 5:1, pi)",
23 | linters = ifelse_censor_linter()
24 | )
25 |
26 | lint(
27 | text = "ifelse(x > 0, x, 0)",
28 | linters = ifelse_censor_linter()
29 | )
30 |
31 | # okay
32 | lint(
33 | text = "pmin(5:1, pi)",
34 | linters = ifelse_censor_linter()
35 | )
36 |
37 | lint(
38 | text = "pmax(x, 0)",
39 | linters = ifelse_censor_linter()
40 | )
41 |
42 | }
43 | \seealso{
44 | \link{linters} for a complete list of linters available in lintr.
45 | }
46 | \section{Tags}{
47 | \link[=best_practices_linters]{best_practices}, \link[=efficiency_linters]{efficiency}
48 | }
49 |
--------------------------------------------------------------------------------
/man/yoda_test_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/yoda_test_linter.R
3 | \name{yoda_test_linter}
4 | \alias{yoda_test_linter}
5 | \title{Block obvious "yoda tests"}
6 | \usage{
7 | yoda_test_linter()
8 | }
9 | \description{
10 | Yoda tests use \verb{(expected, actual)} instead of the more common \verb{(actual, expected)}.
11 | This is not always possible to detect statically; this linter focuses on
12 | the simple case of testing an expression against a literal value, e.g.
13 | \verb{(1L, foo(x))} should be \verb{(foo(x), 1L)}.
14 | }
15 | \examples{
16 | # will produce lints
17 | lint(
18 | text = "expect_equal(2, x)",
19 | linters = yoda_test_linter()
20 | )
21 |
22 | lint(
23 | text = 'expect_identical("a", x)',
24 | linters = yoda_test_linter()
25 | )
26 |
27 | # okay
28 | lint(
29 | text = "expect_equal(x, 2)",
30 | linters = yoda_test_linter()
31 | )
32 |
33 | lint(
34 | text = 'expect_identical(x, "a")',
35 | linters = yoda_test_linter()
36 | )
37 |
38 | }
39 | \seealso{
40 | \link{linters} for a complete list of linters available in lintr.
41 | \url{https://en.wikipedia.org/wiki/Yoda_conditions}
42 | }
43 | \section{Tags}{
44 | \link[=best_practices_linters]{best_practices}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat}, \link[=readability_linters]{readability}
45 | }
46 |
--------------------------------------------------------------------------------
/man/robustness_linters.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/linter_tag_docs.R
3 | \name{robustness_linters}
4 | \alias{robustness_linters}
5 | \title{Robustness linters}
6 | \description{
7 | Linters highlighting code robustness issues, such as possibly wrong edge case behavior.
8 | }
9 | \seealso{
10 | \link{linters} for a complete list of linters available in lintr.
11 | }
12 | \section{Linters}{
13 | The following linters are tagged with 'robustness':
14 | \itemize{
15 | \item{\code{\link{absolute_path_linter}}}
16 | \item{\code{\link{all_equal_linter}}}
17 | \item{\code{\link{backport_linter}}}
18 | \item{\code{\link{class_equals_linter}}}
19 | \item{\code{\link{download_file_linter}}}
20 | \item{\code{\link{equals_na_linter}}}
21 | \item{\code{\link{for_loop_index_linter}}}
22 | \item{\code{\link{missing_package_linter}}}
23 | \item{\code{\link{namespace_linter}}}
24 | \item{\code{\link{nonportable_path_linter}}}
25 | \item{\code{\link{object_overwrite_linter}}}
26 | \item{\code{\link{routine_registration_linter}}}
27 | \item{\code{\link{sample_int_linter}}}
28 | \item{\code{\link{seq_linter}}}
29 | \item{\code{\link{strings_as_factors_linter}}}
30 | \item{\code{\link{T_and_F_symbol_linter}}}
31 | \item{\code{\link{terminal_close_linter}}}
32 | \item{\code{\link{undesirable_function_linter}}}
33 | \item{\code{\link{undesirable_operator_linter}}}
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/man/unnecessary_placeholder_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/unnecessary_placeholder_linter.R
3 | \name{unnecessary_placeholder_linter}
4 | \alias{unnecessary_placeholder_linter}
5 | \title{Block usage of pipeline placeholders if unnecessary}
6 | \usage{
7 | unnecessary_placeholder_linter()
8 | }
9 | \description{
10 | The argument placeholder \code{.} in magrittr pipelines is unnecessary if
11 | passed as the first positional argument; using it can cause confusion
12 | and impacts readability.
13 | }
14 | \details{
15 | This is true for forward (\verb{\%>\%}), assignment (\verb{\%<>\%}), and tee (\verb{\%T>\%}) operators.
16 | }
17 | \examples{
18 | # will produce lints
19 | lint(
20 | text = "x \%>\% sum(., na.rm = TRUE)",
21 | linters = unnecessary_placeholder_linter()
22 | )
23 |
24 | # okay
25 | lint(
26 | text = "x \%>\% sum(na.rm = TRUE)",
27 | linters = unnecessary_placeholder_linter()
28 | )
29 |
30 | lint(
31 | text = "x \%>\% lm(data = ., y ~ z)",
32 | linters = unnecessary_placeholder_linter()
33 | )
34 |
35 | lint(
36 | text = "x \%>\% outer(., .)",
37 | linters = unnecessary_placeholder_linter()
38 | )
39 |
40 | }
41 | \seealso{
42 | \link{linters} for a complete list of linters available in lintr.
43 | }
44 | \section{Tags}{
45 | \link[=best_practices_linters]{best_practices}, \link[=readability_linters]{readability}
46 | }
47 |
--------------------------------------------------------------------------------
/man/pipe_return_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipe_return_linter.R
3 | \name{pipe_return_linter}
4 | \alias{pipe_return_linter}
5 | \title{Block usage of return() in magrittr pipelines}
6 | \usage{
7 | pipe_return_linter()
8 | }
9 | \description{
10 | \code{\link[=return]{return()}} inside a magrittr pipeline does not actually execute \code{return()}
11 | like you'd expect:
12 | }
13 | \details{
14 | \if{html}{\out{}}\preformatted{bad_usage <- function(x) \{
15 | x \%>\%
16 | return()
17 | FALSE
18 | \}
19 | }\if{html}{\out{
}}
20 |
21 | \code{bad_usage(TRUE)} will return \code{FALSE}! It will technically work "as expected"
22 | if this is the final statement in the function body, but such usage is misleading.
23 | Instead, assign the pipe outcome to a variable and return that.
24 | }
25 | \examples{
26 | # will produce lints
27 | lint(
28 | text = "function(x) x \%>\% return()",
29 | linters = pipe_return_linter()
30 | )
31 |
32 | # okay
33 | code <- "function(x) {\n y <- sum(x)\n return(y)\n}"
34 | writeLines(code)
35 | lint(
36 | text = code,
37 | linters = pipe_return_linter()
38 | )
39 |
40 | }
41 | \seealso{
42 | \link{linters} for a complete list of linters available in lintr.
43 | }
44 | \section{Tags}{
45 | \link[=best_practices_linters]{best_practices}, \link[=common_mistakes_linters]{common_mistakes}
46 | }
47 |
--------------------------------------------------------------------------------
/man/quotes_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/quotes_linter.R
3 | \name{quotes_linter}
4 | \alias{quotes_linter}
5 | \title{Character string quote linter}
6 | \usage{
7 | quotes_linter(delimiter = c("\\"", "'"))
8 | }
9 | \arguments{
10 | \item{delimiter}{Which quote delimiter to accept. Defaults to the tidyverse
11 | default of \verb{"} (double-quoted strings).}
12 | }
13 | \description{
14 | Check that the desired quote delimiter is used for string constants.
15 | }
16 | \examples{
17 | # will produce lints
18 | lint(
19 | text = "c('a', 'b')",
20 | linters = quotes_linter()
21 | )
22 |
23 | # okay
24 | lint(
25 | text = 'c("a", "b")',
26 | linters = quotes_linter()
27 | )
28 |
29 | code_lines <- "paste0(x, '\"this is fine\"')"
30 | writeLines(code_lines)
31 | lint(
32 | text = code_lines,
33 | linters = quotes_linter()
34 | )
35 |
36 | # okay
37 | lint(
38 | text = "c('a', 'b')",
39 | linters = quotes_linter(delimiter = "'")
40 | )
41 |
42 | }
43 | \seealso{
44 | \itemize{
45 | \item \link{linters} for a complete list of linters available in lintr.
46 | \item \url{https://style.tidyverse.org/syntax.html#character-vectors}
47 | }
48 | }
49 | \section{Tags}{
50 | \link[=configurable_linters]{configurable}, \link[=consistency_linters]{consistency}, \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style}
51 | }
52 |
--------------------------------------------------------------------------------
/man/nonportable_path_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/nonportable_path_linter.R
3 | \name{nonportable_path_linter}
4 | \alias{nonportable_path_linter}
5 | \title{Non-portable path linter}
6 | \usage{
7 | nonportable_path_linter(lax = TRUE)
8 | }
9 | \arguments{
10 | \item{lax}{Less stringent linting, leading to fewer false positives.
11 | If \code{TRUE}, only lint path strings, which
12 | \itemize{
13 | \item contain at least two path elements, with one having at least two characters and
14 | \item contain only alphanumeric chars (including UTF-8), spaces, and win32-allowed punctuation
15 | }}
16 | }
17 | \description{
18 | Check that \code{\link[=file.path]{file.path()}} is used to construct safe and portable paths.
19 | }
20 | \examples{
21 | # will produce lints
22 | lint(
23 | text = "'abcdefg/hijklmnop/qrst/uv/wxyz'",
24 | linters = nonportable_path_linter()
25 | )
26 |
27 | # okay
28 | lint(
29 | text = "file.path('abcdefg', 'hijklmnop', 'qrst', 'uv', 'wxyz')",
30 | linters = nonportable_path_linter()
31 | )
32 |
33 | }
34 | \seealso{
35 | \itemize{
36 | \item \link{linters} for a complete list of linters available in lintr.
37 | \item \code{\link[=absolute_path_linter]{absolute_path_linter()}}
38 | }
39 | }
40 | \section{Tags}{
41 | \link[=best_practices_linters]{best_practices}, \link[=configurable_linters]{configurable}, \link[=robustness_linters]{robustness}
42 | }
43 |
--------------------------------------------------------------------------------
/.github/workflows/check-all-examples.yaml:
--------------------------------------------------------------------------------
1 | # Make sure all examples run successfully, even the ones that are not supposed
2 | # to be run or tested on CRAN machines by default.
3 | #
4 | # The examples that fail should use
5 | # - `if (FALSE) { ... }` (if example is included only for illustrative purposes)
6 | # - `try({ ... })` (if the intent is to show the error)
7 | #
8 | # This workflow helps find such failing examples that need to be modified.
9 |
10 | on:
11 | push:
12 | branches: main
13 | pull_request:
14 | branches: main
15 |
16 | name: check-all-examples
17 |
18 | jobs:
19 | check-all-examples:
20 | runs-on: ubuntu-latest
21 | env:
22 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
23 |
24 | steps:
25 | - uses: actions/checkout@v6
26 |
27 | - uses: r-lib/actions/setup-r@v2
28 | with:
29 | use-public-rspm: true
30 |
31 | - uses: r-lib/actions/setup-r-dependencies@v2
32 | with:
33 | install-quarto: false
34 | pak-version: devel
35 | extra-packages: |
36 | any::pkgload
37 | local::.
38 |
39 | - name: Run examples
40 | run: |
41 | options(crayon.enabled = TRUE)
42 | pkgload::load_all()
43 | setwd("man")
44 | for (rd in list.files(pattern = "\\.Rd")) pkgload::run_example(rd, run_dontrun = TRUE, run_donttest = TRUE, quiet = TRUE)
45 | shell: Rscript {0}
46 |
--------------------------------------------------------------------------------
/man/T_and_F_symbol_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/T_and_F_symbol_linter.R
3 | \name{T_and_F_symbol_linter}
4 | \alias{T_and_F_symbol_linter}
5 | \title{\code{T} and \code{F} symbol linter}
6 | \usage{
7 | T_and_F_symbol_linter()
8 | }
9 | \description{
10 | Although they can be synonyms, avoid the symbols \code{T} and \code{F}, and use \code{TRUE} and \code{FALSE}, respectively, instead.
11 | \code{T} and \code{F} are not reserved keywords and can be assigned to any other values.
12 | }
13 | \examples{
14 | # will produce lints
15 | lint(
16 | text = "x <- T; y <- F",
17 | linters = T_and_F_symbol_linter()
18 | )
19 |
20 | lint(
21 | text = "T = 1.2; F = 2.4",
22 | linters = T_and_F_symbol_linter()
23 | )
24 |
25 | # okay
26 | lint(
27 | text = "x <- c(TRUE, FALSE)",
28 | linters = T_and_F_symbol_linter()
29 | )
30 |
31 | lint(
32 | text = "t = 1.2; f = 2.4",
33 | linters = T_and_F_symbol_linter()
34 | )
35 |
36 | }
37 | \seealso{
38 | \itemize{
39 | \item \link{linters} for a complete list of linters available in lintr.
40 | \item \url{https://style.tidyverse.org/syntax.html#logical-vectors}
41 | }
42 | }
43 | \section{Tags}{
44 | \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=robustness_linters]{robustness}, \link[=style_linters]{style}
45 | }
46 |
--------------------------------------------------------------------------------
/man/coalesce_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/coalesce_linter.R
3 | \name{coalesce_linter}
4 | \alias{coalesce_linter}
5 | \title{Encourage usage of the null coalescing operator \verb{\%||\%}}
6 | \usage{
7 | coalesce_linter()
8 | }
9 | \description{
10 | The \code{x \%||\% y} is equivalent to
11 | \code{if (is.null(x)) y else x}, but more expressive.
12 | It is exported by R since 4.4.0, and equivalents
13 | have been available in other tidyverse packages
14 | for much longer, e.g. 2008 for ggplot2.
15 | }
16 | \examples{
17 | # will produce lints
18 | lint(
19 | text = "if (is.null(x)) y else x",
20 | linters = coalesce_linter()
21 | )
22 |
23 | lint(
24 | text = "if (!is.null(x)) x else y",
25 | linters = coalesce_linter()
26 | )
27 |
28 | lint(
29 | text = "if (is.null(x[1])) x[2] else x[1]",
30 | linters = coalesce_linter()
31 | )
32 |
33 | # okay
34 | lint(
35 | text = "x \%||\% y",
36 | linters = coalesce_linter()
37 | )
38 |
39 | lint(
40 | text = "x \%||\% y",
41 | linters = coalesce_linter()
42 | )
43 |
44 | lint(
45 | text = "x[1] \%||\% x[2]",
46 | linters = coalesce_linter()
47 | )
48 |
49 |
50 | }
51 | \seealso{
52 | \link{linters} for a complete list of linters available in lintr.
53 | }
54 | \section{Tags}{
55 | \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=readability_linters]{readability}
56 | }
57 |
--------------------------------------------------------------------------------
/man/expect_named_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/expect_named_linter.R
3 | \name{expect_named_linter}
4 | \alias{expect_named_linter}
5 | \title{Require usage of \code{expect_named(x, n)} over \code{expect_equal(names(x), n)}}
6 | \usage{
7 | expect_named_linter()
8 | }
9 | \description{
10 | \code{\link[testthat:expect_named]{testthat::expect_named()}} exists specifically for testing the \code{\link[=names]{names()}} of
11 | an object. \code{\link[testthat:equality-expectations]{testthat::expect_equal()}} can also be used for such tests,
12 | but it is better to use the tailored function instead.
13 | }
14 | \examples{
15 | # will produce lints
16 | lint(
17 | text = 'expect_equal(names(x), "a")',
18 | linters = expect_named_linter()
19 | )
20 |
21 | # okay
22 | lint(
23 | text = 'expect_named(x, "a")',
24 | linters = expect_named_linter()
25 | )
26 |
27 | lint(
28 | text = 'expect_equal(colnames(x), "a")',
29 | linters = expect_named_linter()
30 | )
31 |
32 | lint(
33 | text = 'expect_equal(dimnames(x), "a")',
34 | linters = expect_named_linter()
35 | )
36 |
37 | }
38 | \seealso{
39 | \link{linters} for a complete list of linters available in lintr.
40 | }
41 | \section{Tags}{
42 | \link[=best_practices_linters]{best_practices}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat}, \link[=readability_linters]{readability}
43 | }
44 |
--------------------------------------------------------------------------------
/R/empty_assignment_linter.R:
--------------------------------------------------------------------------------
1 | #' Block assignment of `{}`
2 | #'
3 | #' Assignment of `{}` is the same as assignment of `NULL`; use the latter
4 | #' for clarity. Closely related: [unnecessary_concatenation_linter()].
5 | #'
6 | #' @examples
7 | #' # will produce lints
8 | #' lint(
9 | #' text = "x <- {}",
10 | #' linters = empty_assignment_linter()
11 | #' )
12 | #'
13 | #' writeLines("x = {\n}")
14 | #' lint(
15 | #' text = "x = {\n}",
16 | #' linters = empty_assignment_linter()
17 | #' )
18 | #'
19 | #' # okay
20 | #' lint(
21 | #' text = "x <- { 3 + 4 }",
22 | #' linters = empty_assignment_linter()
23 | #' )
24 | #'
25 | #' lint(
26 | #' text = "x <- NULL",
27 | #' linters = empty_assignment_linter()
28 | #' )
29 | #'
30 | #' @evalRd rd_tags("empty_assignment_linter")
31 | #' @seealso [linters] for a complete list of linters available in lintr.
32 | #' @export
33 | empty_assignment_linter <- make_linter_from_xpath(
34 | # for some reason, the parent in the `=` case is , not , hence parent::expr
35 | xpath = "
36 | //OP-LEFT-BRACE[following-sibling::*[not(self::COMMENT)][1][self::OP-RIGHT-BRACE]]
37 | /parent::expr[
38 | preceding-sibling::LEFT_ASSIGN
39 | or preceding-sibling::EQ_ASSIGN
40 | or following-sibling::RIGHT_ASSIGN
41 | ]
42 | /parent::*
43 | ",
44 | lint_message = "Assign NULL explicitly or, whenever possible, allocate the empty object with the right type and size."
45 | )
46 |
--------------------------------------------------------------------------------
/man/expect_type_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/expect_type_linter.R
3 | \name{expect_type_linter}
4 | \alias{expect_type_linter}
5 | \title{Require usage of \code{expect_type(x, type)} over \code{expect_equal(typeof(x), type)}}
6 | \usage{
7 | expect_type_linter()
8 | }
9 | \description{
10 | \code{\link[testthat:inheritance-expectations]{testthat::expect_type()}} exists specifically for testing the storage type
11 | of objects. \code{\link[testthat:equality-expectations]{testthat::expect_equal()}}, \code{\link[testthat:equality-expectations]{testthat::expect_identical()}}, and
12 | \code{\link[testthat:logical-expectations]{testthat::expect_true()}} can also be used for such tests,
13 | but it is better to use the tailored function instead.
14 | }
15 | \examples{
16 | # will produce lints
17 | lint(
18 | text = 'expect_equal(typeof(x), "double")',
19 | linters = expect_type_linter()
20 | )
21 |
22 | lint(
23 | text = 'expect_identical(typeof(x), "double")',
24 | linters = expect_type_linter()
25 | )
26 |
27 | # okay
28 | lint(
29 | text = 'expect_type(x, "double")',
30 | linters = expect_type_linter()
31 | )
32 |
33 | }
34 | \seealso{
35 | \link{linters} for a complete list of linters available in lintr.
36 | }
37 | \section{Tags}{
38 | \link[=best_practices_linters]{best_practices}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat}
39 | }
40 |
--------------------------------------------------------------------------------
/tests/testthat/test-sarif_output.R:
--------------------------------------------------------------------------------
1 | test_that("`sarif_output` produces expected error", {
2 | skip_if_not_installed("jsonlite")
3 |
4 | l <- lint(text = "x = 1", linters = assignment_linter())
5 | expect_error(sarif_output(l), "Package path needs to be a relative path", fixed = TRUE)
6 | })
7 |
8 | test_that("`sarif_output` writes expected files", {
9 | skip_if_not_installed("jsonlite")
10 |
11 | l <- lint_package(
12 | test_path("dummy_packages", "missing_dep"),
13 | linters = object_length_linter(),
14 | parse_settings = FALSE
15 | )
16 |
17 | withr::with_tempdir({
18 | sarif_output(l)
19 | expect_true(file.exists("lintr_results.sarif"))
20 | })
21 |
22 | withr::with_tempdir({
23 | sarif_output(l, filename = "myfile.sarif")
24 | expect_true(file.exists("myfile.sarif"))
25 | })
26 | })
27 |
28 | test_that("`sarif_output` produces valid files", {
29 | skip_if_not_installed("jsonlite")
30 |
31 | l <- lint_package(
32 | test_path("dummy_packages", "clean"),
33 | linters = default_linters,
34 | parse_settings = FALSE
35 | )
36 |
37 | withr::with_tempdir({
38 | sarif <- sarif_output(l)
39 | sarif <- jsonlite::fromJSON(
40 | "lintr_results.sarif",
41 | simplifyVector = TRUE,
42 | simplifyDataFrame = FALSE,
43 | simplifyMatrix = FALSE
44 | )
45 |
46 | expect_false(is.null(sarif$runs))
47 | expect_false(is.null(sarif$runs[[1L]]$results))
48 | })
49 | })
50 |
--------------------------------------------------------------------------------
/man/nrow_subset_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/nrow_subset_linter.R
3 | \name{nrow_subset_linter}
4 | \alias{nrow_subset_linter}
5 | \title{Block usage of \code{nrow(subset(x, .))}}
6 | \usage{
7 | nrow_subset_linter()
8 | }
9 | \description{
10 | Using \code{nrow(subset(x, condition))} to count the instances where \code{condition}
11 | applies inefficiently requires doing a full subset of \code{x} just to
12 | count the number of rows in the resulting subset.
13 | There are a number of equivalent expressions that don't require the full
14 | subset, e.g. \code{with(x, sum(condition))} (or, more generically,
15 | \code{with(x, sum(condition, na.rm = TRUE))}).
16 | }
17 | \examples{
18 | # will produce lints
19 | lint(
20 | text = "nrow(subset(x, is_treatment))",
21 | linters = nrow_subset_linter()
22 | )
23 |
24 | lint(
25 | text = "nrow(filter(x, is_treatment))",
26 | linters = nrow_subset_linter()
27 | )
28 |
29 | lint(
30 | text = "x \%>\% filter(x, is_treatment) \%>\% nrow()",
31 | linters = nrow_subset_linter()
32 | )
33 |
34 | # okay
35 | lint(
36 | text = "with(x, sum(is_treatment, na.rm = TRUE))",
37 | linters = nrow_subset_linter()
38 | )
39 |
40 | }
41 | \seealso{
42 | \link{linters} for a complete list of linters available in lintr.
43 | }
44 | \section{Tags}{
45 | \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=efficiency_linters]{efficiency}
46 | }
47 |
--------------------------------------------------------------------------------
/R/ids_with_token.R:
--------------------------------------------------------------------------------
1 | #' Get parsed IDs by token
2 | #'
3 | #' Gets the source IDs (row indices) corresponding to given token.
4 | #'
5 | #' @param source_expression A list of source expressions, the result of a call to [get_source_expressions()],
6 | #' for the desired filename.
7 | #' @param value Character. String corresponding to the token to search for.
8 | #' For example:
9 | #'
10 | #' * "SYMBOL"
11 | #' * "FUNCTION"
12 | #' * "EQ_FORMALS"
13 | #' * "$"
14 | #' * "("
15 | #'
16 | #' @param fun For additional flexibility, a function to search for in
17 | #' the `token` column of `parsed_content`. Typically `==` or `%in%`.
18 | #'
19 | #' @examples
20 | #' tmp <- tempfile()
21 | #' writeLines(c("x <- 1", "y <- x + 1"), tmp)
22 | #' source_exprs <- get_source_expressions(tmp)
23 | #' ids_with_token(source_exprs$expressions[[1L]], value = "SYMBOL")
24 | #' with_id(source_exprs$expressions[[1L]], 2L)
25 | #' unlink(tmp)
26 | #'
27 | #' @return `ids_with_token`: The indices of the `parsed_content` data frame
28 | #' entry of the list of source expressions. Indices correspond to the
29 | #' *rows* where `fun` evaluates to `TRUE` for the `value` in the *token* column.
30 | #' @export
31 | ids_with_token <- function(source_expression, value, fun = `==`) {
32 | if (!is_lint_level(source_expression, "expression")) {
33 | return(integer())
34 | }
35 | loc <- which(fun(source_expression$parsed_content$token, value))
36 | if (loc %==% integer()) {
37 | return(integer())
38 | }
39 | loc
40 | }
41 |
--------------------------------------------------------------------------------
/tests/testthat/test-expect_not_linter.R:
--------------------------------------------------------------------------------
1 | test_that("expect_not_linter skips allowed usages", {
2 | expect_lint("expect_true(x)", NULL, expect_not_linter())
3 | # NB: also applies to tinytest, but it's sufficient to test testthat
4 | expect_lint("testthat::expect_true(x)", NULL, expect_not_linter())
5 | expect_lint("expect_false(x)", NULL, expect_not_linter())
6 | expect_lint("testthat::expect_false(x)", NULL, expect_not_linter())
7 |
8 | # not a strict ban on !
9 | ## (expect_false(x && y) is the same, but it's not clear which to prefer)
10 | expect_lint("expect_true(!x || !y)", NULL, expect_not_linter())
11 | })
12 |
13 | test_that("expect_not_linter blocks simple disallowed usages", {
14 | linter <- expect_not_linter()
15 | lint_msg <- rex::rex("expect_false(x) is better than expect_true(!x), and vice versa.")
16 |
17 | expect_lint("expect_true(!x)", lint_msg, linter)
18 | expect_lint("testthat::expect_true(!x)", lint_msg, linter)
19 | expect_lint("expect_false(!foo(x))", lint_msg, linter)
20 | expect_lint("testthat::expect_true(!(x && y))", lint_msg, linter)
21 | })
22 |
23 | test_that("lints vectorize", {
24 | lint_msg <- rex::rex("expect_false(x) is better than expect_true(!x), and vice versa.")
25 |
26 | expect_lint(
27 | trim_some("{
28 | expect_true(!x)
29 | expect_false(!y)
30 | }"),
31 | list(
32 | list(lint_msg, line_number = 2L),
33 | list(lint_msg, line_number = 3L)
34 | ),
35 | expect_not_linter()
36 | )
37 | })
38 |
--------------------------------------------------------------------------------
/.github/workflows/pkgdown.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main, master]
6 | pull_request:
7 | branches: [main, master]
8 | release:
9 | types: [published]
10 | workflow_dispatch:
11 |
12 | name: pkgdown
13 |
14 | jobs:
15 | pkgdown:
16 | runs-on: ubuntu-latest
17 | # Only restrict concurrency for non-PR jobs
18 | concurrency:
19 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
20 | env:
21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
22 | steps:
23 | - uses: actions/checkout@v6
24 |
25 | - uses: r-lib/actions/setup-pandoc@v2
26 |
27 | - uses: r-lib/actions/setup-r@v2
28 | with:
29 | use-public-rspm: true
30 |
31 | - uses: r-lib/actions/setup-r-dependencies@v2
32 | with:
33 | install-quarto: false
34 | extra-packages: any::pkgdown, local::.
35 | needs: website
36 |
37 | - name: Build site
38 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
39 | shell: Rscript {0}
40 |
41 | - name: Deploy to GitHub pages 🚀
42 | if: github.event_name != 'pull_request'
43 | uses: JamesIves/github-pages-deploy-action@v4.7.6
44 | with:
45 | clean: false
46 | branch: gh-pages
47 | folder: docs
48 |
--------------------------------------------------------------------------------
/man/is_numeric_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/is_numeric_linter.R
3 | \name{is_numeric_linter}
4 | \alias{is_numeric_linter}
5 | \title{Redirect \code{is.numeric(x) || is.integer(x)} to just use \code{is.numeric(x)}}
6 | \usage{
7 | is_numeric_linter()
8 | }
9 | \description{
10 | \code{\link[=is.numeric]{is.numeric()}} returns \code{TRUE} when \code{typeof(x)} is \code{double} or \code{integer} --
11 | testing \code{is.numeric(x) || is.integer(x)} is thus redundant.
12 | }
13 | \details{
14 | NB: This linter plays well with \code{\link[=class_equals_linter]{class_equals_linter()}}, which can help
15 | avoid further \code{is.numeric()} equivalents like
16 | \code{any(class(x) == c("numeric", "integer"))}.
17 | }
18 | \examples{
19 | # will produce lints
20 | lint(
21 | text = "is.numeric(y) || is.integer(y)",
22 | linters = is_numeric_linter()
23 | )
24 |
25 | lint(
26 | text = 'class(z) \%in\% c("numeric", "integer")',
27 | linters = is_numeric_linter()
28 | )
29 |
30 | # okay
31 | lint(
32 | text = "is.numeric(y) || is.factor(y)",
33 | linters = is_numeric_linter()
34 | )
35 |
36 | lint(
37 | text = 'class(z) \%in\% c("numeric", "integer", "factor")',
38 | linters = is_numeric_linter()
39 | )
40 |
41 | }
42 | \seealso{
43 | \link{linters} for a complete list of linters available in lintr.
44 | }
45 | \section{Tags}{
46 | \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=readability_linters]{readability}
47 | }
48 |
--------------------------------------------------------------------------------
/man/class_equals_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/class_equals_linter.R
3 | \name{class_equals_linter}
4 | \alias{class_equals_linter}
5 | \title{Block comparison of class with \code{==}}
6 | \usage{
7 | class_equals_linter()
8 | }
9 | \description{
10 | Usage like \code{class(x) == "character"} is prone to error since class in R
11 | is in general a vector. The correct version for S3 classes is \code{\link[=inherits]{inherits()}}:
12 | \code{inherits(x, "character")}. Often, class \code{k} will have an \code{is.} equivalent,
13 | for example \code{\link[=is.character]{is.character()}} or \code{\link[=is.data.frame]{is.data.frame()}}.
14 | }
15 | \details{
16 | Similar reasoning applies for \code{class(x) \%in\% "character"}.
17 | }
18 | \examples{
19 | # will produce lints
20 | lint(
21 | text = 'is_lm <- class(x) == "lm"',
22 | linters = class_equals_linter()
23 | )
24 |
25 | lint(
26 | text = 'if ("lm" \%in\% class(x)) is_lm <- TRUE',
27 | linters = class_equals_linter()
28 | )
29 |
30 | # okay
31 | lint(
32 | text = 'is_lm <- inherits(x, "lm")',
33 | linters = class_equals_linter()
34 | )
35 |
36 | lint(
37 | text = 'if (inherits(x, "lm")) is_lm <- TRUE',
38 | linters = class_equals_linter()
39 | )
40 |
41 | }
42 | \seealso{
43 | \link{linters} for a complete list of linters available in lintr.
44 | }
45 | \section{Tags}{
46 | \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=robustness_linters]{robustness}
47 | }
48 |
--------------------------------------------------------------------------------
/tests/testthat/knitr_formats/test.Rmd:
--------------------------------------------------------------------------------
1 | # Test #
2 |
3 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
4 | tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
5 | vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
6 | no sea takimata sanctus est Lorem ipsum dolor sit amet.
7 |
8 | ```{r}
9 | a = 1
10 | ```
11 |
12 | Test
13 | ====
14 |
15 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
16 | tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
17 | vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
18 | no sea takimata sanctus est Lorem ipsum dolor sit amet.
19 |
20 | ```{r}
21 | b <- function(x) {
22 | d = 1
23 | }
24 |
25 | ```
26 |
27 | ```{r engine="python"}
28 | a=[]
29 |
30 | a[0]=1
31 | ```
32 |
33 | ```
34 | Plain code blocks can be written after three or more backticks
35 | - R Markdown: The Definitive Guide. Xie, Allaire and Grolemund (2.5.2)
36 | ```
37 |
38 | ```r
39 | # This is a non-evaluated block of R code for formatting in markdown.
40 | # It should not be linted
41 | abc = 123
42 | ```
43 |
44 | ```cpp
45 | // Some C++ code for formatting by markdown
46 |
47 | ```
48 |
49 | Calls to a non-R knitr-engine using {engine_name} syntax.
50 |
51 | ```{python}
52 | # Python that looks like R
53 | a = list()
54 | b = {2}
55 | print(a)
56 | ```
57 |
58 | ```{python}
59 | # Python that's definitely not R
60 | a = []
61 | a.append(2)
62 | print(a)
63 | ```
64 |
--------------------------------------------------------------------------------
/man/print_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/print_linter.R
3 | \name{print_linter}
4 | \alias{print_linter}
5 | \title{Block usage of print() for logging}
6 | \usage{
7 | print_linter()
8 | }
9 | \description{
10 | The default print method for character vectors is appropriate for interactively inspecting objects,
11 | not for logging messages. Thus checked-in usage like \code{print(paste('Data has', nrow(DF), 'rows.'))}
12 | is better served by using \code{\link[=cat]{cat()}}, e.g. \code{cat(sprintf('Data has \%d rows.\\n', nrow(DF)))} (noting that
13 | using \code{cat()} entails supplying your own line returns, and that \code{\link[glue:glue]{glue::glue()}} might be preferable
14 | to \code{\link[=sprintf]{sprintf()}} for constructing templated strings). Lastly, note that \code{\link[=message]{message()}} differs slightly
15 | from \code{cat()} in that it prints to \code{stderr} by default, not \code{stdout}, but is still a good option
16 | to consider for logging purposes.
17 | }
18 | \examples{
19 | # will produce lints
20 | lint(
21 | text = "print('a')",
22 | linters = print_linter()
23 | )
24 |
25 | lint(
26 | text = "print(paste(x, 'y'))",
27 | linters = print_linter()
28 | )
29 |
30 | # okay
31 | lint(
32 | text = "print(x)",
33 | linters = print_linter()
34 | )
35 |
36 | }
37 | \seealso{
38 | \link{linters} for a complete list of linters available in lintr.
39 | }
40 | \section{Tags}{
41 | \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}
42 | }
43 |
--------------------------------------------------------------------------------
/tests/testthat/test-expect_s4_class_linter.R:
--------------------------------------------------------------------------------
1 | test_that("expect_s4_class_linter skips allowed usages", {
2 | linter <- expect_s4_class_linter()
3 |
4 | # expect_s4_class doesn't have an inverted version
5 | expect_lint("expect_true(!is(x, 'class'))", NULL, linter)
6 | # NB: also applies to tinytest, but it's sufficient to test testthat
7 | expect_lint("testthat::expect_s3_class(!is(x, 'class'))", NULL, linter)
8 |
9 | # expect_s4_class() doesn't have info= or label= arguments
10 | expect_lint("expect_true(is(x, 'SpatialPoly'), info = 'x should be SpatialPoly')", NULL, linter)
11 | expect_lint("expect_true(is(x, 'SpatialPoly'), label = 'x inheritance')", NULL, linter)
12 | })
13 |
14 | test_that("expect_s4_class blocks simple disallowed usages", {
15 | linter <- expect_s4_class_linter()
16 | lint_msg <- rex::rex("expect_s4_class(x, k) is better than expect_true(is(x, k))")
17 |
18 | expect_lint("expect_true(is(x, 'data.frame'))", lint_msg, linter)
19 | # namespace qualification is irrelevant
20 | expect_lint("testthat::expect_true(methods::is(x, 'SpatialPolygonsDataFrame'))", lint_msg, linter)
21 | })
22 |
23 | test_that("lints vectorize", {
24 | lint_msg <- rex::rex("expect_s4_class(x, k) is better than expect_true(is(x, k))")
25 |
26 | expect_lint(
27 | trim_some("{
28 | expect_true(is(x, 'data.frame'))
29 | expect_true(is(x, 'SpatialPolygonsDataFrame'))
30 | }"),
31 | list(
32 | list(lint_msg, line_number = 2L),
33 | list(lint_msg, line_number = 3L)
34 | ),
35 | expect_s4_class_linter()
36 | )
37 | })
38 |
--------------------------------------------------------------------------------
/.github/workflows/repo-meta-tests.yaml:
--------------------------------------------------------------------------------
1 | # Various repo-level tests for code quality
2 | on:
3 | push:
4 | branches: [main]
5 | pull_request:
6 | branches: [main]
7 |
8 | name: repo-meta-tests
9 |
10 | jobs:
11 | repo-meta-tests:
12 | runs-on: ubuntu-latest
13 | env:
14 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
15 |
16 | steps:
17 | - uses: actions/checkout@v6
18 |
19 | - uses: r-lib/actions/setup-r@v2
20 | with:
21 | r-version: "release"
22 | use-public-rspm: true
23 |
24 | - uses: r-lib/actions/setup-r-dependencies@v2
25 | with:
26 | install-quarto: false
27 | extra-packages: |
28 | any::roxygen2
29 |
30 | - name: Ensure lint metadata is tested
31 | run: |
32 | options(crayon.enabled = TRUE)
33 | callr::rscript(".dev/lint_metadata_test.R")
34 | shell: Rscript {0}
35 |
36 | - name: Generate some foreign locales
37 | run: |
38 | sudo locale-gen en_US.utf8 hu_HU.utf8 ja_JP.utf8
39 |
40 | - name: Ensure roxygen content matches man directory
41 | run: |
42 | callr::rscript(".dev/roxygen_test.R")
43 | shell: Rscript {0}
44 |
45 | - name: Ensure defunct linters exist
46 | run: |
47 | callr::rscript(".dev/defunct_linters_test.R")
48 | shell: Rscript {0}
49 |
50 | - name: Ensure any objects in the namespace are actually needed
51 | run: |
52 | callr::rscript(".dev/unused_helpers_test.R")
53 | shell: Rscript {0}
54 |
--------------------------------------------------------------------------------
/tests/testthat/test-for_loop_index_linter.R:
--------------------------------------------------------------------------------
1 | test_that("for_loop_index_linter skips allowed usages", {
2 | linter <- for_loop_index_linter()
3 |
4 | expect_lint("for (xi in x) {}", NULL, linter)
5 |
6 | # this is OK, so not every symbol is problematic
7 | expect_lint("for (col in DF$col) {}", NULL, linter)
8 | expect_lint("for (col in S4@col) {}", NULL, linter)
9 | expect_lint("for (col in DT[, col]) {}", NULL, linter)
10 |
11 | # make sure symbol check is scoped
12 | expect_lint(
13 | trim_some("
14 | {
15 | for (i in 1:10) {
16 | 42L
17 | }
18 | i <- 7L
19 | }
20 | "),
21 | NULL,
22 | linter
23 | )
24 | })
25 |
26 | test_that("for_loop_index_linter blocks simple disallowed usages", {
27 | linter <- for_loop_index_linter()
28 | lint_msg <- "Don't re-use any sequence symbols as the index symbol in a for loop"
29 |
30 | expect_lint("for (x in x) {}", lint_msg, linter)
31 | # these also overwrite a variable in calling scope
32 | expect_lint("for (x in foo(x)) {}", lint_msg, linter)
33 | # arbitrary nesting
34 | expect_lint("for (x in foo(bar(y, baz(2, x)))) {}", lint_msg, linter)
35 | })
36 |
37 | test_that("lints vectorize", {
38 | lint_msg <- "Don't re-use any sequence symbols as the index symbol in a for loop"
39 |
40 | expect_lint(
41 | trim_some("{
42 | for (x in x) {}
43 | for (y in y) {}
44 | }"),
45 | list(
46 | list(lint_msg, line_number = 2L),
47 | list(lint_msg, line_number = 3L)
48 | ),
49 | for_loop_index_linter()
50 | )
51 | })
52 |
--------------------------------------------------------------------------------
/R/pipe_return_linter.R:
--------------------------------------------------------------------------------
1 | #' Block usage of return() in magrittr pipelines
2 | #'
3 | #' [return()] inside a magrittr pipeline does not actually execute `return()`
4 | #' like you'd expect:
5 | #'
6 | #' ```r
7 | #' bad_usage <- function(x) {
8 | #' x %>%
9 | #' return()
10 | #' FALSE
11 | #' }
12 | #' ```
13 | #'
14 | #' `bad_usage(TRUE)` will return `FALSE`! It will technically work "as expected"
15 | #' if this is the final statement in the function body, but such usage is misleading.
16 | #' Instead, assign the pipe outcome to a variable and return that.
17 | #'
18 | #' @examples
19 | #' # will produce lints
20 | #' lint(
21 | #' text = "function(x) x %>% return()",
22 | #' linters = pipe_return_linter()
23 | #' )
24 | #'
25 | #' # okay
26 | #' code <- "function(x) {\n y <- sum(x)\n return(y)\n}"
27 | #' writeLines(code)
28 | #' lint(
29 | #' text = code,
30 | #' linters = pipe_return_linter()
31 | #' )
32 | #'
33 | #' @evalRd rd_tags("pipe_return_linter")
34 | #' @seealso [linters] for a complete list of linters available in lintr.
35 | #' @export
36 | pipe_return_linter <- make_linter_from_xpath(
37 | # NB: Native pipe disallows this at the parser level, so there's no need
38 | # to lint in valid R code.
39 | xpath = "
40 | //SPECIAL[text() = '%>%']
41 | /following-sibling::expr[expr/SYMBOL_FUNCTION_CALL[text() = 'return']]
42 | ",
43 | lint_message = paste(
44 | "Avoid return() as the final step of a magrittr pipeline. ",
45 | "Instead, assign the output of the pipeline to a well-named object and return that."
46 | )
47 | )
48 |
--------------------------------------------------------------------------------
/tests/testthat/test-numeric_leading_zero_linter.R:
--------------------------------------------------------------------------------
1 | test_that("numeric_leading_zero_linter skips allowed usages", {
2 | linter <- numeric_leading_zero_linter()
3 |
4 | expect_lint("a <- 0.1", NULL, linter)
5 | expect_lint("b <- -0.2", NULL, linter)
6 | expect_lint("c <- 3.0", NULL, linter)
7 | expect_lint("d <- 4L", NULL, linter)
8 | expect_lint("e <- TRUE", NULL, linter)
9 | expect_lint("f <- 0.5e6", NULL, linter)
10 | expect_lint("g <- 0x78", NULL, linter)
11 | expect_lint("h <- 0.9 + 0.1i", NULL, linter)
12 | expect_lint("h <- 0.9+0.1i", NULL, linter)
13 | expect_lint("h <- 0.9 - 0.1i", NULL, linter)
14 | expect_lint("i <- 2L + 3.4i", NULL, linter)
15 | })
16 |
17 | test_that("numeric_leading_zero_linter blocks simple disallowed usages", {
18 | linter <- numeric_leading_zero_linter()
19 | lint_msg <- rex::rex("Include the leading zero for fractional numeric constants.")
20 |
21 | expect_lint("a <- .1", lint_msg, linter)
22 | expect_lint("b <- -.2", lint_msg, linter)
23 | expect_lint("c <- .3 + 4.5i", lint_msg, linter)
24 | expect_lint("d <- 6.7 + .8i", lint_msg, linter)
25 | expect_lint("d <- 6.7+.8i", lint_msg, linter)
26 | expect_lint("e <- .9e10", lint_msg, linter)
27 | })
28 |
29 | test_that("lints vectorize", {
30 | lint_msg <- rex::rex("Include the leading zero for fractional numeric constants.")
31 |
32 | expect_lint(
33 | trim_some("{
34 | .1
35 | -.2
36 | }"),
37 | list(
38 | list(lint_msg, line_number = 2L),
39 | list(lint_msg, line_number = 3L)
40 | ),
41 | numeric_leading_zero_linter()
42 | )
43 | })
44 |
--------------------------------------------------------------------------------
/man/implicit_integer_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/implicit_integer_linter.R
3 | \name{implicit_integer_linter}
4 | \alias{implicit_integer_linter}
5 | \title{Implicit integer linter}
6 | \usage{
7 | implicit_integer_linter(allow_colon = FALSE)
8 | }
9 | \arguments{
10 | \item{allow_colon}{Logical, default \code{FALSE}. If \code{TRUE}, expressions involving \code{:}
11 | won't throw a lint regardless of whether the inputs are implicitly integers.}
12 | }
13 | \description{
14 | Check that integers are explicitly typed using the form \code{1L} instead of \code{1}.
15 | }
16 | \examples{
17 | # will produce lints
18 | lint(
19 | text = "x <- 1",
20 | linters = implicit_integer_linter()
21 | )
22 |
23 | lint(
24 | text = "x[2]",
25 | linters = implicit_integer_linter()
26 | )
27 |
28 | lint(
29 | text = "1:10",
30 | linters = implicit_integer_linter()
31 | )
32 |
33 | # okay
34 | lint(
35 | text = "x <- 1.0",
36 | linters = implicit_integer_linter()
37 | )
38 |
39 | lint(
40 | text = "x <- 1L",
41 | linters = implicit_integer_linter()
42 | )
43 |
44 | lint(
45 | text = "x[2L]",
46 | linters = implicit_integer_linter()
47 | )
48 |
49 | lint(
50 | text = "1:10",
51 | linters = implicit_integer_linter(allow_colon = TRUE)
52 | )
53 |
54 | }
55 | \seealso{
56 | \link{linters} for a complete list of linters available in lintr.
57 | }
58 | \section{Tags}{
59 | \link[=best_practices_linters]{best_practices}, \link[=configurable_linters]{configurable}, \link[=consistency_linters]{consistency}, \link[=style_linters]{style}
60 | }
61 |
--------------------------------------------------------------------------------
/pkgdown/_pkgdown.yaml:
--------------------------------------------------------------------------------
1 | url: https://lintr.r-lib.org
2 |
3 | template:
4 | bootstrap: 5
5 | light-switch: true
6 | includes:
7 | in_header: |
8 |
9 |
10 | development:
11 | mode: "auto"
12 |
13 | reference:
14 | - title: Linting
15 | contents:
16 | - lint
17 | - linters_with_defaults
18 |
19 | - title: Configuration
20 | contents:
21 | - read_settings
22 | - exclude
23 | - linters_with_tags
24 | - modify_defaults
25 |
26 | - title: Individual linters
27 | contents:
28 | - ends_with("linter")
29 |
30 | - title: Groups of linters
31 | contents:
32 | - ends_with("linters")
33 |
34 | - title: Common default configurations
35 | contents:
36 | - all_undesirable_functions
37 | - starts_with("default_")
38 |
39 | - title: Utilities
40 | contents:
41 | - Linter
42 | - expect_lint
43 | - expect_lint_free
44 | - ids_with_token
45 | - is_lint_level
46 | - get_r_string
47 | - use_lintr
48 | - make_linter_from_xpath
49 | - xml_nodes_to_lints
50 | - xp_call_name
51 |
52 | - title: Meta-tooling
53 | contents:
54 | - Lint
55 | - checkstyle_output
56 | - sarif_output
57 | - gitlab_output
58 | - clear_cache
59 | - get_source_expressions
60 | - parse_exclusions
61 |
62 | news:
63 | releases:
64 | - text: "Version 3.0.0"
65 | href: https://www.tidyverse.org/blog/2022/07/lintr-3-0-0/
66 |
--------------------------------------------------------------------------------
/man/expect_null_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/expect_null_linter.R
3 | \name{expect_null_linter}
4 | \alias{expect_null_linter}
5 | \title{Require usage of \code{expect_null} for checking \code{NULL}}
6 | \usage{
7 | expect_null_linter()
8 | }
9 | \description{
10 | Require usage of \code{expect_null(x)} over \code{expect_equal(x, NULL)} and similar
11 | usages.
12 | }
13 | \details{
14 | \code{\link[testthat:expect_null]{testthat::expect_null()}} exists specifically for testing for \code{NULL} objects.
15 | \code{\link[testthat:equality-expectations]{testthat::expect_equal()}}, \code{\link[testthat:equality-expectations]{testthat::expect_identical()}}, and
16 | \code{\link[testthat:logical-expectations]{testthat::expect_true()}} can also be used for such tests,
17 | but it is better to use the tailored function instead.
18 | }
19 | \examples{
20 | # will produce lints
21 | lint(
22 | text = "expect_equal(x, NULL)",
23 | linters = expect_null_linter()
24 | )
25 |
26 | lint(
27 | text = "expect_identical(x, NULL)",
28 | linters = expect_null_linter()
29 | )
30 |
31 | lint(
32 | text = "expect_true(is.null(x))",
33 | linters = expect_null_linter()
34 | )
35 |
36 |
37 | # okay
38 | lint(
39 | text = "expect_null(x)",
40 | linters = expect_null_linter()
41 | )
42 |
43 | }
44 | \seealso{
45 | \link{linters} for a complete list of linters available in lintr.
46 | }
47 | \section{Tags}{
48 | \link[=best_practices_linters]{best_practices}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat}
49 | }
50 |
--------------------------------------------------------------------------------
/man/expect_true_false_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/expect_true_false_linter.R
3 | \name{expect_true_false_linter}
4 | \alias{expect_true_false_linter}
5 | \title{Require usage of \code{expect_true(x)} over \code{expect_equal(x, TRUE)}}
6 | \usage{
7 | expect_true_false_linter()
8 | }
9 | \description{
10 | \code{\link[testthat:logical-expectations]{testthat::expect_true()}} and \code{\link[testthat:logical-expectations]{testthat::expect_false()}} exist specifically
11 | for testing the \code{TRUE}/\code{FALSE} value of an object.
12 | \code{\link[testthat:equality-expectations]{testthat::expect_equal()}} and \code{\link[testthat:equality-expectations]{testthat::expect_identical()}} can also be
13 | used for such tests, but it is better to use the tailored function instead.
14 | }
15 | \examples{
16 | # will produce lints
17 | lint(
18 | text = "expect_equal(x, TRUE)",
19 | linters = expect_true_false_linter()
20 | )
21 |
22 | lint(
23 | text = "expect_equal(x, FALSE)",
24 | linters = expect_true_false_linter()
25 | )
26 |
27 | # okay
28 | lint(
29 | text = "expect_true(x)",
30 | linters = expect_true_false_linter()
31 | )
32 |
33 | lint(
34 | text = "expect_false(x)",
35 | linters = expect_true_false_linter()
36 | )
37 |
38 | }
39 | \seealso{
40 | \link{linters} for a complete list of linters available in lintr.
41 | }
42 | \section{Tags}{
43 | \link[=best_practices_linters]{best_practices}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat}, \link[=readability_linters]{readability}
44 | }
45 |
--------------------------------------------------------------------------------
/man/trailing_whitespace_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/trailing_whitespace_linter.R
3 | \name{trailing_whitespace_linter}
4 | \alias{trailing_whitespace_linter}
5 | \title{Trailing whitespace linter}
6 | \usage{
7 | trailing_whitespace_linter(allow_empty_lines = FALSE, allow_in_strings = TRUE)
8 | }
9 | \arguments{
10 | \item{allow_empty_lines}{Suppress lints for lines that contain only whitespace.}
11 |
12 | \item{allow_in_strings}{Suppress lints for trailing whitespace in string constants.}
13 | }
14 | \description{
15 | Check that there are no space characters at the end of source lines.
16 | }
17 | \examples{
18 | # will produce lints
19 | lint(
20 | text = "x <- 1.2 ",
21 | linters = trailing_whitespace_linter()
22 | )
23 |
24 | code_lines <- "a <- TRUE\n \nb <- FALSE"
25 | writeLines(code_lines)
26 | lint(
27 | text = code_lines,
28 | linters = trailing_whitespace_linter()
29 | )
30 |
31 | # okay
32 | lint(
33 | text = "x <- 1.2",
34 | linters = trailing_whitespace_linter()
35 | )
36 |
37 | lint(
38 | text = "x <- 1.2 # comment about this assignment",
39 | linters = trailing_whitespace_linter()
40 | )
41 |
42 | code_lines <- "a <- TRUE\n \nb <- FALSE"
43 | writeLines(code_lines)
44 | lint(
45 | text = code_lines,
46 | linters = trailing_whitespace_linter(allow_empty_lines = TRUE)
47 | )
48 |
49 | }
50 | \seealso{
51 | \link{linters} for a complete list of linters available in lintr.
52 | }
53 | \section{Tags}{
54 | \link[=configurable_linters]{configurable}, \link[=default_linters]{default}, \link[=style_linters]{style}
55 | }
56 |
--------------------------------------------------------------------------------
/man/pipe_consistency_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipe_consistency_linter.R
3 | \name{pipe_consistency_linter}
4 | \alias{pipe_consistency_linter}
5 | \title{Pipe consistency linter}
6 | \usage{
7 | pipe_consistency_linter(pipe = c("|>", "\%>\%", "auto"))
8 | }
9 | \arguments{
10 | \item{pipe}{Which pipe operator is valid (either \code{"\%>\%"} or \code{"|>"}). The default
11 | is the native pipe (\verb{|>}). \code{"auto"} will instead
12 | only enforce consistency, i.e., that in any given file there is only one pipe.}
13 | }
14 | \description{
15 | Check that the recommended pipe operator is used, or more conservatively that
16 | pipes are consistent by file.
17 | }
18 | \examples{
19 | # will produce lints
20 | lint(
21 | text = "1:3 |> mean() \%>\% as.character()",
22 | linters = pipe_consistency_linter()
23 | )
24 |
25 | lint(
26 | text = "1:3 \%>\% mean() \%>\% as.character()",
27 | linters = pipe_consistency_linter("|>")
28 | )
29 |
30 | # okay
31 | lint(
32 | text = "1:3 |> mean() |> as.character()",
33 | linters = pipe_consistency_linter()
34 | )
35 |
36 | lint(
37 | text = "1:3 \%>\% mean() \%>\% as.character()",
38 | linters = pipe_consistency_linter("\%>\%")
39 | )
40 | }
41 | \seealso{
42 | \itemize{
43 | \item \link{linters} for a complete list of linters available in lintr.
44 | \item \url{https://style.tidyverse.org/pipes.html#magrittr}
45 | }
46 | }
47 | \section{Tags}{
48 | \link[=configurable_linters]{configurable}, \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style}
49 | }
50 |
--------------------------------------------------------------------------------
/R/terminal_close_linter.R:
--------------------------------------------------------------------------------
1 | #' Prohibit close() from terminating a function definition
2 | #'
3 | #' Functions that end in `close(x)` are almost always better written by using
4 | #' `on.exit(close(x))` close to where `x` is defined and/or opened.
5 | #'
6 | #' @examples
7 | #' # will produce lints
8 | #' code <- paste(
9 | #' "f <- function(fl) {",
10 | #' " conn <- file(fl, open = 'r')",
11 | #' " readLines(conn)",
12 | #' " close(conn)",
13 | #' "}",
14 | #' sep = "\n"
15 | #' )
16 | #' writeLines(code)
17 | #' lint(
18 | #' text = code,
19 | #' linters = terminal_close_linter()
20 | #' )
21 | #'
22 | #' # okay
23 | #' code <- paste(
24 | #' "f <- function(fl) {",
25 | #' " conn <- file(fl, open = 'r')",
26 | #' " on.exit(close(conn))",
27 | #' " readLines(conn)",
28 | #' "}",
29 | #' sep = "\n"
30 | #' )
31 | #' writeLines(code)
32 | #' lint(
33 | #' text = code,
34 | #' linters = terminal_close_linter()
35 | #' )
36 | #'
37 | #' @evalRd rd_tags("terminal_close_linter")
38 | #' @seealso [linters] for a complete list of linters available in lintr.
39 | #' @export
40 | terminal_close_linter <- make_linter_from_xpath(
41 | xpath = "
42 | (//FUNCTION | //OP-LAMBDA)
43 | /following-sibling::expr
44 | /expr[last()][
45 | expr/SYMBOL_FUNCTION_CALL[text() = 'close']
46 | or expr[
47 | SYMBOL_FUNCTION_CALL[text() = 'return']
48 | and following-sibling::expr/expr/SYMBOL_FUNCTION_CALL[text() = 'close']
49 | ]
50 | ]
51 | ",
52 | lint_message = "Use on.exit(close(x)) to close connections instead of running it as the last call in a function."
53 | )
54 |
--------------------------------------------------------------------------------
/man/download_file_linter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/download_file_linter.R
3 | \name{download_file_linter}
4 | \alias{download_file_linter}
5 | \title{Recommend usage of a portable \code{mode} value for downloading files}
6 | \usage{
7 | download_file_linter()
8 | }
9 | \description{
10 | \code{mode = "w"} (the default) or \code{mode = "a"} in \code{download.file()} can generate broken files
11 | on Windows. Instead, \code{\link[utils:download.file]{utils::download.file()}} recommends the usage of \code{mode = "wb"}
12 | and \code{mode = "ab"}.
13 | If \code{method = "curl"} or \code{method = "wget"}, no \code{mode} should be provided as it will be ignored.
14 | }
15 | \examples{
16 | # will produce lints
17 | lint(
18 | text = "download.file(x = my_url)",
19 | linters = download_file_linter()
20 | )
21 |
22 | lint(
23 | text = "download.file(x = my_url, mode = 'w')",
24 | linters = download_file_linter()
25 | )
26 |
27 | lint(
28 | text = "download.file(x = my_url, method = 'curl', mode = 'wb')",
29 | linters = download_file_linter()
30 | )
31 |
32 | # okay
33 | lint(
34 | text = "download.file(x = my_url, mode = 'wb')",
35 | linters = download_file_linter()
36 | )
37 |
38 | lint(
39 | text = "download.file(x = my_url, method = 'curl')",
40 | linters = download_file_linter()
41 | )
42 |
43 | }
44 | \seealso{
45 | \link{linters} for a complete list of linters available in lintr.
46 | }
47 | \section{Tags}{
48 | \link[=best_practices_linters]{best_practices}, \link[=common_mistakes_linters]{common_mistakes}, \link[=robustness_linters]{robustness}
49 | }
50 |
--------------------------------------------------------------------------------
/R/expect_length_linter.R:
--------------------------------------------------------------------------------
1 | #' Require usage of `expect_length(x, n)` over `expect_equal(length(x), n)`
2 | #'
3 | #' [testthat::expect_length()] exists specifically for testing the [length()] of
4 | #' an object. [testthat::expect_equal()] can also be used for such tests,
5 | #' but it is better to use the tailored function instead.
6 | #'
7 | #' @examples
8 | #' # will produce lints
9 | #' lint(
10 | #' text = "expect_equal(length(x), 2L)",
11 | #' linters = expect_length_linter()
12 | #' )
13 | #'
14 | #' # okay
15 | #' lint(
16 | #' text = "expect_length(x, 2L)",
17 | #' linters = expect_length_linter()
18 | #' )
19 | #'
20 | #' @evalRd rd_tags("expect_length_linter")
21 | #' @seealso [linters] for a complete list of linters available in lintr.
22 | #' @export
23 | expect_length_linter <- function() {
24 | # TODO(#2465): also catch expect_true(length(x) == 1)
25 | xpath <- "
26 | following-sibling::expr[
27 | expr[1][SYMBOL_FUNCTION_CALL[text() = 'length']]
28 | and (position() = 1 or preceding-sibling::expr[NUM_CONST])
29 | ]
30 | /parent::expr[not(SYMBOL_SUB[text() = 'info' or contains(text(), 'label')])]
31 | "
32 |
33 | Linter(linter_level = "expression", function(source_expression) {
34 | xml_calls <- source_expression$xml_find_function_calls(c("expect_equal", "expect_identical"))
35 | bad_expr <- xml_find_all(xml_calls, xpath)
36 |
37 | matched_function <- xp_call_name(bad_expr)
38 | lint_message <- sprintf("expect_length(x, n) is better than %s(length(x), n)", matched_function)
39 | xml_nodes_to_lints(bad_expr, source_expression, lint_message, type = "warning")
40 | })
41 | }
42 |
--------------------------------------------------------------------------------