46 const CXXRecordDecl *Parent = MatchedOperator->getParent();
47 const QualType SubscriptThisObjType =
48 MatchedOperator->getFunctionObjectParameterReferenceType();
50 for (
const CXXMethodDecl *Method : Parent->methods()) {
52 if (MatchedOperator->getAccess() < Method->getAccess())
55 if (MatchedOperator->isConst() != Method->isConst())
58 const QualType AtThisObjType =
59 Method->getFunctionObjectParameterReferenceType();
60 if (SubscriptThisObjType != AtThisObjType)
63 if (!Method->getNameInfo().getName().isIdentifier() ||
64 Method->getName() !=
"at")
67 const bool SameReturnType =
68 Method->getReturnType() == MatchedOperator->getReturnType();
72 const bool SameNumberOfArguments =
73 Method->getNumParams() == MatchedOperator->getNumParams();
74 if (!SameNumberOfArguments)
77 for (
unsigned ArgInd = 0; ArgInd < Method->getNumParams(); ArgInd++) {
78 const bool SameArgType =
79 Method->parameters()[ArgInd]->getOriginalType() ==
80 MatchedOperator->parameters()[ArgInd]->getOriginalType();
105 const MatchFinder::MatchResult &Result) {
107 const auto *MatchedExpr = Result.Nodes.getNodeAs<CallExpr>(
"caller");
109 if (FixMode ==
None) {
110 diag(MatchedExpr->getCallee()->getBeginLoc(),
111 "possibly unsafe 'operator[]', consider bounds-safe alternatives")
112 << MatchedExpr->getCallee()->getSourceRange();
116 if (
const auto *OCE = dyn_cast<CXXOperatorCallExpr>(MatchedExpr)) {
118 const auto LeftBracket = SourceRange(OCE->getCallee()->getBeginLoc(),
119 OCE->getCallee()->getBeginLoc());
120 const auto RightBracket =
121 SourceRange(OCE->getOperatorLoc(), OCE->getOperatorLoc());
125 const auto *MatchedOperator =
126 Result.Nodes.getNodeAs<CXXMethodDecl>(
"operator");
130 diag(MatchedExpr->getCallee()->getBeginLoc(),
131 "possibly unsafe 'operator[]', consider "
132 "bounds-safe alternatives")
133 << MatchedExpr->getCallee()->getSourceRange();
137 diag(MatchedExpr->getCallee()->getBeginLoc(),
138 "possibly unsafe 'operator[]', consider "
139 "bounds-safe alternative 'at()'")
140 << MatchedExpr->getCallee()->getSourceRange()
141 << FixItHint::CreateReplacement(LeftBracket,
".at(")
142 << FixItHint::CreateReplacement(RightBracket,
")");
144 diag(Alternative->getBeginLoc(),
"viable 'at()' is defined here",
146 << Alternative->getNameInfo().getSourceRange();
153 const bool EmptySubscript =
154 MatchedExpr->getDirectCallee()->getNumParams() == 0;
156 if (EmptySubscript) {
157 auto D = diag(MatchedExpr->getCallee()->getBeginLoc(),
158 "possibly unsafe 'operator[]'%select{, use safe "
159 "function '%1() instead|}0")
160 << FixFunctionEmptyArgs.empty() << FixFunctionEmptyArgs.str()
161 << MatchedExpr->getCallee()->getSourceRange();
162 if (!FixFunctionEmptyArgs.empty()) {
163 D << FixItHint::CreateInsertion(OCE->getArg(0)->getBeginLoc(),
164 FixFunctionEmptyArgs.str() +
"(")
165 << FixItHint::CreateRemoval(LeftBracket)
166 << FixItHint::CreateReplacement(RightBracket,
")");
169 diag(MatchedExpr->getCallee()->getBeginLoc(),
170 "possibly unsafe 'operator[]', use safe function '%0()' instead")
171 << FixFunction.str() << MatchedExpr->getCallee()->getSourceRange()
172 << FixItHint::CreateInsertion(OCE->getArg(0)->getBeginLoc(),
173 FixFunction.str() +
"(")
174 << FixItHint::CreateReplacement(LeftBracket,
", ")
175 << FixItHint::CreateReplacement(RightBracket,
")");
178 }
else if (
const auto *MCE = dyn_cast<CXXMemberCallExpr>(MatchedExpr)) {
180 const auto *Callee = dyn_cast<MemberExpr>(MCE->getCallee());
185 const auto *MatchedOperator =
186 Result.Nodes.getNodeAs<CXXMethodDecl>(
"operator");
190 diag(Callee->getBeginLoc(),
"possibly unsafe 'operator[]', consider "
191 "bounds-safe alternative 'at()'")
192 << Callee->getSourceRange();
195 diag(MatchedExpr->getCallee()->getBeginLoc(),
196 "possibly unsafe 'operator[]', consider "
197 "bounds-safe alternative 'at()'")
198 << FixItHint::CreateReplacement(
199 SourceRange(Callee->getMemberLoc(), Callee->getEndLoc()),
202 diag(Alternative->getBeginLoc(),
"viable 'at()' defined here",
204 << Alternative->getNameInfo().getSourceRange();
208 const auto *Callee = dyn_cast<MemberExpr>(MCE->getCallee());
210 const bool EmptySubscript =
211 MCE->getMethodDecl()->getNumNonObjectParams() == 0;
213 std::string BeginInsertion =
214 (EmptySubscript ? FixFunctionEmptyArgs.str() : FixFunction.str()) +
217 if (Callee->isArrow())
218 BeginInsertion +=
"*";
222 if (EmptySubscript) {
223 auto D = diag(MatchedExpr->getCallee()->getBeginLoc(),
224 "possibly unsafe 'operator[]'%select{, use safe "
225 "function '%1()' instead|}0")
226 << FixFunctionEmptyArgs.empty() << FixFunctionEmptyArgs.str()
227 << Callee->getSourceRange();
229 if (!FixFunctionEmptyArgs.empty()) {
230 D << FixItHint::CreateInsertion(MatchedExpr->getBeginLoc(),
232 << FixItHint::CreateRemoval(
233 SourceRange(Callee->getOperatorLoc(),
234 MCE->getRParenLoc().getLocWithOffset(-1)));
237 diag(Callee->getBeginLoc(),
238 "possibly unsafe 'operator[]', use safe function '%0()' instead")
239 << FixFunction.str() << Callee->getSourceRange()
240 << FixItHint::CreateInsertion(MatchedExpr->getBeginLoc(),
242 << FixItHint::CreateReplacement(
244 Callee->getOperatorLoc(),
245 MCE->getArg(0)->getBeginLoc().getLocWithOffset(-1)),
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.