libcpp: Diagnose __has_include outside of preprocessor directives [PR93545]
authorJakub Jelinek <jakub@redhat.com>
Tue, 4 Feb 2020 12:39:59 +0000 (13:39 +0100)
committerJakub Jelinek <jakub@redhat.com>
Tue, 4 Feb 2020 12:39:59 +0000 (13:39 +0100)
The standard says http://eel.is/c++draft/cpp.cond#7.sentence-2 that
__has_include can't appear at arbitrary places in the source.  As we have
not recognized __has_include* outside of preprocessing directives in the
past, accepting it there now would be a regression.  The patch does still
allow it in #define if it is then used in preprocessing directives, I guess
that use isn't strictly valid either, but clang seems to accept it.

2020-02-04  Jakub Jelinek  <jakub@redhat.com>

* macro.c (builtin_has_include): Diagnose __has_include* use outside
of preprocessing directives.

* c-c++-common/cpp/has-include-1.c: New test.
* c-c++-common/cpp/has-include-next-1.c: New test.
* c-c++-common/gomp/has-include-1.c: New test.

gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/cpp/has-include-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/cpp/has-include-next-1.c [new file with mode: 0644]
libcpp/ChangeLog
libcpp/macro.c

index e46561a540cf598d3b1b58371a41d93c71f35553..25e9595a6f620ad7d55364c4011cd1cbeb67d7be 100644 (file)
@@ -1,5 +1,9 @@
 2020-02-04  Jakub Jelinek  <jakub@redhat.com>
 
+       * c-c++-common/cpp/has-include-1.c: New test.
+       * c-c++-common/cpp/has-include-next-1.c: New test.
+       * c-c++-common/gomp/has-include-1.c: New test.
+
        PR preprocessor/93545
        * c-c++-common/cpp/pr88974.c: Expect another diagnostics during error
        recovery.
