Files
Garrick Aden-Buie 3c18aca49b fix: add stack trace fences to hide internal render pipeline frames (#4358)
* test: add failing tests for render* stack trace fence coverage

Add tests for renderPlot, renderPrint, renderText, renderUI,
renderTable, and renderImage verifying that internal rendering
pipeline frames are hidden by stack trace fences.

All 6 tests fail, revealing that:
- All render functions leak `renderFunc` through the fences
- renderPlot additionally leaks `drawPlot`, `drawReactive`
- renderPrint additionally leaks `with_promise_domain`

Relates to #4357

* refactor: move stack trace test helpers to helper-stacks.R

Move extractStackTrace, cleanLocs, dumpTests, and
captureFilteredRenderTrace into helper-stacks.R so they are
available to all test files. Rename causeRenderError to
captureFilteredRenderTrace.

* fix: add stack trace fences to hide internal render pipeline frames

Add ..stacktraceoff../..stacktraceon.. fence pairs so that internal
rendering pipeline frames (renderFunc, hybrid_chain, drawPlot, etc.)
are hidden from filtered stack traces in the debugger.

- markRenderFunction: wrap renderFunc() call with ..stacktraceoff..
- createRenderFunction: wrap func() with ..stacktraceon.. to restore
  visibility for user code
- renderPrint: wrap func() with ..stacktraceon.. inside promise domain

For renderPlot, the existing ..stacktraceon from installExprFunction
is sufficient once the outer ..stacktraceoff.. is in place.

Fixes #4357

* fix: Legacy datatable stack traces

* chore: Add news bullet

* chore: Just normalize path in the place that needs it

* chore: don't normalize file path

* fix: Handle srcfilealias in stack traces and telemetry

Following commit 272dda27e, which normalized paths only in the #line
directive, sourceUTF8() now creates srcfilealias objects for user code.
This broke code that assumed only package code had srcfile$original.

## How the new approach works

When sourceUTF8() wraps code with a #line directive:

```r
file <- 'app.R'  # Keep original path (relative/symlink/as-typed)
lines <- c(
  '..stacktraceon..({',
  sprintf('#line 1 "%s"', normalizePath(file, ...)),  # Normalize HERE
  readLines(file),
  '})'
)
src <- srcfilecopy(file, lines, isFile = TRUE)  # Uses original path
expr <- parse(text = lines, srcfile = src)
```

The parser sees the #line path differs from srcfilecopy's path, so it
creates a srcfilealias with:
- srcfile$filename = absolute path (from #line, for source refs)
- srcfile$original$filename = original path (from srcfilecopy)

This gives us both: accurate source references + user-friendly paths.

## Changes made

1. Add getSrcfileFilename() helper
   - Prefers $original$filename (user-typed path) when available
   - Falls back to $filename (absolute) for old-style srcfile objects
   - Ensures stack traces show "app.R#10" not "/abs/path/app.R#10"

2. Add isPackageFile() helper
   - Checks if absolute path is under .libPaths()
   - More reliable than checking for $original presence

3. Fix getCallCategories()
   - Now uses isPackageFile() instead of checking $original
   - User code properly categorized as "user" (bold blue in traces)
   - Package code properly categorized as "pkg" (de-emphasized)

4. Update getLocs() and otel_srcref_attributes()
   - Use getSrcfileFilename() to show user-friendly paths

## Benefits

- Stack traces preserve relative paths and symlinks as users typed them
- User vs package code still correctly distinguished
- Better IDE integration (paths match what user entered)
- Telemetry contains meaningful file paths

* fix: Avoid using startsWith()

* fix: Use reverse clamped cumsum for stack trace fence filtering

Replace the forward cumulative sum in stripStackTraces() with a reverse
clamped cumulative sum so that an unmatched `..stacktraceoff..` (one
with no corresponding inner `..stacktraceon..`) is a no-op. This fixes
a regression where markRenderFunction-only callers (e.g. htmlwidgets)
had their user frames hidden when called outside a reactive domain.

The new algorithm concatenates all trace segments into a single vector,
performs vectorized fence scoring, and computes visibility via the
identity: clamped_cumsum = cumsum - pmin(0, cummin(cumsum)).

Fixes #4357

* refactor: Extract skip_if_shiny_otel_tracer_is_enabled() helper

* fix: Handle srcfilealias in reactive auto-labeling

The normalizePath() in sourceUTF8() causes R to create srcfilealias
objects whose $lines is NULL, breaking rassignSrcrefToLabel() and
rexprSrcrefToLabel(). Add getSrcfileLines() helper (alongside
getSrcfileFilename()) to resolve lines from the original srcfilecopy
using srcref[7] for the correct line number.

* fix: Enforce path-boundary check in isPackageFile()

The prefix-only matching in isPackageFile() could misclassify paths
like "/usr/lib/Rcpp/..." as inside "/usr/lib/R". Normalize library
paths with a trailing slash before comparison to ensure proper
path-boundary matching.

* fix: Prefer original filename only for non-package srcfilealias

When a package is installed with keep.source.pkgs = TRUE, the
srcfilecopy original filename may point to a collated build-time path.
For package files (under .libPaths()), keep srcfile$filename to avoid
regressing stack traces and telemetry with install-time paths.

* Update R/conditions.R

---------

Co-authored-by: Carson Sievert <cpsievert1@gmail.com>
2026-02-18 17:01:33 -06:00
..
2020-09-25 14:15:34 -05:00
2016-03-08 16:54:28 -06:00
2025-10-14 15:40:36 -04:00
2024-02-02 16:53:48 -06:00
2021-05-20 13:58:16 -05:00
2023-01-25 11:18:20 -06:00
2024-03-08 09:15:37 -06:00
2021-01-13 14:18:12 -06:00
2019-09-12 09:09:09 -05:00
2021-09-10 14:46:30 -05:00
2021-09-10 14:55:53 -05:00
2023-08-15 11:40:19 -05:00
2019-06-19 15:28:03 -05:00
2020-09-25 14:15:34 -05:00
2025-10-14 15:40:36 -04:00