| //===--- RestrictSystemIncludesCheck.cpp - clang-tidy----------------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "RestrictSystemIncludesCheck.h" |
| #include "clang/Frontend/CompilerInstance.h" |
| #include "clang/Lex/HeaderSearch.h" |
| #include "clang/Lex/PPCallbacks.h" |
| #include "clang/Lex/Preprocessor.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/Support/Path.h" |
| #include <cstring> |
| |
| namespace clang { |
| namespace tidy { |
| namespace fuchsia { |
| |
| class RestrictedIncludesPPCallbacks : public PPCallbacks { |
| public: |
| explicit RestrictedIncludesPPCallbacks(RestrictSystemIncludesCheck &Check, |
| SourceManager &SM) |
| : Check(Check), SM(SM) {} |
| |
| void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, |
| StringRef FileName, bool IsAngled, |
| CharSourceRange FilenameRange, const FileEntry *File, |
| StringRef SearchPath, StringRef RelativePath, |
| const Module *Imported, |
| SrcMgr::CharacteristicKind FileType) override; |
| void EndOfMainFile() override; |
| |
| private: |
| struct IncludeDirective { |
| IncludeDirective() = default; |
| IncludeDirective(SourceLocation Loc, CharSourceRange Range, |
| StringRef Filename, StringRef FullPath, bool IsInMainFile) |
| : Loc(Loc), Range(Range), IncludeFile(Filename), IncludePath(FullPath), |
| IsInMainFile(IsInMainFile) {} |
| |
| SourceLocation Loc; // '#' location in the include directive |
| CharSourceRange Range; // SourceRange for the file name |
| std::string IncludeFile; // Filename as a string |
| std::string IncludePath; // Full file path as a string |
| bool IsInMainFile; // Whether or not the include is in the main file |
| }; |
| |
| using FileIncludes = llvm::SmallVector<IncludeDirective, 8>; |
| llvm::SmallDenseMap<FileID, FileIncludes> IncludeDirectives; |
| |
| RestrictSystemIncludesCheck &Check; |
| SourceManager &SM; |
| }; |
| |
| void RestrictedIncludesPPCallbacks::InclusionDirective( |
| SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, |
| bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, |
| StringRef SearchPath, StringRef RelativePath, const Module *Imported, |
| SrcMgr::CharacteristicKind FileType) { |
| if (!Check.contains(FileName) && SrcMgr::isSystem(FileType)) { |
| SmallString<256> FullPath; |
| llvm::sys::path::append(FullPath, SearchPath); |
| llvm::sys::path::append(FullPath, RelativePath); |
| // Bucket the allowed include directives by the id of the file they were |
| // declared in. |
| IncludeDirectives[SM.getFileID(HashLoc)].emplace_back( |
| HashLoc, FilenameRange, FileName, FullPath.str(), |
| SM.isInMainFile(HashLoc)); |
| } |
| } |
| |
| void RestrictedIncludesPPCallbacks::EndOfMainFile() { |
| for (const auto &Bucket : IncludeDirectives) { |
| const FileIncludes &FileDirectives = Bucket.second; |
| |
| // Emit fixits for all restricted includes. |
| for (const auto &Include : FileDirectives) { |
| // Fetch the length of the include statement from the start to just after |
| // the newline, for finding the end (including the newline). |
| unsigned ToLen = std::strcspn(SM.getCharacterData(Include.Loc), "\n") + 1; |
| CharSourceRange ToRange = CharSourceRange::getCharRange( |
| Include.Loc, Include.Loc.getLocWithOffset(ToLen)); |
| |
| if (!Include.IsInMainFile) { |
| auto D = Check.diag( |
| Include.Loc, |
| "system include %0 not allowed, transitively included from %1"); |
| D << Include.IncludeFile << SM.getFilename(Include.Loc); |
| D << FixItHint::CreateRemoval(ToRange); |
| continue; |
| } |
| auto D = Check.diag(Include.Loc, "system include %0 not allowed"); |
| D << Include.IncludeFile; |
| D << FixItHint::CreateRemoval(ToRange); |
| } |
| } |
| } |
| |
| void RestrictSystemIncludesCheck::registerPPCallbacks( |
| CompilerInstance &Compiler) { |
| Compiler.getPreprocessor().addPPCallbacks( |
| llvm::make_unique<RestrictedIncludesPPCallbacks>( |
| *this, Compiler.getSourceManager())); |
| } |
| |
| void RestrictSystemIncludesCheck::storeOptions( |
| ClangTidyOptions::OptionMap &Opts) { |
| Options.store(Opts, "Includes", AllowedIncludes); |
| } |
| |
| } // namespace fuchsia |
| } // namespace tidy |
| } // namespace clang |