blob: 6c123e27549e7ccb7c766aef46f9c9059c3b578a [file] [log] [blame] [edit]
//===--- FblLimitsCheck.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 "FblLimitsCheck.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 FblLimitsPPCallbacks : public PPCallbacks {
public:
explicit FblLimitsPPCallbacks(FblLimitsCheck &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;
FblLimitsCheck &Check;
SourceManager &SM;
};
void FblLimitsPPCallbacks::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/limits.h") {
unsigned End = std::strcspn(SM.getCharacterData(HashLoc), "\n") + 1;
IncludeRange =
CharSourceRange::getCharRange(HashLoc, HashLoc.getLocWithOffset(End));
found = true;
}
}
void FblLimitsPPCallbacks::EndOfMainFile() {
// Any instance of <fbl/limits.h> should be removed, since the decls in it
// will be replaced in this check and <limits> added.
if (found) {
if (!SM.isInMainFile(IncludeRange.getBegin())) {
Check.diag(IncludeRange.getBegin(),
"including fbl/limits.h is deprecated, transitively "
"included from %0")
<< SM.getFilename(IncludeRange.getBegin())
<< FixItHint::CreateRemoval(IncludeRange);
return;
}
Check.diag(IncludeRange.getBegin(), "including fbl/limits.h is deprecated")
<< FixItHint::CreateRemoval(IncludeRange);
}
}
void FblLimitsCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
valueDecl(hasType(cxxRecordDecl(
allOf(hasDeclContext(namespaceDecl(hasName("::fbl"))),
hasName("numeric_limits")))))
.bind("decl"),
this);
Finder->addMatcher(
cxxConstructExpr(hasDeclaration(functionDecl(
allOf(hasDeclContext(cxxRecordDecl(hasDeclContext(
namespaceDecl(hasName("::fbl"))))),
hasName("numeric_limits")))))
.bind("expr"),
this);
}
void FblLimitsCheck::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<FblLimitsPPCallbacks>(*this,
Compiler.getSourceManager()));
}
void FblLimitsCheck::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::numeric_limits is deprecated, use "
"std::numeric_limits instead")
<< FixItHint::CreateReplacement(SourceRange(Start, End),
"std::numeric_limits");
// Add in the <limits> header, since we know this file uses it.
if (llvm::Optional<FixItHint> IncludeFixit =
Inserter->CreateIncludeInsertion(SM.getFileID(Start), "limits",
/*IsAngled=*/true)) {
Diag << *IncludeFixit;
}
}
void FblLimitsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", IncludeStyle);
}
} // namespace zircon
} // namespace tidy
} // namespace clang