Skip to content

MA0243: Use an escape sequence for invisible characters - #1737

Merged
meziantou merged 3 commits into
mainfrom
feature/invisible-char-detection-02fe18
Oct 19, 2026
Merged

meziantou merged 3 commits into
mainfrom
feature/invisible-char-detection-02fe18

Conversation

@meziantou

Copy link
Copy Markdown
Owner

Adds MA0243 (Usage, Info, disabled by default), which reports invisible characters and special spaces written directly in string and character literals, and a code fix that replaces them with escape sequences.

Reported characters

  • Control characters: U+0000–U+001F (including tab), U+007F, U+0080–U+009F. CR/LF are not reported (MA0101)
  • Special spaces: U+00A0, U+1680, U+2000–U+200A, U+202F, U+205F, U+3000
  • Zero-width / format characters: soft hyphen, ZWSP, ZWNJ, word joiner, BOM, Hangul fillers, braille blank, interlinear annotations…
  • Bidirectional control characters (Trojan Source): U+061C, U+200E, U+200F, U+202A–U+202E, U+2066–U+2069
  • Line/paragraph separators: U+2028, U+2029

The characters used by emoji sequences (ZWJ, variation selectors, tag characters) are not reported. The list is explicit rather than based on CharUnicodeInfo, so the result does not depend on the Unicode version of the runtime (Visual Studio vs. .NET SDK).

Scope

  • Regular, verbatim, raw, interpolated and UTF-8 strings, and char literals. Only the text of interpolated strings is analyzed; nested literals in the holes are analyzed on their own.
  • For multi-line raw strings, the indentation and the whitespace after the opening / before the closing delimiter are not reported, as they are not part of the value.
  • One diagnostic per character, located on the character.

Code fix

  • Uses \t, \0, \a, \b, \f, \v, \uXXXX or \UXXXXXXXX (fixed-width escapes, never \x).
  • Verbatim strings (including $@"" and @""u8) are converted to regular strings.
  • No fix for raw strings, nor for verbatim interpolated strings whose format clause contains \ or ".
  • The whole literal is rewritten, so the fixes of all the characters of a literal are identical and merged by Fix All.

docs/comparison-with-other-analyzers.md lists Sonar S2479 as a similar rule.

New rule (Info, disabled by default) that reports invisible characters and special spaces in string and character literals: control characters (including tab), no-break and other special spaces, zero-width and format characters, bidirectional control characters, and line/paragraph separators. The characters used by emoji sequences (ZWJ, variation selectors, tag characters) are not reported.

