r-lib / styler

Non-invasive pretty printing of R code
https://styler.r-lib.org
Other
725 stars 71 forks source link

Fails to create parseable code #1093

Closed flying-sheep closed 1 year ago

flying-sheep commented 1 year ago
Click here for contents of tests/testthat/test_escaping.r ```r has_dt <- requireNamespace('data.table', quietly = TRUE) has_tibble <- requireNamespace('tibble', quietly = TRUE) test_that('unprintables get escaped', { expect_identical(repr_html('\1'), "'\\001'") }) test_that('simple LaTeX escaping works', { expect_identical(latex_escape('\\'), '\\textbackslash{}') expect_identical(latex_escape('{}'), '\\{\\}') expect_identical(latex_escape('$'), '\\$') expect_identical(latex_escape('^'), '\\textasciicircum{}') expect_identical(latex_escape('_'), '\\_') expect_identical(latex_escape('%'), '\\%') expect_identical(latex_escape('#'), '\\#') expect_identical(latex_escape('&'), '\\&') expect_identical(latex_escape('~'), '\\textasciitilde{}') expect_identical(latex_escape('|'), '\\textbar{}') expect_identical(latex_escape('[]'), '{[}{]}') }) test_that('simple HTML escaping works', { expect_identical(html_escape('&'), '&') expect_identical(html_escape('<'), '<') expect_identical(html_escape('>'), '>') }) test_that('LaTeX escaping in vectors works', { expect_identical(repr_latex('['), "'{[}'") expect_identical(repr_latex(c('[', '|')), "\\begin{enumerate*} \\item '{[}' \\item '\\textbar{}' \\end{enumerate*} ") }) test_that('HTML escaping in vectors works', { expect_identical(repr_html('<'), "'<'") expect_identical( repr_html(c('<', '&')), paste0(list_style, "
  1. '<'
  2. '&'
\n") ) }) test_that('LaTeX escaping in matrices works', { expect_identical(repr_latex(matrix(c('[', '{', '%', '#'), 2, 2, TRUE)), 'A matrix: 2 \u00D7 2 of type chr \\begin{tabular}{ll} \t {[} & \\{\\\\ \t \\% & \\#\\\\ \\end{tabular} ') expect_identical(repr_latex(matrix(c(']', '}', '&', '_'), 2, 2, TRUE, list(c('$', '#'), c('%', '|')))), 'A matrix: 2 \u00D7 2 of type chr \\begin{tabular}{r|ll} & \\% & \\textbar{}\\\\ \\hline \t\\$ & {]} & \\}\\\\ \t\\# & \\& & \\_\\\\ \\end{tabular} ') }) test_that('HTML escaping in matrices works', { expect_identical(repr_html(matrix(c('[', '{', '%', '#'), 2, 2, TRUE)), ' \t \t
A matrix: 2 \u00D7 2 of type chr
[{
%#
') expect_identical(repr_html(matrix(c(']', '}', '&', '_'), 2, 2, TRUE, list(c('$', '#'), c('%', '|')))), ' \t \t \t
A matrix: 2 \u00D7 2 of type chr
%|
$]}
#&_
') }) test_that('LaTeX escaping in lists works', { expect_identical(repr_latex(list(lbr = '[')), "\\textbf{\\$lbr} = '{[}'") expect_identical(repr_latex(list(`&` = '%')), "\\textbf{\\$`\\&`} = '\\%'") }) test_that('HTML escaping in lists works', { expect_identical(repr_html(list(lt = '<')), "$lt = '<'") expect_identical(repr_html(list(`&` = '<')), "$`&` = '<'") }) test_that('Factors are maintained in small arrays for text', { df <- data.frame(a = 1:4, b = factor(1:4, levels = 1:4, labels = c("A", "B", "C", "D"))) expected <- " a b\n1 1 A\n2 2 B\n3 3 C\n4 4 D" expect_identical(repr_text(df), expected) if (has_dt) { dt <- data.table::as.data.table(df) answer <- repr_text(dt) expect_identical(answer, expected) } if (has_tibble) { dtbl <- tibble::as_tibble(df) answer <- repr_text(dtbl) expect_identical(answer, expected) } }) test_that('Factors are maintained in small arrays for HTML', { df <- data.frame(a = 1:4, b = factor(1:4, levels = 1:4, labels = c("A", "B", "C", "D"))) expected <- ' \t \t \t \t \t \t
A data.frame: 4 \u00D7 2
ab
<int><fct>
1A
2B
3C
4D
' expect_identical(repr_html(df), expected) if (has_dt) { dt <- data.table::as.data.table(df) expect_identical(repr_html(dt), sub('data\\.frame', 'data.table', expected)) } if (has_tibble) { dtbl <- tibble::as_tibble(df) expect_identical(repr_html(dtbl), sub('data\\.frame', 'tibble', expected)) } }) test_that('Factors are sanitized in small data.frames for HTML', { df <- data.frame(a = 1:4, b = factor(1:4, levels = 1:4, labels = c("A&", "B>", "C", "D"))) expected <- ' \t \t \t \t \t \t
A data.frame: 4 \u00D7 2
ab
<int><fct>
1A&
2B>
3C
4D
' expect_identical(repr_html(df), expected) if (has_dt) { dt <- data.table::as.data.table(df) expect_identical(repr_html(dt), sub('data\\.frame', 'data.table', expected)) } if (has_tibble) { dtbl <- tibble::as_tibble(df) expect_identical(repr_html(dtbl), sub('data\\.frame', 'tibble', expected)) } }) test_that('Factors are maintained in small arrays for LaTeX', { df <- data.frame(a = 1:4, b = factor(1:4, levels = 1:4, labels = c("A", "B", "C", "D"))) expected <- 'A data.frame: 4 \u00D7 2 \\begin{tabular}{ll} a & b\\\\ & \\\\ \\hline \t 1 & A\\\\ \t 2 & B\\\\ \t 3 & C\\\\ \t 4 & D\\\\ \\end{tabular} ' expect_identical(repr_latex(df), expected) if (has_dt) { dt <- data.table::as.data.table(df) expect_identical(repr_latex(dt), sub('data\\.frame', 'data.table', expected)) } if (has_tibble) { dtbl <- tibble::as_tibble(df) expect_identical(repr_latex(dtbl), sub('data\\.frame', 'tibble', expected)) } }) test_that('Factors are sanitized in small data.frames for LaTeX', { df <- data.frame(a = 1:4, b = factor(1:4, levels = 1:4, labels = c("A&", "B%", "_C_", "D"))) expected <- 'A data.frame: 4 \u00D7 2 \\begin{tabular}{ll} a & b\\\\ & \\\\ \\hline \t 1 & A\\& \\\\ \t 2 & B\\% \\\\ \t 3 & \\_C\\_\\\\ \t 4 & D \\\\ \\end{tabular} ' expect_identical(repr_latex(df), expected) if (has_dt) { dt <- data.table::as.data.table(df) expect_identical(repr_latex(dt), sub('data\\.frame', 'data.table', expected)) } if (has_tibble) { dtbl <- tibble::as_tibble(df) expect_identical(repr_latex(dtbl), sub('data\\.frame', 'tibble', expected)) } }) test_that('vector entries with consecutive spaces get wrapped', { v <- c('one space', 'two spaces') expect_identical( repr_html(v), paste0( list_style, '
    ', "
  1. 'one space'
  2. ", "
  3. 'two spaces'
  4. ", '
\n' ) ) }) ```
styler::style_file("tests/testthat/test_escaping.r")
#> Styling  1  files:
#>  tests/testthat/test_escaping.r
#> Warning: When processing tests/testthat/test_escaping.r: 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.
#> ⚠ 
#> ────────────────────────────────────────
#> Status   Count   Legend 
#> ✔    0   File unchanged.
#> ℹ    0   File changed.
#> ✖    1   Styling threw an error.
#> ────────────────────────────────────────

