Blame SOURCES/gcc10-add-Wbidirectional.patch

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