The code fix replaces the characters with escape sequences, and converts verbatim strings to regular strings. Raw strings have no code fix as they do not support escape sequences.
Comment thread src/Meziantou.Analyzer/Rules/UseEscapeSequenceForInvisibleCharacterCommon.cs Outdated
@meziantou
meziantou merged commit 89e5c9c into main Oct 19, 2026
14 checks passed
@meziantou
meziantou deleted the feature/invisible-char-detection-02fe18 branch October 19, 2026 17:11
This was referenced Oct 19, 2026
vdolek pushed a commit to EvilBrainsTechnology/EvilCase that referenced this pull request Oct 20, 2026
Updated
[Meziantou.Analyzer](https://github.com/meziantou/Meziantou.Analyzer)
from 3.0.312 to 3.0.315.

<details>
<summary>Release notes</summary>

_Sourced from [Meziantou.Analyzer's
releases](https://github.com/meziantou/Meziantou.Analyzer/releases)._

## 3.0.315

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.315>

## What's Changed
* Use two generations in BoundedCache instead of sorting the entries by
last usage by @​meziantou in
meziantou/Meziantou.Analyzer#1739


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.314...3.0.315

## 3.0.314

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.314>

## What's Changed
* MA0011: find the overload before computing the culture sensitivity by
@​meziantou in meziantou/Meziantou.Analyzer#1738


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.313...3.0.314

## 3.0.313

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.313>

## What's Changed
* Fix the CA5394 build error in BoundedCacheTests by @​meziantou in
meziantou/Meziantou.Analyzer#1735
* MA0243: Use an escape sequence for invisible characters by @​meziantou
in meziantou/Meziantou.Analyzer#1737


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.312...3.0.313

Commits viewable in [compare
view](meziantou/Meziantou.Analyzer@3.0.312...3.0.315).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=Meziantou.Analyzer&package-manager=nuget&previous-version=3.0.312&new-version=3.0.315)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
kivilev pushed a commit to kivilev/plsql-developer-text-tools that referenced this pull request Oct 21, 2026
Updated
[Meziantou.Analyzer](https://github.com/meziantou/Meziantou.Analyzer)
from 3.0.303 to 3.0.315.

<details>
<summary>Release notes</summary>

_Sourced from [Meziantou.Analyzer's
releases](https://github.com/meziantou/Meziantou.Analyzer/releases)._

## 3.0.315

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.315>

## What's Changed
* Use two generations in BoundedCache instead of sorting the entries by
last usage by @​meziantou in
meziantou/Meziantou.Analyzer#1739


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.314...3.0.315

## 3.0.314

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.314>

## What's Changed
* MA0011: find the overload before computing the culture sensitivity by
@​meziantou in meziantou/Meziantou.Analyzer#1738


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.313...3.0.314

## 3.0.313

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.313>

## What's Changed
* Fix the CA5394 build error in BoundedCacheTests by @​meziantou in
meziantou/Meziantou.Analyzer#1735
* MA0243: Use an escape sequence for invisible characters by @​meziantou
in meziantou/Meziantou.Analyzer#1737


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.312...3.0.313

## 3.0.312

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.312>

## What's Changed
* Read the usage of the cache entries once before sorting them for the
eviction by @​meziantou in
meziantou/Meziantou.Analyzer#1734


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.311...3.0.312

## 3.0.311

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.311>

## What's Changed
* Fuzz: remove the Moq, MSTest, Newtonsoft.Json, NUnit and Serilog
package references by @​meziantou in
meziantou/Meziantou.Analyzer#1712
* Fuzz: report a regression only when the fuzzed commit contains the fix
by @​meziantou in
meziantou/Meziantou.Analyzer#1733
* MA0110: track the node to fix when the regex is in an attribute of the
type by @​meziantou in
meziantou/Meziantou.Analyzer#1731


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.310...3.0.311

## 3.0.310

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.310>

## What's Changed
* MA0190: find the method declaration after updating the call sites by
@​meziantou in meziantou/Meziantou.Analyzer#1708
* MA0011: do not bind the invocations of attribute arguments
speculatively by @​meziantou in
meziantou/Meziantou.Analyzer#1709
* Fuzz: do not blame the rules for the inputs on which Roslyn fails by
@​meziantou in meziantou/Meziantou.Analyzer#1710
* MA0010: do not offer the code fix when the class has no name by
@​meziantou in meziantou/Meziantou.Analyzer#1700


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.309...3.0.310

## 3.0.309

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.309>

## What's Changed
* MA0110: keep the variable when it cannot be removed or renamed by
@​meziantou in meziantou/Meziantou.Analyzer#1698
* MA0071: do not move the attributes of the else block by @​meziantou in
meziantou/Meziantou.Analyzer#1699
* MA0077: do not throw when Equals(T) is declared several times by
@​meziantou in meziantou/Meziantou.Analyzer#1701
* MA0187: do not offer the code fix when the property has no name by
@​meziantou in meziantou/Meziantou.Analyzer#1702
* MA0102: do not offer the code fix when the diagnostic is not on a
member by @​meziantou in
meziantou/Meziantou.Analyzer#1703
* MA0117: do not offer the code fix when the property has no name by
@​meziantou in meziantou/Meziantou.Analyzer#1704
* MA0189: do not offer the code fix when the fixed buffer has no name or
type by @​meziantou in
meziantou/Meziantou.Analyzer#1705
* MA0089: fix the single-character constants that are not literals by
@​meziantou in meziantou/Meziantou.Analyzer#1706
* MA0004: fix the await expressions without semicolon by @​meziantou in
meziantou/Meziantou.Analyzer#1707


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.308...3.0.309

## 3.0.308

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.308>

## What's Changed
* MA0137, MA0138: do not offer the rename when the name would be empty
or the declaration does not compile by @​meziantou in
meziantou/Meziantou.Analyzer#1697
* MA0067: do not report System.Guid used as an attribute by @​meziantou
in meziantou/Meziantou.Analyzer#1696


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.307...3.0.308

## 3.0.307

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.307>

## What's Changed
* MA0002: insert the comparer at the right position when an extension
method is called as a static method by @​meziantou in
meziantou/Meziantou.Analyzer#1670
* MA0044: do not offer the code fix when ToString is used as a statement
by @​meziantou in
meziantou/Meziantou.Analyzer#1669
* MA0185: do not offer the code fix when string.Create is used as a
statement by @​meziantou in
meziantou/Meziantou.Analyzer#1668
* MA0101: do not offer the code fix for interpolated strings by
@​meziantou in meziantou/Meziantou.Analyzer#1667
* MA0187: do not offer the code fix when the class has an explicit
constructor by @​meziantou in
meziantou/Meziantou.Analyzer#1666
* MA0004: fix the code fix when the await expression is an argument by
@​meziantou in meziantou/Meziantou.Analyzer#1665
* MA0102: do not report partial members by @​meziantou in
meziantou/Meziantou.Analyzer#1664
* MA0093: do not offer EventArgs.Empty when the event args are generic
by @​meziantou in
meziantou/Meziantou.Analyzer#1663
* MA0053: do not report classes used as generic constraints by
@​meziantou in meziantou/Meziantou.Analyzer#1662
* Overload finder: Half is not implicitly convertible to float or double
by @​meziantou in
meziantou/Meziantou.Analyzer#1661
* MA0042, MA0045: make the enclosing function async in the code fix by
@​meziantou in meziantou/Meziantou.Analyzer#1660


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.306...3.0.307

## 3.0.306

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.306>

## What's Changed
* Allow the fuzzer to open up to 50 issues per run by @​meziantou in
meziantou/Meziantou.Analyzer#1652
* Build the fuzz project after the other builds of build_with_analyzers
by @​meziantou in
meziantou/Meziantou.Analyzer#1671
* MA0038: fix the analyzer crash on invalid code and the code fixes that
break the code by @​meziantou in
meziantou/Meziantou.Analyzer#1653
* MA0194: do not throw on invalid code by @​meziantou in
meziantou/Meziantou.Analyzer#1654
* MA0009, MA0023: do not throw when the GeneratedRegex arguments are
invalid by @​meziantou in
meziantou/Meziantou.Analyzer#1655
* MA0110: do not report invalid patterns and fix the partial property
used as a statement by @​meziantou in
meziantou/Meziantou.Analyzer#1656
* MA0182: remove all the parts of a partial type and analyze the
delegate signatures by @​meziantou in
meziantou/Meziantou.Analyzer#1657
* MA0036: do not report classes that contain protected members by
@​meziantou in meziantou/Meziantou.Analyzer#1658
* MA0137, MA0138: do not rename a method to a name that is already used
by @​meziantou in
meziantou/Meziantou.Analyzer#1659


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.305...3.0.306

## 3.0.305

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.305>

## What's Changed
* MA0008: do not overflow the stack on cyclic struct layouts by
@​meziantou in meziantou/Meziantou.Analyzer#1640


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.304...3.0.305

## 3.0.304

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.304>

## What's Changed
* Add fuzz tests for the analyzers and the code fixers by @​meziantou in
meziantou/Meziantou.Analyzer#1641


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.303...3.0.304

Commits viewable in [compare
view](meziantou/Meziantou.Analyzer@3.0.303...3.0.315).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=Meziantou.Analyzer&package-manager=nuget&previous-version=3.0.303&new-version=3.0.315)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This was referenced Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant