From 47642d2b7ea175a6c0d80bf14354246ca20af342 Mon Sep 17 00:00:00 2001 From: Alp Toker Date: Tue, 3 Dec 2013 06:13:01 +0000 Subject: [PATCH] Emit an extension warning when changing system header tokens clang converts keywords to identifiers for compatibility with various system headers such as GNU libc. Implement a -Wkeyword-compat extension warning to diagnose those cases. The warning is on by default but will generally be ignored in system headers. It can however be enabled globally to aid standards conformance testing. This also changes the __uptr keyword avoidance from r195710 to no longer special-case system headers, bringing it in line with other similar workarounds in clang. Implementation returns bool for symmetry with token annotation functions. Some examples: warning: keyword '__is_pod' will be treated as an identifier for the remainder of the translation unit [-Wkeyword-compat] struct __is_pod warning: keyword '__uptr' will be treated as an identifier here [-Wkeyword-compat] union w *__uptr; llvm-svn: 196212 --- clang/include/clang/Basic/DiagnosticGroups.td | 1 + clang/include/clang/Basic/DiagnosticParseKinds.td | 3 +++ clang/include/clang/Parse/Parser.h | 7 +++++++ clang/lib/Parse/ParseDecl.cpp | 13 +++++-------- clang/lib/Parse/ParseDeclCXX.cpp | 6 ++---- clang/lib/Parse/Parser.cpp | 11 +++++++++++ clang/test/PCH/cxx-traits.cpp | 4 +++- clang/test/PCH/cxx-traits.h | 4 ++-- clang/test/Sema/Inputs/ms-keyword-system-header.h | 3 +++ clang/test/Sema/ms-keyword-system-header.c | 2 ++ 10 files changed, 39 insertions(+), 15 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 6de1a48d9f9d..ec1eb1a8b59d 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -50,6 +50,7 @@ def BuiltinRequiresHeader : DiagGroup<"builtin-requires-header">; def C99Compat : DiagGroup<"c99-compat">; def CXXCompat: DiagGroup<"c++-compat">; def ExternCCompat : DiagGroup<"extern-c-compat">; +def KeywordCompat : DiagGroup<"keyword-compat">; def GNUCaseRange : DiagGroup<"gnu-case-range">; def CastAlign : DiagGroup<"cast-align">; def : DiagGroup<"cast-qual">; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 4d0641de5493..2feab52ef4d1 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -58,6 +58,9 @@ def ext_plain_complex : ExtWarn< def ext_integer_complex : Extension< "complex integer types are a GNU extension">, InGroup; def ext_thread_before : Extension<"'__thread' before '%0'">; +def ext_keyword_as_ident : ExtWarn< + "keyword '%0' will be treated as an identifier %select{here|for the remainder of the translation unit}1">, + InGroup; def error_empty_enum : Error<"use of empty enum">; def err_invalid_sign_spec : Error<"'%0' cannot be signed or unsigned">; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 8807e3b47b76..81ba5e1882ee 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -563,6 +563,13 @@ private: const char *&PrevSpec, unsigned &DiagID, bool &isInvalid); + /// TryKeywordIdentFallback - For compatibility with system headers using + /// keywords as identifiers, attempt to convert the current token to an + /// identifier and optionally disable the keyword for the remainder of the + /// translation unit. This returns false if the token was not replaced, + /// otherwise emits a diagnostic and returns true. + bool TryKeywordIdentFallback(bool DisableKeyword); + /// \brief Get the TemplateIdAnnotation from the token. TemplateIdAnnotation *takeTemplateIdAnnotation(const Token &tok); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 881e2252fd41..4fce10fc32fc 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2739,10 +2739,8 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // then treat __is_signed as an identifier rather than as a keyword. if (DS.getTypeSpecType() == TST_bool && DS.getTypeQualifiers() == DeclSpec::TQ_const && - DS.getStorageClassSpec() == DeclSpec::SCS_static) { - Tok.getIdentifierInfo()->RevertTokenIDToIdentifier(); - Tok.setKind(tok::identifier); - } + DS.getStorageClassSpec() == DeclSpec::SCS_static) + TryKeywordIdentFallback(true); // We're done with the declaration-specifiers. goto DoneWithDeclSpec; @@ -4488,10 +4486,9 @@ void Parser::ParseTypeQualifierListOpt(DeclSpec &DS, // GNU libc headers in C mode use '__uptr' as an identifer which conflicts // with the MS modifier keyword. if (VendorAttributesAllowed && !getLangOpts().CPlusPlus && - IdentifierRequired && DS.isEmpty() && NextToken().is(tok::semi) && - PP.getSourceManager().isInSystemHeader(Loc)) { - Tok.setKind(tok::identifier); - continue; + IdentifierRequired && DS.isEmpty() && NextToken().is(tok::semi)) { + if (TryKeywordIdentFallback(false)) + continue; } case tok::kw___sptr: case tok::kw___w64: diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 32e151cf2c29..d4a4ded252bd 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1198,15 +1198,13 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, Tok.is(tok::kw___is_scalar) || Tok.is(tok::kw___is_signed) || Tok.is(tok::kw___is_unsigned) || - Tok.is(tok::kw___is_void))) { + Tok.is(tok::kw___is_void))) // GNU libstdc++ 4.2 and libc++ use certain intrinsic names as the // name of struct templates, but some are keywords in GCC >= 4.3 // and Clang. Therefore, when we see the token sequence "struct // X", make X into a normal identifier rather than a keyword, to // allow libstdc++ 4.2 and libc++ to work properly. - Tok.getIdentifierInfo()->RevertTokenIDToIdentifier(); - Tok.setKind(tok::identifier); - } + TryKeywordIdentFallback(true); // Parse the (optional) nested-name-specifier. CXXScopeSpec &SS = DS.getTypeSpecScope(); diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 0f950ceabc55..59ffc0562aac 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1517,6 +1517,17 @@ Parser::TryAnnotateName(bool IsAddressOfOperand, return ANK_Unresolved; } +bool Parser::TryKeywordIdentFallback(bool DisableKeyword) { + assert(Tok.isNot(tok::identifier)); + Diag(Tok, diag::ext_keyword_as_ident) + << PP.getSpelling(Tok) + << DisableKeyword; + if (DisableKeyword) + Tok.getIdentifierInfo()->RevertTokenIDToIdentifier(); + Tok.setKind(tok::identifier); + return true; +} + /// TryAnnotateTypeOrScopeToken - If the current token position is on a /// typename (possibly qualified in C++) or a C++ scope specifier not followed /// by a typename, TryAnnotateTypeOrScopeToken will replace one or more tokens diff --git a/clang/test/PCH/cxx-traits.cpp b/clang/test/PCH/cxx-traits.cpp index 938f36f2c279..ffdfccc6f47f 100644 --- a/clang/test/PCH/cxx-traits.cpp +++ b/clang/test/PCH/cxx-traits.cpp @@ -2,9 +2,11 @@ // RUN: %clang_cc1 -include %S/cxx-traits.h -std=c++11 -fsyntax-only -verify %s // RUN: %clang_cc1 -x c++-header -std=c++11 -emit-pch -o %t %S/cxx-traits.h -// RUN: %clang_cc1 -std=c++11 -include-pch %t -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++11 -include-pch %t -DPCH -fsyntax-only -verify %s +#ifdef PCH // expected-no-diagnostics +#endif bool _Is_pod_comparator = __is_pod::__value; bool _Is_empty_check = __is_empty::__value; diff --git a/clang/test/PCH/cxx-traits.h b/clang/test/PCH/cxx-traits.h index 8b62002789d6..836804ef2c88 100644 --- a/clang/test/PCH/cxx-traits.h +++ b/clang/test/PCH/cxx-traits.h @@ -1,12 +1,12 @@ // Header for PCH test cxx-traits.cpp template -struct __is_pod { +struct __is_pod { // expected-warning {{keyword '__is_pod' will be treated as an identifier for the remainder of the translation unit}} enum { __value }; }; template -struct __is_empty { +struct __is_empty { // expected-warning {{keyword '__is_empty' will be treated as an identifier for the remainder of the translation unit}} enum { __value }; }; diff --git a/clang/test/Sema/Inputs/ms-keyword-system-header.h b/clang/test/Sema/Inputs/ms-keyword-system-header.h index 13cfe3a6ea32..43a3db7a12ba 100644 --- a/clang/test/Sema/Inputs/ms-keyword-system-header.h +++ b/clang/test/Sema/Inputs/ms-keyword-system-header.h @@ -2,5 +2,8 @@ typedef union { union w *__uptr; +#if defined(MS) && defined(NOT_SYSTEM) + // expected-warning@-2 {{keyword '__uptr' will be treated as an identifier here}} +#endif int *__iptr; } WS __attribute__((__transparent_union__)); diff --git a/clang/test/Sema/ms-keyword-system-header.c b/clang/test/Sema/ms-keyword-system-header.c index bf7a9f4c8e4b..b4ff5683cdfd 100644 --- a/clang/test/Sema/ms-keyword-system-header.c +++ b/clang/test/Sema/ms-keyword-system-header.c @@ -1,4 +1,6 @@ // RUN: %clang_cc1 -fms-extensions -D MS -isystem %S/Inputs %s -fsyntax-only -verify +// RUN: %clang_cc1 -fms-extensions -D MS -Wno-keyword-compat -I %S/Inputs %s -fsyntax-only -verify +// RUN: %clang_cc1 -fms-extensions -D MS -D NOT_SYSTEM -I %S/Inputs %s -fsyntax-only -verify // RUN: %clang_cc1 -isystem %S/Inputs %s -fsyntax-only -verify // PR17824: GNU libc uses MS keyword __uptr as an identifier in C mode