Nothing
# fuzzer disable: comment_injection
test_that("indentation linter flags unindented expressions", {
linter <- indentation_linter(indent = 2L)
expect_no_lint(
trim_some("
lapply(1:10, function(i) {
i %% 2
})
"),
linter
)
expect_lint(
trim_some("
lapply(1:10, function(i) {
i %% 2 # indentation is only 1 character
})
"),
"Indentation",
linter
)
expect_lint(
trim_some("
lapply(1:10, function(i) {
# indentation is only 1 character
i %% 2
})
"),
"Indentation",
linter
)
# no double-block indents even if the indentation-starting tokens are immediately next to each other
expect_lint(
trim_some("
local({
# no lint
})
local({
# must lint
})
"),
list(line_number = 6L, message = "Indentation"),
linter
)
expect_no_lint(
trim_some("
lapply(1:10, function(i) {
i %% 2
})
"),
indentation_linter(indent = 4L)
)
expect_lint(
trim_some("
lapply(1:10, function(i) {
i %% 2 # indentation is only 2 characters
})
"),
"Indentation",
indentation_linter(indent = 4L)
)
# ugly code, but still correctly indented
expect_no_lint(
trim_some("
list(
1,
2)
"),
linter
)
# comments do not trigger hanging indent rule
expect_no_lint(
trim_some("
list( # comment
ok
)
"),
linter
)
# comments do not suppress block indents (#1751)
expect_no_lint(
trim_some("
a <- # comment
42L
"),
linter
)
# assignment triggers indent
expect_no_lint(
trim_some("
a <-
expr(
42
)
"),
linter
)
expect_no_lint(
trim_some("
if (cond)
code
if (cond) code else code2
if (cond) {
code
} else
code
if (cond) {
code
} else {
code
}
"),
linter
)
})
test_that("indentation linter flags improper closing curly braces", {
linter <- indentation_linter(indent = 2L)
expect_no_lint(
trim_some("
lapply(1:10, function(i) {
{
i %% 2
}
})
"),
linter
)
expect_lint(
trim_some("
lapply(1:10, function(i) {
i %% 2
} # closing curly doesn't return to parent indentation
)
"),
"Indentation",
linter
)
})
test_that("function argument indentation works in tidyverse-style", { # nofuzz: function_lambda
linter <- indentation_linter()
expect_no_lint(
trim_some("
function(a = 1L,
b = 2L) {
a + b
}
"),
linter
)
# old double-indent style (#1754, #2830) lints under tidyverse style
expect_lint(
trim_some("
function(
a = 1L,
b = 2L) {
a + b
}
"),
rex::rex("Indentation should be 2 spaces but is 4 spaces."),
linter
)
expect_lint(
trim_some("
function(
a = 1L,
b = 2L) {
a + b
}
"),
rex::rex("Indentation should be 2 spaces but is 6 spaces."),
linter
)
# Hanging is only allowed if there is an argument next to "("
expect_lint(
trim_some("
function(
a = 1L,
b = 2L) {
a + b
}
"),
rex::rex("Indentation should be 2 spaces but is 9 spaces (or start argument on previous line)"),
linter
)
expect_no_lint(
trim_some("
function(
a = 1L,
b = 2L
) {
a + b
}
"),
linter
)
# anchor is correctly found with assignments as well
expect_no_lint( # nofuzz: assignment
trim_some("
test <- function(a = 1L,
b = 2L) {
a + b
}
"),
linter
)
expect_lint(
trim_some("
function(a = 1L,
b = 2L) {
a + b
}
"),
"Hanging",
linter
)
# This is a case for brace_linter
expect_no_lint(
trim_some("
function(a = 1L,
b = 2L)
{
a + b
}
"),
linter
)
expect_lint(
trim_some("
function(
a = function(
x) {
x
}) {
a()
}
"),
list(rex::rex("Indentation should be 4 spaces but is 8 spaces."), line_number = 3L),
linter
)
# proper single indentation of nested function definitions.
expect_no_lint(
trim_some("
function(
a = function(
x
) {
x
}
) {
a()
}
"),
linter
)
})
test_that("function argument indentation works in always-hanging-style", { # nofuzz: function_lambda
linter <- indentation_linter(hanging_indent_style = "always")
expect_no_lint(
trim_some("
function(a = 1L,
b = 2L) {
a + b
}
"),
linter
)
expect_lint(
trim_some("
function(
a = 1L,
b = 2L) {
a + b
}
"),
"Hanging",
linter
)
expect_no_lint(
trim_some("
function(
a = 1L,
b = 2L) {
a + b
}
"),
linter
)
# Block is only allowed if there is no argument next to ")"
expect_lint(
trim_some("
function(
a = 1L,
b = 2L) {
a + b
}
"),
"Hanging",
linter
)
expect_no_lint(
trim_some("
function(
a = 1L,
b = 2L
) {
a + b
}
"),
linter
)
# anchor is correctly found with assignments as well
expect_no_lint( # nofuzz: assignment
trim_some("
test <- function(a = 1L,
b = 2L) {
a + b
}
"),
linter
)
expect_lint(
trim_some("
function(a = 1L,
b = 2L) {
a + b
}
"),
"Hanging",
linter
)
# This is a case for brace_linter
expect_no_lint(
trim_some("
function(a = 1L,
b = 2L)
{
a + b
}
"),
linter
)
})
test_that("indentation with operators works", {
linter <- indentation_linter()
expect_no_lint(
trim_some("
a %>%
b()
"),
linter
)
expect_no_lint(
trim_some("
(a + b + c) /
(d + e + f) /
(g + h + i)
"),
linter
)
expect_lint(
trim_some("
a %>%
b()
"),
"Indentation",
linter
)
expect_lint(
trim_some("
a +
b()
"),
"Indentation",
linter
)
expect_no_lint(
trim_some("
abc$
def$
ghi
"),
linter
)
expect_no_lint(
trim_some("
abc@
def@
ghi
"),
linter
)
expect_lint(
trim_some("
first_step() +
second_step()
"),
list(rex::rex("Indentation should be 2 spaces but is 12 spaces."), line_number = 2L),
linter
)
})
test_that("indentation with bracket works", {
linter <- indentation_linter()
expect_no_lint(
trim_some("
dt[
, col := 42L
][
, ok
]
bla[hanging,
also_ok]
"),
linter
)
expect_no_lint(
trim_some("
abc[[
'elem'
]]
def[[a,
b]]
"),
linter
)
})
test_that("indentation works with control flow statements", {
linter <- indentation_linter()
expect_no_lint(
trim_some("
if (TRUE) {
do_something
} else {
do_other_thing
}
"),
linter
)
expect_lint(
trim_some("
while (1 > 2) {
do_something
}
"),
"Indentation",
linter
)
expect_lint(
trim_some("
if (FALSE) {
do_something
} else {
do_other_thing
}
"),
"Indentation",
linter
)
})
test_that("indentation lint messages are dynamic", {
linter <- indentation_linter()
expect_lint(
trim_some("
local({
# should be 2
})
"),
rex::rex("Indentation should be 2 spaces but is 4 spaces."),
linter
)
expect_lint(
trim_some("
fun(
3) # should be 4
"),
rex::rex("Hanging indent should be 4 spaces but is 2 spaces."),
linter
)
})
test_that("indentation within string constants is ignored", {
linter <- indentation_linter()
expect_no_lint(
trim_some("
x <- '
an indented string
'
"),
linter
)
expect_no_lint(
trim_some("
x <- '
an indented string with 3 spaces indentation
'
"),
linter
)
# first line of a multi-line string can induce a lint
expect_lint(
trim_some("
foo(
'
string
')
bar('
string2
')
baz('
string3
')
x <- '
string4
'
"),
list(
list(rex::rex("Hanging indent should be 4 spaces but is 0 spaces."), line_number = 2L),
list(rex::rex("Indentation should be 0 spaces but is 2 spaces"), line_number = 11L)
),
linter
)
})
test_that("combined hanging and block indent works", {
linter <- indentation_linter()
expect_no_lint(
trim_some("
func(hang, and,
block(
combined
))
"),
linter
)
expect_no_lint(
trim_some("
func(ha,
func2(ab,
block(
indented
)))
"),
linter
)
expect_no_lint(
trim_some("
func(func2(
a = 42
))
"),
linter
)
# Adapted from cli R/ansi.R L231-234
expect_no_lint(
trim_some("
stopifnot(is.character(style) && length(style) == 1 ||
is_rgb_matrix(style) && ncol(style) == 1,
is.logical(bg) && length(bg) == 1,
is.numeric(colors) && length(colors) == 1)
"),
linter
)
# Adapted from cli inst/scripts/up.R L26-37
expect_no_lint(
trim_some("
http_head(url, ...)$
then(function(res) {
if (res$status_code < 300) {
cli_alert_success()
} else {
cli_alert_danger()
}
})$
catch(error = function(err) {
e <- if (grepl('timed out', err$message)) 'timed out' else 'error'
cli_alert_danger()
})
"),
linter
)
# S4 equivalence
expect_no_lint(
trim_some("
http_head(url, ...)@
then(function(res) {
if (res$status_code < 300) {
cli_alert_success()
} else {
cli_alert_danger()
}
})@
catch(error = function(err) {
e <- if (grepl('timed out', err$message)) 'timed out' else 'error'
cli_alert_danger()
})
"),
linter
)
})
test_that("hanging_indent_stlye works", {
code_block_multi_line <- "map(x, f,\n extra_arg = 42\n)"
code_hanging_multi_line <- "map(x, f,\n extra_arg = 42\n)"
code_block_same_line <- "map(x, f,\n extra_arg = 42)"
code_hanging_same_line <- "map(x, f,\n extra_arg = 42)"
tidy_linter <- indentation_linter()
hanging_linter <- indentation_linter(hanging_indent_style = "always")
non_hanging_linter <- indentation_linter(hanging_indent_style = "never")
expect_no_lint(code_block_multi_line, tidy_linter)
expect_lint(code_block_multi_line, "Hanging indent", hanging_linter)
expect_no_lint(code_block_multi_line, non_hanging_linter)
expect_lint(code_hanging_multi_line, "Indent", tidy_linter)
expect_no_lint(code_hanging_multi_line, hanging_linter)
expect_lint(code_hanging_multi_line, "Indent", non_hanging_linter)
expect_lint(code_block_same_line, "Hanging indent", tidy_linter)
expect_lint(code_block_same_line, "Hanging indent", hanging_linter)
expect_no_lint(code_block_same_line, non_hanging_linter)
expect_no_lint(code_hanging_same_line, tidy_linter)
expect_no_lint(code_hanging_same_line, hanging_linter)
expect_lint(code_hanging_same_line, "Indent", non_hanging_linter)
# regression test for #1898
expect_no_lint(
trim_some("
outer_fun(inner_fun(x,
one_indent = 42L
))
"),
tidy_linter
)
expect_no_lint(
trim_some("
outer_fun(inner_fun(x, # this is first arg
one_indent = 42L # this is second arg
))
"),
tidy_linter
)
expect_no_lint(
trim_some("
outer_fun(inner_fun(
x,
one_indent = 42L
))
"),
tidy_linter
)
expect_no_lint(
trim_some("
outer_fun(
inner_fun(
x,
one_indent = 42L
)
)
"),
tidy_linter
)
expect_lint(
trim_some("
outer_f(
inner_g(x,
y = 2)
)
"),
list(rex::rex("Hanging indent should be 10 spaces but is 2 spaces."), line_number = 3L),
tidy_linter
)
# don't suggest starting argument on previous line
expect_lint(
trim_some("
outer_f(
inner_g(x,
y = 2)
)
"),
list(rex::rex("Hanging indent should be 10 spaces but is 8 spaces."), line_number = 3L),
tidy_linter
)
})
test_that("previous token is respected when recommending to 'start argument on previous line'", {
expect_lint(
trim_some("
result <- {
do_something()
}
abc[[
'elem'
]]
def[
'key'
]
first_step() +
second_step()
"),
list(
list(rex::rex("should be 2 spaces but is 11 spaces."), line_number = 2L),
list(rex::rex("should be 2 spaces but is 5 spaces (or start argument on previous line)."), line_number = 5L),
list(rex::rex("should be 2 spaces but is 4 spaces (or start argument on previous line)."), line_number = 8L),
list(rex::rex("should be 2 spaces but is 14 spaces."), line_number = 11L)
),
indentation_linter()
)
})
test_that("assignment_as_infix works", {
# test function call restorator and LEFT_ASSIGN suppressor
code_infix <- trim_some("
ok_code <-
var1 +
f(
var2 +
var3
) +
var4
")
# test that innermost ancestor token decides the indentation
code_infix_2 <- trim_some("
lapply(x,
function(e) {
temp_var <-
e +
42
}
)
")
# test brace restorator
code_infix_3 <- trim_some("
ok_code <-
if (condition) {
a +
b
} else {
c +
d
} +
e
")
# test EQ_ASSIGN, EQ_SUB and EQ_FORMALS suppressors
code_infix_4 <- trim_some("
# EQ_ASSIGN
ok_code =
a +
b
# EQ_SUB
f(
a =
b +
c
)
# EQ_FORMALS
f <- function(
a =
b +
c
) {
NULL
}
")
code_no_infix <- trim_some("
ok_code <-
var1 +
f(
var2 +
var3
) +
var4
")
tidy_linter <- indentation_linter()
no_infix_linter <- indentation_linter(assignment_as_infix = FALSE)
expect_no_lint(code_infix, tidy_linter)
expect_no_lint(code_infix_2, tidy_linter)
expect_no_lint(code_infix_3, tidy_linter)
expect_no_lint(code_infix_4, tidy_linter)
expect_lint(code_no_infix, rex::rex("Indentation should be 2 spaces but is 4 spaces."), tidy_linter)
expect_lint(code_infix, rex::rex("Indentation should be 4 spaces but is 2 spaces."), no_infix_linter)
expect_lint(code_infix_2, rex::rex("Indentation should be 8 spaces but is 6 spaces."), no_infix_linter)
expect_lint(code_infix_3, rex::rex("Indentation should be 4 spaces but is 2 spaces."), no_infix_linter)
expect_lint(code_infix_4, list(
list(line_number = 4L, rex::rex("Indentation should be 4 spaces but is 2 spaces.")),
list(line_number = 10L, rex::rex("Indentation should be 6 spaces but is 4 spaces.")),
list(line_number = 17L, rex::rex("Indentation should be 6 spaces but is 4 spaces."))
), no_infix_linter)
expect_no_lint(code_no_infix, no_infix_linter)
})
test_that("consecutive same-level lints are suppressed", {
bad_code <- trim_some("
ok_code <- 42
wrong_hanging <- fun(a, b, c,
d, e %>%
f())
wrong_block <- function() {
a + b
c + d
if (a == 24)
boo
}
wrong_hanging_args <- function(a = 1, b = 2,
c = 3, d = 4,
e = 5, f = 6)
{
a + b + c + d + e + f
}
")
expect_lint(
bad_code,
list(
list(line_number = 4L, message = "Hanging indent"),
list(line_number = 8L, message = "Indentation"),
list(line_number = 15L, message = "Hanging indent")
),
indentation_linter()
)
})
test_that("native pipe is supported", {
linter <- indentation_linter()
expect_no_lint(
trim_some("
a |>
foo()
"),
linter
)
expect_no_lint(
trim_some("
b <- a |>
foo()
"),
linter
)
})
test_that("it doesn't error on invalid code", {
# Part of #1427
expect_lint("function() {)", list(linter = "error", message = rex::rex("unexpected ')'")), indentation_linter())
})
test_that("function shorthand is handled", {
linter <- indentation_linter()
expect_no_lint(
trim_some("
lapply(1:10, \\(i) {
i %% 2
})
"),
linter
)
expect_lint(
trim_some("
lapply(1:10, \\(i) {
i %% 2 # indentation is only 1 character
})
"),
"Indentation",
linter
)
expect_no_lint(
trim_some(R"(
\(
a = 1L,
b = 2L
) {
a + b
}
)"),
linter
)
})
test_that("lint metadata works for 0-space case", {
expect_lint(
trim_some("
if (TRUE) {
FALSE
}
"),
list(ranges = list(1L:2L)),
indentation_linter()
)
})
test_that("for loop gets correct linting", {
linter <- indentation_linter()
lint_msg <- rex::rex("Indentation should be 2 spaces")
expect_no_lint(
trim_some("
for (i in 1:10)
1
"),
linter
)
expect_lint(
trim_some("
for (i in 1:10)
1
"),
lint_msg,
linter
)
expect_no_lint(
trim_some("
for (i in 1:10) {
1
}
"),
linter
)
expect_lint(
trim_some("
for (i in 1:10) {
1
}
"),
lint_msg,
linter
)
})
# fuzzer enable: comment_injection
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.