Blame SOURCES/gcc10-add-Wbidirectional.patch

3588e6
From a241a9b727f03afe62a161a2662a0f1192fd523a Mon Sep 17 00:00:00 2001
3588e6
From: Marek Polacek <polacek@redhat.com>
3588e6
Date: Wed, 6 Oct 2021 14:33:59 -0400
3588e6
Subject: [PATCH] cpp: Implement -Wbidirectional=[none|unpaired|any]
3588e6
3588e6
This patch implements -Wbidirectional=[none|unpaired|any] to warn about
3588e6
possibly dangerous bidirectional characters.
3588e6
3588e6
gcc/c-family/ChangeLog:
3588e6
3588e6
	* c.opt (Wbidirectional, Wbidirectional=): New option.
3588e6
3588e6
gcc/ChangeLog:
3588e6
3588e6
	* doc/invoke.texi: Document -Wbidirectional.
3588e6
3588e6
libcpp/ChangeLog:
3588e6
3588e6
	* include/cpplib.h (enum cpp_bidirectional_level): New.
3588e6
	(struct cpp_options): Add cpp_warn_bidirectional.
3588e6
	(enum cpp_warning_reason): Add CPP_W_BIDIRECTIONAL.
3588e6
	* init.c (cpp_create_reader): Set cpp_warn_bidirectional.
3588e6
	* lex.c (bidi): New namespace.
3588e6
	(get_bidi_utf8): New function.
3588e6
	(get_bidi_ucn): Likewise.
3588e6
	(maybe_warn_bidi_on_close): Likewise.
3588e6
	(maybe_warn_bidi_on_char): Likewise.
3588e6
	(_cpp_skip_block_comment): Implement warning about bidirectional
3588e6
	characters.
3588e6
	(skip_line_comment): Likewise.
3588e6
	(forms_identifier_p): Likewise.
3588e6
	(lex_identifier): Likewise.
3588e6
	(lex_string): Likewise.
3588e6
	(lex_raw_string): Likewise.
3588e6
3588e6
gcc/testsuite/ChangeLog:
3588e6
3588e6
	* c-c++-common/Wbidirectional-1.c: New test.
3588e6
	* c-c++-common/Wbidirectional-2.c: New test.
3588e6
	* c-c++-common/Wbidirectional-3.c: New test.
3588e6
	* c-c++-common/Wbidirectional-4.c: New test.
3588e6
	* c-c++-common/Wbidirectional-5.c: New test.
3588e6
	* c-c++-common/Wbidirectional-6.c: New test.
3588e6
	* c-c++-common/Wbidirectional-7.c: New test.
3588e6
	* c-c++-common/Wbidirectional-8.c: New test.
3588e6
	* c-c++-common/Wbidirectional-9.c: New test.
3588e6
	* c-c++-common/Wbidirectional-10.c: New test.
3588e6
	* c-c++-common/Wbidirectional-11.c: New test.
3588e6
	* c-c++-common/Wbidirectional-12.c: New test.
3588e6
	* c-c++-common/Wbidirectional-13.c: New test.
3588e6
---
3588e6
 gcc/c-family/c.opt                            |  24 ++
3588e6
 gcc/doc/invoke.texi                           |  19 +-
3588e6
 gcc/testsuite/c-c++-common/Wbidirectional-1.c |  11 +
3588e6
 .../c-c++-common/Wbidirectional-10.c          |  27 ++
3588e6
 .../c-c++-common/Wbidirectional-11.c          |  12 +
3588e6
 .../c-c++-common/Wbidirectional-12.c          |  18 +
3588e6
 .../c-c++-common/Wbidirectional-13.c          |  16 +
3588e6
 gcc/testsuite/c-c++-common/Wbidirectional-2.c |   8 +
3588e6
 gcc/testsuite/c-c++-common/Wbidirectional-3.c |  10 +
3588e6
 gcc/testsuite/c-c++-common/Wbidirectional-4.c | 165 ++++++++
3588e6
 gcc/testsuite/c-c++-common/Wbidirectional-5.c | 165 ++++++++
3588e6
 gcc/testsuite/c-c++-common/Wbidirectional-6.c | 154 +++++++
3588e6
 gcc/testsuite/c-c++-common/Wbidirectional-7.c |   8 +
3588e6
 gcc/testsuite/c-c++-common/Wbidirectional-8.c |  12 +
3588e6
 gcc/testsuite/c-c++-common/Wbidirectional-9.c |  28 ++
3588e6
 libcpp/include/cpplib.h                       |  18 +-
3588e6
 libcpp/init.c                                 |   1 +
3588e6
 libcpp/lex.c                                  | 391 +++++++++++++++++-
3588e6
 18 files changed, 1072 insertions(+), 15 deletions(-)
3588e6
 create mode 100644 gcc/testsuite/c-c++-common/Wbidirectional-1.c
3588e6
 create mode 100644 gcc/testsuite/c-c++-common/Wbidirectional-10.c
3588e6
 create mode 100644 gcc/testsuite/c-c++-common/Wbidirectional-11.c
3588e6
 create mode 100644 gcc/testsuite/c-c++-common/Wbidirectional-12.c
3588e6
 create mode 100644 gcc/testsuite/c-c++-common/Wbidirectional-13.c
3588e6
 create mode 100644 gcc/testsuite/c-c++-common/Wbidirectional-2.c
3588e6
 create mode 100644 gcc/testsuite/c-c++-common/Wbidirectional-3.c
3588e6
 create mode 100644 gcc/testsuite/c-c++-common/Wbidirectional-4.c
3588e6
 create mode 100644 gcc/testsuite/c-c++-common/Wbidirectional-5.c
3588e6
 create mode 100644 gcc/testsuite/c-c++-common/Wbidirectional-6.c
3588e6
 create mode 100644 gcc/testsuite/c-c++-common/Wbidirectional-7.c
3588e6
 create mode 100644 gcc/testsuite/c-c++-common/Wbidirectional-8.c
3588e6
 create mode 100644 gcc/testsuite/c-c++-common/Wbidirectional-9.c
3588e6
3588e6
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
3588e6
index 06457ac739e..09391824676 100644
3588e6
--- a/gcc/c-family/c.opt
3588e6
+++ b/gcc/c-family/c.opt
3588e6
@@ -374,6 +374,30 @@ Wbad-function-cast
3588e6
 C ObjC Var(warn_bad_function_cast) Warning
3588e6
 Warn about casting functions to incompatible types.
3588e6
 
3588e6
+Wbidirectional
3588e6
+C ObjC C++ ObjC++ Warning Alias(Wbidirectional=,any,none)
3588e6
+;
3588e6
+
3588e6
+Wbidirectional=
3588e6
+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)
3588e6
+-Wbidirectional=[none|unpaired|any] Warn about UTF-8 bidirectional characters.
3588e6
+
3588e6
+; Required for these enum values.
3588e6
+SourceInclude
3588e6
+cpplib.h
3588e6
+
3588e6
+Enum
3588e6
+Name(cpp_bidirectional_level) Type(int) UnknownError(argument %qs to %<-Wbidirectional%> not recognized)
3588e6
+
3588e6
+EnumValue
3588e6
+Enum(cpp_bidirectional_level) String(none) Value(bidirectional_none)
3588e6
+
3588e6
+EnumValue
3588e6
+Enum(cpp_bidirectional_level) String(unpaired) Value(bidirectional_unpaired)
3588e6
+
3588e6
+EnumValue
3588e6
+Enum(cpp_bidirectional_level) String(any) Value(bidirectional_any)
3588e6
+
3588e6
 Wbool-compare
3588e6
 C ObjC C++ ObjC++ Var(warn_bool_compare) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
3588e6
 Warn about boolean expression compared with an integer value different from true/false.
3588e6
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
3588e6
index b64ec18ae46..e32858ce767 100644
3588e6
--- a/gcc/doc/invoke.texi
3588e6
+++ b/gcc/doc/invoke.texi
3588e6
@@ -304,7 +304,9 @@ Objective-C and Objective-C++ Dialects}.
3588e6
 -Warith-conversion @gol
3588e6
 -Warray-bounds  -Warray-bounds=@var{n} @gol
3588e6
 -Wno-attributes  -Wattribute-alias=@var{n} -Wno-attribute-alias @gol
3588e6
--Wno-attribute-warning  -Wbool-compare  -Wbool-operation @gol
3588e6
+-Wno-attribute-warning  @gol
3588e6
+-Wbidirectional=@r{[}none@r{|}unpaired@r{|}any@r{]} @gol
3588e6
+-Wbool-compare  -Wbool-operation @gol
3588e6
 -Wno-builtin-declaration-mismatch @gol
3588e6
 -Wno-builtin-macro-redefined  -Wc90-c99-compat  -Wc99-c11-compat @gol
3588e6
 -Wc11-c2x-compat @gol
