
Block instances of unnecessary nesting
Source:R/unnecessary_nesting_linter.R
      unnecessary_nesting_linter.RdExcessive nesting harms readability. Use helper functions or early returns to reduce nesting wherever possible.
Usage
unnecessary_nesting_linter(
  allow_assignment = TRUE,
  allow_functions = c("switch", "try", "tryCatch", "withCallingHandlers", "quote",
    "expression", "bquote", "substitute", "with_parameters_test_that", "reactive",
    "observe", "observeEvent", "renderCachedPlot", "renderDataTable", "renderImage",
    "renderPlot", "renderPrint", "renderTable", "renderText", "renderUI"),
  branch_exit_calls = character()
)Arguments
- allow_assignment
 Logical, default
TRUE, in which case braced expressions consisting only of a single assignment are skipped. ifFALSE, all braced expressions with only one child expression are linted. TheTRUEcase facilitates interaction withimplicit_assignment_linter()for certain cases where an implicit assignment is necessary, so a braced assignment is used to further distinguish the assignment. See examples.- allow_functions
 Character vector of functions which always allow one-child braced expressions.
testthat::test_that()is always allowed because testthat requires a braced expression in itscodeargument. The other defaults similarly compute on expressions in a way which is worth highlighting by em-bracing them, even if there is only one expression, whileswitch()is allowed for its use as a control flow analogous toif/else.]- branch_exit_calls
 Character vector of functions which are considered as "exiting" a branch for the purpose of recommending removing nesting in a branch lacking an exit call when the other branch terminates with one. Calls which always interrupt or quit the current call or R session, e.g.
stop()andq(), are always included.
See also
cyclocomp_linter()for another linter that penalizes overly complex code.linters for a complete list of linters available in lintr.
Examples
# will produce lints
code <- "if (A) {\n  stop('A is bad!')\n} else {\n  do_good()\n}"
writeLines(code)
#> if (A) {
#>   stop('A is bad!')
#> } else {
#>   do_good()
#> }
lint(
  text = code,
  linters = unnecessary_nesting_linter()
)
#> <text>:1:1: warning: [unnecessary_nesting_linter] Reduce the nesting of this if/else statement by unnesting the portion without an exit clause (i.e., stop(), return(), abort(), quit(), q()).
#> if (A) {
#> ^~~~~~~~
code <- "tryCatch(\n  {\n    foo()\n  },\n  error = identity\n)"
writeLines(code)
#> tryCatch(
#>   {
#>     foo()
#>   },
#>   error = identity
#> )
lint(
  text = code,
  linters = unnecessary_nesting_linter()
)
#> ℹ No lints found.
code <- "expect_warning(\n  {\n    x <- foo()\n  },\n  'warned'\n)"
writeLines(code)
#> expect_warning(
#>   {
#>     x <- foo()
#>   },
#>   'warned'
#> )
lint(
  text = code,
  linters = unnecessary_nesting_linter(allow_assignment = FALSE)
)
#> <text>:2:3: warning: [unnecessary_nesting_linter] Reduce the nesting of this statement by removing the braces {}.
#>   {
#>   ^
code <- "if (x) { \n  if (y) { \n   return(1L) \n  } \n}"
writeLines(code)
#> if (x) { 
#>   if (y) { 
#>    return(1L) 
#>   } 
#> }
lint(
  text = code,
  linters = unnecessary_nesting_linter()
)
#> <text>:2:3: warning: [unnecessary_nesting_linter] Don't use nested `if` statements, where a single `if` with the combined conditional expression will do. For example, instead of `if (x) { if (y) { ... }}`, use `if (x && y) { ... }`.
#>   if (y) { 
#>   ^~~~~~~~~
lint(
  text = "my_quote({x})",
  linters = unnecessary_nesting_linter()
)
#> ℹ No lints found.
code <- paste(
  "if (A) {",
  "  stop('A is bad because a.')",
  "} else {",
  "  warning('!A requires caution.')",
  "}",
  sep = "\n"
)
writeLines(code)
#> if (A) {
#>   stop('A is bad because a.')
#> } else {
#>   warning('!A requires caution.')
#> }
lint(
  text = code,
  linters = unnecessary_nesting_linter()
)
#> <text>:1:1: warning: [unnecessary_nesting_linter] Reduce the nesting of this if/else statement by unnesting the portion without an exit clause (i.e., stop(), return(), abort(), quit(), q()).
#> if (A) {
#> ^~~~~~~~
# okay
code <- "if (A) {\n  stop('A is bad because a.')\n} else {\n  stop('!A is bad too.')\n}"
writeLines(code)
#> if (A) {
#>   stop('A is bad because a.')
#> } else {
#>   stop('!A is bad too.')
#> }
lint(
  text = code,
  linters = unnecessary_nesting_linter()
)
#> ℹ No lints found.
code <- "capture.output({\n  foo()\n})"
writeLines(code)
#> capture.output({
#>   foo()
#> })
lint(
  text = code,
  linters = unnecessary_nesting_linter()
)
#> ℹ No lints found.
code <- "expect_warning(\n  {\n    x <- foo()\n  },\n  'warned'\n)"
writeLines(code)
#> expect_warning(
#>   {
#>     x <- foo()
#>   },
#>   'warned'
#> )
lint(
  text = code,
  linters = unnecessary_nesting_linter()
)
#> ℹ No lints found.
code <- "if (x && y) { \n  return(1L) \n}"
writeLines(code)
#> if (x && y) { 
#>   return(1L) 
#> }
lint(
  text = code,
  linters = unnecessary_nesting_linter()
)
#> ℹ No lints found.
code <- "if (x) { \n  y <- x + 1L\n  if (y) { \n   return(1L) \n  } \n}"
writeLines(code)
#> if (x) { 
#>   y <- x + 1L
#>   if (y) { 
#>    return(1L) 
#>   } 
#> }
lint(
  text = code,
  linters = unnecessary_nesting_linter()
)
#> ℹ No lints found.
lint(
  text = "my_quote({x})",
  linters = unnecessary_nesting_linter(allow_functions = "my_quote")
)
#> ℹ No lints found.
code <- paste(
  "if (A) {",
  "  stop('A is bad because a.')",
  "} else {",
  "  warning('!A requires caution.')",
  "}",
  sep = "\n"
)
writeLines(code)
#> if (A) {
#>   stop('A is bad because a.')
#> } else {
#>   warning('!A requires caution.')
#> }
lint(
  text = code,
  linters = unnecessary_nesting_linter(branch_exit_calls = c("stop", "warning"))
)
#> ℹ No lints found.