Skip to content

Commit 56a3f3c

Browse files
committed
[C] Diagnose use of C++ keywords in C
This adds a new diagnostic group, -Widentifier-is-c++-keyword, which is off by default and grouped under -Wc++-compat. The diagnostic catches use of C++ keywords in C code. This change additionally fixes an issue with -Wreserved-identifier not diagnosing use of reserved identifiers in function parameter lists. Partially fixes #21898
1 parent ecdd3fd commit 56a3f3c

File tree

7 files changed

+279
-4
lines changed

7 files changed

+279
-4
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ C Language Changes
143143
- Added ``-Wimplicit-void-ptr-cast``, grouped under ``-Wc++-compat``, which
144144
diagnoses implicit conversion from ``void *`` to another pointer type as
145145
being incompatible with C++. (#GH17792)
146+
- Added ``-Widentifier-is-c++-keyword``, grouped under ``-Wc++-compat``, which
147+
diagnoses when a C++ keyword is used as an identifier in C. Partially
148+
addresses #GH21898.
146149

147150
C2y Feature Support
148151
^^^^^^^^^^^^^^^^^^^

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,9 @@ def C99Compat : DiagGroup<"c99-compat">;
155155
def C23Compat : DiagGroup<"c23-compat">;
156156
def : DiagGroup<"c2x-compat", [C23Compat]>;
157157

158+
def CppKeywordInC : DiagGroup<"identifier-is-c++-keyword">;
158159
def ImplicitVoidPtrCast : DiagGroup<"implicit-void-ptr-cast">;
159-
def CXXCompat: DiagGroup<"c++-compat", [ImplicitVoidPtrCast]>;
160+
def CXXCompat: DiagGroup<"c++-compat", [ImplicitVoidPtrCast, CppKeywordInC]>;
160161
def ExternCCompat : DiagGroup<"extern-c-compat">;
161162
def KeywordCompat : DiagGroup<"keyword-compat">;
162163
def GNUCaseRange : DiagGroup<"gnu-case-range">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,9 @@ def warn_unused_lambda_capture: Warning<"lambda capture %0 is not "
496496
"%select{used|required to be captured for this use}1">,
497497
InGroup<UnusedLambdaCapture>, DefaultIgnore;
498498

499+
def warn_identifier_is_cpp_keyword : Warning<
500+
"identifier %0 conflicts with a C++ keyword">,
501+
InGroup<CppKeywordInC>, DefaultIgnore;
499502
def warn_reserved_extern_symbol: Warning<
500503
"identifier %0 is reserved because %select{"
501504
"<ERROR>|" // ReservedIdentifierStatus::NotReserved

clang/include/clang/Basic/IdentifierTable.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -444,13 +444,18 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
444444
}
445445
bool isCPlusPlusOperatorKeyword() const { return IsCPPOperatorKeyword; }
446446

447-
/// Return true if this token is a keyword in the specified language.
447+
/// Return true if this identifier uses a keyword token and is a keyword in
448+
/// the specified language.
448449
bool isKeyword(const LangOptions &LangOpts) const;
449450

450-
/// Return true if this token is a C++ keyword in the specified
451-
/// language.
451+
/// Return true if this identifier uses a keyword token and is a C++ keyword
452+
/// in the specified language.
452453
bool isCPlusPlusKeyword(const LangOptions &LangOpts) const;
453454

455+
/// Returns true if the name of this identifier matches a keyword given the
456+
/// specified language options.
457+
bool isNameKeyword(const LangOptions &LangOpts) const;
458+
454459
/// Get and set FETokenInfo. The language front-end is allowed to associate
455460
/// arbitrary metadata with this token.
456461
void *getFETokenInfo() const { return FETokenInfo; }

clang/lib/Basic/IdentifierTable.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,14 @@ static KeywordStatus getTokenKwStatus(const LangOptions &LangOpts,
343343
}
344344
}
345345

346+
static KeywordStatus getNameKwStatus(const LangOptions &LangOpts,
347+
StringRef Name) {
348+
return llvm::StringSwitch<KeywordStatus>(Name)
349+
#define KEYWORD(NAME, FLAGS) .Case(#NAME, getKeywordStatus(LangOpts, FLAGS))
350+
#include "clang/Basic/TokenKinds.def"
351+
.Default(KS_Disabled);
352+
}
353+
346354
/// Returns true if the identifier represents a keyword in the
347355
/// specified language.
348356
bool IdentifierInfo::isKeyword(const LangOptions &LangOpts) const {
@@ -355,6 +363,25 @@ bool IdentifierInfo::isKeyword(const LangOptions &LangOpts) const {
355363
}
356364
}
357365

