Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expect comparison #955

Merged
merged 39 commits into from
Mar 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
540f116
extend infix_spaces_linter to be more flexible & correct
MichaelChirico Mar 13, 2022
87c8387
git mangled
MichaelChirico Mar 13, 2022
0a82a30
clarifying comments
MichaelChirico Mar 13, 2022
7154f10
test of grouped exclusion by %%
MichaelChirico Mar 13, 2022
8311dd4
missed ~ in docs
MichaelChirico Mar 13, 2022
c202157
Merge branch 'master' into assign-spaces
MichaelChirico Mar 13, 2022
0211f71
Merge branch 'master' into assign-spaces
MichaelChirico Mar 13, 2022
117e289
use angle brackets for external URL
MichaelChirico Mar 13, 2022
69341f7
Merge branch 'master' of github.com:jimhester/lintr into assign-spaces
MichaelChirico Mar 13, 2022
81e1f7d
remove linter from DB & document()
MichaelChirico Mar 13, 2022
4e1f4c8
sAF=FALSE for R<4
MichaelChirico Mar 13, 2022
0051252
sQuote version issue
MichaelChirico Mar 13, 2022
ad92c3f
more tests
MichaelChirico Mar 13, 2022
9e93816
Merge branch 'assign-spaces' of github.com:jimhester/lintr into assig…
MichaelChirico Mar 13, 2022
d9db267
Merge branch 'master' into assign-spaces
MichaelChirico Mar 13, 2022
8712ad9
Merge branch 'master' into assign-spaces
AshesITR Mar 14, 2022
26daa34
switch from with() usage to appease object_usage_linter
MichaelChirico Mar 14, 2022
4c04a71
Merge branch 'master' into assign-spaces
MichaelChirico Mar 16, 2022
daeb8be
initial conversion of expect_comparison_linter
MichaelChirico Mar 16, 2022
ace8e9f
mini-edit to create a commit
AshesITR Mar 16, 2022
a6f771f
Merge branch 'master' into assign-spaces
MichaelChirico Mar 16, 2022
3f66033
roxygenize
MichaelChirico Mar 16, 2022
14f2381
roxygenize
MichaelChirico Mar 16, 2022
1eddd29
fix new lintr lints caught by the improvement
MichaelChirico Mar 16, 2022
533a768
Merge branch 'master' into assign-spaces
MichaelChirico Mar 17, 2022
52f890d
Merge branch 'master' into expect_comparison
MichaelChirico Mar 17, 2022
7c7c611
Merge branch 'assign-spaces' into expect_comparison
MichaelChirico Mar 17, 2022
a0c9dfc
expect_comparison_linter
MichaelChirico Mar 17, 2022
2b17aec
remove clutter
MichaelChirico Mar 17, 2022
bf35053
Merge branch 'master' into expect_comparison
MichaelChirico Mar 17, 2022
5b8ef74
Merge branch 'master' into expect_comparison
MichaelChirico Mar 18, 2022
6655b0e
Merge branch 'master' into expect_comparison
MichaelChirico Mar 20, 2022
5b9bcf3
Merge branch 'master' into expect_comparison
MichaelChirico Mar 20, 2022
8f2073c
#nolint in right place
MichaelChirico Mar 20, 2022
2cfe0b9
use new xp_or()
MichaelChirico Mar 20, 2022
fe90a91
Merge branch 'expect_comparison' of github.com:r-lib/lintr into expec…
MichaelChirico Mar 20, 2022
493ff74
use new xml_nodes_to_lint feature
MichaelChirico Mar 21, 2022
2f7500f
Merge branch 'master' into expect_comparison
MichaelChirico Mar 21, 2022
d37a67c
Merge branch 'master' into expect_comparison
AshesITR Mar 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Collate:
'duplicate_argument_linter.R'
'equals_na_linter.R'
'exclude.R'
'expect_comparison_linter.R'
'expect_length_linter.R'
'expect_lint.R'
'expect_named_linter.R'
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export(default_undesirable_functions)
export(default_undesirable_operators)
export(duplicate_argument_linter)
export(equals_na_linter)
export(expect_comparison_linter)
export(expect_length_linter)
export(expect_lint)
export(expect_lint_free)
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ function calls. (#850, #851, @renkun-ken)
+ `expect_true_false_linter()` Require usage of `expect_true(x)` over `expect_equal(x, TRUE)` and similar
+ `expect_named_linter()` Require usage of `expect_named(x, n)` over `expect_equal(names(x), n)` and similar
* `expect_length_linter()` Require usage of `expect_length(x, n)` over `expect_equal(length(x), n)` and similar
* `expect_comparison_linter()` Require usage of `expect_gt(x, y)` over `expect_true(x > y)` and similar
* `assignment_linter()` now lints right assignment (`->` and `->>`) and gains two arguments. `allow_cascading_assign` (`TRUE` by default) toggles whether to lint `<<-` and `->>`; `allow_right_assign` toggles whether to lint `->` and `->>` (#915, @michaelchirico)
* `infix_spaces_linter()` gains argument `exclude_operators` to disable lints on selected infix operators. By default, all "low-precedence" operators throw lints; see `?infix_spaces_linter` for an enumeration of these. (#914 @michaelchirico)
* `infix_spaces_linter()` now throws a lint on `a~b` and `function(a=1) {}` (#930, @michaelchirico)
Expand Down
48 changes: 48 additions & 0 deletions R/expect_comparison_linter.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#' Require usage of expect_gt(x, y) over expect_true(x > y) (and similar)
#'
#' [testthat::expect_gt()], [testthat::expect_gte()], [testthat::expect_lt()],
#' [testthat::expect_lte()], and [testthat::expect_equal()] exist specifically
#' for testing comparisons between two objects. [testthat::expect_true()] can
#' also be used for such tests, but it is better to use the tailored function
#' instead.
#'
#' @evalRd rd_tags("expect_comparison_linter")
#' @seealso [linters] for a complete list of linters available in lintr.
#' @export
expect_comparison_linter <- function() {
Linter(function(source_file) {
if (length(source_file$parsed_content) == 0L) {
return(list())
}

xml <- source_file$xml_parsed_content

# != doesn't have a clean replacement
comparator_nodes <- # nolint: object_usage_linter. TODO(#942): remove this.
setdiff(as.list(infix_metadata$xml_tag[infix_metadata$comparator]), "NE")
xpath <- glue::glue("//expr[
expr[SYMBOL_FUNCTION_CALL[text() = 'expect_true']]
and expr[2][ {xp_or(comparator_nodes)} ]
]")

bad_expr <- xml2::xml_find_all(xml, xpath)

comparator_expectation_map <- c(
`>` = "expect_gt", `>=` = "expect_gte",
`<` = "expect_lt", `<=` = "expect_lte",
`==` = "expect_identical"
)

return(lapply(
bad_expr,
xml_nodes_to_lint,
source_file,
lint_message = function(expr) {
comparator <- xml2::xml_text(xml2::xml_find_first(expr, "expr[2]/*[2]"))
expectation <- comparator_expectation_map[[comparator]]
sprintf("%s(x, y) is better than expect_true(x %s y).", expectation, comparator)
},
type = "warning"
))
})
}
2 changes: 2 additions & 0 deletions R/infix_spaces_linter.R
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ infix_metadata$unary <- infix_metadata$xml_tag %in% c("OP-PLUS", "OP-MINUS", "OP
infix_metadata$low_precedence <- infix_metadata$string_value %in% c(
"+", "-", "~", ">", ">=", "<", "<=", "==", "!=", "&", "&&", "|", "||", "<-", "->", "=", "%%", "/", "*"
)
# comparators come up in several lints
infix_metadata$comparator <- infix_metadata$string_value %in% c("<", "<=", ">", ">=", "==", "!=")

#' Infix spaces linter
#'
Expand Down
1 change: 1 addition & 0 deletions inst/lintr/linters.csv
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ commented_code_linter,style readability best_practices default
cyclocomp_linter,style readability best_practices default configurable
duplicate_argument_linter,correctness common_mistakes configurable
equals_na_linter,robustness correctness common_mistakes default
expect_comparison_linter,package_development best_practices
expect_length_linter,package_development best_practices readability
expect_named_linter,package_development best_practices readability
expect_not_linter,package_development best_practices readability
Expand Down
1 change: 1 addition & 0 deletions man/best_practices_linters.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions man/expect_comparison_linter.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions man/linters.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/package_development_linters.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions tests/testthat/test-expect_comparison_linter.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
test_that("expect_comparison_linter skips allowed usages", {
# there's no expect_ne() for this operator
expect_lint("expect_true(x != y)", NULL, expect_comparison_linter())
# NB: also applies to tinytest, but it's sufficient to test testthat
expect_lint("testthat::expect_true(x != y)", NULL, expect_comparison_linter())

# multiple comparisons are OK
expect_lint("expect_true(x > y || x > z)", NULL, expect_comparison_linter())
})

test_that("expect_comparison_linter blocks simple disallowed usages", {
expect_lint(
"expect_true(x > y)",
rex::rex("expect_gt(x, y) is better than expect_true(x > y)."),
expect_comparison_linter()
)

# namespace qualification is irrelevant
expect_lint(
"testthat::expect_true(x < y)",
rex::rex("expect_lt(x, y) is better than expect_true(x < y)."),
expect_comparison_linter()
)

expect_lint(
"expect_true(foo(x) >= y[[2]])",
rex::rex("expect_gte(x, y) is better than expect_true(x >= y)."),
expect_comparison_linter()
)

expect_lint(
"expect_true(x <= y)",
rex::rex("expect_lte(x, y) is better than expect_true(x <= y)."),
expect_comparison_linter()
)

expect_lint(
"expect_true(x == (y == 2))",
rex::rex("expect_identical(x, y) is better than expect_true(x == y)."),
expect_comparison_linter()
)
})