From ca398c2de670bf67741627de51c3ba8f4db12937 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 17 May 2022 23:22:21 +0200 Subject: [PATCH] fix #2126: check `file` and `code` chunk option when checking label duplication (#2127) Co-authored-by: Yihui Xie --- NEWS.md | 2 ++ R/block.R | 27 +++++++++++++++++++-------- R/parser.R | 3 ++- tests/testit/test-parser.R | 20 ++++++++++++++++++++ 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/NEWS.md b/NEWS.md index 38a68280ef..2cdcbb12cd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,8 @@ - The internal function `knitr:::wrap()` has been removed from this package. If you rely on this function, you will have to use the exported function `knitr::sew()` instead. +- Duplicate chunk label error will now be thrown with code chunks using `code` or `file` chunk options (thanks, @mine-cetinkaya-rundel, #2126). + # CHANGES IN knitr VERSION 1.39 ## MAJOR CHANGES diff --git a/R/block.R b/R/block.R index c68655e683..ba592a9c57 100644 --- a/R/block.R +++ b/R/block.R @@ -30,14 +30,7 @@ call_block = function(block) { if (inherits(params$ref.label, 'AsIs') && is.null(params$opts.label)) params$opts.label = ref.label } - # if chunk option 'file' is provided, read the file(s) as the chunk body; - # otherwise if 'code' is provided, use it; if neither 'file' nor 'code' is - # provided, use the chunk body - params[["code"]] = if (is.null(code_file <- params[['file']])) { - params[["code"]] %n% unlist(knit_code$get(ref.label), use.names = FALSE) - } else { - in_input_dir(xfun::read_all(code_file)) - } + params[['code']] = get_code(params, label, ref.label) # opts.label = TRUE means inheriting chunk options from ref.label if (isTRUE(params$opts.label)) params$opts.label = ref.label @@ -125,6 +118,24 @@ call_block = function(block) { block_exec(params) } +# if chunk option 'file' is provided, read the file(s) as the chunk body; +# otherwise if 'code' is provided, use it; if neither 'file' nor 'code' is +# provided, use the chunk body +get_code = function(params, label, ref.label) { + if (is.null(code <- params[['code']]) && is.null(file <- params[['file']])) + return(unlist(knit_code$get(ref.label), use.names = FALSE)) + if (!is.null(file)) code = in_input_dir(xfun::read_all(file)) + set_code(label, code) + code +} + +# replace code in knit_code but preserve attributes +set_code = function(label, code) { + res = knit_code$get(label) + attributes(code) = attributes(res) + knit_code$set(setNames(list(code), label)) +} + # options that should affect cache when cache level = 1,2 cache1.opts = c('code', 'eval', 'cache', 'cache.path', 'cache.globals', 'message', 'warning', 'error') # more options affecting cache level 2 diff --git a/R/parser.R b/R/parser.R index 083d303f94..b5bfea2852 100644 --- a/R/parser.R +++ b/R/parser.R @@ -105,7 +105,7 @@ parse_block = function(code, header, params.src, markdown_mode = out_format('mar code = parts$code label = params$label; .knitEnv$labels = c(.knitEnv$labels, label) - if (length(code)) { + if (length(code) || length(params$file) || length(params$code)) { if (label %in% names(knit_code$get())) { if (identical(getOption('knitr.duplicate.label'), 'allow')) { params$label = label = unnamed_chunk(label) @@ -114,6 +114,7 @@ parse_block = function(code, header, params.src, markdown_mode = out_format('mar one_string(knit_code$get(label)) ) } + code = as.character(code) knit_code$set(setNames(list(structure(code, chunk_opts = params)), label)) } diff --git a/tests/testit/test-parser.R b/tests/testit/test-parser.R index 345d26e218..1033e0a92b 100644 --- a/tests/testit/test-parser.R +++ b/tests/testit/test-parser.R @@ -95,3 +95,23 @@ assert( ) knit_code$restore() + +# duplication of labels + +knit_code$restore(list(a = '1+1')) + +assert( + !has_error(parse_block(NULL, '', 'label = "a"')), + has_error(parse_block('2+2', '', 'label = "a"')), + has_error(parse_block(NULL, '','label = "a", code = "2+2"')), + has_error(parse_block(NULL, '','label = "a", file = "dummy.R"')) +) +op = options(knitr.duplicate.label = 'allow') +assert( + !has_error(parse_block('2+2', '', 'label = "a"')), + !has_error(parse_block(NULL, '','label = "a", code = "2+2"')), + !has_error(parse_block(NULL, '','label = "a", file = "dummy.R"')) +) +options(op) + +knit_code$restore()