diff --git a/gcc/testsuite/c-c++-common/cpp/has-include-1.c b/gcc/testsuite/c-c++-common/cpp/has-include-1.c
new file mode 100644 (file)
index 0000000..1d9f011
--- /dev/null
@@ -0,0 +1,104 @@
+/* { dg-do preprocess } */
+
+#if __has_include ("stdlib.h")
+#else
+#error error 1
+#endif
+#if __has_include (<stdlib.h>)
+#else
+#error error 2
+#endif
+#if !__has_include ("stdlib.h")
+#error error 3
+#elif !__has_include (<stdlib.h>)
+#error error 4
+#endif
+#if __has_include ("stdlib.h") && __has_include (<stdlib.h>)
+#else
+#error error 5
+#endif
+#if !defined(__has_include)
+#error error 6
+#endif
+#ifndef __has_include
+#error error 7
+#endif
+#ifdef __has_include
+#else
+#error error 8
+#endif
+#define m1 __has_include("stdlib.h")
+#define m2 ("stdlib.h")
+#define m3 ("has-include-1-nonexistent.h")
+#define m4 has-include-1-nonexistent-2.h>)
+#define m5 <stdlib.h>
+#if !m1
+#error error 9
+#endif
+#if !__has_include m2
+#error error 10
+#endif
+#if __has_include m3
+#error error 11
+#endif
+#if __has_include (<m4
+#error error 12
+#endif
+#if !__has_include (m5)
+#error error 13
+#endif
+__has_include (<stdlib.h>)             /* { dg-error "used outside of preprocessing directive" } */
+m1                                     /* { dg-error "used outside of preprocessing directive" } */
+#if 1
+m1                                     /* { dg-error "used outside of preprocessing directive" } */
+#endif
+#if 0
+#elif 1
+m1                                     /* { dg-error "used outside of preprocessing directive" } */
+#endif
+#if 0
+m1
+#endif
+#if 0
+#elif 0
+m1
+#endif
+#if __has_include "stdlib.h")          /* { dg-error "missing" } */
+#endif
+#if __has_include (stdlib.h)           /* { dg-error "operator|missing" } */
+#endif
+#if __has_include ()                   /* { dg-error "operator|missing" } */
+#endif
+#if __has_include )                    /* { dg-error "operator|missing" } */
+#endif
+#if __has_include ("stdlib.h)
+#endif
+/* { dg-error "operator|missing\[^\n\r]*after" "" { target *-*-* } .-2 } */
+/* { dg-warning "missing terminating" "" { target *-*-* } .-3 } */
+#if __has_include (stdlib.h>)          /* { dg-error "operator|missing" } */
+#endif
+#if __has_include ("stdlib.h"          /* { dg-error "missing" } */
+#endif
+#if __has_include (                    /* { dg-error "operator|missing" } */
+#endif
+#if __has_include                      /* { dg-error "operator|missing" } */
+#endif
+#if __has_include"stdlib.h"            /* { dg-error "missing" } */
+#endif
+#if __has_include'h'                   /* { dg-error "operator|missing" } */
+#endif
+#if __has_include('h'                  /* { dg-error "operator|missing" } */
+#endif
+#if __has_include('h')                 /* { dg-error "operator" } */
+#endif
+#define H(h) __has_include(h)
+#if H(<stdlib.h>)
+#else
+#error error 14
+#endif
+void
+foo ()
+{
+#pragma omp parallel if (__has_include ("<stdlib.h>"))
+  ;
+}
diff --git a/gcc/testsuite/c-c++-common/cpp/has-include-next-1.c b/gcc/testsuite/c-c++-common/cpp/has-include-next-1.c
new file mode 100644 (file)
index 0000000..79e528d
--- /dev/null
@@ -0,0 +1,104 @@
+/* { dg-do preprocess } */
+
+#if __has_include_next ("stdlib.h")
+#else
+#error error 1
+#endif
+#if __has_include_next (<stdlib.h>)
+#else
+#error error 2
+#endif
+#if !__has_include_next ("stdlib.h")
+#error error 3
+#elif !__has_include_next (<stdlib.h>)
+#error error 4
+#endif
+#if __has_include_next ("stdlib.h") && __has_include_next (<stdlib.h>)
+#else
+#error error 5
+#endif
+#if !defined(__has_include_next)
+#error error 6
+#endif
+#ifndef __has_include_next
+#error error 7
+#endif
+#ifdef __has_include_next
+#else
+#error error 8
+#endif
+#define m1 __has_include_next("stdlib.h")
+#define m2 ("stdlib.h")
+#define m3 ("has-include-1-nonexistent.h")
+#define m4 has-include-1-nonexistent-2.h>)
+#define m5 <stdlib.h>
+#if !m1
+#error error 9
+#endif
+#if !__has_include_next m2
+#error error 10
+#endif
+#if __has_include_next m3
+#error error 11
+#endif
+#if __has_include_next (<m4
+#error error 12
+#endif
+#if !__has_include_next (m5)
+#error error 13
+#endif
+__has_include_next (<stdlib.h>)                /* { dg-error "used outside of preprocessing directive" } */
+m1                                     /* { dg-error "used outside of preprocessing directive" } */
+#if 1
+m1                                     /* { dg-error "used outside of preprocessing directive" } */
+#endif
+#if 0
+#elif 1
+m1                                     /* { dg-error "used outside of preprocessing directive" } */
+#endif
+#if 0
+m1
+#endif
+#if 0
+#elif 0
+m1
+#endif
+#if __has_include_next "stdlib.h")     /* { dg-error "missing" } */
+#endif
+#if __has_include_next (stdlib.h)      /* { dg-error "operator|missing" } */
+#endif
+#if __has_include_next ()              /* { dg-error "operator|missing" } */
+#endif
+#if __has_include_next )               /* { dg-error "operator|missing" } */
+#endif
+#if __has_include_next ("stdlib.h)
+#endif
+/* { dg-error "operator|missing\[^\n\r]*after" "" { target *-*-* } .-2 } */
+/* { dg-warning "missing terminating" "" { target *-*-* } .-3 } */
+#if __has_include_next (stdlib.h>)     /* { dg-error "operator|missing" } */
+#endif
+#if __has_include_next ("stdlib.h"     /* { dg-error "missing" } */
+#endif
+#if __has_include_next (               /* { dg-error "operator|missing" } */
+#endif
+#if __has_include_next                 /* { dg-error "operator|missing" } */
+#endif
+#if __has_include_next"stdlib.h"       /* { dg-error "missing" } */
+#endif
+#if __has_include_next'h'              /* { dg-error "operator|missing" } */
+#endif
+#if __has_include_next('h'             /* { dg-error "operator|missing" } */
+#endif
+#if __has_include_next('h')            /* { dg-error "operator" } */
+#endif
+#define H(h) __has_include_next(h)
+#if H(<stdlib.h>)
+#else
+#error error 14
+#endif
+void
+foo ()
+{
+#pragma omp parallel if (__has_include_next ("<stdlib.h>"))
+  ;
+}
index 11c0773adb6e979e49343180e2d3834f76eac49b..cc5c575ca83e64b65adbcbbb3942a2564098c4b4 100644 (file)
@@ -1,5 +1,8 @@
 2020-02-04  Jakub Jelinek  <jakub@redhat.com>
 
+       * macro.c (builtin_has_include): Diagnose __has_include* use outside
+       of preprocessing directives.
+
        PR preprocessor/93545
        * macro.c (cpp_get_token_no_padding): New function.
        (builtin_has_include): Use it instead of cpp_get_token.  Don't check
index 9f9f83134b4fea71c28f9c840c2faa16901634f6..ec0030742c09b46f438c06c1392f26a0c56f48a5 100644 (file)
@@ -359,6 +359,11 @@ builtin_has_include (cpp_reader *pfile, cpp_hashnode *op, bool has_next)
 {
   int result = 0;
 
+  if (!pfile->state.in_directive)
+    cpp_error (pfile, CPP_DL_ERROR,
+              "\"%s\" used outside of preprocessing directive",
+              NODE_NAME (op));
+
   pfile->state.angled_headers = true;
   const cpp_token *token = cpp_get_token_no_padding (pfile);
   bool paren = token->type == CPP_OPEN_PAREN;