366+
bool IdentifierInfo::isNameKeyword(const LangOptions &LangOpts) const {
367+
// This differs from IdentifierInfo::isCPlusPlusKeyword(). That function
368+
// tests if the identifier is a keyword token in C++ mode and then isn't a
369+
// keyword token in C modes. In our case, if it was a keyword, we wouldn't
370+
// have gotten the identifier for it anyway, so that function will always
371+
// return false for us. Instead, check against the token definitions directly.
372+
//
373+
// FIXME: It would be nice to handle things like char8_t and wchar_t here as
374+
// well, but those require looking at the currently selected language options
375+
// which would make this interface confusing with isCPlusPlusKeyword.
376+
switch (getNameKwStatus(LangOpts, getName())) {
377+
case KS_Enabled:
378+
case KS_Extension:
379+
return true;
380+
default:
381+
return false;
382+
}
383+
}
384+
358385
/// Returns true if the identifier represents a C++ keyword in the
359386
/// specified language.
360387
bool IdentifierInfo::isCPlusPlusKeyword(const LangOptions &LangOpts) const {

clang/lib/Sema/SemaDecl.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
#include "llvm/ADT/STLForwardCompat.h"
6262
#include "llvm/ADT/SmallString.h"
6363
#include "llvm/ADT/StringExtras.h"
64+
#include "llvm/ADT/StringSwitch.h"
6465
#include "llvm/Support/SaveAndRestore.h"
6566
#include "llvm/TargetParser/Triple.h"
6667
#include <algorithm>
@@ -6107,6 +6108,23 @@ static bool isFromSystemHeader(SourceManager &SM, const Decl *D) {
61076108
SM.isInSystemMacro(D->getLocation());
61086109
}
61096110

6111+
static bool isKeywordInCPlusPlus(const Sema &S, const IdentifierInfo* II) {
6112+
if (!II)
6113+
return false;
6114+
6115+
// Clear all the C language options, set all the C++ language options, but
6116+
// otherwise leave all the defaults alone. This isn't ideal because some
6117+
// feature flags are language-specific, but this is a reasonable
6118+
// approximation.
6119+
LangOptions LO = S.getLangOpts();
6120+
LO.CPlusPlus = LO.CPlusPlus11 = LO.CPlusPlus14 = LO.CPlusPlus17 =
6121+
LO.CPlusPlus20 = LO.CPlusPlus23 = LO.CPlusPlus26 = 1;
6122+
LO.C99 = LO.C11 = LO.C17 = LO.C23 = LO.C2y = 0;
6123+
LO.Char8 = 1; // Presume this is always a keyword in C++.
6124+
LO.WChar = 1; // Presume this is always a keyword in C++.
6125+
return II->isNameKeyword(LO);
6126+
}
6127+
61106128
void Sema::warnOnReservedIdentifier(const NamedDecl *D) {
61116129
// Avoid warning twice on the same identifier, and don't warn on redeclaration
61126130
// of system decl.
@@ -6118,6 +6136,10 @@ void Sema::warnOnReservedIdentifier(const NamedDecl *D) {
61186136
Diag(D->getLocation(), diag::warn_reserved_extern_symbol)
61196137
<< D << static_cast<int>(Status);
61206138
}
6139+
// Diagnose use of C++ keywords in C as being incompatible with C++.
6140+
if (!getLangOpts().CPlusPlus &&
6141+
isKeywordInCPlusPlus(*this, D->getIdentifier()))
6142+
Diag(D->getLocation(), diag::warn_identifier_is_cpp_keyword) << D;
61216143
}
61226144

61236145
Decl *Sema::ActOnDeclarator(Scope *S, Declarator &D) {
@@ -15311,6 +15333,7 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D,
1531115333
if (D.isInvalidType())
1531215334
New->setInvalidDecl();
1531315335

15336+
warnOnReservedIdentifier(New);
1531415337
CheckExplicitObjectParameter(*this, New, ExplicitThisLoc);
1531515338

1531615339
assert(S->isFunctionPrototypeScope());

0 commit comments

Comments
 (0)