10 #include "clang/Frontend/CompilerInstance.h"
11 #include "clang/Lex/PPCallbacks.h"
12 #include "clang/Lex/Preprocessor.h"
13 #include "clang/Tooling/Tooling.h"
14 #include "llvm/Support/Path.h"
22 SmallString<256> Result =
Path;
23 llvm::sys::path::remove_dots(Result,
true);
24 return std::string(Result.str());
30 HeaderGuardPPCallbacks(Preprocessor *PP, HeaderGuardCheck *Check)
31 : PP(PP), Check(Check) {}
33 void FileChanged(SourceLocation
Loc, FileChangeReason Reason,
34 SrcMgr::CharacteristicKind FileType,
35 FileID PrevFID)
override {
38 SourceManager &SM = PP->getSourceManager();
39 if (Reason == EnterFile && FileType == SrcMgr::C_User) {
40 if (
const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(
Loc))) {
47 void Ifndef(SourceLocation
Loc,
const Token &MacroNameTok,
48 const MacroDefinition &
MD)
override {
53 Ifndefs[MacroNameTok.getIdentifierInfo()] =
54 std::make_pair(
Loc, MacroNameTok.getLocation());
57 void MacroDefined(
const Token &MacroNameTok,
58 const MacroDirective *
MD)
override {
61 Macros.emplace_back(MacroNameTok,
MD->getMacroInfo());
64 void Endif(SourceLocation
Loc, SourceLocation IfLoc)
override {
69 void EndOfMainFile()
override {
71 SourceManager &SM = PP->getSourceManager();
73 for (
const auto &MacroEntry :
Macros) {
74 const MacroInfo *MI = MacroEntry.second;
79 if (!MI->isUsedForHeaderGuard())
83 SM.getFileEntryForID(SM.getFileID(MI->getDefinitionLoc()));
88 if (!Check->shouldFixHeaderGuard(
FileName))
92 SourceLocation Ifndef =
93 Ifndefs[MacroEntry.first.getIdentifierInfo()].second;
94 SourceLocation Define = MacroEntry.first.getLocation();
95 SourceLocation EndIf =
96 EndIfs[Ifndefs[MacroEntry.first.getIdentifierInfo()].first];
100 StringRef CurHeaderGuard =
101 MacroEntry.first.getIdentifierInfo()->getName();
102 std::vector<FixItHint> FixIts;
103 std::string NewGuard = checkHeaderGuardDefinition(
104 Ifndef, Define, EndIf,
FileName, CurHeaderGuard, FixIts);
108 checkEndifComment(
FileName, EndIf, NewGuard, FixIts);
112 if (!FixIts.empty()) {
113 if (CurHeaderGuard != NewGuard) {
114 Check->diag(Ifndef,
"header guard does not follow preferred style")
117 Check->diag(EndIf,
"#endif for a header guard should reference the "
118 "guard macro in a comment")
125 checkGuardlessHeaders();
129 bool wouldFixEndifComment(StringRef
FileName, SourceLocation EndIf,
130 StringRef HeaderGuard,
131 size_t *EndIfLenPtr =
nullptr) {
132 if (!EndIf.isValid())
134 const char *EndIfData = PP->getSourceManager().getCharacterData(EndIf);
135 size_t EndIfLen = std::strcspn(EndIfData,
"\r\n");
137 *EndIfLenPtr = EndIfLen;
139 StringRef EndIfStr(EndIfData, EndIfLen);
140 EndIfStr = EndIfStr.substr(EndIfStr.find_first_not_of(
"#endif \t"));
143 size_t FindEscapedNewline = EndIfStr.find_last_not_of(
' ');
144 if (FindEscapedNewline != StringRef::npos &&
145 EndIfStr[FindEscapedNewline] ==
'\\')
149 EndIfStr.consume_front(
"//") ||
150 (EndIfStr.consume_front(
"/*") && EndIfStr.consume_back(
"*/"));
152 return Check->shouldSuggestEndifComment(
FileName);
154 return EndIfStr.trim() != HeaderGuard;
160 std::string checkHeaderGuardDefinition(SourceLocation Ifndef,
161 SourceLocation Define,
162 SourceLocation EndIf,
164 StringRef CurHeaderGuard,
165 std::vector<FixItHint> &FixIts) {
166 std::string CPPVar = Check->getHeaderGuard(
FileName, CurHeaderGuard);
167 CPPVar = Check->sanitizeHeaderGuard(CPPVar);
168 std::string CPPVarUnder = CPPVar +
'_';
172 if (Ifndef.isValid() && CurHeaderGuard != CPPVar &&
173 (CurHeaderGuard != CPPVarUnder ||
174 wouldFixEndifComment(
FileName, EndIf, CurHeaderGuard))) {
175 FixIts.push_back(FixItHint::CreateReplacement(
176 CharSourceRange::getTokenRange(
177 Ifndef, Ifndef.getLocWithOffset(CurHeaderGuard.size())),
179 FixIts.push_back(FixItHint::CreateReplacement(
180 CharSourceRange::getTokenRange(
181 Define, Define.getLocWithOffset(CurHeaderGuard.size())),
185 return std::string(CurHeaderGuard);
190 void checkEndifComment(StringRef
FileName, SourceLocation EndIf,
191 StringRef HeaderGuard,
192 std::vector<FixItHint> &FixIts) {
194 if (wouldFixEndifComment(
FileName, EndIf, HeaderGuard, &EndIfLen)) {
195 FixIts.push_back(FixItHint::CreateReplacement(
196 CharSourceRange::getCharRange(EndIf,
197 EndIf.getLocWithOffset(EndIfLen)),
198 Check->formatEndIf(HeaderGuard)));
204 void checkGuardlessHeaders() {
208 for (
const auto &FE :
Files) {
210 if (!Check->shouldSuggestToAddHeaderGuard(
FileName))
213 SourceManager &SM = PP->getSourceManager();
214 FileID FID = SM.translateFile(FE.getValue());
215 SourceLocation StartLoc = SM.getLocForStartOfFile(FID);
216 if (StartLoc.isInvalid())
219 std::string CPPVar = Check->getHeaderGuard(
FileName);
220 CPPVar = Check->sanitizeHeaderGuard(CPPVar);
221 std::string CPPVarUnder = CPPVar +
'_';
227 bool SeenMacro =
false;
228 for (
const auto &MacroEntry :
Macros) {
229 StringRef
Name = MacroEntry.first.getIdentifierInfo()->getName();
230 SourceLocation DefineLoc = MacroEntry.first.getLocation();
231 if ((
Name == CPPVar ||
Name == CPPVarUnder) &&
232 SM.isWrittenInSameFile(StartLoc, DefineLoc)) {
233 Check->diag(DefineLoc,
"code/includes outside of area guarded by "
234 "header guard; consider moving it");
243 Check->diag(StartLoc,
"header is missing header guard")
244 << FixItHint::CreateInsertion(
245 StartLoc,
"#ifndef " + CPPVar +
"\n#define " + CPPVar +
"\n\n")
246 << FixItHint::CreateInsertion(
247 SM.getLocForEndOfFile(FID),
248 Check->shouldSuggestEndifComment(
FileName)
249 ?
"\n#" + Check->formatEndIf(CPPVar) +
"\n"
255 void clearAllState() {
262 std::vector<std::pair<Token, const MacroInfo *>>
Macros;
263 llvm::StringMap<const FileEntry *>
Files;
264 std::map<const IdentifierInfo *, std::pair<SourceLocation, SourceLocation>>
266 std::map<SourceLocation, SourceLocation> EndIfs;
269 HeaderGuardCheck *Check;
274 Options.
store(Opts,
"HeaderFileExtensions", RawStringHeaderFileExtensions);
279 Preprocessor *ModuleExpanderPP) {
280 PP->addPPCallbacks(std::make_unique<HeaderGuardPPCallbacks>(PP,
this));
285 return Guard.drop_while([](
char C) {
return C ==
'_'; }).str();
299 return "endif // " + HeaderGuard.str();