Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
* ensure a trailing blank line also if the input is cached (#867).
* Preserve trailing blank line in roxygen examples to simplify concatenation of
examples (#880).
* added guarantee that styled code is parsable (#892).
* An error is now thrown on styling if input unicode characters can't be
correctly parsed for Windows and R < 4.2 (#883).
* Fix argument name `filetype` in Example for `style_dir()` (#855).
Expand Down
4 changes: 2 additions & 2 deletions R/communicate.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
#' changes carefully.
#' @param changed Boolean with indicating for each file whether or not it has
#' been changed.
#' @inheritParams can_verify_roundtrip
#' @inheritParams parse_tree_must_be_identical
#' @keywords internal
communicate_warning <- function(changed, transformers) {
if (any(changed, na.rm = TRUE) &&
!can_verify_roundtrip(transformers) &&
!parse_tree_must_be_identical(transformers) &&
!getOption("styler.quiet", FALSE)
) {
cat("Please review the changes carefully!", fill = TRUE)
Expand Down
34 changes: 26 additions & 8 deletions R/transform-files.R
Original file line number Diff line number Diff line change
Expand Up @@ -246,9 +246,11 @@ parse_transform_serialize_r <- function(text,
) %>%
unlist()

if (can_verify_roundtrip(transformers)) {
verify_roundtrip(text, text_out)
}
verify_roundtrip(
text, text_out,
parsable_only = !parse_tree_must_be_identical(transformers)
)

text_out <- convert_newlines_to_linebreaks(text_out)
if (cache_is_activated()) {
cache_by_expression(text_out, transformers, more_specs = more_specs)
Expand Down Expand Up @@ -349,18 +351,23 @@ apply_transformers <- function(pd_nested, transformers) {
#' @param transformers The list of transformer functions used for styling.
#' Needed for reverse engineering the scope.
#' @keywords internal
can_verify_roundtrip <- function(transformers) {
parse_tree_must_be_identical <- function(transformers) {
length(transformers$token) == 0
}

#' Verify the styling
#'
#' If scope was set to "line_breaks" or lower (compare [tidyverse_style()]),
#' we can compare the expression before and after styling and return an error if
#' it is not the same. Note that this method ignores roxygen code examples and
#' it is not the same.
#' If that's not possible, a weaker guarantee that we want to give is that the
#' resulting code is parsable.
#' @param parsable_only If we should only check for the code to be parsable.
#' @inheritParams expressions_are_identical
#' @section Limitation:
#' Note that this method ignores roxygen code examples and
#' comments and no verification can be conducted if tokens are in the styling
#' scope.
#' @inheritParams expressions_are_identical
#' @importFrom rlang abort
#' @examples
#' styler:::verify_roundtrip("a+1", "a + 1")
Expand All @@ -369,8 +376,19 @@ can_verify_roundtrip <- function(transformers) {
#' styler:::verify_roundtrip("a+1", "b - 3")
#' }
#' @keywords internal
verify_roundtrip <- function(old_text, new_text) {
if (!expressions_are_identical(old_text, new_text)) {
verify_roundtrip <- function(old_text, new_text, parsable_only = FALSE) {
if (parsable_only) {
rlang::with_handlers(
parse_safely(new_text),
error = function(e) {
rlang::abort(paste0(
"Styling resulted in code that isn't parsable. This should not ",
"happen. Please file a bug report on GitHub ",
"(https://github.com/r-lib/styler/issues) using a reprex."
))
}
)
} else if (!expressions_are_identical(old_text, new_text)) {
msg <- paste(
"The expression evaluated before the styling is not the same as the",
"expression after styling. This should not happen. Please file a",
Expand Down

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

13 changes: 11 additions & 2 deletions man/verify_roundtrip.Rd

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

4 changes: 2 additions & 2 deletions tests/testthat/escaping/basic-escape-in.R
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ x <- ' 2' # there is a tab emebbed (created with writeLines("x <- '\t2'"))
x <- '\001'
'\x01'

"\01"
'\01'
"\001"
'\001'

#' things
#'
Expand Down
8 changes: 4 additions & 4 deletions tests/testthat/escaping/basic-escape-in_tree

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

4 changes: 2 additions & 2 deletions tests/testthat/escaping/basic-escape-out.R
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ x <- " 2" # there is a tab emebbed (created with writeLines("x <- '\t2'"))
x <- "\001"
"\x01"

"\0"
"\0"
"\001"
"\001"

#' things
#'
Expand Down
26 changes: 21 additions & 5 deletions tests/testthat/test-roundtrip.R
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
context("roundtrip works")


test_that("can_verify_roundtrip works", {
expect_true(can_verify_roundtrip(tidyverse_style(scope = "line_breaks")))
expect_true(can_verify_roundtrip(tidyverse_style(scope = "spaces")))
expect_true(can_verify_roundtrip(tidyverse_style(scope = "indention")))
expect_false(can_verify_roundtrip(tidyverse_style(scope = "tokens")))
test_that("parse_tree_must_be_identical works", {
expect_true(
parse_tree_must_be_identical(tidyverse_style(scope = "line_breaks"))
)
expect_true(parse_tree_must_be_identical(tidyverse_style(scope = "spaces")))
expect_true(
parse_tree_must_be_identical(tidyverse_style(scope = "indention"))
)
expect_false(parse_tree_must_be_identical(tidyverse_style(scope = "tokens")))
})

test_that("correct styling does not give an error", {
Expand All @@ -15,3 +19,15 @@ test_that("correct styling does not give an error", {
test_that("corrupt styling does give an error", {
expect_error(verify_roundtrip("1-1", "1 + 1"), "bug")
})


test_that("the output is asserted to be parsable", {
expect_error(
verify_roundtrip("1+1", "1 +) 1", parsable_only = TRUE),
"Styling resulted in code that isn't parsable."
)

expect_silent(
verify_roundtrip("1+1", "1 + 1", parsable_only = TRUE)
)
})