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);
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()));
85 Files.erase(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();
134 bool wouldFixEndifComment(StringRef
FileName, SourceLocation EndIf,
135 StringRef HeaderGuard,
136 size_t *EndIfLenPtr =
nullptr) {
137 if (!EndIf.isValid())
139 const char *EndIfData = PP->getSourceManager().getCharacterData(EndIf);
140 size_t EndIfLen = std::strcspn(EndIfData,
"\r\n");
142 *EndIfLenPtr = EndIfLen;
144 StringRef EndIfStr(EndIfData, EndIfLen);
145 EndIfStr = EndIfStr.substr(EndIfStr.find_first_not_of(
"#endif \t"));
148 size_t FindEscapedNewline = EndIfStr.find_last_not_of(
' ');
149 if (FindEscapedNewline != StringRef::npos &&
150 EndIfStr[FindEscapedNewline] ==
'\\')
154 !(EndIfStr.startswith(
"//") ||
155 (EndIfStr.startswith(
"/*") && EndIfStr.endswith(
"*/"))))
158 return (EndIfStr !=
"// " + HeaderGuard.str()) &&
159 (EndIfStr !=
"/* " + HeaderGuard.str() +
" */");
165 std::string checkHeaderGuardDefinition(SourceLocation Ifndef,
166 SourceLocation Define,
167 SourceLocation EndIf,
169 StringRef CurHeaderGuard,
170 std::vector<FixItHint> &FixIts) {
171 std::string CPPVar = Check->
getHeaderGuard(FileName, CurHeaderGuard);
172 std::string CPPVarUnder = CPPVar +
'_';
176 if (Ifndef.isValid() && CurHeaderGuard != CPPVar &&
177 (CurHeaderGuard != CPPVarUnder ||
178 wouldFixEndifComment(FileName, EndIf, CurHeaderGuard))) {
179 FixIts.push_back(FixItHint::CreateReplacement(
181 Ifndef, Ifndef.getLocWithOffset(CurHeaderGuard.size())),
183 FixIts.push_back(FixItHint::CreateReplacement(
185 Define, Define.getLocWithOffset(CurHeaderGuard.size())),
189 return CurHeaderGuard;
194 void checkEndifComment(StringRef FileName, SourceLocation EndIf,
195 StringRef HeaderGuard,
196 std::vector<FixItHint> &FixIts) {
198 if (wouldFixEndifComment(FileName, EndIf, HeaderGuard, &EndIfLen)) {
199 FixIts.push_back(FixItHint::CreateReplacement(
200 CharSourceRange::getCharRange(EndIf,
201 EndIf.getLocWithOffset(EndIfLen)),
208 void checkGuardlessHeaders() {
212 for (
const auto &FE :
Files) {
213 StringRef FileName = FE.getKey();
217 SourceManager &SM = PP->getSourceManager();
218 FileID FID = SM.translateFile(FE.getValue());
219 SourceLocation StartLoc = SM.getLocForStartOfFile(FID);
220 if (StartLoc.isInvalid())
224 std::string CPPVarUnder = CPPVar +
'_';
230 bool SeenMacro =
false;
231 for (
const auto &MacroEntry : Macros) {
232 StringRef
Name = MacroEntry.first.getIdentifierInfo()->getName();
233 SourceLocation DefineLoc = MacroEntry.first.getLocation();
234 if ((Name == CPPVar || Name == CPPVarUnder) &&
235 SM.isWrittenInSameFile(StartLoc, DefineLoc)) {
236 Check->
diag(DefineLoc,
"code/includes outside of area guarded by " 237 "header guard; consider moving it");
246 Check->
diag(StartLoc,
"header is missing header guard")
247 << FixItHint::CreateInsertion(
248 StartLoc,
"#ifndef " + CPPVar +
"\n#define " + CPPVar +
"\n\n")
249 << FixItHint::CreateInsertion(
250 SM.getLocForEndOfFile(FID),
258 std::vector<std::pair<Token, const MacroInfo *>> Macros;
259 llvm::StringMap<const FileEntry *>
Files;
260 std::map<const IdentifierInfo *, std::pair<SourceLocation, SourceLocation>>
262 std::map<SourceLocation, SourceLocation> EndIfs;
271 Preprocessor *ModuleExpanderPP) {
272 PP->addPPCallbacks(std::make_unique<HeaderGuardPPCallbacks>(PP,
this));
286 return "endif // " + HeaderGuard.str();
SourceLocation Loc
'#' location in the include directive
static std::string cleanPath(StringRef Path)
canonicalize a path by removing ./ and ../ components.
std::vector< HeaderHandle > Path
static constexpr llvm::StringLiteral Name
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static GeneratorRegistry::Add< MDGenerator > MD(MDGenerator::Format, "Generator for MD output.")
bool isHeaderFileExtension(StringRef FileName, const HeaderFileExtensionsSet &HeaderFileExtensions)
Decides whether a file has a header file extension.
llvm::StringMap< std::string > Files
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.