blob: 1c7d82956f5268d5a0aeaf8479a41b022105881a [file] [log] [blame] [edit]
//===--- 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