3588e6
@@ -6758,6 +6760,21 @@ Attributes considered include @code{allo
3588e6
 This is the default.  You can disable these warnings with either
3588e6
 @option{-Wno-attribute-alias} or @option{-Wattribute-alias=0}.
3588e6
 
3588e6
+@item -Wbidirectional=@r{[}none@r{|}unpaired@r{|}any@r{]}
3588e6
+@opindex Wbidirectional=
3588e6
+@opindex Wbidirectional
3588e6
+@opindex Wno-bidirectional
3588e6
+Warn about UTF-8 bidirectional characters.  Such characters can change
3588e6
+left-to-right writing direction into right-to-left (and vice versa),
3588e6
+which can cause confusion between the logical order and visual order.
3588e6
+This may be dangerous; for instance, it may seem that a piece of code
3588e6
+is not commented out, whereas it in fact is.
3588e6
+
3588e6
+There are three levels of warning supported by GCC@.  The default is
3588e6
+@option{-Wbidirectional=unpaired}, which warns about improperly terminated
3588e6
+bidi contexts.  @option{-Wbidirectional=none} turns the warning off.
3588e6
+@option{-Wbidirectional=any} warns about any use of bidirectional characters.
3588e6
+
3588e6
 @item -Wbool-compare
3588e6
 @opindex Wno-bool-compare
3588e6
 @opindex Wbool-compare
3588e6
diff --git a/gcc/testsuite/c-c++-common/Wbidirectional-1.c b/gcc/testsuite/c-c++-common/Wbidirectional-1.c
3588e6
new file mode 100644
3588e6
index 00000000000..750de81fdd8
3588e6
--- /dev/null
3588e6
+++ b/gcc/testsuite/c-c++-common/Wbidirectional-1.c
3588e6
@@ -0,0 +1,11 @@
3588e6
+/* { dg-do compile } */
3588e6
+
3588e6
+int main() {
3588e6
+    int isAdmin = 0;
3588e6
+    /*‮ } ⁦if (isAdmin)⁩ ⁦ begin admins only */
3588e6
+/* { dg-warning "bidirectional" "" { target *-*-* } .-1 } */
3588e6
+        __builtin_printf("You are an admin.\n");
3588e6
+    /* end admins only ‮ { ⁦*/
3588e6
+/* { dg-warning "bidirectional" "" { target *-*-* } .-1 } */
3588e6
+    return 0;
3588e6
+}
3588e6
diff --git a/gcc/testsuite/c-c++-common/Wbidirectional-10.c b/gcc/testsuite/c-c++-common/Wbidirectional-10.c
3588e6
new file mode 100644
3588e6
index 00000000000..cd4abeeefbd
3588e6
--- /dev/null
3588e6
+++ b/gcc/testsuite/c-c++-common/Wbidirectional-10.c
3588e6
@@ -0,0 +1,27 @@
3588e6
+/* { dg-do compile } */
3588e6
+/* { dg-options "-Wbidirectional=unpaired" } */
3588e6
+/* More nesting testing.  */
3588e6
+
3588e6
+/* RLE‫ LRI⁦ PDF‬ PDI⁩*/
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int LRE_\u202a_PDF_\u202c;
3588e6
+int LRE_\u202a_PDF_\u202c_LRE_\u202a_PDF_\u202c;
3588e6
+int LRE_\u202a_LRI_\u2066_PDF_\u202c_PDI_\u2069;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int RLE_\u202b_RLI_\u2067_PDF_\u202c_PDI_\u2069;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int RLE_\u202b_RLI_\u2067_PDI_\u2069_PDF_\u202c;
3588e6
+int FSI_\u2068_LRO_\u202d_PDI_\u2069_PDF_\u202c;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int FSI_\u2068;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int FSI_\u2068_PDI_\u2069;
3588e6
+int FSI_\u2068_FSI_\u2068_PDI_\u2069;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+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;
3588e6
+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;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+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;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int RLI_\u2067_RLI_\u2067_RLI_\u2067_RLI_\u2067_FSI_\u2068_PDI_\u2069_PDI_\u2069_PDI_\u2069_PDI_\u2069;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
diff --git a/gcc/testsuite/c-c++-common/Wbidirectional-11.c b/gcc/testsuite/c-c++-common/Wbidirectional-11.c
3588e6
new file mode 100644
3588e6
index 00000000000..43d699acc64
3588e6
--- /dev/null
3588e6
+++ b/gcc/testsuite/c-c++-common/Wbidirectional-11.c
3588e6
@@ -0,0 +1,12 @@
3588e6
+/* { dg-do compile } */
3588e6
+/* { dg-options "-Wbidirectional=unpaired" } */
3588e6
+/* Test that we warn when mixing UCN and UTF-8.  */
3588e6
+
3588e6
+int LRE_‪_PDF_\u202c;
3588e6
+/* { dg-warning "mismatch" "" { target *-*-* } .-1 } */
3588e6
+int LRE_\u202a_PDF_‬_;
3588e6
+/* { dg-warning "mismatch" "" { target *-*-* } .-1 } */
3588e6
+const char *s1 = "LRE_‪_PDF_\u202c";
3588e6
+/* { dg-warning "mismatch" "" { target *-*-* } .-1 } */
3588e6
+const char *s2 = "LRE_\u202a_PDF_‬";
3588e6
+/* { dg-warning "mismatch" "" { target *-*-* } .-1 } */
3588e6
diff --git a/gcc/testsuite/c-c++-common/Wbidirectional-12.c b/gcc/testsuite/c-c++-common/Wbidirectional-12.c
3588e6
new file mode 100644
3588e6
index 00000000000..20d1566401a
3588e6
--- /dev/null
3588e6
+++ b/gcc/testsuite/c-c++-common/Wbidirectional-12.c
3588e6
@@ -0,0 +1,18 @@
3588e6
+/* { dg-do compile { target { c || c++11 } } } */
3588e6
+/* { dg-options "-Wbidirectional=any" } */
3588e6
+/* Test raw strings.  */
3588e6
+
3588e6
+const char *s1 = R"(a b c LRE‪ 1 2 3 PDF‬ x y z)";
3588e6
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
3588e6
+const char *s2 = R"(a b c RLE‫ 1 2 3 PDF‬ x y z)";
3588e6
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
3588e6
+const char *s3 = R"(a b c LRO‭ 1 2 3 PDF‬ x y z)";
3588e6
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
3588e6
+const char *s4 = R"(a b c RLO‮ 1 2 3 PDF‬ x y z)";
3588e6
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
3588e6
+const char *s7 = R"(a b c FSI⁨ 1 2 3 PDI⁩ x y) z";
3588e6
+/* { dg-warning "U\\+2068" "" { target *-*-* } .-1 } */
3588e6
+const char *s8 = R"(a b c PDI⁩ x y )z";
3588e6
+/* { dg-warning "U\\+2069" "" { target *-*-* } .-1 } */
3588e6
+const char *s9 = R"(a b c PDF‬ x y z)";
3588e6
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-1 } */
3588e6
diff --git a/gcc/testsuite/c-c++-common/Wbidirectional-13.c b/gcc/testsuite/c-c++-common/Wbidirectional-13.c
3588e6
new file mode 100644
3588e6
index 00000000000..08010e3b37b
3588e6
--- /dev/null
3588e6
+++ b/gcc/testsuite/c-c++-common/Wbidirectional-13.c
3588e6
@@ -0,0 +1,16 @@
3588e6
+/* { dg-do compile { target { c || c++11 } } } */
3588e6
+/* { dg-options "-Wbidirectional=unpaired" } */
3588e6
+/* Test raw strings.  */
3588e6
+
3588e6
+const char *s1 = R"(a b c LRE‪ 1 2 3)";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+const char *s2 = R"(a b c RLE‫ 1 2 3)";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+const char *s3 = R"(a b c LRO‭ 1 2 3)";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+const char *s4 = R"(a b c FSI⁨ 1 2 3)";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+const char *s5 = R"(a b c LRI⁦ 1 2 3)";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+const char *s6 = R"(a b c RLI⁧ 1 2 3)";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
diff --git a/gcc/testsuite/c-c++-common/Wbidirectional-2.c b/gcc/testsuite/c-c++-common/Wbidirectional-2.c
3588e6
new file mode 100644
3588e6
index 00000000000..4e04202e058
3588e6
--- /dev/null
3588e6
+++ b/gcc/testsuite/c-c++-common/Wbidirectional-2.c
3588e6
@@ -0,0 +1,8 @@
3588e6
+/* { dg-do compile } */
3588e6
+
3588e6
+int main() {
3588e6
+    /* Say hello; newline⁧/*/ return 0 ;
3588e6
+/* { dg-warning "bidirectional" "" { target *-*-* } .-1 } */
3588e6
+    __builtin_printf("Hello world.\n");
3588e6
+    return 0;
3588e6
+}
3588e6
diff --git a/gcc/testsuite/c-c++-common/Wbidirectional-3.c b/gcc/testsuite/c-c++-common/Wbidirectional-3.c
3588e6
new file mode 100644
3588e6
index 00000000000..921300e94e0
3588e6
--- /dev/null
3588e6
+++ b/gcc/testsuite/c-c++-common/Wbidirectional-3.c
3588e6
@@ -0,0 +1,10 @@
3588e6
+/* { dg-do compile } */
3588e6
+
3588e6
+int main() {
3588e6
+    const char* access_level = "user";
3588e6
+    if (__builtin_strcmp(access_level, "user‮ ⁦// Check if admin⁩ ⁦")) {
3588e6
+/* { dg-warning "bidirectional" "" { target *-*-* } .-1 } */
3588e6
+        __builtin_printf("You are an admin.\n");
3588e6
+    }
3588e6
+    return 0;
3588e6
+}
3588e6
diff --git a/gcc/testsuite/c-c++-common/Wbidirectional-4.c b/gcc/testsuite/c-c++-common/Wbidirectional-4.c
3588e6
new file mode 100644
3588e6
index 00000000000..e6638aecc6a
3588e6
--- /dev/null
3588e6
+++ b/gcc/testsuite/c-c++-common/Wbidirectional-4.c
3588e6
@@ -0,0 +1,165 @@
3588e6
+/* { dg-do compile } */
3588e6
+/* { dg-options "-Wbidirectional=any -Wno-multichar -Wno-overflow" } */
3588e6
+/* Test all bidi chars in various contexts (identifiers, comments,
3588e6
+   string literals, character constants), both UCN and UTF-8.  The bidi
3588e6
+   chars here are properly terminated, except for the character constants.  */
3588e6
+
3588e6
+/* a b c LRE‪ 1 2 3 PDF‬ x y z */
3588e6
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
3588e6
+/* a b c RLE‫ 1 2 3 PDF‬ x y z */
3588e6
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
3588e6
+/* a b c LRO‭ 1 2 3 PDF‬ x y z */
3588e6
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
3588e6
+/* a b c RLO‮ 1 2 3 PDF‬ x y z */
3588e6
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
3588e6
+/* a b c LRI⁦ 1 2 3 PDI⁩ x y z */
3588e6
+/* { dg-warning "U\\+2066" "" { target *-*-* } .-1 } */
3588e6
+/* a b c RLI⁧ 1 2 3 PDI⁩ x y */
3588e6
+/* { dg-warning "U\\+2067" "" { target *-*-* } .-1 } */
3588e6
+/* a b c FSI⁨ 1 2 3 PDI⁩ x y z */
3588e6
+/* { dg-warning "U\\+2068" "" { target *-*-* } .-1 } */
3588e6
+
3588e6
+/* Same but C++ comments instead.  */
3588e6
+// a b c LRE‪ 1 2 3 PDF‬ x y z
3588e6
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
3588e6
+// a b c RLE‫ 1 2 3 PDF‬ x y z
3588e6
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
3588e6
+// a b c LRO‭ 1 2 3 PDF‬ x y z
3588e6
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
3588e6
+// a b c RLO‮ 1 2 3 PDF‬ x y z
3588e6
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
3588e6
+// a b c LRI⁦ 1 2 3 PDI⁩ x y z
3588e6
+/* { dg-warning "U\\+2066" "" { target *-*-* } .-1 } */
3588e6
+// a b c RLI⁧ 1 2 3 PDI⁩ x y
3588e6
+/* { dg-warning "U\\+2067" "" { target *-*-* } .-1 } */
3588e6
+// a b c FSI⁨ 1 2 3 PDI⁩ x y z
3588e6
+/* { dg-warning "U\\+2068" "" { target *-*-* } .-1 } */
3588e6
+
3588e6
+/* Here we're closing an unopened context, warn when =any.  */
3588e6
+/* a b c PDI⁩ x y z */
3588e6
+/* { dg-warning "U\\+2069" "" { target *-*-* } .-1 } */
3588e6
+/* a b c PDF‬ x y z */
3588e6
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-1 } */
3588e6
+// a b c PDI⁩ x y z
3588e6
+/* { dg-warning "U\\+2069" "" { target *-*-* } .-1 } */
3588e6
+// a b c PDF‬ x y z
3588e6
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-1 } */
3588e6
+
3588e6
+void
3588e6
+g1 ()
3588e6
+{
3588e6
+  const char *s1 = "a b c LRE‪ 1 2 3 PDF‬ x y z";
3588e6
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
3588e6
+  const char *s2 = "a b c RLE‫ 1 2 3 PDF‬ x y z";
3588e6
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
3588e6
+  const char *s3 = "a b c LRO‭ 1 2 3 PDF‬ x y z";
3588e6
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
3588e6
+  const char *s4 = "a b c RLO‮ 1 2 3 PDF‬ x y z";
3588e6
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
3588e6
+  const char *s5 = "a b c LRI⁦ 1 2 3 PDI⁩ x y z";
3588e6
+/* { dg-warning "U\\+2066" "" { target *-*-* } .-1 } */
3588e6
+  const char *s6 = "a b c RLI⁧ 1 2 3 PDI⁩ x y z";
3588e6
+/* { dg-warning "U\\+2067" "" { target *-*-* } .-1 } */
3588e6
+  const char *s7 = "a b c FSI⁨ 1 2 3 PDI⁩ x y z";
3588e6
+/* { dg-warning "U\\+2068" "" { target *-*-* } .-1 } */
3588e6
+  const char *s8 = "a b c PDI⁩ x y z";
3588e6
+/* { dg-warning "U\\+2069" "" { target *-*-* } .-1 } */
3588e6
+  const char *s9 = "a b c PDF‬ x y z";
3588e6
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-1 } */
3588e6
+
3588e6
+  const char *s10 = "a b c LRE\u202a 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
3588e6
+  const char *s11 = "a b c LRE\u202A 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
3588e6
+  const char *s12 = "a b c RLE\u202b 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
3588e6
+  const char *s13 = "a b c RLE\u202B 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
3588e6
+  const char *s14 = "a b c LRO\u202d 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
3588e6
+  const char *s15 = "a b c LRO\u202D 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
3588e6
+  const char *s16 = "a b c RLO\u202e 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
3588e6
+  const char *s17 = "a b c RLO\u202E 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
3588e6
+  const char *s18 = "a b c LRI\u2066 1 2 3 PDI\u2069 x y z";
3588e6
+/* { dg-warning "U\\+2066" "" { target *-*-* } .-1 } */
3588e6
+  const char *s19 = "a b c RLI\u2067 1 2 3 PDI\u2069 x y z";
3588e6
+/* { dg-warning "U\\+2067" "" { target *-*-* } .-1 } */
3588e6
+  const char *s20 = "a b c FSI\u2068 1 2 3 PDI\u2069 x y z";
3588e6
+/* { dg-warning "U\\+2068" "" { target *-*-* } .-1 } */
3588e6
+}
3588e6
+
3588e6
+void
3588e6
+g2 ()
3588e6
+{
3588e6
+  const char c1 = '\u202a';
3588e6
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
3588e6
+  const char c2 = '\u202A';
3588e6
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
3588e6
+  const char c3 = '\u202b';
3588e6
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
3588e6
+  const char c4 = '\u202B';
3588e6
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
3588e6
+  const char c5 = '\u202d';
3588e6
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
3588e6
+  const char c6 = '\u202D';
3588e6
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
3588e6
+  const char c7 = '\u202e';
3588e6
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
3588e6
+  const char c8 = '\u202E';
3588e6
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
3588e6
+  const char c9 = '\u2066';
3588e6
+/* { dg-warning "U\\+2066" "" { target *-*-* } .-1 } */
3588e6
+  const char c10 = '\u2067';
3588e6
+/* { dg-warning "U\\+2067" "" { target *-*-* } .-1 } */
3588e6
+  const char c11 = '\u2068';
3588e6
+/* { dg-warning "U\\+2068" "" { target *-*-* } .-1 } */
3588e6
+}
3588e6
+
3588e6
+int a‪b‬c;
3588e6
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
3588e6
+int a‫b‬c;
3588e6
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
3588e6
+int a‭b‬c;
3588e6
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
3588e6
+int a‮b‬c;
3588e6
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
3588e6
+int a⁦b⁩c;
3588e6
+/* { dg-warning "U\\+2066" "" { target *-*-* } .-1 } */
3588e6
+int a⁧b⁩c;
3588e6
+/* { dg-warning "U\\+2067" "" { target *-*-* } .-1 } */
3588e6
+int a⁨b⁩c;
3588e6
+/* { dg-warning "U\\+2068" "" { target *-*-* } .-1 } */
3588e6
+int A‬X;
3588e6
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-1 } */
3588e6
+int A\u202cY;
3588e6
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-1 } */
3588e6
+int A\u202CY2;
3588e6
+/* { dg-warning "U\\+202C" "" { target *-*-* } .-1 } */
3588e6
+
3588e6
+int d\u202ae\u202cf;
3588e6
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
3588e6
+int d\u202Ae\u202cf2;
3588e6
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
3588e6
+int d\u202be\u202cf;
3588e6
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
3588e6
+int d\u202Be\u202cf2;
3588e6
+/* { dg-warning "U\\+202B" "" { target *-*-* } .-1 } */
3588e6
+int d\u202de\u202cf;
3588e6
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
3588e6
+int d\u202De\u202cf2;
3588e6
+/* { dg-warning "U\\+202D" "" { target *-*-* } .-1 } */
3588e6
+int d\u202ee\u202cf;
3588e6
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
3588e6
+int d\u202Ee\u202cf2;
3588e6
+/* { dg-warning "U\\+202E" "" { target *-*-* } .-1 } */
3588e6
+int d\u2066e\u2069f;
3588e6
+/* { dg-warning "U\\+2066" "" { target *-*-* } .-1 } */
3588e6
+int d\u2067e\u2069f;
3588e6
+/* { dg-warning "U\\+2067" "" { target *-*-* } .-1 } */
3588e6
+int d\u2068e\u2069f;
3588e6
+/* { dg-warning "U\\+2068" "" { target *-*-* } .-1 } */
3588e6
+int X\u2069;
3588e6
+/* { dg-warning "U\\+2069" "" { target *-*-* } .-1 } */
3588e6
diff --git a/gcc/testsuite/c-c++-common/Wbidirectional-5.c b/gcc/testsuite/c-c++-common/Wbidirectional-5.c
3588e6
new file mode 100644
3588e6
index 00000000000..45d3402c941
3588e6
--- /dev/null
3588e6
+++ b/gcc/testsuite/c-c++-common/Wbidirectional-5.c
3588e6
@@ -0,0 +1,165 @@
3588e6
+/* { dg-do compile } */
3588e6
+/* { dg-options "-Wbidirectional=unpaired -Wno-multichar -Wno-overflow" } */
3588e6
+/* Test all bidi chars in various contexts (identifiers, comments,
3588e6
+   string literals, character constants), both UCN and UTF-8.  The bidi
3588e6
+   chars here are properly terminated, except for the character constants.  */
3588e6
+
3588e6
+/* a b c LRE‪ 1 2 3 PDF‬ x y z */
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* a b c RLE‫ 1 2 3 PDF‬ x y z */
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* a b c LRO‭ 1 2 3 PDF‬ x y z */
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* a b c RLO‮ 1 2 3 PDF‬ x y z */
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* a b c LRI⁦ 1 2 3 PDI⁩ x y z */
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* a b c RLI⁧ 1 2 3 PDI⁩ x y */
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* a b c FSI⁨ 1 2 3 PDI⁩ x y z */
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+
3588e6
+/* Same but C++ comments instead.  */
3588e6
+// a b c LRE‪ 1 2 3 PDF‬ x y z
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+// a b c RLE‫ 1 2 3 PDF‬ x y z
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+// a b c LRO‭ 1 2 3 PDF‬ x y z
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+// a b c RLO‮ 1 2 3 PDF‬ x y z
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+// a b c LRI⁦ 1 2 3 PDI⁩ x y z
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+// a b c RLI⁧ 1 2 3 PDI⁩ x y
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+// a b c FSI⁨ 1 2 3 PDI⁩ x y z
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+
3588e6
+/* Here we're closing an unopened context, warn when =any.  */
3588e6
+/* a b c PDI⁩ x y z */
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* a b c PDF‬ x y z */
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+// a b c PDI⁩ x y z
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+// a b c PDF‬ x y z
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+
3588e6
+void
3588e6
+g1 ()
3588e6
+{
3588e6
+  const char *s1 = "a b c LRE‪ 1 2 3 PDF‬ x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s2 = "a b c RLE‫ 1 2 3 PDF‬ x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s3 = "a b c LRO‭ 1 2 3 PDF‬ x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s4 = "a b c RLO‮ 1 2 3 PDF‬ x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s5 = "a b c LRI⁦ 1 2 3 PDI⁩ x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s6 = "a b c RLI⁧ 1 2 3 PDI⁩ x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s7 = "a b c FSI⁨ 1 2 3 PDI⁩ x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s8 = "a b c PDI⁩ x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s9 = "a b c PDF‬ x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+
3588e6
+  const char *s10 = "a b c LRE\u202a 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s11 = "a b c LRE\u202A 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s12 = "a b c RLE\u202b 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s13 = "a b c RLE\u202B 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s14 = "a b c LRO\u202d 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s15 = "a b c LRO\u202D 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s16 = "a b c RLO\u202e 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s17 = "a b c RLO\u202E 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s18 = "a b c LRI\u2066 1 2 3 PDI\u2069 x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s19 = "a b c RLI\u2067 1 2 3 PDI\u2069 x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s20 = "a b c FSI\u2068 1 2 3 PDI\u2069 x y z";
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+}
3588e6
+
3588e6
+void
3588e6
+g2 ()
3588e6
+{
3588e6
+  const char c1 = '\u202a';
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char c2 = '\u202A';
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char c3 = '\u202b';
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char c4 = '\u202B';
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char c5 = '\u202d';
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char c6 = '\u202D';
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char c7 = '\u202e';
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char c8 = '\u202E';
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char c9 = '\u2066';
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char c10 = '\u2067';
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char c11 = '\u2068';
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+}
3588e6
+
3588e6
+int a‪b‬c;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int a‫b‬c;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int a‭b‬c;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int a‮b‬c;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int a⁦b⁩c;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int a⁧b⁩c;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int a⁨b⁩c;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int A‬X;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int A\u202cY;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int A\u202CY2;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+
3588e6
+int d\u202ae\u202cf;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int d\u202Ae\u202cf2;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int d\u202be\u202cf;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int d\u202Be\u202cf2;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int d\u202de\u202cf;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int d\u202De\u202cf2;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int d\u202ee\u202cf;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int d\u202Ee\u202cf2;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int d\u2066e\u2069f;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int d\u2067e\u2069f;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int d\u2068e\u2069f;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int X\u2069;
3588e6
+/* { dg-bogus "unpaired" "" { target *-*-* } .-1 } */
3588e6
diff --git a/gcc/testsuite/c-c++-common/Wbidirectional-6.c b/gcc/testsuite/c-c++-common/Wbidirectional-6.c
3588e6
new file mode 100644
3588e6
index 00000000000..1be017f828d
3588e6
--- /dev/null
3588e6
+++ b/gcc/testsuite/c-c++-common/Wbidirectional-6.c
3588e6
@@ -0,0 +1,154 @@
3588e6
+/* { dg-do compile } */
3588e6
+/* { dg-options "-Wbidirectional=unpaired" } */
3588e6
+/* Test nesting of bidi chars in various contexts.  */
3588e6
+
3588e6
+/* Terminated by the wrong char:  */
3588e6
+/* a b c LRE‪ 1 2 3 PDI⁩ x y z */
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* a b c RLE‫ 1 2 3 PDI⁩ x y  z*/
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* a b c LRO‭ 1 2 3 PDI⁩ x y z */
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* a b c RLO‮ 1 2 3 PDI⁩ x y z */
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* a b c LRI⁦ 1 2 3 PDF‬ x y z */
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* a b c RLI⁧ 1 2 3 PDF‬ x y z */
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* a b c FSI⁨ 1 2 3 PDF‬ x y  z*/
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+
3588e6
+/* LRE‪ PDF‬ */
3588e6
+/* LRE‪ LRE‪ PDF‬ PDF‬ */
3588e6
+/* PDF‬ LRE‪ PDF‬ */
3588e6
+/* LRE‪ PDF‬ LRE‪ PDF‬ */
3588e6
+/* LRE‪ LRE‪ PDF‬ */
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* PDF‬ LRE‪ */
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+
3588e6
+// a b c LRE‪ 1 2 3 PDI⁩ x y z
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+// a b c RLE‫ 1 2 3 PDI⁩ x y  z*/
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+// a b c LRO‭ 1 2 3 PDI⁩ x y z 
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+// a b c RLO‮ 1 2 3 PDI⁩ x y z 
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+// a b c LRI⁦ 1 2 3 PDF‬ x y z 
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+// a b c RLI⁧ 1 2 3 PDF‬ x y z 
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+// a b c FSI⁨ 1 2 3 PDF‬ x y  z
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+
3588e6
+// LRE‪ PDF‬ 
3588e6
+// LRE‪ LRE‪ PDF‬ PDF‬
3588e6
+// PDF‬ LRE‪ PDF‬
3588e6
+// LRE‪ PDF‬ LRE‪ PDF‬
3588e6
+// LRE‪ LRE‪ PDF‬
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+// PDF‬ LRE‪
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+
3588e6
+void
3588e6
+g1 ()
3588e6
+{
3588e6
+  const char *s1 = "a b c LRE‪ 1 2 3 PDI⁩ x y z";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s2 = "a b c LRE\u202a 1 2 3 PDI\u2069 x y z";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s3 = "a b c RLE‫ 1 2 3 PDI⁩ x y ";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s4 = "a b c RLE\u202b 1 2 3 PDI\u2069 x y z";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s5 = "a b c LRO‭ 1 2 3 PDI⁩ x y z";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s6 = "a b c LRO\u202d 1 2 3 PDI\u2069 x y z";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s7 = "a b c RLO‮ 1 2 3 PDI⁩ x y z";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s8 = "a b c RLO\u202e 1 2 3 PDI\u2069 x y z";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s9 = "a b c LRI⁦ 1 2 3 PDF‬ x y z";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s10 = "a b c LRI\u2066 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s11 = "a b c RLI⁧ 1 2 3 PDF‬ x y z\
3588e6
+    ";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-2 } */
3588e6
+  const char *s12 = "a b c RLI\u2067 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s13 = "a b c FSI⁨ 1 2 3 PDF‬ x y z";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s14 = "a b c FSI\u2068 1 2 3 PDF\u202c x y z";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s15 = "PDF‬ LRE‪";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s16 = "PDF\u202c LRE\u202a";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s17 = "LRE‪ PDF‬";
3588e6
+  const char *s18 = "LRE\u202a PDF\u202c";
3588e6
+  const char *s19 = "LRE‪ LRE‪ PDF‬ PDF‬";
3588e6
+  const char *s20 = "LRE\u202a LRE\u202a PDF\u202c PDF\u202c";
3588e6
+  const char *s21 = "PDF‬ LRE‪ PDF‬";
3588e6
+  const char *s22 = "PDF\u202c LRE\u202a PDF\u202c";
3588e6
+  const char *s23 = "LRE‪ LRE‪ PDF‬";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s24 = "LRE\u202a LRE\u202a PDF\u202c";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s25 = "PDF‬ LRE‪";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s26 = "PDF\u202c LRE\u202a";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s27 = "PDF‬ LRE\u202a";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+  const char *s28 = "PDF\u202c LRE‪";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+}
3588e6
+
3588e6
+int aLRE‪bPDI⁩;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int A\u202aB\u2069C;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int aRLE‫bPDI⁩;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int a\u202bB\u2069c;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int aLRO‭bPDI⁩;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int a\u202db\u2069c2;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int aRLO‮bPDI⁩;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int a\u202eb\u2069;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int aLRI⁦bPDF‬;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int a\u2066b\u202c;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int aRLI⁧bPDF‬c
3588e6
+;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-2 } */
3588e6
+int a\u2067b\u202c;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int aFSI⁨bPDF‬;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int a\u2068b\u202c;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int aFSI⁨bPD\u202C;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int aFSI\u2068bPDF‬_;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int aLRE‪bPDF‬b; 
3588e6
+int A\u202aB\u202c;
3588e6
+int a_LRE‪_LRE‪_b_PDF‬_PDF‬;
3588e6
+int A\u202aA\u202aB\u202cB\u202c;
3588e6
+int aPDF‬bLREadPDF‬;
3588e6
+int a_\u202C_\u202a_\u202c;
3588e6
+int a_LRE‪_b_PDF‬_c_LRE‪_PDF‬;
3588e6
+int a_\u202a_\u202c_\u202a_\u202c_;
3588e6
+int a_LRE‪_b_PDF‬_c_LRE‪;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int a_\u202a_\u202c_\u202a_;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
diff --git a/gcc/testsuite/c-c++-common/Wbidirectional-7.c b/gcc/testsuite/c-c++-common/Wbidirectional-7.c
3588e6
new file mode 100644
3588e6
index 00000000000..f0f7b3ca14a
3588e6
--- /dev/null
3588e6
+++ b/gcc/testsuite/c-c++-common/Wbidirectional-7.c
3588e6
@@ -0,0 +1,8 @@
3588e6
+/* { dg-do compile } */
3588e6
+/* { dg-options "-Wbidirectional=any" } */
3588e6
+/* Test we ignore UCNs in comments.  */
3588e6
+
3588e6
+// a b c \u202a 1 2 3
3588e6
+// a b c \u202A 1 2 3
3588e6
+/* a b c \u202a 1 2 3 */
3588e6
+/* a b c \u202A 1 2 3 */
3588e6
diff --git a/gcc/testsuite/c-c++-common/Wbidirectional-8.c b/gcc/testsuite/c-c++-common/Wbidirectional-8.c
3588e6
new file mode 100644
3588e6
index 00000000000..c7d02193131
3588e6
--- /dev/null
3588e6
+++ b/gcc/testsuite/c-c++-common/Wbidirectional-8.c
3588e6
@@ -0,0 +1,12 @@
3588e6
+/* { dg-do compile } */
3588e6
+/* { dg-options "-Wbidirectional=any" } */
3588e6
+/* Test \u vs \U.  */
3588e6
+
3588e6
+int a_\u202A;
3588e6
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
3588e6
+int a_\u202a_2;
3588e6
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
3588e6
+int a_\U0000202A_3;
3588e6
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
3588e6
+int a_\U0000202a_4;
3588e6
+/* { dg-warning "U\\+202A" "" { target *-*-* } .-1 } */
3588e6
diff --git a/gcc/testsuite/c-c++-common/Wbidirectional-9.c b/gcc/testsuite/c-c++-common/Wbidirectional-9.c
3588e6
new file mode 100644
3588e6
index 00000000000..d029209babb
3588e6
--- /dev/null
3588e6
+++ b/gcc/testsuite/c-c++-common/Wbidirectional-9.c
3588e6
@@ -0,0 +1,28 @@
3588e6
+/* { dg-do compile } */
3588e6
+/* { dg-options "-Wbidirectional=unpaired" } */
3588e6
+/* Test that we properly separate bidi contexts (comment/identifier/character
3588e6
+   constant/string literal).  */
3588e6
+
3588e6
+/* LRE ->‪<- */ int pdf_\u202c_1;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* RLE ->‫<- */ int pdf_\u202c_2;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* LRO ->‭<- */ int pdf_\u202c_3;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* RLO ->‮<- */ int pdf_\u202c_4;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* LRI ->⁦<-*/ int pdi_\u2069_1;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* RLI ->⁧<- */ int pdi_\u2069_12;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* FSI ->⁨<- */ int pdi_\u2069_3;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+
3588e6
+const char *s1 = "LRE\u202a"; /* PDF ->‬<- */
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+/* LRE ->‪<- */ const char *s2 = "PDF\u202c";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+const char *s3 = "LRE\u202a"; int pdf_\u202c_5;
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
+int lre_\u202a; const char *s4 = "PDF\u202c";
3588e6
+/* { dg-warning "unpaired" "" { target *-*-* } .-1 } */
3588e6
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
3588e6
index 6e2fcb6b1f2..e48d13c4ee1 100644
3588e6
--- a/libcpp/include/cpplib.h
3588e6
+++ b/libcpp/include/cpplib.h
3588e6
@@ -308,6 +308,17 @@ enum cpp_normalize_level {
3588e6
   normalized_none
3588e6
 };
