Skip to content

replace <pattern> with <text> in <text> is a silent no-op: returns the input unchanged, exit 0, no diagnostic (and literal string replace is unreachable) #698

Description

@logbie

Summary

replace <pattern> with <text> in <text> silently returns the input unchanged — it parses, type-checks, runs, exits 0, and produces a wrong answer with no diagnostic.

Reproduction

create pattern w:
    "world"
end pattern
store s as "hello world world"
display "[" with (replace w with "there" in s) with "]"

Command:

wfl d1_pattern_replace_noop.wfl

Expected

[hello there there].

replace is documented as the pattern-replacement keyword in
Docs/reference/keyword-reference.md:155 (| replace | Pattern replacement | ✗ |),
and the expression form is a first-class production in the parser
(src/parser/expr/primary.rs:1218-1238 and src/parser/expr/binary.rs:648-665),
the analyzer, the type checker, and the interpreter
(src/interpreter/mod.rs:14628).

Actual

[hello world world]

Exit code: 0. No warning, no error, no diagnostic on stderr.

Root cause: native_pattern_replace validates all three arguments and then discards them.

// src/stdlib/pattern.rs:214-262
pub fn native_pattern_replace(args: Vec<Value>, line: usize, column: usize) -> Result<Value, RuntimeError> {
    ...
    let _pattern = ...;      // validated, then unused
    let _replacement = ...;  // validated, then unused
    // TODO: Update to use new pattern system for replacement
    Ok(Value::Text(Arc::from(text)))
}

Passing a Text where the Pattern is expected does fail loudly
(Second argument must be a pattern), so the failure is specific to the
supported, documented form.

Compounding problem — the literal string replace is unreachable from WFL source.
A working 3-argument literal replace exists and is unit-tested
(native_replace, src/stdlib/text.rs:435, tests at :456-472), but replace
lexes as Token::KeywordReplace and the parser only accepts the
<pattern> with <x> in <y> shape:

display replace of s and "world" and "there"
=> error[ERROR]: Unexpected token in expression: KeywordOf   (exit 2)

The repository already documents this dead end in a test comment:

// TestPrograms/stdlib_comprehensive.wfl:95-96
// Note: string replace() is a native function but 'replace' keyword is reserved
// for pattern replacement. String replace is tested in Rust unit tests.

So today WFL has no reachable string-replacement operation at all: the pattern
form silently no-ops, and the literal form cannot be called. The documented
workaround shape is join of (split <text> by <needle>) and <replacement>.

Environment

  • wfl --version: WebFirst Language (WFL) version 26.8.4
  • binary: system install C:\Program Files\wfl\bin\wfl.exe
  • commit: c277d8f
  • OS: Windows 11 Pro 10.0.26200
  • build: release

Also reproduces identically on the repo build G:\repos\wfl\target\release\wfl.exe
(version 26.8.2), so this is not a regression — it is long-standing.
No .wflcfg in scope for the repro.

Context

Found while porting G:/repos/JShrink/src/JShrink/Minifier.php (a 738-line PHP
JavaScript minifier) to WFL. JShrink's unlock() step is a literal str_replace
and its lock() step is a preg_replace with backreferences.

This one was encountered, not blocking: the port uses
join of (split src by needle) and replacement for the literal replace and a
pattern_find_all + rebuild loop for the backreference case, so no stub was left.
It is filed because of how it fails, not because it blocked us — a program that
reaches for the documented keyword gets a silently wrong result and a zero exit
status, which is the worst failure mode in the set. Separately, replacement with
capture-group backreferences ($1/$2) has no form at all; that is a distinct
enhancement, not part of this bug.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions