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