3588e6
 
3588e6
+/* The possible bidirectional characters checking levels, from least
3588e6
+   restrictive to most.  */
3588e6
+enum cpp_bidirectional_level {
3588e6
+  /* No checking.  */
3588e6
+  bidirectional_none,
3588e6
+  /* Only detect unpaired uses of bidirectional characters.  */
3588e6
+  bidirectional_unpaired,
3588e6
+  /* Detect any use of bidirectional characters.  */
3588e6
+  bidirectional_any
3588e6
+};
3588e6
+
3588e6
 /* This structure is nested inside struct cpp_reader, and
3588e6
    carries all the options visible to the command line.  */
3588e6
 struct cpp_options
3588e6
@@ -518,6 +529,10 @@ struct cpp_options
3588e6
   /* True if warn about differences between C++98 and C++11.  */
3588e6
   bool cpp_warn_cxx11_compat;
3588e6
 
3588e6
+  /* Nonzero of bidirectional characters checking is on.  See enum
3588e6
+     cpp_bidirectional_level.  */
3588e6
+  unsigned char cpp_warn_bidirectional;
3588e6
+
3588e6
   /* Dependency generation.  */
3588e6
   struct
3588e6
   {
3588e6
@@ -616,7 +631,8 @@ enum cpp_warning_reason {
3588e6
   CPP_W_C90_C99_COMPAT,
3588e6
   CPP_W_C11_C2X_COMPAT,
3588e6
   CPP_W_CXX11_COMPAT,
3588e6
-  CPP_W_EXPANSION_TO_DEFINED
3588e6
+  CPP_W_EXPANSION_TO_DEFINED,
3588e6
+  CPP_W_BIDIRECTIONAL
3588e6
 };
3588e6
 
3588e6
 /* Callback for header lookup for HEADER, which is the name of a
3588e6
diff --git a/libcpp/init.c b/libcpp/init.c
3588e6
index 5a424e23553..f9a8f5f088f 100644
3588e6
--- a/libcpp/init.c
3588e6
+++ b/libcpp/init.c
3588e6
@@ -223,6 +223,7 @@ cpp_create_reader (enum c_lang lang, cpp_hash_table *table,
3588e6
       = ENABLE_CANONICAL_SYSTEM_HEADERS;
3588e6
   CPP_OPTION (pfile, ext_numeric_literals) = 1;
3588e6
   CPP_OPTION (pfile, warn_date_time) = 0;
3588e6
+  CPP_OPTION (pfile, cpp_warn_bidirectional) = bidirectional_unpaired;
3588e6
 
3588e6
   /* Default CPP arithmetic to something sensible for the host for the
3588e6
      benefit of dumb users like fix-header.  */
3588e6
diff --git a/libcpp/lex.c b/libcpp/lex.c
3588e6
index 8e3ef096bbe..d9c39a4105f 100644
3588e6
--- a/libcpp/lex.c
3588e6
+++ b/libcpp/lex.c
3588e6
@@ -1164,6 +1164,284 @@ _cpp_process_line_notes (cpp_reader *pfi
3588e6
     }
3588e6
 }
