| //===--- FblOptionalCheck.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 "FblOptionalCheck.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/ASTMatchers/ASTMatchFinder.h" |
| #include "clang/Frontend/CompilerInstance.h" |
| #include "clang/Lex/Lexer.h" |
| #include "llvm/ADT/Optional.h" |
| |
| using namespace clang::ast_matchers; |
| |
| namespace clang { |
| namespace tidy { |
| namespace zircon { |
| |
| class FblOptionalPPCallbacks : public PPCallbacks { |
| public: |
| explicit FblOptionalPPCallbacks(FblOptionalCheck &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: |
| bool found = false; |
| CharSourceRange IncludeRange; |
| |
| FblOptionalCheck &Check; |
| SourceManager &SM; |
| }; |
| |
| void FblOptionalPPCallbacks::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 (FileName == "fbl/optional.h") { |
| unsigned End = std::strcspn(SM.getCharacterData(HashLoc), "\n") + 1; |
| IncludeRange = |
| CharSourceRange::getCharRange(HashLoc, HashLoc.getLocWithOffset(End)); |
| found = true; |
| } |
| } |
| |
| void FblOptionalPPCallbacks::EndOfMainFile() { |
| // Any instance of <fbl/optional.h> should be removed, since the decls in it |
| // will be replaced in this check and <optional> added. |
| if (found) { |
| if (!SM.isInMainFile(IncludeRange.getBegin())) { |
| Check.diag(IncludeRange.getBegin(), |
| "including fbl/optional.h is deprecated, transitively " |
| "included from %0") |
| << SM.getFilename(IncludeRange.getBegin()) |
| << FixItHint::CreateRemoval(IncludeRange); |
| return; |
| } |
| Check.diag(IncludeRange.getBegin(), |
| "including fbl/optional.h is deprecated") |
| << FixItHint::CreateRemoval(IncludeRange); |
| } |
| } // namespace zircon |
| |
| void FblOptionalCheck::registerMatchers(MatchFinder *Finder) { |
| Finder->addMatcher( |
| valueDecl(hasType(cxxRecordDecl( |
| allOf(hasDeclContext(namespaceDecl(hasName("::fbl"))), |
| hasName("optional"))))) |
| .bind("decl"), |
| this); |
| |
| Finder->addMatcher( |
| cxxConstructExpr(hasDeclaration(functionDecl( |
| allOf(hasDeclContext(cxxRecordDecl(hasDeclContext( |
| namespaceDecl(hasName("::fbl"))))), |
| hasName("optional"))))) |
| .bind("expr"), |
| this); |
| } |
| |
| void FblOptionalCheck::registerPPCallbacks(CompilerInstance &Compiler) { |
| Inserter = llvm::make_unique<utils::IncludeInserter>( |
| Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle); |
| Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks()); |
| Compiler.getPreprocessor().addPPCallbacks( |
| llvm::make_unique<FblOptionalPPCallbacks>(*this, |
| Compiler.getSourceManager())); |
| } |
| |
| void FblOptionalCheck::check(const MatchFinder::MatchResult &Result) { |
| SourceManager &SM = *Result.SourceManager; |
| SourceLocation Start; |
| SourceLocation End; |
| if (const auto *V = Result.Nodes.getNodeAs<ValueDecl>("decl")) { |
| Start = V->getBeginLoc(); |
| End = V->getEndLoc(); |
| } else if (const auto *V = Result.Nodes.getNodeAs<Expr>("expr")) { |
| Start = V->getBeginLoc(); |
| End = V->getEndLoc(); |
| } else |
| return; |
| |
| // If it's a macro, we want to extract information about the macro instead of |
| // the expr. |
| if (Start.isMacroID()) { |
| Start = SM.getSpellingLoc(Start); |
| End = SM.getSpellingLoc(End); |
| } |
| |
| bool Invalid = false; |
| StringRef Statement = Lexer::getSourceText( |
| CharSourceRange::getCharRange(Start, End), SM, getLangOpts(), &Invalid); |
| if (Invalid || !Statement.startswith("fbl")) |
| return; |
| |
| size_t LAngle = Statement.find("<"); |
| if (LAngle != std::string::npos) |
| End = Start.getLocWithOffset(LAngle - 1); |
| |
| DiagnosticBuilder Diag = |
| diag(Start, "use of fbl::optional is deprecated, use " |
| "std::optional instead") |
| << FixItHint::CreateReplacement(SourceRange(Start, End), "std::optional"); |
| |
| // Add in the <optional> header, since we know this file uses it. |
| if (llvm::Optional<FixItHint> IncludeFixit = |
| Inserter->CreateIncludeInsertion(SM.getFileID(Start), "optional", |
| /*IsAngled=*/true)) { |
| Diag << *IncludeFixit; |
| } |
| } |
| |
| void FblOptionalCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
| Options.store(Opts, "IncludeStyle", IncludeStyle); |
| } |
| |
| } // namespace zircon |
| } // namespace tidy |
| } // namespace clang |