InGroup<IncompleteModule>, DefaultIgnore;
def err_expected_id_building_module : Error<
"expected a module name in '__building_module' expression">;
-
+
+def warn_header_guard : Warning<
+ "%0 is used as a header guard here, followed by #define of a different macro">,
+ InGroup<DiagGroup<"header-guard">>;
+def note_header_guard : Note<
+ "%0 is defined here; did you mean %1?">;
}
getFileInfo(File).ControllingMacro = ControllingMacro;
}
+ /// \brief Return true if this is the first time encountering this header.
+ bool FirstTimeLexingFile(const FileEntry *File) {
+ return getFileInfo(File).NumIncludes == 1;
+ }
+
/// \brief Determine whether this file is intended to be safe from
/// multiple inclusions, e.g., it has \#pragma once or a controlling
/// macro.
#ifndef LLVM_CLANG_MULTIPLEINCLUDEOPT_H
#define LLVM_CLANG_MULTIPLEINCLUDEOPT_H
+#include "clang/Basic/SourceLocation.h"
+
namespace clang {
class IdentifierInfo;
/// \#endif can be easily detected.
bool ReadAnyTokens;
+ /// ImmediatelyAfterTopLevelIfndef - This is true when the only tokens
+ /// processed in the file so far is an #ifndef and an identifier. Used in
+ /// the detection of header guards in a file.
+ bool ImmediatelyAfterTopLevelIfndef;
+
/// ReadAnyTokens - This is set to false when a file is first opened and true
/// any time a token is returned to the client or a (non-multiple-include)
/// directive is parsed. When the final #endif is parsed this is reset back
/// TheMacro - The controlling macro for a file, if valid.
///
const IdentifierInfo *TheMacro;
+
+ /// DefinedMacro - The macro defined right after TheMacro, if any.
+ const IdentifierInfo *DefinedMacro;
+
+ SourceLocation MacroLoc;
+ SourceLocation DefinedLoc;
public:
MultipleIncludeOpt() {
ReadAnyTokens = false;
+ ImmediatelyAfterTopLevelIfndef = false;
DidMacroExpansion = false;
TheMacro = 0;
+ DefinedMacro = 0;
+ }
+
+ SourceLocation GetMacroLocation() const {
+ return MacroLoc;
+ }
+
+ SourceLocation GetDefinedLocation() const {
+ return DefinedLoc;
+ }
+
+ void resetImmediatelyAfterTopLevelIfndef() {
+ ImmediatelyAfterTopLevelIfndef = false;
+ }
+
+ void SetDefinedMacro(IdentifierInfo *M, SourceLocation Loc) {
+ DefinedMacro = M;
+ DefinedLoc = Loc;
}
/// Invalidate - Permanently mark this file as not being suitable for the
// If we have read tokens but have no controlling macro, the state-machine
// below can never "accept".
ReadAnyTokens = true;
+ ImmediatelyAfterTopLevelIfndef = false;
+ DefinedMacro = 0;
TheMacro = 0;
}
/// the "ifndef x" would count as reading tokens.
bool getHasReadAnyTokensVal() const { return ReadAnyTokens; }
+ /// getImmediatelyAfterTopLevelIfndef - returns true if the last directive
+ /// was an #ifndef at the beginning of the file.
+ bool getImmediatelyAfterTopLevelIfndef() const {
+ return ImmediatelyAfterTopLevelIfndef;
+ }
+
// If a token is read, remember that we have seen a side-effect in this file.
- void ReadToken() { ReadAnyTokens = true; }
+ void ReadToken() {
+ ReadAnyTokens = true;
+ ImmediatelyAfterTopLevelIfndef = false;
+ }
/// ExpandedMacro - When a macro is expanded with this lexer as the current
/// buffer, this method is called to disable the MIOpt if needed.
/// ensures that this is only called if there are no tokens read before the
/// \#ifndef. The caller is required to do this, because reading the \#if
/// line obviously reads in in tokens.
- void EnterTopLevelIFNDEF(const IdentifierInfo *M) {
+ void EnterTopLevelIfndef(const IdentifierInfo *M, SourceLocation Loc) {
// If the macro is already set, this is after the top-level #endif.
if (TheMacro)
return Invalidate();
// Remember that we're in the #if and that we have the macro.
ReadAnyTokens = true;
+ ImmediatelyAfterTopLevelIfndef = true;
TheMacro = M;
+ MacroLoc = Loc;
}
/// \brief Invoked when a top level conditional (except \#ifndef) is found.
// At this point, we haven't "read any tokens" but we do have a controlling
// macro.
ReadAnyTokens = false;
+ ImmediatelyAfterTopLevelIfndef = false;
}
/// \brief Once the entire file has been lexed, if there is a controlling
return TheMacro;
return 0;
}
+
+ /// \brief If the ControllingMacro is followed by a macro definition, return
+ /// the macro that was defined.
+ const IdentifierInfo *GetDefinedMacro() const {
+ return DefinedMacro;
+ }
};
} // end namespace clang
void HandleMicrosoftImportDirective(Token &Tok);
// Macro handling.
- void HandleDefineDirective(Token &Tok);
+ void HandleDefineDirective(Token &Tok, bool ImmediatelyAfterTopLevelIfndef);
void HandleUndefDirective(Token &Tok);
// Conditional Inclusion.
CurPPLexer->ParsingPreprocessorDirective = true;
if (CurLexer) CurLexer->SetKeepWhitespaceMode(false);
+ bool ImmediatelyAfterTopLevelIfndef =
+ CurPPLexer->MIOpt.getImmediatelyAfterTopLevelIfndef();
+ CurPPLexer->MIOpt.resetImmediatelyAfterTopLevelIfndef();
+
++NumDirectives;
// We are about to read a token. For the multiple-include optimization FA to
// C99 6.10.3 - Macro Replacement.
case tok::pp_define:
- return HandleDefineDirective(Result);
+ return HandleDefineDirective(Result, ImmediatelyAfterTopLevelIfndef);
case tok::pp_undef:
return HandleUndefDirective(Result);
/// HandleDefineDirective - Implements \#define. This consumes the entire macro
/// line then lets the caller lex the next real token.
-void Preprocessor::HandleDefineDirective(Token &DefineTok) {
+void Preprocessor::HandleDefineDirective(Token &DefineTok,
+ bool ImmediatelyAfterHeaderGuard) {
++NumDefined;
Token MacroNameTok;
// marking each of the identifiers as being used as macro arguments. Also,
// check other constraints on the first token of the macro body.
if (Tok.is(tok::eod)) {
+ if (ImmediatelyAfterHeaderGuard) {
+ // Save this macro information since it may part of a header guard.
+ CurPPLexer->MIOpt.SetDefinedMacro(MacroNameTok.getIdentifierInfo(),
+ MacroNameTok.getLocation());
+ }
// If there is no body to this macro, we have no special handling here.
} else if (Tok.hasLeadingSpace()) {
// This is a normal token with leading space. Clear the leading space
// handle.
if (!ReadAnyTokensBeforeDirective && MI == 0) {
assert(isIfndef && "#ifdef shouldn't reach here");
- CurPPLexer->MIOpt.EnterTopLevelIFNDEF(MII);
+ CurPPLexer->MIOpt.EnterTopLevelIfndef(MII, MacroNameTok.getLocation());
} else
CurPPLexer->MIOpt.EnterTopLevelConditional();
}
// directive seen, handle it for the multiple-include optimization.
if (CurPPLexer->getConditionalStackDepth() == 0) {
if (!ReadAnyTokensBeforeDirective && IfNDefMacro && ConditionalTrue)
- CurPPLexer->MIOpt.EnterTopLevelIFNDEF(IfNDefMacro);
+ CurPPLexer->MIOpt.EnterTopLevelIfndef(IfNDefMacro, ConditionalBegin);
else
CurPPLexer->MIOpt.EnterTopLevelConditional();
}
CurPPLexer->MIOpt.GetControllingMacroAtEndOfFile()) {
// Okay, this has a controlling macro, remember in HeaderFileInfo.
if (const FileEntry *FE =
- SourceMgr.getFileEntryForID(CurPPLexer->getFileID()))
+ SourceMgr.getFileEntryForID(CurPPLexer->getFileID())) {
HeaderInfo.SetFileControllingMacro(FE, ControllingMacro);
+ if (const IdentifierInfo *DefinedMacro =
+ CurPPLexer->MIOpt.GetDefinedMacro()) {
+ if (!ControllingMacro->hasMacroDefinition() &&
+ DefinedMacro != ControllingMacro &&
+ HeaderInfo.FirstTimeLexingFile(FE)) {
+ // Emit a warning for a bad header guard.
+ Diag(CurPPLexer->MIOpt.GetMacroLocation(),
+ diag::warn_header_guard)
+ << CurPPLexer->MIOpt.GetMacroLocation()
+ << ControllingMacro;
+ Diag(CurPPLexer->MIOpt.GetDefinedLocation(),
+ diag::note_header_guard)
+ << CurPPLexer->MIOpt.GetDefinedLocation()
+ << DefinedMacro
+ << ControllingMacro
+ << FixItHint::CreateReplacement(
+ CurPPLexer->MIOpt.GetDefinedLocation(),
+ ControllingMacro->getName());
+ }
+ }
+ }
}
}
--- /dev/null
+#ifndef bad_header_guard
+#define bad_guard
+
+#endif
--- /dev/null
+#ifndef different_define
+#define FOO 5
+
+#endif
--- /dev/null
+#ifndef good_header_guard
+#define good_header_guard
+
+#endif
--- /dev/null
+#ifndef multiple
+#define multi
+
+#endif
--- /dev/null
+#ifndef no_define
+
+#endif
--- /dev/null
+#ifndef out_of_order
+
+#define something_else
+
+#define out_of_order
+
+#endif
--- /dev/null
+#ifndef tokens_between
+
+const int pi = 3;
+
+#define pi_is_bad
+
+#endif
--- /dev/null
+// RUN: %clang_cc1 -fsyntax-only -Wno-header-guard %s
+// RUN: %clang_cc1 -fsyntax-only -Wheader-guard %s 2>&1 | FileCheck %s
+
+#include "Inputs/good-header-guard.h"
+#include "Inputs/no-define.h"
+#include "Inputs/different-define.h"
+#include "Inputs/out-of-order-define.h"
+#include "Inputs/tokens-between-ifndef-and-define.h"
+
+#include "Inputs/bad-header-guard.h"
+// CHECK: In file included from {{.*}}header.cpp:{{[0-9]*}}:
+// CHECK: {{.*}}bad-header-guard.h:1:9: warning: 'bad_header_guard' is used as a header guard here, followed by #define of a different macro
+// CHECK: {{^}}#ifndef bad_header_guard
+// CHECK: {{^}} ^~~~~~~~~~~~~~~~
+// CHECK: {{.*}}bad-header-guard.h:2:9: note: 'bad_guard' is defined here; did you mean 'bad_header_guard'?
+// CHECK: {{^}}#define bad_guard
+// CHECK: {{^}} ^~~~~~~~~
+// CHECK: {{^}} bad_header_guard
+
+#include "Inputs/multiple.h"
+#include "Inputs/multiple.h"
+#include "Inputs/multiple.h"
+#include "Inputs/multiple.h"
+// CHECK: In file included from {{.*}}header.cpp:{{[0-9]*}}:
+// CHECK: {{.*}}multiple.h:1:9: warning: 'multiple' is used as a header guard here, followed by #define of a different macro
+// CHECK: {{^}}#ifndef multiple
+// CHECK: {{^}} ^~~~~~~~
+// CHECK: {{.*}}multiple.h:2:9: note: 'multi' is defined here; did you mean 'multiple'?
+// CHECK: {{^}}#define multi
+// CHECK: {{^}} ^~~~~
+// CHECK: {{^}} multiple
+
+// CHECK: 2 warnings generated.