Skip to content

No lint keeps raw bidi controls out of first-party sources, and the authoring path that introduces them was hit twice while fixing #610 #628

Description

@Yaraslaut

#610 converted nine assertion lines in src/qt/forms/tests/tst_i18n.qml from raw
U+061C / U+200E / U+200F to \uXXXX escapes, and explicitly deferred the lint
that would keep them that way:

Optionally, a lint could keep it that way: a repo-wide scan rejecting raw
U+061C / U+200E / U+200F / U+202A–U+202E / U+2066–U+2069 outside comments would
also catch the "trojan source" shape in first-party code generally. That is a
bigger decision than this issue and should not block the mechanical fix.

Filing it, because while implementing #610 and #591 I hit the hazard the lint
would catch — twice, in the same session, in the opposite direction from the one
#610 describes.

Verification status

Reproduced, on myself, during PR #630. Not a hypothetical and not inferred
from reading.

Writing new source through a tool whose payload is JSON, I typed the six
characters ؜ intending them to land in the file as six characters. JSON
decoded the escape before the file was written, so what landed was the single
invisible character U+061C. It happened in two separate files before I checked:

$ grep -nP '[^\x00-\x7F]' include/morph/render/locale_format.hpp | cat -A
109:    /// The locale's negative-sign string, e.g. `"M-bM-^HM-^R"`. Empty is read as the$
112:    /// The locale's positive-sign string, e.g. `"M-XM-^\+"`. Entry-edge only:$
116:    /// The locale's DIGIT ZERO, e.g. `"M-YM- "`. The ten digits are the ten$
...
$ grep -nP '[^\x00-\x7F]' tests/test_render_locale_format.cpp
799:    // Before morph#591 this edge emitted "؜-1٬050٫25" -- the
856:    // A decision, not a consequence: "٥5" is rejected rather than read as

(M-XM-^\ is D8 9C, U+061C. The same measurement the #610 triage comment
used.) Baseline for comparison: the same header at 0e3b8823 had 2
non-ASCII lines, both pre-existing em-dashes in prose.

I caught it by grepping, fixed it, and both files are ASCII-clean in #630 apart
from the pre-existing em-dashes and box-drawing characters. But nothing in the
repository would have caught it if I had not thought to look, and in these two
cases the characters were inside comments, where the consequence is only
unreadability. In a string literal it is a silently wrong test, which is
precisely cost #2 of #610.

What I did not verify

  • I did not write the lint, so I have not measured its false-positive rate
    against the tree. docs/ prose and the ladder fixtures are the obvious places
    where a legitimate non-ASCII character might live.
  • I did not check whether any tool in CI or in a pre-commit hook already
    normalises or rejects these; .pre-commit-config.yaml has
    trailing-whitespace, end-of-file-fixer, check-yaml, check-json,
    mixed-line-ending, clang-format and codespell, none of which look at
    this.
  • I did not establish that a human contributor hits this. My evidence is about
    a machine-authored edit path. That is a real and now-common authoring path for
    this repository, but it is one path, and the finding should be priced as such.

Why it matters

The failure is silent in both directions:

The tree-wide scan is known to be cheap and currently clean — the #610 triage
comment measured it over the full trojan-source class and found exactly one
file, which #630 has now fixed:

$ grep -rlP '[\x{061C}\x{200E}\x{200F}\x{202A}-\x{202E}\x{2066}-\x{2069}]' src/ tests/ examples/
(no output on this branch)

So the lint would go in green, which is the cheapest moment to add one.

What is actually being decided

Not "should we scan" but three narrower things, which is why this is its own
issue and not a line in #610:

  1. Which code points. The bidi controls alone, or the full trojan-source
    class (adding U+202A–U+202E and U+2066–U+2069), or every non-ASCII character
    in src/, tests/ and include/ with an allowlist.
  2. Where. First-party sources only, or docs/ too — docs/spec/ prose
    legitimately contains em-dashes and box drawing, so a naive scan over the
    whole tree fails on day one.
  3. Comments or not. tst_i18n.qml embeds raw invisible bidi control characters in its assertions, while its C++ mirror escapes them #610's wording says "outside comments". That is harder
    to implement than it sounds for QML and C++, and the two cases above were
    in comments, where the cost is real but smaller. A simpler rule that
    covers comments too may be the better trade.

What would change the verdict

  • Close as fixed when a CI step fails on a first-party file containing a raw
    character from the agreed class, and a deliberately-introduced one is shown to
    fail it (a check that passes over a clean tree proves nothing on its own).
  • Close as won't-fix if the project decides the grep in tst_i18n.qml embeds raw invisible bidi control characters in its assertions, while its C++ mirror escapes them #610's acceptance
    criterion is enough, in which case that decision should be written where a
    reader of tst_i18n.qml or tests/test_render_locale_format.cpp will find
    it, since both files currently maintain the convention by hand.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: ciSubsystem: cienhancementNew feature or requesttriage: validWell-framed; implement as written

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions