aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorMarek Polacek <polacek@redhat.com>2021-10-06 14:33:59 -0400
committerMarek Polacek <polacek@redhat.com>2021-11-16 21:56:16 -0500
commit51c500269bf53749b107807d84271385fad35628 (patch)
treeb7fd2eeed15448294f60989bed252e3488b251e0 /gcc
parent111fd515f2894d7cddf62f80c69765c43ae18577 (diff)
downloadgcc-51c500269bf53749b107807d84271385fad35628.zip
gcc-51c500269bf53749b107807d84271385fad35628.tar.gz
gcc-51c500269bf53749b107807d84271385fad35628.tar.bz2
libcpp: Implement -Wbidi-chars for CVE-2021-42574 [PR103026]
From a link below: "An issue was discovered in the Bidirectional Algorithm in the Unicode Specification through 14.0. It permits the visual reordering of characters via control sequences, which can be used to craft source code that renders different logic than the logical ordering of tokens ingested by compilers and interpreters. Adversaries can leverage this to encode source code for compilers accepting Unicode such that targeted vulnerabilities are introduced invisibly to human reviewers." More info: https://nvd.nist.gov/vuln/detail/CVE-2021-42574 https://trojansource.codes/ This is not a compiler bug. However, to mitigate the problem, this patch implements -Wbidi-chars=[none|unpaired|any] to warn about possibly misleading Unicode bidirectional control characters the preprocessor may encounter. The default is =unpaired, which warns about improperly terminated bidirectional control characters; e.g. a LRE without its corresponding PDF. The level =any warns about any use of bidirectional control characters. This patch handles both UCNs and UTF-8 characters. UCNs designating bidi characters in identifiers are accepted since r204886. Then r217144 enabled -fextended-identifiers by default. Extended characters in C/C++ identifiers have been accepted since r275979. However, this patch still warns about mixing UTF-8 and UCN bidi characters; there seems to be no good reason to allow mixing them. We warn in different contexts: comments (both C and C++-style), string literals, character constants, and identifiers. Expectedly, UCNs are ignored in comments and raw string literals. The bidirectional control characters can nest so this patch handles that as well. I have not included nor tested this at all with Fortran (which also has string literals and line comments). Dave M. posted patches improving diagnostic involving Unicode characters. This patch does not make use of this new infrastructure yet. PR preprocessor/103026 gcc/c-family/ChangeLog: * c.opt (Wbidi-chars, Wbidi-chars=): New option. gcc/ChangeLog: * doc/invoke.texi: Document -Wbidi-chars. libcpp/ChangeLog: * include/cpplib.h (enum cpp_bidirectional_level): New. (struct cpp_options): Add cpp_warn_bidirectional. (enum cpp_warning_reason): Add CPP_W_BIDIRECTIONAL. * internal.h (struct cpp_reader): Add warn_bidi_p member function. * init.c (cpp_create_reader): Set cpp_warn_bidirectional. * lex.c (bidi): New namespace. (get_bidi_utf8): New function. (get_bidi_ucn): Likewise. (maybe_warn_bidi_on_close): Likewise. (maybe_warn_bidi_on_char): Likewise. (_cpp_skip_block_comment): Implement warning about bidirectional control characters. (skip_line_comment): Likewise. (forms_identifier_p): Likewise. (lex_identifier): Likewise. (lex_string): Likewise. (lex_raw_string): Likewise. gcc/testsuite/ChangeLog: * c-c++-common/Wbidi-chars-1.c: New test. * c-c++-common/Wbidi-chars-2.c: New test. * c-c++-common/Wbidi-chars-3.c: New test. * c-c++-common/Wbidi-chars-4.c: New test. * c-c++-common/Wbidi-chars-5.c: New test. * c-c++-common/Wbidi-chars-6.c: New test. * c-c++-common/Wbidi-chars-7.c: New test. * c-c++-common/Wbidi-chars-8.c: New test. * c-c++-common/Wbidi-chars-9.c: New test. * c-c++-common/Wbidi-chars-10.c: New test. * c-c++-common/Wbidi-chars-11.c: New test. * c-c++-common/Wbidi-chars-12.c: New test. * c-c++-common/Wbidi-chars-13.c: New test. * c-c++-common/Wbidi-chars-14.c: New test. * c-c++-common/Wbidi-chars-15.c: New test. * c-c++-common/Wbidi-chars-16.c: New test. * c-c++-common/Wbidi-chars-17.c: New test.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/c-family/c.opt24
-rw-r--r--gcc/doc/invoke.texi21
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-1.c12
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-10.c27
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-11.c13
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-12.c19
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-13.c17
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-14.c38
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-15.c59
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-16.c26
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-17.c30
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-2.c9
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-3.c11
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-4.c188
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-5.c188
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-6.c155
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-7.c9
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-8.c13
-rw-r--r--gcc/testsuite/c-c++-common/Wbidi-chars-9.c29
19 files changed, 887 insertions, 1 deletions
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 8a4cd63..3976fc3 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -374,6 +374,30 @@ Wbad-function-cast
C ObjC Var(warn_bad_function_cast) Warning
Warn about casting functions to incompatible types.
+Wbidi-chars
+C ObjC C++ ObjC++ Warning Alias(Wbidi-chars=,any,none)
+;
+
+Wbidi-chars=
+C ObjC C++ ObjC++ RejectNegative Joined Warning CPP(cpp_warn_bidirectional) CppReason(CPP_W_BIDIRECTIONAL) Var(warn_bidirectional) Init(bidirectional_unpaired) Enum(cpp_bidirectional_level)
+-Wbidi-chars=[none|unpaired|any] Warn about UTF-8 bidirectional control characters.
+
+; Required for these enum values.
+SourceInclude
+cpplib.h
+
+Enum
+Name(cpp_bidirectional_level) Type(int) UnknownError(argument %qs to %<-Wbidi-chars%> not recognized)
+
+EnumValue
+Enum(cpp_bidirectional_level) String(none) Value(bidirectional_none)
+
+EnumValue
+Enum(cpp_bidirectional_level) String(unpaired) Value(bidirectional_unpaired)
+
+EnumValue
+Enum(cpp_bidirectional_level) String(any) Value(bidirectional_any)
+
Wbool-compare
C ObjC C++ ObjC++ Var(warn_bool_compare) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
Warn about boolean expression compared with an integer value different from true/false.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 6070288..a22758d 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -327,7 +327,9 @@ Objective-C and Objective-C++ Dialects}.
-Warith-conversion @gol
-Warray-bounds -Warray-bounds=@var{n} -Warray-compare @gol
-Wno-attributes -Wattribute-alias=@var{n} -Wno-attribute-alias @gol
--Wno-attribute-warning -Wbool-compare -Wbool-operation @gol
+-Wno-attribute-warning @gol
+-Wbidi-chars=@r{[}none@r{|}unpaired@r{|}any@r{]} @gol
+-Wbool-compare -Wbool-operation @gol
-Wno-builtin-declaration-mismatch @gol
-Wno-builtin-macro-redefined -Wc90-c99-compat -Wc99-c11-compat @gol
-Wc11-c2x-compat @gol
@@ -7678,6 +7680,23 @@ Attributes considered include @code{alloc_align}, @code{alloc_size},
This is the default. You can disable these warnings with either
@option{-Wno-attribute-alias} or @option{-Wattribute-alias=0}.
+@item -Wbidi-chars=@r{[}none@r{|}unpaired@r{|}any@r{]}
+@opindex Wbidi-chars=
+@opindex Wbidi-chars
+@opindex Wno-bidi-chars
+Warn about possibly misleading UTF-8 bidirectional control characters in
+comments, string literals, character constants, and identifiers. Such
+characters can change left-to-right writing direction into right-to-left
+(and vice versa), which can cause confusion between the logical order and
+visual order. This may be dangerous; for instance, it may seem that a piece
+of code is not commented out, whereas it in fact is.
+
+There are three levels of warning supported by GCC@. The default is
+@option{-Wbidi-chars=unpaired}, which warns about improperly terminated
+bidi contexts. @option{-Wbidi-chars=none} turns the warning off.
+@option{-Wbidi-chars=any} warns about any use of bidirectional control
+characters.
+
@item -Wbool-compare
@opindex Wno-bool-compare
@opindex Wbool-compare
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-1.c b/gcc/testsuite/c-c++-common/Wbidi-chars-1.c
new file mode 100644
index 0000000..34f5ac1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-1.c
@@ -0,0 +1,12 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile } */
+
+int main() {
+ int isAdmin = 0;
+ /*‮ } ⁦if (isAdmin)⁩ ⁦ begin admins only */
+/* { dg-warning "bidirectional" "" { target *-*-* } .-1 } */
+ __builtin_printf("You are an admin.\n");
+ /* end admins only ‮ { ⁦*/
+/* { dg-warning "bidirectional" "" { target *-*-* } .-1 } */
+ return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-10.c b/gcc/testsuite/c-c++-common/Wbidi-chars-10.c
new file mode 100644
index 0000000..3f851b6
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-10.c
@@ -0,0 +1,27 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile } */
+/* { dg-options "-Wbidi-chars=unpaired" } */
+/* More nesting testing. */
+
+/* RLE‫ LRI⁦ PDF‬ PDI⁩*/
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int LRE_\u202a_PDF_\u202c;
+int LRE_\u202a_PDF_\u202c_LRE_\u202a_PDF_\u202c;
+int LRE_\u202a_LRI_\u2066_PDF_\u202c_PDI_\u2069;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int RLE_\u202b_RLI_\u2067_PDF_\u202c_PDI_\u2069;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int RLE_\u202b_RLI_\u2067_PDI_\u2069_PDF_\u202c;
+int FSI_\u2068_LRO_\u202d_PDI_\u2069_PDF_\u202c;
+int FSI_\u2068;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int FSI_\u2068_PDI_\u2069;
+int FSI_\u2068_FSI_\u2068_PDI_\u2069;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int RLI_\u2067_RLI_\u2067_RLI_\u2067_RLI_\u2067_RLI_\u2067_RLI_\u2067_RLI_\u2067_PDI_\u2069_PDI_\u2069_PDI_\u2069_PDI_\u2069_PDI_\u2069_PDI_\u2069_PDI_\u2069;
+int RLI_\u2067_RLI_\u2067_RLI_\u2067_RLI_\u2067_RLI_\u2067_RLI_\u2067_RLI_\u2067_PDI_\u2069_PDI_\u2069_PDI_\u2069_PDI_\u2069_PDI_\u2069_PDI_\u2069;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int RLI_\u2067_RLI_\u2067_RLI_\u2067_RLI_\u2067_RLI_\u2067_RLI_\u2067_RLI_\u2067_PDI_\u2069_PDI_\u2069_PDI_\u2069_PDI_\u2069_PDI_\u2069_PDI_\u2069_PDF_\u202c;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int RLI_\u2067_RLI_\u2067_RLI_\u2067_RLI_\u2067_FSI_\u2068_PDI_\u2069_PDI_\u2069_PDI_\u2069_PDI_\u2069;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-11.c b/gcc/testsuite/c-c++-common/Wbidi-chars-11.c
new file mode 100644
index 0000000..270ce23
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-11.c
@@ -0,0 +1,13 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile } */
+/* { dg-options "-Wbidi-chars=unpaired" } */
+/* Test that we warn when mixing UCN and UTF-8. */
+
+int LRE_‪_PDF_\u202c;
+/* { dg-warning "mismatch" "" { target *-*-* } .-1 } */
+int LRE_\u202a_PDF_‬_;
+/* { dg-warning "mismatch" "" { target *-*-* } .-1 } */
+const char *s1 = "LRE_‪_PDF_\u202c";
+/* { dg-warning "mismatch" "" { target *-*-* } .-1 } */
+const char *s2 = "LRE_\u202a_PDF_‬";
+/* { dg-warning "mismatch" "" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-12.c b/gcc/testsuite/c-c++-common/Wbidi-chars-12.c
new file mode 100644
index 0000000..b07eec1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-12.c
@@ -0,0 +1,19 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-options "-Wbidi-chars=any" } */
+/* Test raw strings. */
+
+const char *s1 = R"(a b c LRE‪ 1 2 3 PDF‬ x y z)";
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
+const char *s2 = R"(a b c RLE‫ 1 2 3 PDF‬ x y z)";
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
+const char *s3 = R"(a b c LRO‭ 1 2 3 PDF‬ x y z)";
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
+const char *s4 = R"(a b c RLO‮ 1 2 3 PDF‬ x y z)";
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
+const char *s7 = R"(a b c FSI⁨ 1 2 3 PDI⁩ x y) z";
+/* { dg-warning "U\\+2068" "" { target *-*-* } .-1 } */
+const char *s8 = R"(a b c PDI⁩ x y )z";
+/* { dg-warning "U\\+2069" "" { target *-*-* } .-1 } */
+const char *s9 = R"(a b c PDF‬ x y z)";
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-13.c b/gcc/testsuite/c-c++-common/Wbidi-chars-13.c
new file mode 100644
index 0000000..b2dd9fd
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-13.c
@@ -0,0 +1,17 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-options "-Wbidi-chars=unpaired" } */
+/* Test raw strings. */
+
+const char *s1 = R"(a b c LRE‪ 1 2 3)";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+const char *s2 = R"(a b c RLE‫ 1 2 3)";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+const char *s3 = R"(a b c LRO‭ 1 2 3)";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+const char *s4 = R"(a b c FSI⁨ 1 2 3)";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+const char *s5 = R"(a b c LRI⁦ 1 2 3)";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+const char *s6 = R"(a b c RLI⁧ 1 2 3)";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-14.c b/gcc/testsuite/c-c++-common/Wbidi-chars-14.c
new file mode 100644
index 0000000..ba5f75d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-14.c
@@ -0,0 +1,38 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile } */
+/* { dg-options "-Wbidi-chars=unpaired" } */
+/* Test PDI handling, which also pops any subsequent LREs, RLEs, LROs,
+ or RLOs. */
+
+/* LRI_⁦_LRI_⁦_RLE_‫_RLE_‫_RLE_‫_PDI_⁩*/
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+// LRI_⁦_RLE_‫_RLE_‫_RLE_‫_PDI_⁩
+// LRI_⁦_RLO_‮_RLE_‫_RLE_‫_PDI_⁩
+// LRI_⁦_RLO_‮_RLE_‫_PDI_⁩
+// FSI_⁨_RLO_‮_PDI_⁩
+// FSI_⁨_FSI_⁨_RLO_‮_PDI_⁩
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+
+int LRI_\u2066_LRI_\u2066_LRE_\u202a_LRE_\u202a_LRE_\u202a_PDI_\u2069;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int LRI_\u2066_LRI_\u2066_LRE_\u202a_LRE_\u202a_LRE_\u202a_PDI_\u2069_PDI_\u2069;
+int LRI_\u2066_LRI_\u2066_LRI_\u2066_LRE_\u202a_LRE_\u202a_LRE_\u202a_PDI_\u2069_PDI_\u2069;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int PDI_\u2069;
+int LRI_\u2066_PDI_\u2069;
+int RLI_\u2067_PDI_\u2069;
+int LRE_\u202a_LRI_\u2066_PDI_\u2069;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int LRI_\u2066_LRE_\u202a_PDF_\u202c_PDI_\u2069;
+int LRI_\u2066_LRE_\u202a_LRE_\u202a_PDF_\u202c_PDI_\u2069;
+int RLI_\u2067_LRI_\u2066_LRE_\u202a_LRE_\u202a_PDF_\u202c_PDI_\u2069;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int FSI_\u2068_LRI_\u2066_LRE_\u202a_LRE_\u202a_PDF_\u202c_PDI_\u2069;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int RLO_\u202e_PDI_\u2069;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int RLI_\u2067_PDI_\u2069_RLI_\u2067;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int FSI_\u2068_PDF_\u202c_PDI_\u2069;
+int FSI_\u2068_FSI_\u2068_PDF_\u202c_PDI_\u2069;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-15.c b/gcc/testsuite/c-c++-common/Wbidi-chars-15.c
new file mode 100644
index 0000000..a0ce8ff
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-15.c
@@ -0,0 +1,59 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile } */
+/* { dg-options "-Wbidi-chars=unpaired" } */
+/* Test unpaired bidi control chars in multiline comments. */
+
+/*
+ * LRE‪ end
+ */
+/* { dg-warning "unpaired" "" { target *-*-* } .-2 } */
+/*
+ * RLE‫ end
+ */
+/* { dg-warning "unpaired" "" { target *-*-* } .-2 } */
+/*
+ * LRO‭ end
+ */
+/* { dg-warning "unpaired" "" { target *-*-* } .-2 } */
+/*
+ * RLO‮ end
+ */
+/* { dg-warning "unpaired" "" { target *-*-* } .-2 } */
+/*
+ * LRI⁦ end
+ */
+/* { dg-warning "unpaired" "" { target *-*-* } .-2 } */
+/*
+ * RLI⁧ end
+ */
+/* { dg-warning "unpaired" "" { target *-*-* } .-2 } */
+/*
+ * FSI⁨ end
+ */
+/* { dg-warning "unpaired" "" { target *-*-* } .-2 } */
+/* LRE‪
+ PDF‬ */
+/* { dg-warning "unpaired" "" { target *-*-* } .-2 } */
+/* FSI⁨
+ PDI⁩ */
+/* { dg-warning "unpaired" "" { target *-*-* } .-2 } */
+
+/* LRE<‪>
+ *
+ */
+/* { dg-warning "unpaired" "" { target *-*-* } .-3 } */
+
+/*
+ * LRE<‪>
+ */
+/* { dg-warning "unpaired" "" { target *-*-* } .-2 } */
+
+/*
+ *
+ * LRE<‪> */
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+
+/* RLI<⁧> */ /* PDI<⁩> */
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+/* LRE<‪> */ /* PDF<‬> */
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-16.c b/gcc/testsuite/c-c++-common/Wbidi-chars-16.c
new file mode 100644
index 0000000..baa0159
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-16.c
@@ -0,0 +1,26 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile } */
+/* { dg-options "-Wbidi-chars=any" } */
+/* Test LTR/RTL chars. */
+
+/* LTR<‎> */
+/* { dg-warning "U\\+200E" "" { target *-*-* } .-1 } */
+// LTR<‎>
+/* { dg-warning "U\\+200E" "" { target *-*-* } .-1 } */
+/* RTL<‏> */
+/* { dg-warning "U\\+200F" "" { target *-*-* } .-1 } */
+// RTL<‏>
+/* { dg-warning "U\\+200F" "" { target *-*-* } .-1 } */
+
+const char *s1 = "LTR<‎>";
+/* { dg-warning "U\\+200E" "" { target *-*-* } .-1 } */
+const char *s2 = "LTR\u200e";
+/* { dg-warning "U\\+200E" "" { target *-*-* } .-1 } */
+const char *s3 = "LTR\u200E";
+/* { dg-warning "U\\+200E" "" { target *-*-* } .-1 } */
+const char *s4 = "RTL<‏>";
+/* { dg-warning "U\\+200F" "" { target *-*-* } .-1 } */
+const char *s5 = "RTL\u200f";
+/* { dg-warning "U\\+200F" "" { target *-*-* } .-1 } */
+const char *s6 = "RTL\u200F";
+/* { dg-warning "U\\+200F" "" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-17.c b/gcc/testsuite/c-c++-common/Wbidi-chars-17.c
new file mode 100644
index 0000000..07cb432
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-17.c
@@ -0,0 +1,30 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile } */
+/* { dg-options "-Wbidi-chars=unpaired" } */
+/* Test LTR/RTL chars. */
+
+/* LTR<‎> */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+// LTR<‎>
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+/* RTL<‏> */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+// RTL<‏>
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int ltr_\u200e;
+/* { dg-error "universal character " "" { target *-*-* } .-1 } */
+int rtl_\u200f;
+/* { dg-error "universal character " "" { target *-*-* } .-1 } */
+
+const char *s1 = "LTR<‎>";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+const char *s2 = "LTR\u200e";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+const char *s3 = "LTR\u200E";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+const char *s4 = "RTL<‏>";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+const char *s5 = "RTL\u200f";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+const char *s6 = "RTL\u200F";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-2.c b/gcc/testsuite/c-c++-common/Wbidi-chars-2.c
new file mode 100644
index 0000000..2340374
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-2.c
@@ -0,0 +1,9 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile } */
+
+int main() {
+ /* Say hello; newline⁧/*/ return 0 ;
+/* { dg-warning "bidirectional" "" { target *-*-* } .-1 } */
+ __builtin_printf("Hello world.\n");
+ return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-3.c b/gcc/testsuite/c-c++-common/Wbidi-chars-3.c
new file mode 100644
index 0000000..9dc7edb
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-3.c
@@ -0,0 +1,11 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile } */
+
+int main() {
+ const char* access_level = "user";
+ if (__builtin_strcmp(access_level, "user‮ ⁦// Check if admin⁩ ⁦")) {
+/* { dg-warning "bidirectional" "" { target *-*-* } .-1 } */
+ __builtin_printf("You are an admin.\n");
+ }
+ return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-4.c b/gcc/testsuite/c-c++-common/Wbidi-chars-4.c
new file mode 100644
index 0000000..639e5c6
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-4.c
@@ -0,0 +1,188 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile } */
+/* { dg-options "-Wbidi-chars=any -Wno-multichar -Wno-overflow" } */
+/* Test all bidi chars in various contexts (identifiers, comments,
+ string literals, character constants), both UCN and UTF-8. The bidi
+ chars here are properly terminated, except for the character constants. */
+
+/* a b c LRE‪ 1 2 3 PDF‬ x y z */
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
+/* a b c RLE‫ 1 2 3 PDF‬ x y z */
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
+/* a b c LRO‭ 1 2 3 PDF‬ x y z */
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
+/* a b c RLO‮ 1 2 3 PDF‬ x y z */
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
+/* a b c LRI⁦ 1 2 3 PDI⁩ x y z */
+/* { dg-warning "U\\+2066" "" { target *-*-* } .-1 } */
+/* a b c RLI⁧ 1 2 3 PDI⁩ x y */
+/* { dg-warning "U\\+2067" "" { target *-*-* } .-1 } */
+/* a b c FSI⁨ 1 2 3 PDI⁩ x y z */
+/* { dg-warning "U\\+2068" "" { target *-*-* } .-1 } */
+
+/* Same but C++ comments instead. */
+// a b c LRE‪ 1 2 3 PDF‬ x y z
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
+// a b c RLE‫ 1 2 3 PDF‬ x y z
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
+// a b c LRO‭ 1 2 3 PDF‬ x y z
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
+// a b c RLO‮ 1 2 3 PDF‬ x y z
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
+// a b c LRI⁦ 1 2 3 PDI⁩ x y z
+/* { dg-warning "U\\+2066" "" { target *-*-* } .-1 } */
+// a b c RLI⁧ 1 2 3 PDI⁩ x y
+/* { dg-warning "U\\+2067" "" { target *-*-* } .-1 } */
+// a b c FSI⁨ 1 2 3 PDI⁩ x y z
+/* { dg-warning "U\\+2068" "" { target *-*-* } .-1 } */
+
+/* Here we're closing an unopened context, warn when =any. */
+/* a b c PDI⁩ x y z */
+/* { dg-warning "U\\+2069" "" { target *-*-* } .-1 } */
+/* a b c PDF‬ x y z */
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-1 } */
+// a b c PDI⁩ x y z
+/* { dg-warning "U\\+2069" "" { target *-*-* } .-1 } */
+// a b c PDF‬ x y z
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-1 } */
+
+/* Multiline comments. */
+/* a b c PDI⁩ x y z
+ */
+/* { dg-warning "U\\+2069" "" { target *-*-* } .-2 } */
+/* a b c PDF‬ x y z
+ */
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-2 } */
+/* first
+ a b c PDI⁩ x y z
+ */
+/* { dg-warning "U\\+2069" "" { target *-*-* } .-2 } */
+/* first
+ a b c PDF‬ x y z
+ */
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-2 } */
+/* first
+ a b c PDI⁩ x y z */
+/* { dg-warning "U\\+2069" "" { target *-*-* } .-1 } */
+/* first
+ a b c PDF‬ x y z */
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-1 } */
+
+void
+g1 ()
+{
+ const char *s1 = "a b c LRE‪ 1 2 3 PDF‬ x y z";
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
+ const char *s2 = "a b c RLE‫ 1 2 3 PDF‬ x y z";
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
+ const char *s3 = "a b c LRO‭ 1 2 3 PDF‬ x y z";
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
+ const char *s4 = "a b c RLO‮ 1 2 3 PDF‬ x y z";
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
+ const char *s5 = "a b c LRI⁦ 1 2 3 PDI⁩ x y z";
+/* { dg-warning "U\\+2066" "" { target *-*-* } .-1 } */
+ const char *s6 = "a b c RLI⁧ 1 2 3 PDI⁩ x y z";
+/* { dg-warning "U\\+2067" "" { target *-*-* } .-1 } */
+ const char *s7 = "a b c FSI⁨ 1 2 3 PDI⁩ x y z";
+/* { dg-warning "U\\+2068" "" { target *-*-* } .-1 } */
+ const char *s8 = "a b c PDI⁩ x y z";
+/* { dg-warning "U\\+2069" "" { target *-*-* } .-1 } */
+ const char *s9 = "a b c PDF‬ x y z";
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-1 } */
+
+ const char *s10 = "a b c LRE\u202a 1 2 3 PDF\u202c x y z";
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
+ const char *s11 = "a b c LRE\u202A 1 2 3 PDF\u202c x y z";
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
+ const char *s12 = "a b c RLE\u202b 1 2 3 PDF\u202c x y z";
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
+ const char *s13 = "a b c RLE\u202B 1 2 3 PDF\u202c x y z";
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
+ const char *s14 = "a b c LRO\u202d 1 2 3 PDF\u202c x y z";
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
+ const char *s15 = "a b c LRO\u202D 1 2 3 PDF\u202c x y z";
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
+ const char *s16 = "a b c RLO\u202e 1 2 3 PDF\u202c x y z";
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
+ const char *s17 = "a b c RLO\u202E 1 2 3 PDF\u202c x y z";
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
+ const char *s18 = "a b c LRI\u2066 1 2 3 PDI\u2069 x y z";
+/* { dg-warning "U\\+2066" "" { target *-*-* } .-1 } */
+ const char *s19 = "a b c RLI\u2067 1 2 3 PDI\u2069 x y z";
+/* { dg-warning "U\\+2067" "" { target *-*-* } .-1 } */
+ const char *s20 = "a b c FSI\u2068 1 2 3 PDI\u2069 x y z";
+/* { dg-warning "U\\+2068" "" { target *-*-* } .-1 } */
+}
+
+void
+g2 ()
+{
+ const char c1 = '\u202a';
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
+ const char c2 = '\u202A';
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
+ const char c3 = '\u202b';
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
+ const char c4 = '\u202B';
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
+ const char c5 = '\u202d';
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
+ const char c6 = '\u202D';
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
+ const char c7 = '\u202e';
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
+ const char c8 = '\u202E';
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
+ const char c9 = '\u2066';
+/* { dg-warning "U\\+2066" "" { target *-*-* } .-1 } */
+ const char c10 = '\u2067';
+/* { dg-warning "U\\+2067" "" { target *-*-* } .-1 } */
+ const char c11 = '\u2068';
+/* { dg-warning "U\\+2068" "" { target *-*-* } .-1 } */
+}
+
+int a‪b‬c;
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
+int a‫b‬c;
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
+int a‭b‬c;
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
+int a‮b‬c;
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
+int a⁦b⁩c;
+/* { dg-warning "U\\+2066" "" { target *-*-* } .-1 } */
+int a⁧b⁩c;
+/* { dg-warning "U\\+2067" "" { target *-*-* } .-1 } */
+int a⁨b⁩c;
+/* { dg-warning "U\\+2068" "" { target *-*-* } .-1 } */
+int A‬X;
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-1 } */
+int A\u202cY;
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-1 } */
+int A\u202CY2;
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-1 } */
+
+int d\u202ae\u202cf;
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
+int d\u202Ae\u202cf2;
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
+int d\u202be\u202cf;
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
+int d\u202Be\u202cf2;
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
+int d\u202de\u202cf;
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
+int d\u202De\u202cf2;
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
+int d\u202ee\u202cf;
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
+int d\u202Ee\u202cf2;
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
+int d\u2066e\u2069f;
+/* { dg-warning "U\\+2066" "" { target *-*-* } .-1 } */
+int d\u2067e\u2069f;
+/* { dg-warning "U\\+2067" "" { target *-*-* } .-1 } */
+int d\u2068e\u2069f;
+/* { dg-warning "U\\+2068" "" { target *-*-* } .-1 } */
+int X\u2069;
+/* { dg-warning "U\\+2069" "" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-5.c b/gcc/testsuite/c-c++-common/Wbidi-chars-5.c
new file mode 100644
index 0000000..68cb053
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-5.c
@@ -0,0 +1,188 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile } */
+/* { dg-options "-Wbidi-chars=unpaired -Wno-multichar -Wno-overflow" } */
+/* Test all bidi chars in various contexts (identifiers, comments,
+ string literals, character constants), both UCN and UTF-8. The bidi
+ chars here are properly terminated, except for the character constants. */
+
+/* a b c LRE‪ 1 2 3 PDF‬ x y z */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+/* a b c RLE‫ 1 2 3 PDF‬ x y z */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+/* a b c LRO‭ 1 2 3 PDF‬ x y z */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+/* a b c RLO‮ 1 2 3 PDF‬ x y z */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+/* a b c LRI⁦ 1 2 3 PDI⁩ x y z */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+/* a b c RLI⁧ 1 2 3 PDI⁩ x y */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+/* a b c FSI⁨ 1 2 3 PDI⁩ x y z */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+
+/* Same but C++ comments instead. */
+// a b c LRE‪ 1 2 3 PDF‬ x y z
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+// a b c RLE‫ 1 2 3 PDF‬ x y z
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+// a b c LRO‭ 1 2 3 PDF‬ x y z
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+// a b c RLO‮ 1 2 3 PDF‬ x y z
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+// a b c LRI⁦ 1 2 3 PDI⁩ x y z
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+// a b c RLI⁧ 1 2 3 PDI⁩ x y
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+// a b c FSI⁨ 1 2 3 PDI⁩ x y z
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+
+/* Here we're closing an unopened context, warn when =any. */
+/* a b c PDI⁩ x y z */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+/* a b c PDF‬ x y z */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+// a b c PDI⁩ x y z
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+// a b c PDF‬ x y z
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+
+/* Multiline comments. */
+/* a b c PDI⁩ x y z
+ */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-2 } */
+/* a b c PDF‬ x y z
+ */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-2 } */
+/* first
+ a b c PDI⁩ x y z
+ */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-2 } */
+/* first
+ a b c PDF‬ x y z
+ */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-2 } */
+/* first
+ a b c PDI⁩ x y z */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+/* first
+ a b c PDF‬ x y z */
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+
+void
+g1 ()
+{
+ const char *s1 = "a b c LRE‪ 1 2 3 PDF‬ x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s2 = "a b c RLE‫ 1 2 3 PDF‬ x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s3 = "a b c LRO‭ 1 2 3 PDF‬ x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s4 = "a b c RLO‮ 1 2 3 PDF‬ x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s5 = "a b c LRI⁦ 1 2 3 PDI⁩ x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s6 = "a b c RLI⁧ 1 2 3 PDI⁩ x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s7 = "a b c FSI⁨ 1 2 3 PDI⁩ x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s8 = "a b c PDI⁩ x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s9 = "a b c PDF‬ x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+
+ const char *s10 = "a b c LRE\u202a 1 2 3 PDF\u202c x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s11 = "a b c LRE\u202A 1 2 3 PDF\u202c x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s12 = "a b c RLE\u202b 1 2 3 PDF\u202c x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s13 = "a b c RLE\u202B 1 2 3 PDF\u202c x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s14 = "a b c LRO\u202d 1 2 3 PDF\u202c x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s15 = "a b c LRO\u202D 1 2 3 PDF\u202c x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s16 = "a b c RLO\u202e 1 2 3 PDF\u202c x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s17 = "a b c RLO\u202E 1 2 3 PDF\u202c x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s18 = "a b c LRI\u2066 1 2 3 PDI\u2069 x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s19 = "a b c RLI\u2067 1 2 3 PDI\u2069 x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+ const char *s20 = "a b c FSI\u2068 1 2 3 PDI\u2069 x y z";
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+}
+
+void
+g2 ()
+{
+ const char c1 = '\u202a';
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char c2 = '\u202A';
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char c3 = '\u202b';
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char c4 = '\u202B';
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char c5 = '\u202d';
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char c6 = '\u202D';
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char c7 = '\u202e';
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char c8 = '\u202E';
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char c9 = '\u2066';
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char c10 = '\u2067';
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char c11 = '\u2068';
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+}
+
+int a‪b‬c;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int a‫b‬c;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int a‭b‬c;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int a‮b‬c;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int a⁦b⁩c;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int a⁧b⁩c;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int a⁨b⁩c;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int A‬X;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int A\u202cY;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int A\u202CY2;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+
+int d\u202ae\u202cf;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int d\u202Ae\u202cf2;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int d\u202be\u202cf;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int d\u202Be\u202cf2;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int d\u202de\u202cf;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int d\u202De\u202cf2;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int d\u202ee\u202cf;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int d\u202Ee\u202cf2;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int d\u2066e\u2069f;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int d\u2067e\u2069f;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int d\u2068e\u2069f;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
+int X\u2069;
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-6.c b/gcc/testsuite/c-c++-common/Wbidi-chars-6.c
new file mode 100644
index 0000000..0ce6fff
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-6.c
@@ -0,0 +1,155 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile } */
+/* { dg-options "-Wbidi-chars=unpaired" } */
+/* Test nesting of bidi chars in various contexts. */
+
+/* Terminated by the wrong char: */
+/* a b c LRE‪ 1 2 3 PDI⁩ x y z */
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+/* a b c RLE‫ 1 2 3 PDI⁩ x y z*/
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+/* a b c LRO‭ 1 2 3 PDI⁩ x y z */
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+/* a b c RLO‮ 1 2 3 PDI⁩ x y z */
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+/* a b c LRI⁦ 1 2 3 PDF‬ x y z */
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+/* a b c RLI⁧ 1 2 3 PDF‬ x y z */
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+/* a b c FSI⁨ 1 2 3 PDF‬ x y z*/
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+
+/* LRE‪ PDF‬ */
+/* LRE‪ LRE‪ PDF‬ PDF‬ */
+/* PDF‬ LRE‪ PDF‬ */
+/* LRE‪ PDF‬ LRE‪ PDF‬ */
+/* LRE‪ LRE‪ PDF‬ */
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+/* PDF‬ LRE‪ */
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+
+// a b c LRE‪ 1 2 3 PDI⁩ x y z
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+// a b c RLE‫ 1 2 3 PDI⁩ x y z*/
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+// a b c LRO‭ 1 2 3 PDI⁩ x y z
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+// a b c RLO‮ 1 2 3 PDI⁩ x y z
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+// a b c LRI⁦ 1 2 3 PDF‬ x y z
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+// a b c RLI⁧ 1 2 3 PDF‬ x y z
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+// a b c FSI⁨ 1 2 3 PDF‬ x y z
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+
+// LRE‪ PDF‬
+// LRE‪ LRE‪ PDF‬ PDF‬
+// PDF‬ LRE‪ PDF‬
+// LRE‪ PDF‬ LRE‪ PDF‬
+// LRE‪ LRE‪ PDF‬
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+// PDF‬ LRE‪
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+
+void
+g1 ()
+{
+ const char *s1 = "a b c LRE‪ 1 2 3 PDI⁩ x y z";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s2 = "a b c LRE\u202a 1 2 3 PDI\u2069 x y z";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s3 = "a b c RLE‫ 1 2 3 PDI⁩ x y ";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s4 = "a b c RLE\u202b 1 2 3 PDI\u2069 x y z";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s5 = "a b c LRO‭ 1 2 3 PDI⁩ x y z";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s6 = "a b c LRO\u202d 1 2 3 PDI\u2069 x y z";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s7 = "a b c RLO‮ 1 2 3 PDI⁩ x y z";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s8 = "a b c RLO\u202e 1 2 3 PDI\u2069 x y z";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s9 = "a b c LRI⁦ 1 2 3 PDF‬ x y z";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s10 = "a b c LRI\u2066 1 2 3 PDF\u202c x y z";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s11 = "a b c RLI⁧ 1 2 3 PDF‬ x y z\
+ ";
+/* { dg-warning "unpaired" "" { target *-*-* } .-2 } */
+ const char *s12 = "a b c RLI\u2067 1 2 3 PDF\u202c x y z";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s13 = "a b c FSI⁨ 1 2 3 PDF‬ x y z";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s14 = "a b c FSI\u2068 1 2 3 PDF\u202c x y z";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s15 = "PDF‬ LRE‪";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s16 = "PDF\u202c LRE\u202a";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s17 = "LRE‪ PDF‬";
+ const char *s18 = "LRE\u202a PDF\u202c";
+ const char *s19 = "LRE‪ LRE‪ PDF‬ PDF‬";
+ const char *s20 = "LRE\u202a LRE\u202a PDF\u202c PDF\u202c";
+ const char *s21 = "PDF‬ LRE‪ PDF‬";
+ const char *s22 = "PDF\u202c LRE\u202a PDF\u202c";
+ const char *s23 = "LRE‪ LRE‪ PDF‬";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s24 = "LRE\u202a LRE\u202a PDF\u202c";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s25 = "PDF‬ LRE‪";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s26 = "PDF\u202c LRE\u202a";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s27 = "PDF‬ LRE\u202a";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+ const char *s28 = "PDF\u202c LRE‪";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+}
+
+int aLRE‪bPDI⁩;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int A\u202aB\u2069C;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int aRLE‫bPDI⁩;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int a\u202bB\u2069c;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int aLRO‭bPDI⁩;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int a\u202db\u2069c2;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int aRLO‮bPDI⁩;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int a\u202eb\u2069;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int aLRI⁦bPDF‬;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int a\u2066b\u202c;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int aRLI⁧bPDF‬c
+;
+/* { dg-warning "unpaired" "" { target *-*-* } .-2 } */
+int a\u2067b\u202c;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int aFSI⁨bPDF‬;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int a\u2068b\u202c;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int aFSI⁨bPD\u202C;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int aFSI\u2068bPDF‬_;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int aLRE‪bPDF‬b;
+int A\u202aB\u202c;
+int a_LRE‪_LRE‪_b_PDF‬_PDF‬;
+int A\u202aA\u202aB\u202cB\u202c;
+int aPDF‬bLREadPDF‬;
+int a_\u202C_\u202a_\u202c;
+int a_LRE‪_b_PDF‬_c_LRE‪_PDF‬;
+int a_\u202a_\u202c_\u202a_\u202c_;
+int a_LRE‪_b_PDF‬_c_LRE‪;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int a_\u202a_\u202c_\u202a_;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-7.c b/gcc/testsuite/c-c++-common/Wbidi-chars-7.c
new file mode 100644
index 0000000..d012d42
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-7.c
@@ -0,0 +1,9 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile } */
+/* { dg-options "-Wbidi-chars=any" } */
+/* Test we ignore UCNs in comments. */
+
+// a b c \u202a 1 2 3
+// a b c \u202A 1 2 3
+/* a b c \u202a 1 2 3 */
+/* a b c \u202A 1 2 3 */
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-8.c b/gcc/testsuite/c-c++-common/Wbidi-chars-8.c
new file mode 100644
index 0000000..4f54c50
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-8.c
@@ -0,0 +1,13 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile } */
+/* { dg-options "-Wbidi-chars=any" } */
+/* Test \u vs \U. */
+
+int a_\u202A;
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
+int a_\u202a_2;
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
+int a_\U0000202A_3;
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
+int a_\U0000202a_4;
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/c-c++-common/Wbidi-chars-9.c b/gcc/testsuite/c-c++-common/Wbidi-chars-9.c
new file mode 100644
index 0000000..e2af1b1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wbidi-chars-9.c
@@ -0,0 +1,29 @@
+/* PR preprocessor/103026 */
+/* { dg-do compile } */
+/* { dg-options "-Wbidi-chars=unpaired" } */
+/* Test that we properly separate bidi contexts (comment/identifier/character
+ constant/string literal). */
+
+/* LRE ->‪<- */ int pdf_\u202c_1;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+/* RLE ->‫<- */ int pdf_\u202c_2;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+/* LRO ->‭<- */ int pdf_\u202c_3;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+/* RLO ->‮<- */ int pdf_\u202c_4;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+/* LRI ->⁦<-*/ int pdi_\u2069_1;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+/* RLI ->⁧<- */ int pdi_\u2069_12;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+/* FSI ->⁨<- */ int pdi_\u2069_3;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+
+const char *s1 = "LRE\u202a"; /* PDF ->‬<- */
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+/* LRE ->‪<- */ const char *s2 = "PDF\u202c";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+const char *s3 = "LRE\u202a"; int pdf_\u202c_5;
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
+int lre_\u202a; const char *s4 = "PDF\u202c";
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */