diff --git a/NEWS.md b/NEWS.md index e49c0bf93..55d7d5e80 100644 --- a/NEWS.md +++ b/NEWS.md @@ -62,7 +62,7 @@ * `print_linter()` for discouraging usage of `print()` on string literals like `print("Reached here")` or `print(paste("Found", nrow(DF), "rows."))` (#1894, @MichaelChirico). * `unnecessary_nesting_linter()` for discouraging overly-nested code where an early return or eliminated sub-expression (inside '{') is preferable (#2317 and part of #884, @MichaelChirico). * `consecutive_mutate_linter()` for encouraging consecutive calls to `dplyr::mutate()` to be combined (part of #884, @MichaelChirico). -* `if_switch_linter()` for encouraging `switch()` over repeated `if`/`else` tests (part of #884, @MichaelChirico). +* `if_switch_linter()` for encouraging `switch()` over repeated `if`/`else` tests (#2322 and part of #884, @MichaelChirico). * `nested_pipe_linter()` for discouraging pipes within pipes, e.g. `df1 %>% inner_join(df2 %>% select(a, b))` (part of #884, @MichaelChirico). * `nrow_subset_linter()` for discouraging usage like `nrow(subset(x, conditions))` in favor of something like `with(x, sum(conditions))` which doesn't require a full subset of `x` (#2313, #2314 and part of #884, @MichaelChirico). * `pipe_return_linter()` for discouraging usage of `return()` inside a {magrittr} pipeline (part of #884, @MichaelChirico). diff --git a/R/if_switch_linter.R b/R/if_switch_linter.R index 97b985dac..89415ed94 100644 --- a/R/if_switch_linter.R +++ b/R/if_switch_linter.R @@ -14,6 +14,12 @@ #' approach is roughly linear in the number of conditions that need to #' be evaluated, here up to 3 times). #' +#' @param max_branch_lines,max_branch_expressions Integer, default 0 indicates "no maximum". +#' If set any `if`/`else if`/.../`else` chain where any branch occupies more than +#' this number of lines (resp. expressions) will not be linted. The conjugate +#' applies to `switch()` statements -- if these parameters are set, any `switch()` +#' statement with any overly-complicated branches will be linted. See examples. +#' #' @examples #' # will produce lints #' lint( @@ -21,6 +27,64 @@ #' linters = if_switch_linter() #' ) #' +#' code <- paste( +#' "if (x == 'a') {", +#' " 1", +#' "} else if (x == 'b') {", +#' " 2", +#' "} else if (x == 'c') {", +#' " y <- x", +#' " z <- sqrt(match(y, letters))", +#' " z", +#' "}", +#' sep = "\n" +#' ) +#' writeLines(code) +#' lint( +#' text = code, +#' linters = if_switch_linter() +#' ) +#' +#' code <- paste( +#' "if (x == 'a') {", +#' " 1", +#' "} else if (x == 'b') {", +#' " 2", +#' "} else if (x == 'c') {", +#' " y <- x", +#' " z <- sqrt(", +#' " match(y, letters)", +#' " )", +#' " z", +#' "}", +#' sep = "\n" +#' ) +#' writeLines(code) +#' lint( +#' text = code, +#' linters = if_switch_linter() +#' ) +#' +#' code <- paste( +#' "switch(x,", +#' " a = {", +#' " 1", +#' " 2", +#' " 3", +#' " },", +#' " b = {", +#' " 1", +#' " 2", +#' " }", +#' ")", +#' sep = "\n" +#' ) +#' writeLines(code) +#' lint( +#' text = code, +#' linters = if_switch_linter(max_branch_lines = 2L) +#' ) +#' #' # okay #' lint( #' text = "switch(x, a = 1, b = 2, 3)", @@ -33,18 +97,105 @@ #' linters = if_switch_linter() #' ) #' +#' code <- paste( +#' "if (x == 'a') {", +#' " 1", +#' "} else if (x == 'b') {", +#' " 2", +#' "} else if (x == 'c') {", +#' " y <- x", +#' " z <- sqrt(match(y, letters))", +#' " z", +#' "}", +#' sep = "\n" +#' ) +#' writeLines(code) +#' lint( +#' text = code, +#' linters = if_switch_linter(max_branch_lines = 2L) +#' ) +#' +#' code <- paste( +#' "if (x == 'a') {", +#' " 1", +#' "} else if (x == 'b') {", +#' " 2", +#' "} else if (x == 'c') {", +#' " y <- x", +#' " z <- sqrt(", +#' " match(y, letters)", +#' " )", +#' " z", +#' "}", +#' sep = "\n" +#' ) +#' writeLines(code) +#' lint( +#' text = code, +#' linters = if_switch_linter(max_branch_expressions = 2L) +#' ) +#' +#' code <- paste( +#' "switch(x,", +#' " a = {", +#' " 1", +#' " 2", +#' " 3", +#' " },", +#' " b = {", +#' " 1", +#' " 2", +#' " }", +#' ")", +#' sep = "\n" +#' ) +#' writeLines(code) +#' lint( +#' text = code, +#' linters = if_switch_linter(max_branch_lines = 3L) +#' ) +#' #' @evalRd rd_tags("if_switch_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export -if_switch_linter <- function() { - equal_str_cond <- "expr[1][EQ and expr[STR_CONST]]" +if_switch_linter <- function(max_branch_lines = 0L, max_branch_expressions = 0L) { + equal_str_cond <- "expr[1][EQ and expr/STR_CONST]" + + if (max_branch_lines > 0L || max_branch_expressions > 0L) { + complexity_cond <- xp_or(c( + if (max_branch_lines > 0L) paste("OP-RIGHT-BRACE/@line2 - OP-LEFT-BRACE/@line1 > 1 +", max_branch_lines), + if (max_branch_expressions > 0L) paste("count(expr) >", max_branch_expressions) + )) + branch_expr_cond <- xp_and(c( + xp_or( + # if (x) { } ... + xp_and("preceding-sibling::IF", "position() = 2"), + # if (x) { ... } else { } + xp_and("preceding-sibling::ELSE", "not(IF)") + ), + complexity_cond + )) + max_lines_cond <- glue(".//expr[{branch_expr_cond}]") + + switch_xpath <- glue(" + parent::expr + /parent::expr[expr[ + position() > 2 + and {complexity_cond} + ]] + ") + } else { + max_lines_cond <- "false" + + switch_xpath <- NULL + } # NB: IF AND {...} AND ELSE/... implies >= 3 equality conditions are present # .//expr/IF/...: the expr in `==` that's _not_ the STR_CONST # not(preceding::IF): prevent nested matches which might be incorrect globally # not(. != .): don't match if there are _any_ expr which _don't_ match the top # expr - xpath <- glue(" + if_xpath <- glue(" //IF /parent::expr[ not(preceding-sibling::IF) @@ -58,15 +209,16 @@ if_switch_linter <- function() { .//expr/IF/following-sibling::{equal_str_cond}/expr[not(STR_CONST)] != expr[1][EQ]/expr[not(STR_CONST)] ) + and not({ max_lines_cond }) ] ") Linter(linter_level = "expression", function(source_expression) { xml <- source_expression$xml_parsed_content - bad_expr <- xml_find_all(xml, xpath) + bad_expr <- xml_find_all(xml, if_xpath) - xml_nodes_to_lints( + lints <- xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = paste( @@ -76,5 +228,19 @@ if_switch_linter <- function() { ), type = "warning" ) + + if (!is.null(switch_xpath)) { + xml_calls <- source_expression$xml_find_function_calls("switch") + switch_expr <- xml_find_all(xml_calls, switch_xpath) + + lints <- c(lints, xml_nodes_to_lints( + switch_expr, + source_expression = source_expression, + lint_message = "Prefer repeated if/else statements over overly-complicated switch() statements.", + type = "warning" + )) + } + + lints }) } diff --git a/inst/lintr/linters.csv b/inst/lintr/linters.csv index 77907fb74..f6d614775 100644 --- a/inst/lintr/linters.csv +++ b/inst/lintr/linters.csv @@ -38,7 +38,7 @@ function_argument_linter,style consistency best_practices function_left_parentheses_linter,style readability default function_return_linter,readability best_practices if_not_else_linter,readability consistency configurable -if_switch_linter,best_practices readability consistency efficiency +if_switch_linter,best_practices readability consistency efficiency configurable ifelse_censor_linter,best_practices efficiency implicit_assignment_linter,style best_practices readability configurable implicit_integer_linter,style consistency best_practices configurable diff --git a/man/configurable_linters.Rd b/man/configurable_linters.Rd index 4c3da9ec1..cb1c17a54 100644 --- a/man/configurable_linters.Rd +++ b/man/configurable_linters.Rd @@ -24,6 +24,7 @@ The following linters are tagged with 'configurable': \item{\code{\link{duplicate_argument_linter}}} \item{\code{\link{fixed_regex_linter}}} \item{\code{\link{if_not_else_linter}}} +\item{\code{\link{if_switch_linter}}} \item{\code{\link{implicit_assignment_linter}}} \item{\code{\link{implicit_integer_linter}}} \item{\code{\link{indentation_linter}}} diff --git a/man/if_switch_linter.Rd b/man/if_switch_linter.Rd index e1254ff79..8a7cd302b 100644 --- a/man/if_switch_linter.Rd +++ b/man/if_switch_linter.Rd @@ -4,7 +4,14 @@ \alias{if_switch_linter} \title{Require usage of switch() over repeated if/else blocks} \usage{ -if_switch_linter() +if_switch_linter(max_branch_lines = 0L, max_branch_expressions = 0L) +} +\arguments{ +\item{max_branch_lines, max_branch_expressions}{Integer, default 0 indicates "no maximum". +If set any \code{if}/\verb{else if}/.../\verb{else} chain where any branch occupies more than +this number of lines (resp. expressions) will not be linted. The conjugate +applies to \code{switch()} statements -- if these parameters are set, any \code{switch()} +statement with any overly-complicated branches will be linted. See examples.} } \description{ \code{\link[=switch]{switch()}} statements in R are used to delegate behavior based @@ -29,6 +36,64 @@ lint( linters = if_switch_linter() ) +code <- paste( + "if (x == 'a') {", + " 1", + "} else if (x == 'b') {", + " 2", + "} else if (x == 'c') {", + " y <- x", + " z <- sqrt(match(y, letters))", + " z", + "}", + sep = "\n" +) +writeLines(code) +lint( + text = code, + linters = if_switch_linter() +) + +code <- paste( + "if (x == 'a') {", + " 1", + "} else if (x == 'b') {", + " 2", + "} else if (x == 'c') {", + " y <- x", + " z <- sqrt(", + " match(y, letters)", + " )", + " z", + "}", + sep = "\n" +) +writeLines(code) +lint( + text = code, + linters = if_switch_linter() +) + +code <- paste( + "switch(x,", + " a = {", + " 1", + " 2", + " 3", + " },", + " b = {", + " 1", + " 2", + " }", + ")", + sep = "\n" +) +writeLines(code) +lint( + text = code, + linters = if_switch_linter(max_branch_lines = 2L) +) + # okay lint( text = "switch(x, a = 1, b = 2, 3)", @@ -41,10 +106,68 @@ lint( linters = if_switch_linter() ) +code <- paste( + "if (x == 'a') {", + " 1", + "} else if (x == 'b') {", + " 2", + "} else if (x == 'c') {", + " y <- x", + " z <- sqrt(match(y, letters))", + " z", + "}", + sep = "\n" +) +writeLines(code) +lint( + text = code, + linters = if_switch_linter(max_branch_lines = 2L) +) + +code <- paste( + "if (x == 'a') {", + " 1", + "} else if (x == 'b') {", + " 2", + "} else if (x == 'c') {", + " y <- x", + " z <- sqrt(", + " match(y, letters)", + " )", + " z", + "}", + sep = "\n" +) +writeLines(code) +lint( + text = code, + linters = if_switch_linter(max_branch_expressions = 2L) +) + +code <- paste( + "switch(x,", + " a = {", + " 1", + " 2", + " 3", + " },", + " b = {", + " 1", + " 2", + " }", + ")", + sep = "\n" +) +writeLines(code) +lint( + text = code, + linters = if_switch_linter(max_branch_lines = 3L) +) + } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ -\link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability} +\link[=best_practices_linters]{best_practices}, \link[=configurable_linters]{configurable}, \link[=consistency_linters]{consistency}, \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability} } diff --git a/man/linters.Rd b/man/linters.Rd index 838044aad..10c45d374 100644 --- a/man/linters.Rd +++ b/man/linters.Rd @@ -19,7 +19,7 @@ The following tags exist: \itemize{ \item{\link[=best_practices_linters]{best_practices} (63 linters)} \item{\link[=common_mistakes_linters]{common_mistakes} (11 linters)} -\item{\link[=configurable_linters]{configurable} (42 linters)} +\item{\link[=configurable_linters]{configurable} (43 linters)} \item{\link[=consistency_linters]{consistency} (32 linters)} \item{\link[=correctness_linters]{correctness} (7 linters)} \item{\link[=default_linters]{default} (26 linters)} @@ -74,7 +74,7 @@ The following linters exist: \item{\code{\link{function_left_parentheses_linter}} (tags: default, readability, style)} \item{\code{\link{function_return_linter}} (tags: best_practices, readability)} \item{\code{\link{if_not_else_linter}} (tags: configurable, consistency, readability)} -\item{\code{\link{if_switch_linter}} (tags: best_practices, consistency, efficiency, readability)} +\item{\code{\link{if_switch_linter}} (tags: best_practices, configurable, consistency, efficiency, readability)} \item{\code{\link{ifelse_censor_linter}} (tags: best_practices, efficiency)} \item{\code{\link{implicit_assignment_linter}} (tags: best_practices, configurable, readability, style)} \item{\code{\link{implicit_integer_linter}} (tags: best_practices, configurable, consistency, style)} diff --git a/tests/testthat/test-if_switch_linter.R b/tests/testthat/test-if_switch_linter.R index b321e680e..e6b3e5fe5 100644 --- a/tests/testthat/test-if_switch_linter.R +++ b/tests/testthat/test-if_switch_linter.R @@ -77,3 +77,440 @@ test_that("multiple lints have right metadata", { if_switch_linter() ) }) + +test_that("max_branch_lines= and max_branch_expressions= arguments work", { + max_lines2_linter <- if_switch_linter(max_branch_lines = 2L) + max_lines4_linter <- if_switch_linter(max_branch_lines = 4L) + max_expr2_linter <- if_switch_linter(max_branch_expressions = 2L) + max_expr4_linter <- if_switch_linter(max_branch_expressions = 4L) + lint_msg <- rex::rex("Prefer switch() statements over repeated if/else equality tests") + + one_per_branch_lines <- trim_some(" + if (x == 'a') { + 1 + } else if (x == 'b') { + 2 + } else if (x == 'c') { + 3 + } + ") + expect_lint(one_per_branch_lines, lint_msg, max_lines2_linter) + expect_lint(one_per_branch_lines, lint_msg, max_lines4_linter) + expect_lint(one_per_branch_lines, lint_msg, max_expr2_linter) + expect_lint(one_per_branch_lines, lint_msg, max_expr4_linter) + + two_per_branch_lines <- trim_some(" + if (x == 'a') { + 1 + 2 + } else if (x == 'b') { + 3 + 4 + } else if (x == 'c') { + 5 + 6 + } + ") + expect_lint(two_per_branch_lines, lint_msg, max_lines2_linter) + expect_lint(two_per_branch_lines, lint_msg, max_lines4_linter) + expect_lint(two_per_branch_lines, lint_msg, max_expr2_linter) + expect_lint(two_per_branch_lines, lint_msg, max_expr4_linter) + + three_per_branch_lines <- trim_some(" + if (x == 'a') { + 1 + 2 + 3 + } else if (x == 'b') { + 4 + 5 + 6 + } else if (x == 'c') { + 7 + 8 + 9 + } + ") + expect_lint(three_per_branch_lines, NULL, max_lines2_linter) + expect_lint(three_per_branch_lines, lint_msg, max_lines4_linter) + expect_lint(three_per_branch_lines, NULL, max_expr2_linter) + expect_lint(three_per_branch_lines, lint_msg, max_expr4_linter) + + five_per_branch_lines <- trim_some(" + if (x == 'a') { + 1 + 2 + 3 + 4 + 5 + } else if (x == 'b') { + 6 + 7 + 8 + 9 + 10 + } else if (x == 'c') { + 11 + 12 + 13 + 14 + 15 + } + ") + expect_lint(five_per_branch_lines, NULL, max_lines2_linter) + expect_lint(five_per_branch_lines, NULL, max_lines4_linter) + expect_lint(five_per_branch_lines, NULL, max_expr2_linter) + expect_lint(five_per_branch_lines, NULL, max_expr4_linter) + + five_lines_three_expr_lines <- trim_some(" + if (x == 'a') { + 1 + 2 + foo( + x + ) + } else if (x == 'b') { + 6 + 7 + bar( + y + ) + } else if (x == 'c') { + 11 + 12 + baz( + z + ) + } + ") + expect_lint(five_lines_three_expr_lines, NULL, max_lines2_linter) + expect_lint(five_lines_three_expr_lines, NULL, max_lines4_linter) + expect_lint(five_lines_three_expr_lines, NULL, max_expr2_linter) + expect_lint( + five_lines_three_expr_lines, + list(lint_msg, line_number = 1L), + max_expr4_linter + ) + + five_expr_three_lines_lines <- trim_some(" + if (x == 'a') { + 1 + 2 + 3; 4; 5 + } else if (x == 'b') { + 6 + 7 + 8; 9; 10 + } else if (x == 'c') { + 11 + 12 + 13; 14; 15 + } + ") + expect_lint(five_expr_three_lines_lines, NULL, max_lines2_linter) + expect_lint( + five_expr_three_lines_lines, + list(lint_msg, line_number = 1L), + max_lines4_linter + ) + expect_lint(five_expr_three_lines_lines, NULL, max_expr2_linter) + expect_lint(five_expr_three_lines_lines, NULL, max_expr4_linter) +}) + +test_that("max_branch_lines= and max_branch_expressions= block over-complex switch() too", { + max_lines2_linter <- if_switch_linter(max_branch_lines = 2L) + max_lines4_linter <- if_switch_linter(max_branch_lines = 4L) + max_expr2_linter <- if_switch_linter(max_branch_expressions = 2L) + max_expr4_linter <- if_switch_linter(max_branch_expressions = 4L) + lint_msg <- rex::rex("Prefer repeated if/else statements over overly-complicated switch() statements.") + + one_per_branch_lines <- trim_some(" + switch(x, + a = { + 1 + }, + b = { + 2 + }, + c = { + 3 + } + ) + ") + expect_lint(one_per_branch_lines, NULL, max_lines2_linter) + expect_lint(one_per_branch_lines, NULL, max_lines4_linter) + expect_lint(one_per_branch_lines, NULL, max_expr2_linter) + expect_lint(one_per_branch_lines, NULL, max_expr4_linter) + + two_per_branch_lines <- trim_some(" + switch(x, + a = { + 1 + 2 + }, + b = { + 3 + 4 + }, + c = { + 5 + 6 + } + ) + ") + expect_lint(two_per_branch_lines, NULL, max_lines2_linter) + expect_lint(two_per_branch_lines, NULL, max_lines4_linter) + expect_lint(two_per_branch_lines, NULL, max_expr2_linter) + expect_lint(two_per_branch_lines, NULL, max_expr4_linter) + + three_per_branch_lines <- trim_some(" + switch(x, + a = { + 1 + 2 + 3 + }, + b = { + 4 + 5 + 6 + }, + c = { + 7 + 8 + 9 + } + ) + ") + expect_lint( + three_per_branch_lines, + list(lint_msg, line_number = 1L), + max_lines2_linter + ) + expect_lint(three_per_branch_lines, NULL, max_lines4_linter) + expect_lint( + three_per_branch_lines, + list(lint_msg, line_number = 1L), + max_expr2_linter + ) + expect_lint(three_per_branch_lines, NULL, max_expr4_linter) + + five_per_branch_lines <- trim_some(" + switch(x, + a = { + 1 + 2 + 3 + 4 + 5 + }, + b = { + 6 + 7 + 8 + 9 + 10 + }, + c = { + 11 + 12 + 13 + 14 + 15 + } + ) + ") + expect_lint(five_per_branch_lines, lint_msg, max_lines2_linter) + expect_lint(five_per_branch_lines, lint_msg, max_lines4_linter) + expect_lint(five_per_branch_lines, lint_msg, max_expr2_linter) + expect_lint(five_per_branch_lines, lint_msg, max_expr4_linter) + + five_lines_three_expr_lines <- trim_some(" + switch(x, + a = { + 1 + 2 + foo( + x + ) + }, + b = { + 6 + 7 + bar( + y + ) + }, + c = { + 11 + 12 + baz( + z + ) + } + ) + ") + expect_lint(five_lines_three_expr_lines, lint_msg, max_lines2_linter) + expect_lint(five_lines_three_expr_lines, lint_msg, max_lines4_linter) + expect_lint(five_lines_three_expr_lines, lint_msg, max_expr2_linter) + expect_lint(five_lines_three_expr_lines, NULL, max_expr4_linter) + + five_expr_three_lines_lines <- trim_some(" + switch(x, + a = { + 1 + 2 + 3; 4; 5 + }, + b = { + 6 + 7 + 8; 9; 10 + }, + c = { + 11 + 12 + 13; 14; 15 + } + ) + ") + expect_lint(five_expr_three_lines_lines, lint_msg, max_lines2_linter) + expect_lint(five_expr_three_lines_lines, NULL, max_lines4_linter) + expect_lint(five_expr_three_lines_lines, lint_msg, max_expr2_linter) + expect_lint(five_expr_three_lines_lines, lint_msg, max_expr4_linter) +}) + +test_that("max_branch_lines= and max_branch_expressions= interact correctly", { + linter <- if_switch_linter(max_branch_lines = 5L, max_branch_expressions = 3L) + lint_msg <- rex::rex("Prefer switch() statements over repeated if/else equality tests") + + expect_lint( + trim_some(" + if (x == 'a') { + 1 + } else if (x == 'b') { + 2 + } else if (x == 'c') { + 3 + } + "), + lint_msg, + linter + ) + + expect_lint( + trim_some(" + if (x == 'a') { + foo( + x1, + x2, + x3, + x4 + ) + } else if (x == 'b') { + 2 + } else if (x == 'c') { + 3 + } + "), + NULL, + linter + ) + + expect_lint( + trim_some(" + if (x == 'a') { + 1; 2; 3; 4 + } else if (x == 'b') { + 5 + } else if (x == 'c') { + 6 + } + "), + NULL, + linter + ) +}) + +test_that("max_branch_lines= and max_branch_expressions= work for a terminal 'else' branch", { + max_lines2_linter <- if_switch_linter(max_branch_lines = 2L) + max_expr2_linter <- if_switch_linter(max_branch_expressions = 2L) + lint_msg <- rex::rex("Prefer repeated if/else statements over overly-complicated switch() statements.") + + else_long_lines <- trim_some(" + if (x == 'a') { + 1 + } else if (x == 'b') { + 2 + } else if (x == 'c') { + 3 + } else { + 4 + 5 + 6 + } + ") + expect_lint(else_long_lines, NULL, max_lines2_linter) + expect_lint(else_long_lines, NULL, max_expr2_linter) + + default_long_lines <- trim_some(" + switch(x, + a = { + 1 + }, + b = { + 2 + }, + c = { + 3 + }, + { + 4 + 5 + 6 + } + ) + ") + expect_lint(default_long_lines, lint_msg, max_lines2_linter) + expect_lint(default_long_lines, lint_msg, max_expr2_linter) +}) + +test_that("max_branch_lines= and max_branch_expressions= are guided by the most complex branch", { + max_lines2_linter <- if_switch_linter(max_branch_lines = 2L) + max_expr2_linter <- if_switch_linter(max_branch_expressions = 2L) + lint_msg <- rex::rex("Prefer repeated if/else statements over overly-complicated switch() statements.") + + # no lint if _any_ branch is too complex + if_else_one_branch_lines <- trim_some(" + if (x == 'a') { + 1 + } else if (x == 'b') { + 2 + } else if (x == 'c') { + 3 + 4 + 5 + } + ") + expect_lint(if_else_one_branch_lines, NULL, max_lines2_linter) + expect_lint(if_else_one_branch_lines, NULL, max_expr2_linter) + + # lint if _any_ branch is too complex + switch_one_branch_lines <- trim_some(" + switch(x, + a = { + 1 + }, + b = { + 2 + }, + c = { + 3 + 4 + 5 + } + ) + ") + expect_lint(switch_one_branch_lines, lint_msg, max_lines2_linter) + expect_lint(switch_one_branch_lines, lint_msg, max_expr2_linter) +})