45 const CXXRecordDecl *Parent = MatchedOperator->getParent();
46 const QualType SubscriptThisObjType =
47 MatchedOperator->getFunctionObjectParameterReferenceType();
49 for (
const CXXMethodDecl *Method : Parent->methods()) {
51 if (MatchedOperator->getAccess() < Method->getAccess())
54 if (MatchedOperator->isConst() != Method->isConst())
57 const QualType AtThisObjType =
58 Method->getFunctionObjectParameterReferenceType();
59 if (SubscriptThisObjType != AtThisObjType)
62 if (!Method->getNameInfo().getName().isIdentifier() ||
63 Method->getName() !=
"at")
66 const bool SameReturnType =
67 Method->getReturnType() == MatchedOperator->getReturnType();
71 const bool SameNumberOfArguments =
72 Method->getNumParams() == MatchedOperator->getNumParams();
73 if (!SameNumberOfArguments)
76 for (
unsigned ArgInd = 0; ArgInd < Method->getNumParams(); ArgInd++) {
77 const bool SameArgType =
78 Method->parameters()[ArgInd]->getOriginalType() ==
79 MatchedOperator->parameters()[ArgInd]->getOriginalType();
104 const MatchFinder::MatchResult &Result) {
106 const auto *MatchedExpr = Result.Nodes.getNodeAs<CallExpr>(
"caller");
108 if (FixMode ==
None) {
109 diag(MatchedExpr->getCallee()->getBeginLoc(),
110 "possibly unsafe 'operator[]', consider bounds-safe alternatives")
111 << MatchedExpr->getCallee()->getSourceRange();
115 if (
const auto *OCE = dyn_cast<CXXOperatorCallExpr>(MatchedExpr)) {
117 const auto LeftBracket = SourceRange(OCE->getCallee()->getBeginLoc(),
118 OCE->getCallee()->getBeginLoc());
119 const auto RightBracket =
120 SourceRange(OCE->getOperatorLoc(), OCE->getOperatorLoc());
124 const auto *MatchedOperator =
125 Result.Nodes.getNodeAs<CXXMethodDecl>(
"operator");
129 diag(MatchedExpr->getCallee()->getBeginLoc(),
130 "possibly unsafe 'operator[]', consider "
131 "bounds-safe alternatives")
132 << MatchedExpr->getCallee()->getSourceRange();
136 diag(MatchedExpr->getCallee()->getBeginLoc(),
137 "possibly unsafe 'operator[]', consider "
138 "bounds-safe alternative 'at()'")
139 << MatchedExpr->getCallee()->getSourceRange()
140 << FixItHint::CreateReplacement(LeftBracket,
".at(")
141 << FixItHint::CreateReplacement(RightBracket,
")");
143 diag(Alternative->getBeginLoc(),
"viable 'at()' is defined here",
145 << Alternative->getNameInfo().getSourceRange();
152 const bool EmptySubscript =
153 MatchedExpr->getDirectCallee()->getNumParams() == 0;
155 if (EmptySubscript) {
156 auto D = diag(MatchedExpr->getCallee()->getBeginLoc(),
157 "possibly unsafe 'operator[]'%select{, use safe "
158 "function '%1() instead|}0")
159 << FixFunctionEmptyArgs.empty() << FixFunctionEmptyArgs.str()
160 << MatchedExpr->getCallee()->getSourceRange();
161 if (!FixFunctionEmptyArgs.empty()) {
162 D << FixItHint::CreateInsertion(OCE->getArg(0)->getBeginLoc(),
163 FixFunctionEmptyArgs.str() +
"(")
164 << FixItHint::CreateRemoval(LeftBracket)
165 << FixItHint::CreateReplacement(RightBracket,
")");
168 diag(MatchedExpr->getCallee()->getBeginLoc(),
169 "possibly unsafe 'operator[]', use safe function '%0()' instead")
170 << FixFunction.str() << MatchedExpr->getCallee()->getSourceRange()
171 << FixItHint::CreateInsertion(OCE->getArg(0)->getBeginLoc(),
172 FixFunction.str() +
"(")
173 << FixItHint::CreateReplacement(LeftBracket,
", ")
174 << FixItHint::CreateReplacement(RightBracket,
")");
177 }
else if (
const auto *MCE = dyn_cast<CXXMemberCallExpr>(MatchedExpr)) {
179 const auto *Callee = dyn_cast<MemberExpr>(MCE->getCallee());
184 const auto *MatchedOperator =
185 Result.Nodes.getNodeAs<CXXMethodDecl>(
"operator");
189 diag(Callee->getBeginLoc(),
"possibly unsafe 'operator[]', consider "
190 "bounds-safe alternative 'at()'")
191 << Callee->getSourceRange();
194 diag(MatchedExpr->getCallee()->getBeginLoc(),
195 "possibly unsafe 'operator[]', consider "
196 "bounds-safe alternative 'at()'")
197 << FixItHint::CreateReplacement(
198 SourceRange(Callee->getMemberLoc(), Callee->getEndLoc()),
201 diag(Alternative->getBeginLoc(),
"viable 'at()' defined here",
203 << Alternative->getNameInfo().getSourceRange();
207 const auto *Callee = dyn_cast<MemberExpr>(MCE->getCallee());
209 const bool EmptySubscript =
210 MCE->getMethodDecl()->getNumNonObjectParams() == 0;
212 std::string BeginInsertion =
213 (EmptySubscript ? FixFunctionEmptyArgs.str() : FixFunction.str()) +
216 if (Callee->isArrow())
217 BeginInsertion +=
"*";
221 if (EmptySubscript) {
222 auto D = diag(MatchedExpr->getCallee()->getBeginLoc(),
223 "possibly unsafe 'operator[]'%select{, use safe "
224 "function '%1()' instead|}0")
225 << FixFunctionEmptyArgs.empty() << FixFunctionEmptyArgs.str()
226 << Callee->getSourceRange();
228 if (!FixFunctionEmptyArgs.empty()) {
229 D << FixItHint::CreateInsertion(MatchedExpr->getBeginLoc(),
231 << FixItHint::CreateRemoval(
232 SourceRange(Callee->getOperatorLoc(),
233 MCE->getRParenLoc().getLocWithOffset(-1)));
236 diag(Callee->getBeginLoc(),
237 "possibly unsafe 'operator[]', use safe function '%0()' instead")
238 << FixFunction.str() << Callee->getSourceRange()
239 << FixItHint::CreateInsertion(MatchedExpr->getBeginLoc(),
241 << FixItHint::CreateReplacement(
243 Callee->getOperatorLoc(),
244 MCE->getArg(0)->getBeginLoc().getLocWithOffset(-1)),
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.