3588e6
 
3588e6
+namespace bidi {
3588e6
+  enum kind {
3588e6
+    NONE, LRE, RLE, LRO, RLO, LRI, RLI, FSI, PDF, PDI
3588e6
+  };
3588e6
+
3588e6
+  /* All the UTF-8 encodings of bidi characters start with E2.  */
3588e6
+  const uchar utf8_start = 0xe2;
3588e6
+
3588e6
+  /* A vector holding currently open bidi contexts.  We use a char for
3588e6
+     each context, its LSB is 1 if it represents a PDF context, 0 if it
3588e6
+     represents a PDI context.  The next bit is 1 if this context was open
3588e6
+     by a bidi character written as a UCN, and 0 when it was UTF-8.  */
3588e6
+  semi_embedded_vec <unsigned char, 16> vec;
3588e6
+
3588e6
+  /* Close the whole comment/identifier/string literal/character constant
3588e6
+     context.  */
3588e6
+  void on_close ()
3588e6
+  {
3588e6
+    vec.truncate (0);
3588e6
+  }
3588e6
+
3588e6
+  /* Pop the last element in the vector.  */
3588e6
+  void pop ()
3588e6
+  {
3588e6
+    unsigned int len = vec.count ();
3588e6
+    gcc_checking_assert (len > 0);
3588e6
+    vec.truncate (len - 1);
3588e6
+  }
3588e6
+
3588e6
+  /* Return which context is currently opened.  */
3588e6
+  kind current_ctx ()
3588e6
+  {
3588e6
+    unsigned int len = vec.count ();
3588e6
+    if (len == 0)
3588e6
+      return NONE;
3588e6
+    return (vec[len - 1] & 1) ? PDF : PDI;
3588e6
+  }
3588e6
+
3588e6
+  /* Return true if the current context comes from a UCN origin, that is,
3588e6
+     the bidi char which started this bidi context was written as a UCN.  */
3588e6
+  bool current_ctx_ucn_p ()
3588e6
+  {
3588e6
+    unsigned int len = vec.count ();
3588e6
+    gcc_checking_assert (len > 0);
3588e6
+    return (vec[len - 1] >> 1) & 1;
3588e6
+  }
3588e6
+
3588e6
+  /* We've read a bidi char, update the current vector as necessary.  */
3588e6
+  void on_char (kind k, bool ucn_p)
3588e6
+  {
3588e6
+    switch (k)
3588e6
+      {
3588e6
+      case LRE:
3588e6
+      case RLE:
3588e6
+      case LRO:
3588e6
+      case RLO:
3588e6
+	vec.push (ucn_p ? 3u : 1u);
3588e6
+	break;
3588e6
+      case LRI:
3588e6
+      case RLI:
3588e6
+      case FSI:
3588e6
+	vec.push (ucn_p ? 2u : 0u);
3588e6
+	break;
3588e6
+      case PDF:
3588e6
+	if (current_ctx () == PDF)
3588e6
+	  pop ();
3588e6
+	break;
3588e6
+      case PDI:
3588e6
+	if (current_ctx () == PDI)
3588e6
+	  pop ();
3588e6
+	break;
3588e6
+      [[likely]] case NONE:
3588e6
+	break;
3588e6
+      default:
3588e6
+	abort ();
3588e6
+      }
3588e6
+  }
3588e6
+
3588e6
+  /* Return a descriptive string for K.  */
3588e6
+  const char *to_str (kind k)
3588e6
+  {
3588e6
+    switch (k)
3588e6
+      {
3588e6
+      case LRE:
3588e6
+	return "U+202A (LEFT-TO-RIGHT EMBEDDING)";
3588e6
+      case RLE:
3588e6
+	return "U+202B (RIGHT-TO-LEFT EMBEDDING)";
3588e6
+      case LRO:
3588e6
+	return "U+202D (LEFT-TO-RIGHT OVERRIDE)";
3588e6
+      case RLO:
3588e6
+	return "U+202E (RIGHT-TO-LEFT OVERRIDE)";
3588e6
+      case LRI:
3588e6
+	return "U+2066 (LEFT-TO-RIGHT ISOLATE)";
3588e6
+      case RLI:
3588e6
+	return "U+2067 (RIGHT-TO-LEFT ISOLATE)";
3588e6
+      case FSI:
3588e6
+	return "U+2068 (FIRST STRONG ISOLATE)";
3588e6
+      case PDF:
3588e6
+	return "U+202C (POP DIRECTIONAL FORMATTING)";
3588e6
+      case PDI:
3588e6
+	return "U+2069 (POP DIRECTIONAL ISOLATE)";
3588e6
+      default:
3588e6
+	abort ();
3588e6
+      }
3588e6
+  }
3588e6
+}
3588e6
+
3588e6
+/* Parse a sequence of 3 bytes starting with P and return its bidi code.  */
3588e6
+
3588e6
+static bidi::kind
3588e6
+get_bidi_utf8 (const unsigned char *const p)
3588e6
+{
3588e6
+  gcc_checking_assert (p[0] == bidi::utf8_start);
3588e6
+
3588e6
+  if (p[1] == 0x80)
3588e6
+    switch (p[2])
3588e6
+      {
3588e6
+      case 0xaa:
3588e6
+	return bidi::LRE;
3588e6
+      case 0xab:
3588e6
+	return bidi::RLE;
3588e6
+      case 0xac:
3588e6
+	return bidi::PDF;
3588e6
+      case 0xad:
3588e6
+	return bidi::LRO;
3588e6
+      case 0xae:
3588e6
+	return bidi::RLO;
3588e6
+      default:
3588e6
+	break;
3588e6
+      }
3588e6
+  else if (p[1] == 0x81)
3588e6
+    switch (p[2])
3588e6
+      {
3588e6
+      case 0xa6:
3588e6
+	return bidi::LRI;
3588e6
+      case 0xa7:
3588e6
+	return bidi::RLI;
3588e6
+      case 0xa8:
3588e6
+	return bidi::FSI;
3588e6
+      case 0xa9:
3588e6
+	return bidi::PDI;
3588e6
+      default:
3588e6
+	break;
3588e6
+      }
3588e6
+
3588e6
+  return bidi::NONE;
3588e6
+}
3588e6
+
3588e6
+/* Parse a UCN where P points just past \u or \U and return its bidi code.  */
3588e6
+
3588e6
+static bidi::kind
3588e6
+get_bidi_ucn (const unsigned char *p, bool is_U)
3588e6
+{
3588e6
+  /* 6.4.3 Universal Character Names
3588e6
+      \u hex-quad
3588e6
+      \U hex-quad hex-quad
3588e6
+     where \unnnn means \U0000nnnn.  */
3588e6
+
3588e6
+  if (is_U)
3588e6
+    {
3588e6
+      if (p[0] != '0' || p[1] != '0' || p[2] != '0' || p[3] != '0')
3588e6
+	return bidi::NONE;
3588e6
+      /* Skip 4B so we can treat \u and \U the same below.  */
3588e6
+      p += 4;
3588e6
+    }
3588e6
+
3588e6
+  /* All code points we are looking for start with 20xx.  */
3588e6
+  if (p[0] != '2' || p[1] != '0')
3588e6
+    return bidi::NONE;
3588e6
+  else if (p[2] == '2')
3588e6
+    switch (p[3])
3588e6
+      {
3588e6
+      case 'a':
3588e6
+      case 'A':
3588e6
+	return bidi::LRE;
3588e6
+      case 'b':
3588e6
+      case 'B':
3588e6
+	return bidi::RLE;
3588e6
+      case 'c':
3588e6
+      case 'C':
3588e6
+	return bidi::PDF;
3588e6
+      case 'd':
3588e6
+      case 'D':
3588e6
+	return bidi::LRO;
3588e6
+      case 'e':
3588e6
+      case 'E':
3588e6
+	return bidi::RLO;
3588e6
+      default:
3588e6
+	break;
3588e6
+      }
3588e6
+  else if (p[2] == '6')
3588e6
+    switch (p[3])
3588e6
+      {
3588e6
+      case '6':
3588e6
+	return bidi::LRI;
3588e6
+      case '7':
3588e6
+	return bidi::RLI;
3588e6
+      case '8':
3588e6
+	return bidi::FSI;
3588e6
+      case '9':
3588e6
+	return bidi::PDI;
3588e6
+      default:
3588e6
+	break;
3588e6
+      }
3588e6
+
3588e6
+  return bidi::NONE;
3588e6
+}
3588e6
+
3588e6
+/* We're closing a bidi context, that is, we've encountered a newline,
3588e6
+   are closing a C-style comment, or are at the end of a string literal,
3588e6
+   character constant, or identifier.  Warn if this context was not
3588e6
+   properly terminated by a PDI or PDF.  P points to the last character
3588e6
+   in this context.  */
3588e6
+
3588e6
+static void
3588e6
+maybe_warn_bidi_on_close (cpp_reader *pfile, const uchar *p)
3588e6
+{
3588e6
+  if (CPP_OPTION (pfile, cpp_warn_bidirectional) == bidirectional_unpaired
3588e6
+      && bidi::vec.count () > 0)
3588e6
+    {
3588e6
+      const location_t loc
3588e6
+	= linemap_position_for_column (pfile->line_table,
3588e6
+				       CPP_BUF_COLUMN (pfile->buffer, p));
3588e6
+      cpp_warning_with_line (pfile, CPP_W_BIDIRECTIONAL, loc, 0,
3588e6
+			     "unpaired UTF-8 bidirectional character "
3588e6
+			     "detected");
3588e6
+    }
3588e6
+  /* We're done with this context.  */
3588e6
+  bidi::on_close ();
3588e6
+}
3588e6
+
3588e6
+/* We're at the beginning or in the middle of an identifier/comment/string
3588e6
+   literal/character constant.  Warn if we've encountered a bidi character.
3588e6
+   KIND says which bidi character it was; P points to it in the character
3588e6
+   stream.  UCN_P is true iff this bidi character was written as a UCN.  */
3588e6
+
3588e6
+static void
3588e6
+maybe_warn_bidi_on_char (cpp_reader *pfile, const uchar *p, bidi::kind kind,
3588e6
+			 bool ucn_p)
3588e6
+{
3588e6
+  if (__builtin_expect (kind == bidi::NONE, 1))
3588e6
+    return;
3588e6
+
3588e6
+  const unsigned char warn_bidi = CPP_OPTION (pfile, cpp_warn_bidirectional);
3588e6
+
3588e6
+  if (warn_bidi != bidirectional_none)
3588e6
+    {
3588e6
+      const location_t loc
3588e6
+	= linemap_position_for_column (pfile->line_table,
3588e6
+				       CPP_BUF_COLUMN (pfile->buffer, p));
3588e6
+      /* It seems excessive to warn about a PDI/PDF that is closing
3588e6
+	 an opened context because we've already warned about the
3588e6
+	 opening character.  Except warn when we have a UCN x UTF-8
3588e6
+	 mismatch.  */
3588e6
+      if (kind == bidi::current_ctx ())
3588e6
+	{
3588e6
+	  if (warn_bidi == bidirectional_unpaired
3588e6
+	      && bidi::current_ctx_ucn_p () != ucn_p)
3588e6
+	    cpp_warning_with_line (pfile, CPP_W_BIDIRECTIONAL, loc, 0,
3588e6
+				   "UTF-8 vs UCN mismatch when closing "
3588e6
+				   "a context by \"%s\"", bidi::to_str (kind));
3588e6
+	}
3588e6
+      else if (warn_bidi == bidirectional_any)
3588e6
+	{
3588e6
+	  if (kind == bidi::PDF || kind == bidi::PDI)
3588e6
+	    cpp_warning_with_line (pfile, CPP_W_BIDIRECTIONAL, loc, 0,
3588e6
+				   "\"%s\" is closing an unopened context",
3588e6
+				   bidi::to_str (kind));
3588e6
+	  else
3588e6
+	    cpp_warning_with_line (pfile, CPP_W_BIDIRECTIONAL, loc, 0,
3588e6
+				   "found problematic Unicode character \"%s\"",
3588e6
+				   bidi::to_str (kind));
3588e6
+	}
3588e6
+    }
3588e6
+  /* We're done with this context.  */
3588e6
+  bidi::on_char (kind, ucn_p);
3588e6
+}
3588e6
+
3588e6
 /* Skip a C-style block comment.  We find the end of the comment by
3588e6
    seeing if an asterisk is before every '/' we encounter.  Returns
3588e6
    nonzero if comment terminated by EOF, zero otherwise.
3588e6
@@ -1175,7 +1453,8 @@ _cpp_skip_block_comment (cpp_reader *pfi
3588e6
   cpp_buffer *buffer = pfile->buffer;
3588e6
   const uchar *cur = buffer->cur;
3588e6
   uchar c;
3588e6
-
3588e6
+  const bool warn_bidi_p = (CPP_OPTION (pfile, cpp_warn_bidirectional)
3588e6
+			    != bidirectional_none);
3588e6
   cur++;
3588e6
   if (*cur == '/')
3588e6
     cur++;
3588e6
@@ -1189,7 +1468,11 @@ _cpp_skip_block_comment (cpp_reader *pfi
3588e6
       if (c == '/')
3588e6
 	{
3588e6
 	  if (cur[-2] == '*')
3588e6
-	    break;
3588e6
+	    {
3588e6
+	      if (warn_bidi_p)
3588e6
+		maybe_warn_bidi_on_close (pfile, cur);
3588e6
+	      break;
3588e6
+	    }
3588e6
 
3588e6
 	  /* Warn about potential nested comments, but not if the '/'
3588e6
 	     comes immediately before the true comment delimiter.
3588e6
@@ -1208,6 +1491,8 @@ _cpp_skip_block_comment (cpp_reader *pfi
3588e6
 	{
3588e6
 	  unsigned int cols;
3588e6
 	  buffer->cur = cur - 1;
3588e6
+	  if (warn_bidi_p)
3588e6
+	    maybe_warn_bidi_on_close (pfile, cur);
3588e6
 	  _cpp_process_line_notes (pfile, true);
3588e6
 	  if (buffer->next_line >= buffer->rlimit)
3588e6
 	    return true;
3588e6
@@ -1218,6 +1503,13 @@ _cpp_skip_block_comment (cpp_reader *pfi
3588e6
 
3588e6
 	  cur = buffer->cur;
3588e6
 	}
3588e6
+      /* If this is a beginning of a UTF-8 encoding, it might be
3588e6
+	 a bidirectional character.  */
3588e6
+      else if (__builtin_expect (c == bidi::utf8_start, 0) && warn_bidi_p)
3588e6
+	{
3588e6
+	  bidi::kind kind = get_bidi_utf8 (cur - 1);
3588e6
+	  maybe_warn_bidi_on_char (pfile, cur, kind, /*ucn_p=*/false);
3588e6
+	}
3588e6
     }