Created on 2023-01-26 with reprex v2.0.2

lorenzwalthert commented 1 year ago

Thanks. Would you mind searching for the part of the code that causes the trouble, i.e. binary search on the expression level? That is, trying if styler also fails if you only style first half of the code, if yes, first 1/4, if no 2/4 etc.

MichaelChirico commented 1 year ago

My first guess was this is #991 but my quick scroll didn't see the exact culprit.

lorenzwalthert commented 1 year ago

Reprex:

styler::style_text("'\\1'")
#> Error in `value[[3L]]()`:
#> ! 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.

#> Backtrace:
#>      ▆
#>   1. ├─styler::style_text("'\\1'")
#>   2. │ └─styler (local) transformer(text) at styler/R/ui-styling.R:217:2
#>   3. │   └─text %>% ... at styler/R/transform-files.R:120:6
#>   4. └─styler:::parse_transform_serialize_r(...)
#>   5.   └─styler:::verify_roundtrip(text, text_out, parsable_only = !parse_tree_must_be_identical(transformers)) at styler/R/transform-files.R:287:2
#>   6.     └─rlang::with_handlers(...) at styler/R/transform-files.R:416:4
#>   7.       └─base::tryCatch(.expr, error = `<fn>`)
#>   8.         └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#>   9.           └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#>  10.             └─value[[3L]](cond)
#>  11.               └─rlang::abort(...) at styler/R/transform-files.R:419:8

Created on 2023-02-05 with reprex v2.0.2

MichaelChirico commented 1 year ago

indeed a dupe of #991.

@flying-sheep, the workaround is to use three-digit octal escapes, i.e. \001 instead of \1.