-
Notifications
You must be signed in to change notification settings - Fork 187
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
Add param function_braces
to brace_linter()
#2240
base: main
Are you sure you want to change the base?
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #2240 +/- ##
=======================================
Coverage 98.18% 98.18%
=======================================
Files 125 125
Lines 5715 5729 +14
=======================================
+ Hits 5611 5625 +14
Misses 104 104 ☔ View full report in Codecov by Sentry. |
This seems too aggressive at removing lints. function(x)
AWrapper(y, x)
function(x)
AWrapper(
y,
x
) See #1807 (comment) So omitting braces is only okay if the next (and only) token that makes up the function body starts on the same line, e.g. function(x) AWrapper(
y,
x
) |
I see, sorry didn't look at the mentioned issue closely enough then. Personally, I don't care too much about these special variations, so I'd be happy with having the |
what's supported by this PR is not style guide-compliant, but that's ok, as long as the default is compliant. Agree that this does not close the referenced issue (though related -- it's even more lax). maybe we can have this parameter take 3 values instead: function_braces = c("always", "not_inline", "never") (the naming of the middle option is as usual tricky) if you're interested, give it a try to implement the middle option -- I suspect it's doable with some effort for you. Otherwise, feel free to implement the design above with a stop() for "not yet implemented" on the middle option & we'll handle it in follow up (before the subsequent release). |
@MichaelChirico Thanks for the encouragement, I think I got it. IMHO Open question: When using lintr/tests/testthat/test-expect_lint.R Line 63 in 4a8b931
Which option do you prefer?
|
The default behavior should not change, because it aligns to the style guide. Also, IINM, Also we have four levels I think: # lint on "always"
function(x) x
# lint on "multi_line" and "always"
function(x) wrapper(
x,
y
)
# lint on "???", "multi_line" and "always"
function(x)
wrapper(
x,
y
)
|
Ok. Still bears the question which
I don't think so.
I think we can go with three options only, so function(x)
x + 4
function(x) x +
4 |
Option 3 seems to be viable.
🤦 sorry, I was confused.
I think the former should lint, but the latter isn't easily distinguishable in the AST from function() wrapper(
x,
y
) so it shouldn't lint (at least for some value of |
It won't lint for |
I would like to have an option that distinguishes between function definitions where the body starts on the same line, like function(x) x +
4 , and those where the body does not start on the same line, like function(x)
x + 4 |
67b1c38
to
63c198b
Compare
Ok. I'm afraid I can't help with that. But you could extend the Footnotes
|
require_multi_line
to brace_linter()
function_braces
to brace_linter()
63c198b
to
f37b35b
Compare
Thanks for the effort, I'll try and take a look some time this week to see if I can implement the fourth option. |
@salim-b PTAL, I named it |
Also,
What do you think, @MichaelChirico? |
We should also discuss what to do about multi-line function headers. # these all trigger "always", "multi_line" and "not_inline" atm
function(
a,
b
) a + b
function(
a,
b
) a +
b
function(
a,
b
)
a + b
# this forces allow_single_line = TRUE to be set
function(
a,
b
) { a + b }
# this never lints
function(
a,
b
) {
a + b
} I'm fine with this behavior, but it does make the description of |
I'm not sure whether I understand correctly... but didn't you mean to write Anyways, I think option 3 would be bad since |
I meant https://github.com/r-lib/lintr/pull/2240/files#diff-42f1e9787771f9204786c9d84ece7bc678f89e7cbd6b3d826f9a2e62ba90da5eR325 None of these two ways to define an inline function, with or without braces, is acceptable for function(x) x
function(x) { x }
Good point. |
FYI: I'm holding off on review until after release. thanks for your patience. |
I think I'd still prefer option 1. But you and @MichaelChirico are certainly more qualified to decide on that^^
Since function headers are unrelated to braces, this wouldn't concern |
Yes and we pin down the behavior to make sure it works as intended. |
Co-authored-by: Michael Chirico <michaelchirico4@gmail.com>
Co-authored-by: Michael Chirico <michaelchirico4@gmail.com>
R/brace_linter.R
Outdated
#' `"always"` requires braces for all function definitions, including inline functions. | ||
#' `"not_inline"` requires braces when a function body does not start on the same line as its header. | ||
#' `"multi_line"` requires braces when a function definition spans multiple lines. | ||
#' `"never"` never requires braces in function bodies. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we don't have to include it in this PR, but there is one more style we might cater to in the future, namely, use braces if and only if they're syntactically required.
Consider
A <- function() { NULL }
B <- function() { NULL; NULL }
C <- function() NULL
D <- function() {
NULL
}
E <- function()
NULL
F <- function() {
NULL
NULL
}
G <- function() invisible(
)
H <- function() { invisible(
) }
And the corresponding lint table ( ✔️ ➡️ throw a lint):
function_braces= |
A | B | C | D | E | F | G | H |
---|---|---|---|---|---|---|---|---|
always | ✔️ | ✔️ | ✔️ | |||||
not_inline | ✔️ | |||||||
multi_line | ✔️ | ✔️ | ||||||
never | ||||||||
strict | ✔️ | ✔️ | ✔️ |
(PS we might consider adding any of the above as test cases if they're not yet represented)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mentioning this here to possibly align on the naming for such a style. If we can't come up with a name that's natural for the current function_braces
(or function_bodies
), we might need to think of a different parameter name to accommodate. Hope that makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use braces if and only if they're syntactically required.
What exactly do you mean by "syntactically required"? I'm confused by your examples above, A
, D
and H
are fine without braces (same result)...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What exactly do you mean by "syntactically required"?
i.e., "without braces, the code either wouldn't parse, or would parse incorrectly". In other words "function uses braces" if and only if "function has more than one expression" (I think that should be equivalent to length(body(f)) > 1
)
A, D and H are fine without braces (same result)...
Precisely why they would throw a lint (under "strict" mode, or maybe "minimalist" mode is more evocative) -- that style ditches {
whenever possible. In A/D/H, ditching {
doesn't impact the validity of the code --> lint
…fn-brace # Conflicts: # R/brace_linter.R
This reverts commit 5eaae61 (which breaks tests).
Co-authored-by: Michael Chirico <michaelchirico4@gmail.com>
…fn-brace # Conflicts: # R/brace_linter.R
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
5eaae61 broke tests, thus I reverted it.
@MichaelChirico This PR has been sitting here for |
thanks @salim-b for putting this together. I'm running off your branch for now. look forward to seeing this merged. |
#' | ||
#' @param allow_single_line if `TRUE`, allow an open and closed curly pair on the same line. | ||
#' @param allow_single_line If `TRUE`, allow an open and closed curly pair on the same line. | ||
#' @param function_bodies Whether to require function bodies to be wrapped in curly braces. One of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Whether" kind of implies a binary choice
#' @param function_bodies Whether to require function bodies to be wrapped in curly braces. One of | |
#' @param function_bodies Character string governing when function bodies without curly braces should generate lints. One of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, as @AshesITR points out, I think we should be careful here to specify "function body" for all the rules, since a function definition includes the parameters and those can take up many lines even if the function itself doesn't (not that we expect to see that except in strange cases).
tests/testthat/test-brace_linter.R
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggested new test case:
foo <- function(x) ({
x + 1
})
(I think it should lint, this is an unusual way to declare a function that should be discouraged in favor of plain {
.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, something like:
foo <- function(x) { x +
4 }
Is slightly different from the current set of cases & worth including as a regression test I think.
Fixes #1807.