3588e6
 
3588e6
   buffer->cur = cur;
3588e6
@@ -1233,9 +1525,32 @@ skip_line_comment (cpp_reader *pfile)
3588e6
 {
3588e6
   cpp_buffer *buffer = pfile->buffer;
3588e6
   location_t orig_line = pfile->line_table->highest_line;
3588e6
+  const bool warn_bidi_p = (CPP_OPTION (pfile, cpp_warn_bidirectional)
3588e6
+			    != bidirectional_none);
3588e6
 
3588e6
-  while (*buffer->cur != '\n')
3588e6
-    buffer->cur++;
3588e6
+  if (!warn_bidi_p)
3588e6
+    while (*buffer->cur != '\n')
3588e6
+      buffer->cur++;
3588e6
+  else
3588e6
+    {
3588e6
+      while (*buffer->cur != '\n'
3588e6
+	     && *buffer->cur != bidi::utf8_start)
3588e6
+	buffer->cur++;
3588e6
+      if (__builtin_expect (*buffer->cur == bidi::utf8_start, 0))
3588e6
+	{
3588e6
+	  while (*buffer->cur != '\n')
3588e6
+	    {
3588e6
+	      if (__builtin_expect (*buffer->cur == bidi::utf8_start, 0))
3588e6
+		{
3588e6
+		  bidi::kind kind = get_bidi_utf8 (buffer->cur);
3588e6
+		  maybe_warn_bidi_on_char (pfile, buffer->cur, kind,
3588e6
+					   /*ucn_p=*/false);
3588e6
+		}
3588e6
+	      buffer->cur++;
3588e6
+	    }
3588e6
+	  maybe_warn_bidi_on_close (pfile, buffer->cur);
3588e6
+	}
3588e6
+    }
3588e6
 
3588e6
   _cpp_process_line_notes (pfile, true);
3588e6
   return orig_line != pfile->line_table->highest_line;
3588e6
@@ -1317,11 +1632,14 @@ static const cppchar_t utf8_signifier =
3588e6
 
3588e6
 /* Returns TRUE if the sequence starting at buffer->cur is valid in
3588e6
    an identifier.  FIRST is TRUE if this starts an identifier.  */
3588e6
+
3588e6
 static bool
3588e6
 forms_identifier_p (cpp_reader *pfile, int first,
3588e6
 		    struct normalize_state *state)
3588e6
 {
3588e6
   cpp_buffer *buffer = pfile->buffer;
3588e6
+  const bool warn_bidi_p = (CPP_OPTION (pfile, cpp_warn_bidirectional)
3588e6
+			    != bidirectional_none);
3588e6
 
3588e6
   if (*buffer->cur == '$')
3588e6
     {
3588e6
@@ -1344,6 +1662,13 @@ forms_identifier_p (cpp_reader *pfile, i
3588e6
       cppchar_t s;
3588e6
       if (*buffer->cur >= utf8_signifier)
3588e6
 	{
3588e6
+	  if (__builtin_expect (*buffer->cur == bidi::utf8_start, 0)
3588e6
+	      && warn_bidi_p)
3588e6
+	    {
3588e6
+	      bidi::kind kind = get_bidi_utf8 (buffer->cur);
3588e6
+	      maybe_warn_bidi_on_char (pfile, buffer->cur, kind,
3588e6
+				       /*ucn_p=*/false);
3588e6
+	    }
3588e6
 	  if (_cpp_valid_utf8 (pfile, &buffer->cur, buffer->rlimit, 1 + !first,
3588e6
 			       state, &s))
3588e6
 	    return true;
3588e6
@@ -1352,6 +1677,13 @@ forms_identifier_p (cpp_reader *pfile, i
3588e6
 	       && (buffer->cur[1] == 'u' || buffer->cur[1] == 'U'))
3588e6
 	{
3588e6
 	  buffer->cur += 2;
3588e6
+	  if (warn_bidi_p)
3588e6
+	    {
3588e6
+	      bidi::kind kind = get_bidi_ucn (buffer->cur,
3588e6
+					      buffer->cur[-1] == 'U');
3588e6
+	      maybe_warn_bidi_on_char (pfile, buffer->cur, kind,
3588e6
+				       /*ucn_p=*/true);
3588e6
+	    }
3588e6
 	  if (_cpp_valid_ucn (pfile, &buffer->cur, buffer->rlimit, 1 + !first,
3588e6
 			      state, &s, NULL, NULL))
3588e6
 	    return true;
3588e6
@@ -1460,6 +1792,8 @@ lex_identifier (cpp_reader *pfile, const
3588e6
   const uchar *cur;
3588e6
   unsigned int len;
3588e6
   unsigned int hash = HT_HASHSTEP (0, *base);
3588e6
+  const bool warn_bidi_p = (CPP_OPTION (pfile, cpp_warn_bidirectional)
3588e6
+			    != bidirectional_none);
3588e6
 
3588e6
   cur = pfile->buffer->cur;
3588e6
   if (! starts_ucn)
3588e6
@@ -1476,13 +1810,17 @@ lex_identifier (cpp_reader *pfile, const
3588e6
     {
3588e6
       /* Slower version for identifiers containing UCNs
3588e6
 	 or extended chars (including $).  */
3588e6
-      do {
3588e6
-	while (ISIDNUM (*pfile->buffer->cur))
3588e6
-	  {
3588e6
-	    NORMALIZE_STATE_UPDATE_IDNUM (nst, *pfile->buffer->cur);
3588e6
-	    pfile->buffer->cur++;
3588e6
-	  }
3588e6
-      } while (forms_identifier_p (pfile, false, nst));
3588e6
+      do
3588e6
+	{
3588e6
+	  while (ISIDNUM (*pfile->buffer->cur))
3588e6
+	    {
3588e6
+	      NORMALIZE_STATE_UPDATE_IDNUM (nst, *pfile->buffer->cur);
3588e6
+	      pfile->buffer->cur++;
3588e6
+	    }
3588e6
+	}
3588e6
+      while (forms_identifier_p (pfile, false, nst));
3588e6
+      if (warn_bidi_p)
3588e6
+	maybe_warn_bidi_on_close (pfile, pfile->buffer->cur);
3588e6
       result = _cpp_interpret_identifier (pfile, base,
3588e6
 					  pfile->buffer->cur - base);
3588e6
       *spelling = cpp_lookup (pfile, base, pfile->buffer->cur - base);
3588e6
@@ -1684,6 +2022,8 @@ lex_raw_string (cpp_reader *pfile, cpp_t
3588e6
   _cpp_buff *first_buff = NULL, *last_buff = NULL;
3588e6
   size_t raw_prefix_start;
3588e6
   _cpp_line_note *note = &pfile->buffer->notes[pfile->buffer->cur_note];
3588e6
+  const bool warn_bidi_p = (CPP_OPTION (pfile, cpp_warn_bidirectional)
3588e6
+			    != bidirectional_none);
3588e6
 
3588e6
   type = (*base == 'L' ? CPP_WSTRING :
3588e6
 	  *base == 'U' ? CPP_STRING32 :
3588e6
@@ -1920,8 +2260,16 @@ lex_raw_string (cpp_reader *pfile, cpp_t
3588e6
 	  cur = base = pfile->buffer->cur;
3588e6
 	  note = &pfile->buffer->notes[pfile->buffer->cur_note];
3588e6
 	}
3588e6
+      else if (__builtin_expect ((unsigned char) c == bidi::utf8_start, 0)
3588e6
+	       && warn_bidi_p)
3588e6
+	maybe_warn_bidi_on_char (pfile, cur - 1,
3588e6
+				 get_bidi_utf8 (cur - 1),
3588e6
+				 /*ucn_p=*/false);
3588e6
     }
3588e6
 
3588e6
+  if (warn_bidi_p)
3588e6
+    maybe_warn_bidi_on_close (pfile, cur);
3588e6
+
3588e6
   if (CPP_OPTION (pfile, user_literals))
3588e6
     {
3588e6
       /* If a string format macro, say from inttypes.h, is placed touching
3588e6
@@ -2016,15 +2364,28 @@ lex_string (cpp_reader *pfile, cpp_token
3588e6
   else
3588e6
     terminator = '>', type = CPP_HEADER_NAME;
3588e6
 
3588e6
+  const bool warn_bidi_p = (CPP_OPTION (pfile, cpp_warn_bidirectional)
3588e6
+			    != bidirectional_none);
3588e6
   for (;;)
3588e6
     {
3588e6
       cppchar_t c = *cur++;
3588e6
 
3588e6
       /* In #include-style directives, terminators are not escapable.  */
3588e6
       if (c == '\\' && !pfile->state.angled_headers && *cur != '\n')
3588e6
-	cur++;
3588e6
+	{
3588e6
+	  if ((cur[0] == 'u' || cur[0] == 'U') && warn_bidi_p)
3588e6
+	    {
3588e6
+	      bidi::kind kind = get_bidi_ucn (cur + 1, cur[0] == 'U');
3588e6
+	      maybe_warn_bidi_on_char (pfile, cur, kind, /*ucn_p=*/true);
3588e6
+	    }
3588e6
+	  cur++;
3588e6
+	}
3588e6
       else if (c == terminator)
3588e6
-	break;
3588e6
+	{
3588e6
+	  if (warn_bidi_p)
3588e6
+	    maybe_warn_bidi_on_close (pfile, cur - 1);
3588e6
+	  break;
3588e6
+	}
3588e6
       else if (c == '\n')
3588e6
 	{
3588e6
 	  cur--;
3588e6
@@ -2041,6 +2402,11 @@ lex_string (cpp_reader *pfile, cpp_token
3588e6
 	}
3588e6
       else if (c == '\0')
3588e6
 	saw_NUL = true;
3588e6
+      else if (__builtin_expect (c == bidi::utf8_start, 0) && warn_bidi_p)
3588e6
+	{
3588e6
+	  bidi::kind kind = get_bidi_utf8 (cur - 1);
3588e6
+	  maybe_warn_bidi_on_char (pfile, cur - 1, kind, /*ucn_p=*/false);
3588e6
+	}
3588e6
     }
3588e6
 
3588e6
   if (saw_NUL && !pfile->state.skipping)
3588e6
base-commit: b0b1d8d5d90d7c499e2733e8d01ba8b73217f332
3588e6
-- 
3588e6
2.31.1
3588e6