tests/testthat/test-brace_linter.R

test_that("brace_linter lints braces correctly", {
  open_curly_msg <- rex::rex(
    "Opening curly braces should never go on their own line"
  )
  closed_curly_msg <- rex::rex(
    "Closing curly-braces should always be on their own line, ",
    "unless they are followed by an else."
  )

  linter <- brace_linter()
  expect_lint("blah", NULL, linter)
  expect_lint("a <- function() {\n}", NULL, linter)
  expect_lint("a <- function() {  \n}", NULL, linter)

  expect_lint("a <- function() { 1 }", list(open_curly_msg, closed_curly_msg), linter)
  # allowed by allow_single_line
  expect_lint("a <- function() { 1 }", NULL, brace_linter(allow_single_line = TRUE))

  expect_lint(
    trim_some("
      a <- if(1) {
        1} else {
        2
      }
    "),
    closed_curly_msg,
    linter
  )
  expect_lint(
    trim_some("
      a <- if(1) {
        1
      } else {
        2}
    "),
    closed_curly_msg,
    linter
  )

  expect_lint(
    trim_some("
      a <- if(1) {
        1} else {
        2}
    "),
    list(
      closed_curly_msg,
      closed_curly_msg
    ),
    linter
  )

  # }) is allowed
  expect_lint("eval(bquote({\n...\n}))", NULL, linter)
  # }] is too
  expect_lint("df[, {\n...\n}]", NULL, linter)

  # }, is allowed
  expect_lint(
    trim_some("
      fun({
        statements
      }, param)"),
    NULL,
    linter
  )
  expect_lint(
    trim_some("
      fun(function(a) {
        statements
      }, param)"),
    NULL,
    linter
  )

  # ,<\n>{ is allowed
  expect_lint(
    trim_some("
      switch(
        x,
        'a' = do_something(x),
        'b' = do_another(x),
        {
          do_first(x)
          do_second(x)
        }
      )
    "),
    NULL,
    linter
  )

  # a comment before ,<\n>{ is allowed
  expect_lint(
    trim_some("
      switch(
        x,
        'a' = do_something(x),
        'b' = do_another(x), # comment
        {
          do_first(x)
          do_second(x)
        }
      )
    "),
    NULL,
    linter
  )

  # a comment before <\n>{ is allowed
  expect_lint(
    trim_some("
      switch(stat,
      o = {
        x <- 0.01
      },
      # else
      {
        x <- 2
      }
    )
    "),
    NULL,
    linter
  )

  expect_lint(
    trim_some("
      fun(
        'This is very very very long text.',
        {
          message('This is the code.')
          message('It\\'s stupid, but proves my point.')
        }
      )
    "),
    NULL,
    linter
  )

  # (\n{ is allowed optionally
  expect_lint(
    trim_some("
      tryCatch(
        {
          print(1)
        },
        error = function(err) {
        }
      )
    "),
    NULL,
    linter
  )

  # {{ }} is allowed
  expect_lint("{{ x }}", NULL, linter)

  expect_lint(
    trim_some("
      pkg_name <- function(path = find_package()) {
        if (is.null(path)) {
          return(NULL)
        } else {
          read.dcf(file.path(path, \"DESCRIPTION\"), fields = \"Package\")[1]
        }
      }
    "),
    NULL,
    linter
  )

  expect_lint(
    "a <- function()
     # comment
    {
      1
    }",
    open_curly_msg,
    linter
  )

  expect_lint("a <- function()\n{\n  1 \n}", open_curly_msg, linter)
  expect_lint("a <- function()\n    {\n  1 \n}", open_curly_msg, linter)
  expect_lint("a <- function()\n\t{\n  1 \n}", open_curly_msg, linter)

  # trailing comments are allowed
  expect_lint(
    trim_some('
      if ("P" != "NP") { # what most people expect
        print("Cryptomania is possible")
      }
    '),
    NULL,
    linter
  )
})

test_that("brace_linter lints spaces before open braces", {
  linter <- brace_linter()
  lint_msg <- rex::rex("There should be a space before an opening curly brace.")

  expect_lint(
    "blah <- function(){\n}",
    list(
      message = lint_msg,
      column_number = 19L
    ),
    linter
  )

  expect_lint(
    "\nblah <- function(){\n\n\n}",
    list(
      message = lint_msg,
      column_number = 19L
    ),
    linter
  )

  # should also lint if/else
  expect_lint(
    "a <- if (a){\n} else{\n}",
    list(
      list(message = lint_msg, line_number = 1L, column_number = 12L),
      list(message = lint_msg, line_number = 2L, column_number = 7L)
    ),
    linter
  )

  # should lint repeat{
  expect_lint(
    "repeat{\nblah\n}",
    list(message = lint_msg, line_number = 1L, column_number = 7L),
    linter
  )

  # should ignore strings and comments, as in regexes:
  expect_lint("grepl('(iss){2}', 'Mississippi')", NULL, linter)
  expect_lint(
    "x <- 123 # don't flag (paren){brace} if inside a comment",
    NULL,
    linter
  )
  # should not be thrown when the brace lies on subsequent line
  expect_lint(
    trim_some("
      x <- function()
                     {2}
    "),
    list(
      rex::rex("Opening curly braces should never go on their own line"),
      rex::rex("Closing curly-braces should always be on their own line")
    ), # , but not lint_msg
    linter
  )
})

test_that("brace_linter lints else correctly", {
  linter <- brace_linter()
  expect_lint("if (TRUE) 1 else 2", NULL, linter)
  expect_lint("if (TRUE) 1", NULL, linter)

  lines_brace <- trim_some("
    if (TRUE) {
      1
    } else {
      2
    }
  ")
  expect_lint(lines_brace, NULL, linter)

  # such usage is also not allowed by the style guide, but test anyway
  lines_unbrace <- trim_some("
    foo <- function(x) {
      if (TRUE)
        1
      else
        2
    }
  ")
  expect_lint(lines_unbrace, NULL, linter)

  lines <- trim_some("
    foo <- function(x) {
      if (x) {
        1
      }
      else {
        2
      }
    }
  ")
  expect_lint(
    lines,
    rex::rex("`else` should come on the same line as the previous `}`."),
    linter
  )
})

test_that("brace_linter lints function expressions correctly", {
  linter <- brace_linter()
  expect_lint("function(x) 4", NULL, linter)

  lines <- trim_some("
    function(x) {
      x + 4
    }
  ")
  expect_lint(lines, NULL, linter)

  lines <- trim_some("
    function(x)
      x+4
  ")
  expect_lint(
    lines,
    rex::rex("Any function spanning multiple lines should use curly braces."),
    linter
  )
})

test_that("brace_linter lints if/else matching braces correctly", {
  linter <- brace_linter()

  expect_lint("if (TRUE) 1 else 2", NULL, linter)
  expect_lint("if (TRUE) 1", NULL, linter)

  lines_brace <- trim_some("
    if (TRUE) {
      1
    } else {
      2
    }
  ")
  expect_lint(lines_brace, NULL, linter)

  # such usage is also not allowed by the style guide, but test anyway
  lines_unbrace <- trim_some("
    foo <- function(x) {
      if (TRUE)
        1
      else
        2
    }
  ")
  expect_lint(lines_unbrace, NULL, linter)

  # else if is OK
  lines_else_if <- trim_some("
    if (x) {
     1
    } else if (y) {
     2
    } else {
     3
    }
  ")
  expect_lint(lines_else_if, NULL, linter)

  lines_if <- trim_some("
    foo <- function(x) {
      if (x) {
        1
      } else 2
    }
  ")
  expect_lint(
    lines_if,
    rex::rex("Either both or neither branch in `if`/`else` should use curly braces."),
    linter
  )

  lines_else <- trim_some("
    foo <- function(x) {
      if (x) 1 else {
        2
      }
    }
  ")
  expect_lint(
    lines_else,
    rex::rex("Either both or neither branch in `if`/`else` should use curly braces."),
    linter
  )
})

# Keep up to date with https://github.com/tidyverse/style/issues/191
test_that("empty brace expressions are always allowed inline", {
  expect_lint("while (FALSE) {}", NULL, brace_linter())
  expect_lint("while (FALSE) { }", NULL, brace_linter())
  # only applies when `{` is "attached" to the preceding token on the same line
  expect_lint("while (FALSE)\n{}", rex::rex("Opening curly braces"), brace_linter())
  expect_lint("while (FALSE)\n{ }", rex::rex("Opening curly braces"), brace_linter())
  expect_lint("while (FALSE) {}", NULL, brace_linter(allow_single_line = TRUE))
  expect_lint("while (FALSE) { }", NULL, brace_linter(allow_single_line = TRUE))
})

test_that("formula syntax is linted properly", {
  linter <- brace_linter()
  lint_msg_open <- rex::rex("Opening curly braces should never go on their own line")
  lint_msg_closed <- rex::rex("Closing curly-braces should always be on their own line")

  expect_lint(
    trim_some("
      map(
        .x = 1:4,
        .f = ~ {
                .x + 1
              }
      )"),
    NULL,
    linter
  )

  expect_lint(
    trim_some("
      map(
        .x = 1:4,
        .f = ~ {.x + 1}
      )"),
    list(
      list(message = lint_msg_open, line_number = 3L, column_number = 10L),
      list(message = lint_msg_closed, line_number = 3L, column_number = 17L)
    ),
    linter
  )

  expect_lint(
    trim_some("
      map(
        .x = 1:4,
        .f = ~ { .x + 1
              }
      )"),
    list(
      list(message = lint_msg_open, line_number = 3L, column_number = 10L)
    ),
    linter
  )

  expect_lint(
    trim_some("
      map(
        .x = 1:4,
        .f = ~ {
                .x + 1}
      )"),
    list(
      list(message = lint_msg_closed, line_number = 4L, column_number = 17L)
    ),
    linter
  )
})

test_that("code with pipes is handled correctly", {
  linter <- brace_linter()
  lint_msg_open <- rex::rex("Opening curly braces should never go on their own line")
  lint_msg_closed <- rex::rex("Closing curly-braces should always be on their own line")

  expect_lint(
    trim_some("
      out <- lapply(stuff, function(i) {
        do_something(i)
      }) %>% unlist
    "),
    NULL,
    linter
  )

  expect_lint(
    trim_some("
      1:4 %!>% {
          sum(.)
        }
    "),
    NULL,
    linter
  )

  # %>%\n{ is allowed
  expect_lint(
    trim_some("
      1:4 %T>%
        {
          sum(.)
        }
    "),
    NULL,
    linter
  )

  expect_lint(
    trim_some("
      xx %<>% { sum(.)
        }
    "),
    list(
      list(message = lint_msg_open, line_number = 1L, column_number = 9L)
    ),
    linter
  )

  expect_lint(
    trim_some("
      x %>%
        {
          uvwxyz }
    "),
    list(
      list(message = lint_msg_closed, line_number = 3L, column_number = 12L)
    ),
    linter
  )

  expect_lint(
    trim_some("
      1:4 %>%
        { sum(.) }
    "),
    list(
      list(message = lint_msg_closed, line_number = 2L, column_number = 12L)
    ),
    linter
  )

  expect_lint(
    "1:4 %>% { sum(.) }",
    list(
      list(message = lint_msg_open, line_number = 1L, column_number = 9L),
      list(message = lint_msg_closed, line_number = 1L, column_number = 18L)
    ),
    linter
  )

  skip_if_not_r_version("4.1.0")

  expect_lint(
    trim_some("
      out <- lapply(stuff, function(i) {
        do_something(i)
      }) |> unlist()
    "),
    NULL,
    linter
  )

  expect_lint(
    "local({ 1:4 |> sum() })",
    list(
      list(message = lint_msg_open, line_number = 1L, column_number = 7L)
    ),
    linter
  )
})

test_that("function shorthand is treated like 'full' function", {
  skip_if_not_r_version("4.1.0")
  linter <- brace_linter()

  expect_lint("a <- \\() {  \n}", NULL, linter)
  expect_lint(
    trim_some("
      x <- \\()
              {2}
    "),
    list(
      rex::rex("Opening curly braces should never go on their own line"),
      rex::rex("Closing curly-braces should always be on their own line")
    ),
    linter
  )
})

Try the lintr package in your browser

Any scripts or data that you put into this service are public.

lintr documentation built on Nov. 7, 2023, 5:07 p.m.