1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
//===--- ProBoundsConstantArrayIndexCheck.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 "ProBoundsConstantArrayIndexCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
ProBoundsConstantArrayIndexCheck::ProBoundsConstantArrayIndexCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context), GslHeader(Options.get("GslHeader", "")),
IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
Options.getLocalOrGlobal("IncludeStyle", "llvm"))) {}
void ProBoundsConstantArrayIndexCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "GslHeader", GslHeader);
Options.store(Opts, "IncludeStyle", IncludeStyle);
}
void ProBoundsConstantArrayIndexCheck::registerPPCallbacks(
CompilerInstance &Compiler) {
if (!getLangOpts().CPlusPlus)
return;
Inserter.reset(new utils::IncludeInserter(
Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
}
void ProBoundsConstantArrayIndexCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
// Note: if a struct contains an array member, the compiler-generated
// constructor has an arraySubscriptExpr.
Finder->addMatcher(
arraySubscriptExpr(
hasBase(ignoringImpCasts(hasType(constantArrayType().bind("type")))),
hasIndex(expr().bind("index")), unless(hasAncestor(isImplicit())))
.bind("expr"),
this);
Finder->addMatcher(
cxxOperatorCallExpr(
hasOverloadedOperatorName("[]"),
hasArgument(
0, hasType(cxxRecordDecl(hasName("::std::array")).bind("type"))),
hasArgument(1, expr().bind("index")))
.bind("expr"),
this);
}
void ProBoundsConstantArrayIndexCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *Matched = Result.Nodes.getNodeAs<Expr>("expr");
const auto *IndexExpr = Result.Nodes.getNodeAs<Expr>("index");
if (IndexExpr->isValueDependent())
return; // We check in the specialization.
llvm::APSInt Index;
if (!IndexExpr->isIntegerConstantExpr(Index, *Result.Context, nullptr,
/*isEvaluated=*/true)) {
SourceRange BaseRange;
if (const auto *ArraySubscriptE = dyn_cast<ArraySubscriptExpr>(Matched))
BaseRange = ArraySubscriptE->getBase()->getSourceRange();
else
BaseRange =
dyn_cast<CXXOperatorCallExpr>(Matched)->getArg(0)->getSourceRange();
SourceRange IndexRange = IndexExpr->getSourceRange();
auto Diag = diag(Matched->getExprLoc(),
"do not use array subscript when the index is "
"not an integer constant expression; use gsl::at() "
"instead");
if (!GslHeader.empty()) {
Diag << FixItHint::CreateInsertion(BaseRange.getBegin(), "gsl::at(")
<< FixItHint::CreateReplacement(
SourceRange(BaseRange.getEnd().getLocWithOffset(1),
IndexRange.getBegin().getLocWithOffset(-1)),
", ")
<< FixItHint::CreateReplacement(Matched->getLocEnd(), ")");
Optional<FixItHint> Insertion = Inserter->CreateIncludeInsertion(
Result.SourceManager->getMainFileID(), GslHeader,
/*IsAngled=*/false);
if (Insertion)
Diag << Insertion.getValue();
}
return;
}
const auto *StdArrayDecl =
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("type");
// For static arrays, this is handled in clang-diagnostic-array-bounds.
if (!StdArrayDecl)
return;
if (Index.isSigned() && Index.isNegative()) {
diag(Matched->getExprLoc(), "std::array<> index %0 is negative")
<< Index.toString(10);
return;
}
const TemplateArgumentList &TemplateArgs = StdArrayDecl->getTemplateArgs();
if (TemplateArgs.size() < 2)
return;
// First template arg of std::array is the type, second arg is the size.
const auto &SizeArg = TemplateArgs[1];
if (SizeArg.getKind() != TemplateArgument::Integral)
return;
llvm::APInt ArraySize = SizeArg.getAsIntegral();
// Get uint64_t values, because different bitwidths would lead to an assertion
// in APInt::uge.
if (Index.getZExtValue() >= ArraySize.getZExtValue()) {
diag(Matched->getExprLoc(),
"std::array<> index %0 is past the end of the array "
"(which contains %1 elements)")
<< Index.toString(10) << ArraySize.toString(10, false);
}
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
|