clang 20.0.0git
UnsafeBufferUsage.cpp
Go to the documentation of this file.
1//===- UnsafeBufferUsage.cpp - Replace pointers with modern C++ -----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
11#include "clang/AST/Decl.h"
13#include "clang/AST/Expr.h"
15#include "clang/AST/Stmt.h"
17#include "clang/AST/Type.h"
21#include "clang/Lex/Lexer.h"
23#include "llvm/ADT/APSInt.h"
24#include "llvm/ADT/SmallVector.h"
25#include "llvm/ADT/StringRef.h"
26#include "llvm/Support/Casting.h"
27#include <memory>
28#include <optional>
29#include <queue>
30#include <sstream>
31
32using namespace llvm;
33using namespace clang;
34using namespace ast_matchers;
35
36#ifndef NDEBUG
37namespace {
38class StmtDebugPrinter
39 : public ConstStmtVisitor<StmtDebugPrinter, std::string> {
40public:
41 std::string VisitStmt(const Stmt *S) { return S->getStmtClassName(); }
42
43 std::string VisitBinaryOperator(const BinaryOperator *BO) {
44 return "BinaryOperator(" + BO->getOpcodeStr().str() + ")";
45 }
46
47 std::string VisitUnaryOperator(const UnaryOperator *UO) {
48 return "UnaryOperator(" + UO->getOpcodeStr(UO->getOpcode()).str() + ")";
49 }
50
51 std::string VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
52 return "ImplicitCastExpr(" + std::string(ICE->getCastKindName()) + ")";
53 }
54};
55
56// Returns a string of ancestor `Stmt`s of the given `DRE` in such a form:
57// "DRE ==> parent-of-DRE ==> grandparent-of-DRE ==> ...".
58static std::string getDREAncestorString(const DeclRefExpr *DRE,
59 ASTContext &Ctx) {
60 std::stringstream SS;
61 const Stmt *St = DRE;
62 StmtDebugPrinter StmtPriner;
63
64 do {
65 SS << StmtPriner.Visit(St);
66
67 DynTypedNodeList StParents = Ctx.getParents(*St);
68
69 if (StParents.size() > 1)
70 return "unavailable due to multiple parents";
71 if (StParents.size() == 0)
72 break;
73 St = StParents.begin()->get<Stmt>();
74 if (St)
75 SS << " ==> ";
76 } while (St);
77 return SS.str();
78}
79} // namespace
80#endif /* NDEBUG */
81
82namespace clang::ast_matchers {
83// A `RecursiveASTVisitor` that traverses all descendants of a given node "n"
84// except for those belonging to a different callable of "n".
86public:
87 // Creates an AST visitor that matches `Matcher` on all
88 // descendants of a given node "n" except for the ones
89 // belonging to a different callable of "n".
90 MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher,
91 internal::ASTMatchFinder *Finder,
92 internal::BoundNodesTreeBuilder *Builder,
93 internal::ASTMatchFinder::BindKind Bind,
94 const bool ignoreUnevaluatedContext)
95 : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind),
96 Matches(false), ignoreUnevaluatedContext(ignoreUnevaluatedContext) {
98 ShouldVisitImplicitCode = false; // TODO: let's ignore implicit code for now
99 }
100
101 // Returns true if a match is found in a subtree of `DynNode`, which belongs
102 // to the same callable of `DynNode`.
103 bool findMatch(const DynTypedNode &DynNode) {
104 Matches = false;
105 if (const Stmt *StmtNode = DynNode.get<Stmt>()) {
106 TraverseStmt(const_cast<Stmt *>(StmtNode));
107 *Builder = ResultBindings;
108 return Matches;
109 }
110 return false;
111 }
112
113 // The following are overriding methods from the base visitor class.
114 // They are public only to allow CRTP to work. They are *not *part
115 // of the public API of this class.
116
117 // For the matchers so far used in safe buffers, we only need to match
118 // `Stmt`s. To override more as needed.
119
120 bool TraverseDecl(Decl *Node) override {
121 if (!Node)
122 return true;
123 if (!match(*Node))
124 return false;
125 // To skip callables:
126 if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node))
127 return true;
128 // Traverse descendants
130 }
131
133 // These are unevaluated, except the result expression.
134 if (ignoreUnevaluatedContext)
135 return TraverseStmt(Node->getResultExpr());
136 return DynamicRecursiveASTVisitor::TraverseGenericSelectionExpr(Node);
137 }
138
139 bool
141 // Unevaluated context.
142 if (ignoreUnevaluatedContext)
143 return true;
144 return DynamicRecursiveASTVisitor::TraverseUnaryExprOrTypeTraitExpr(Node);
145 }
146
148 // Unevaluated context.
149 if (ignoreUnevaluatedContext)
150 return true;
151 return DynamicRecursiveASTVisitor::TraverseTypeOfExprTypeLoc(Node);
152 }
153
155 // Unevaluated context.
156 if (ignoreUnevaluatedContext)
157 return true;
158 return DynamicRecursiveASTVisitor::TraverseDecltypeTypeLoc(Node);
159 }
160
162 // Unevaluated context.
163 if (ignoreUnevaluatedContext)
164 return true;
165 return DynamicRecursiveASTVisitor::TraverseCXXNoexceptExpr(Node);
166 }
167
169 // Unevaluated context.
170 if (ignoreUnevaluatedContext)
171 return true;
172 return DynamicRecursiveASTVisitor::TraverseCXXTypeidExpr(Node);
173 }
174
176 if (!TraverseStmt(Node->getExpr()))
177 return false;
178 return DynamicRecursiveASTVisitor::TraverseCXXDefaultInitExpr(Node);
179 }
180
181 bool TraverseStmt(Stmt *Node) override {
182 if (!Node)
183 return true;
184 if (!match(*Node))
185 return false;
187 }
188
189private:
190 // Sets 'Matched' to true if 'Matcher' matches 'Node'
191 //
192 // Returns 'true' if traversal should continue after this function
193 // returns, i.e. if no match is found or 'Bind' is 'BK_All'.
194 template <typename T> bool match(const T &Node) {
195 internal::BoundNodesTreeBuilder RecursiveBuilder(*Builder);
196
197 if (Matcher->matches(DynTypedNode::create(Node), Finder,
198 &RecursiveBuilder)) {
199 ResultBindings.addMatch(RecursiveBuilder);
200 Matches = true;
201 if (Bind != internal::ASTMatchFinder::BK_All)
202 return false; // Abort as soon as a match is found.
203 }
204 return true;
205 }
206
207 const internal::DynTypedMatcher *const Matcher;
208 internal::ASTMatchFinder *const Finder;
209 internal::BoundNodesTreeBuilder *const Builder;
210 internal::BoundNodesTreeBuilder ResultBindings;
211 const internal::ASTMatchFinder::BindKind Bind;
212 bool Matches;
213 bool ignoreUnevaluatedContext;
214};
215
216// Because we're dealing with raw pointers, let's define what we mean by that.
217static auto hasPointerType() {
218 return hasType(hasCanonicalType(pointerType()));
219}
220
221static auto hasArrayType() { return hasType(hasCanonicalType(arrayType())); }
222
223AST_MATCHER_P(Stmt, forEachDescendantEvaluatedStmt, internal::Matcher<Stmt>,
224 innerMatcher) {
225 const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
226
227 MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All,
228 true);
229 return Visitor.findMatch(DynTypedNode::create(Node));
230}
231
232AST_MATCHER_P(Stmt, forEachDescendantStmt, internal::Matcher<Stmt>,
233 innerMatcher) {
234 const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
235
236 MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All,
237 false);
238 return Visitor.findMatch(DynTypedNode::create(Node));
239}
240
241// Matches a `Stmt` node iff the node is in a safe-buffer opt-out region
242AST_MATCHER_P(Stmt, notInSafeBufferOptOut, const UnsafeBufferUsageHandler *,
243 Handler) {
244 return !Handler->isSafeBufferOptOut(Node.getBeginLoc());
245}
246
247AST_MATCHER_P(Stmt, ignoreUnsafeBufferInContainer,
248 const UnsafeBufferUsageHandler *, Handler) {
249 return Handler->ignoreUnsafeBufferInContainer(Node.getBeginLoc());
250}
251
252AST_MATCHER_P(Stmt, ignoreUnsafeLibcCall, const UnsafeBufferUsageHandler *,
253 Handler) {
254 if (Finder->getASTContext().getLangOpts().CPlusPlus)
255 return Handler->ignoreUnsafeBufferInLibcCall(Node.getBeginLoc());
256 return true; /* Only warn about libc calls for C++ */
257}
258
259AST_MATCHER_P(CastExpr, castSubExpr, internal::Matcher<Expr>, innerMatcher) {
260 return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder);
261}
262
263// Matches a `UnaryOperator` whose operator is pre-increment:
265 return Node.getOpcode() == UnaryOperator::Opcode::UO_PreInc;
266}
267
268// Returns a matcher that matches any expression 'e' such that `innerMatcher`
269// matches 'e' and 'e' is in an Unspecified Lvalue Context.
270static auto isInUnspecifiedLvalueContext(internal::Matcher<Expr> innerMatcher) {
271 // clang-format off
272 return
273 expr(anyOf(
275 hasCastKind(CastKind::CK_LValueToRValue),
276 castSubExpr(innerMatcher)),
279 hasLHS(innerMatcher)
280 )
281 ));
282 // clang-format on
283}
284
285// Returns a matcher that matches any expression `e` such that `InnerMatcher`
286// matches `e` and `e` is in an Unspecified Pointer Context (UPC).
287static internal::Matcher<Stmt>
288isInUnspecifiedPointerContext(internal::Matcher<Stmt> InnerMatcher) {
289 // A UPC can be
290 // 1. an argument of a function call (except the callee has [[unsafe_...]]
291 // attribute), or
292 // 2. the operand of a pointer-to-(integer or bool) cast operation; or
293 // 3. the operand of a comparator operation; or
294 // 4. the operand of a pointer subtraction operation
295 // (i.e., computing the distance between two pointers); or ...
296
297 // clang-format off
298 auto CallArgMatcher = callExpr(
299 forEachArgumentWithParamType(
300 InnerMatcher,
301 isAnyPointer() /* array also decays to pointer type*/),
302 unless(callee(
303 functionDecl(hasAttr(attr::UnsafeBufferUsage)))));
304
305 auto CastOperandMatcher =
306 castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral),
307 hasCastKind(CastKind::CK_PointerToBoolean)),
308 castSubExpr(allOf(hasPointerType(), InnerMatcher)));
309
310 auto CompOperandMatcher =
311 binaryOperator(hasAnyOperatorName("!=", "==", "<", "<=", ">", ">="),
312 eachOf(hasLHS(allOf(hasPointerType(), InnerMatcher)),
313 hasRHS(allOf(hasPointerType(), InnerMatcher))));
314
315 // A matcher that matches pointer subtractions:
316 auto PtrSubtractionMatcher =
317 binaryOperator(hasOperatorName("-"),
318 // Note that here we need both LHS and RHS to be
319 // pointer. Then the inner matcher can match any of
320 // them:
321 allOf(hasLHS(hasPointerType()),
322 hasRHS(hasPointerType())),
323 eachOf(hasLHS(InnerMatcher),
324 hasRHS(InnerMatcher)));
325 // clang-format on
326
327 return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher,
328 PtrSubtractionMatcher));
329 // FIXME: any more cases? (UPC excludes the RHS of an assignment. For now we
330 // don't have to check that.)
331}
332
333// Returns a matcher that matches any expression 'e' such that `innerMatcher`
334// matches 'e' and 'e' is in an unspecified untyped context (i.e the expression
335// 'e' isn't evaluated to an RValue). For example, consider the following code:
336// int *p = new int[4];
337// int *q = new int[4];
338// if ((p = q)) {}
339// p = q;
340// The expression `p = q` in the conditional of the `if` statement
341// `if ((p = q))` is evaluated as an RValue, whereas the expression `p = q;`
342// in the assignment statement is in an untyped context.
343static internal::Matcher<Stmt>
344isInUnspecifiedUntypedContext(internal::Matcher<Stmt> InnerMatcher) {
345 // An unspecified context can be
346 // 1. A compound statement,
347 // 2. The body of an if statement
348 // 3. Body of a loop
349 auto CompStmt = compoundStmt(forEach(InnerMatcher));
350 auto IfStmtThen = ifStmt(hasThen(InnerMatcher));
351 auto IfStmtElse = ifStmt(hasElse(InnerMatcher));
352 // FIXME: Handle loop bodies.
353 return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse));
354}
355
356// Given a two-param std::span construct call, matches iff the call has the
357// following forms:
358// 1. `std::span<T>{new T[n], n}`, where `n` is a literal or a DRE
359// 2. `std::span<T>{new T, 1}`
360// 3. `std::span<T>{&var, 1}`
361// 4. `std::span<T>{a, n}`, where `a` is of an array-of-T with constant size
362// `n`
363// 5. `std::span<T>{any, 0}`
364// 6. `std::span<T>{std::addressof(...), 1}`
365AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
366 assert(Node.getNumArgs() == 2 &&
367 "expecting a two-parameter std::span constructor");
368 const Expr *Arg0 = Node.getArg(0)->IgnoreImplicit();
369 const Expr *Arg1 = Node.getArg(1)->IgnoreImplicit();
370 auto HaveEqualConstantValues = [&Finder](const Expr *E0, const Expr *E1) {
371 if (auto E0CV = E0->getIntegerConstantExpr(Finder->getASTContext()))
372 if (auto E1CV = E1->getIntegerConstantExpr(Finder->getASTContext())) {
373 return APSInt::compareValues(*E0CV, *E1CV) == 0;
374 }
375 return false;
376 };
377 auto AreSameDRE = [](const Expr *E0, const Expr *E1) {
378 if (auto *DRE0 = dyn_cast<DeclRefExpr>(E0))
379 if (auto *DRE1 = dyn_cast<DeclRefExpr>(E1)) {
380 return DRE0->getDecl() == DRE1->getDecl();
381 }
382 return false;
383 };
384 std::optional<APSInt> Arg1CV =
385 Arg1->getIntegerConstantExpr(Finder->getASTContext());
386
387 if (Arg1CV && Arg1CV->isZero())
388 // Check form 5:
389 return true;
390 switch (Arg0->IgnoreImplicit()->getStmtClass()) {
391 case Stmt::CXXNewExprClass:
392 if (auto Size = cast<CXXNewExpr>(Arg0)->getArraySize()) {
393 // Check form 1:
394 return AreSameDRE((*Size)->IgnoreImplicit(), Arg1) ||
395 HaveEqualConstantValues(*Size, Arg1);
396 }
397 // TODO: what's placeholder type? avoid it for now.
398 if (!cast<CXXNewExpr>(Arg0)->hasPlaceholderType()) {
399 // Check form 2:
400 return Arg1CV && Arg1CV->isOne();
401 }
402 break;
403 case Stmt::UnaryOperatorClass:
404 if (cast<UnaryOperator>(Arg0)->getOpcode() ==
405 UnaryOperator::Opcode::UO_AddrOf)
406 // Check form 3:
407 return Arg1CV && Arg1CV->isOne();
408 break;
409 case Stmt::CallExprClass:
410 if (const auto *CE = dyn_cast<CallExpr>(Arg0)) {
411 const auto FnDecl = CE->getDirectCallee();
412 if (FnDecl && FnDecl->getNameAsString() == "addressof" &&
413 FnDecl->isInStdNamespace()) {
414 return Arg1CV && Arg1CV->isOne();
415 }
416 }
417 break;
418 default:
419 break;
420 }
421
422 QualType Arg0Ty = Arg0->IgnoreImplicit()->getType();
423
424 if (auto *ConstArrTy =
425 Finder->getASTContext().getAsConstantArrayType(Arg0Ty)) {
426 const APSInt ConstArrSize = APSInt(ConstArrTy->getSize());
427
428 // Check form 4:
429 return Arg1CV && APSInt::compareValues(ConstArrSize, *Arg1CV) == 0;
430 }
431 return false;
432}
433
434AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
435 // FIXME: Proper solution:
436 // - refactor Sema::CheckArrayAccess
437 // - split safe/OOB/unknown decision logic from diagnostics emitting code
438 // - e. g. "Try harder to find a NamedDecl to point at in the note."
439 // already duplicated
440 // - call both from Sema and from here
441
442 uint64_t limit;
443 if (const auto *CATy =
444 dyn_cast<ConstantArrayType>(Node.getBase()
445 ->IgnoreParenImpCasts()
446 ->getType()
447 ->getUnqualifiedDesugaredType())) {
448 limit = CATy->getLimitedSize();
449 } else if (const auto *SLiteral = dyn_cast<StringLiteral>(
450 Node.getBase()->IgnoreParenImpCasts())) {
451 limit = SLiteral->getLength() + 1;
452 } else {
453 return false;
454 }
455
456 if (const auto *IdxLit = dyn_cast<IntegerLiteral>(Node.getIdx())) {
457 const APInt ArrIdx = IdxLit->getValue();
458 if (ArrIdx.isNonNegative() && ArrIdx.getLimitedValue() < limit)
459 return true;
460 }
461 return false;
462}
463
464AST_MATCHER_P(CallExpr, hasNumArgs, unsigned, Num) {
465 return Node.getNumArgs() == Num;
466}
467
468namespace libc_func_matchers {
469// Under `libc_func_matchers`, define a set of matchers that match unsafe
470// functions in libc and unsafe calls to them.
471
472// A tiny parser to strip off common prefix and suffix of libc function names
473// in real code.
474//
475// Given a function name, `matchName` returns `CoreName` according to the
476// following grammar:
477//
478// LibcName := CoreName | CoreName + "_s"
479// MatchingName := "__builtin_" + LibcName |
480// "__builtin___" + LibcName + "_chk" |
481// "__asan_" + LibcName
482//
484 StringRef matchName(StringRef FunName, bool isBuiltin) {
485 // Try to match __builtin_:
486 if (isBuiltin && FunName.starts_with("__builtin_"))
487 // Then either it is __builtin_LibcName or __builtin___LibcName_chk or
488 // no match:
490 FunName.drop_front(10 /* truncate "__builtin_" */));
491 // Try to match __asan_:
492 if (FunName.starts_with("__asan_"))
493 return matchLibcName(FunName.drop_front(7 /* truncate of "__asan_" */));
494 return matchLibcName(FunName);
495 }
496
497 // Parameter `Name` is the substring after stripping off the prefix
498 // "__builtin_".
499 StringRef matchLibcNameOrBuiltinChk(StringRef Name) {
500 if (Name.starts_with("__") && Name.ends_with("_chk"))
501 return matchLibcName(
502 Name.drop_front(2).drop_back(4) /* truncate "__" and "_chk" */);
503 return matchLibcName(Name);
504 }
505
506 StringRef matchLibcName(StringRef Name) {
507 if (Name.ends_with("_s"))
508 return Name.drop_back(2 /* truncate "_s" */);
509 return Name;
510 }
511};
512
513// A pointer type expression is known to be null-terminated, if it has the
514// form: E.c_str(), for any expression E of `std::string` type.
515static bool isNullTermPointer(const Expr *Ptr) {
516 if (isa<StringLiteral>(Ptr->IgnoreParenImpCasts()))
517 return true;
518 if (isa<PredefinedExpr>(Ptr->IgnoreParenImpCasts()))
519 return true;
520 if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Ptr->IgnoreParenImpCasts())) {
521 const CXXMethodDecl *MD = MCE->getMethodDecl();
522 const CXXRecordDecl *RD = MCE->getRecordDecl()->getCanonicalDecl();
523
524 if (MD && RD && RD->isInStdNamespace())
525 if (MD->getName() == "c_str" && RD->getName() == "basic_string")
526 return true;
527 }
528 return false;
529}
530
531// Return true iff at least one of following cases holds:
532// 1. Format string is a literal and there is an unsafe pointer argument
533// corresponding to an `s` specifier;
534// 2. Format string is not a literal and there is least an unsafe pointer
535// argument (including the formatter argument).
536//
537// `UnsafeArg` is the output argument that will be set only if this function
538// returns true.
539static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg,
540 const unsigned FmtArgIdx, ASTContext &Ctx,
541 bool isKprintf = false) {
542 class StringFormatStringHandler
544 const CallExpr *Call;
545 unsigned FmtArgIdx;
546 const Expr *&UnsafeArg;
547
548 public:
549 StringFormatStringHandler(const CallExpr *Call, unsigned FmtArgIdx,
550 const Expr *&UnsafeArg)
551 : Call(Call), FmtArgIdx(FmtArgIdx), UnsafeArg(UnsafeArg) {}
552
553 bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
554 const char *startSpecifier,
555 unsigned specifierLen,
556 const TargetInfo &Target) override {
557 if (FS.getConversionSpecifier().getKind() ==
558 analyze_printf::PrintfConversionSpecifier::sArg) {
559 unsigned ArgIdx = FS.getPositionalArgIndex() + FmtArgIdx;
560
561 if (0 < ArgIdx && ArgIdx < Call->getNumArgs())
562 if (!isNullTermPointer(Call->getArg(ArgIdx))) {
563 UnsafeArg = Call->getArg(ArgIdx); // output
564 // returning false stops parsing immediately
565 return false;
566 }
567 }
568 return true; // continue parsing
569 }
570 };
571
572 const Expr *Fmt = Call->getArg(FmtArgIdx);
573
574 if (auto *SL = dyn_cast<StringLiteral>(Fmt->IgnoreParenImpCasts())) {
575 StringRef FmtStr;
576
577 if (SL->getCharByteWidth() == 1)
578 FmtStr = SL->getString();
579 else if (auto EvaledFmtStr = SL->tryEvaluateString(Ctx))
580 FmtStr = *EvaledFmtStr;
581 else
582 goto CHECK_UNSAFE_PTR;
583
584 StringFormatStringHandler Handler(Call, FmtArgIdx, UnsafeArg);
585
587 Handler, FmtStr.begin(), FmtStr.end(), Ctx.getLangOpts(),
588 Ctx.getTargetInfo(), isKprintf);
589 }
590CHECK_UNSAFE_PTR:
591 // If format is not a string literal, we cannot analyze the format string.
592 // In this case, this call is considered unsafe if at least one argument
593 // (including the format argument) is unsafe pointer.
594 return llvm::any_of(
595 llvm::make_range(Call->arg_begin() + FmtArgIdx, Call->arg_end()),
596 [&UnsafeArg](const Expr *Arg) -> bool {
597 if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg)) {
598 UnsafeArg = Arg;
599 return true;
600 }
601 return false;
602 });
603}
604
605// Matches a FunctionDecl node such that
606// 1. It's name, after stripping off predefined prefix and suffix, is
607// `CoreName`; and
608// 2. `CoreName` or `CoreName[str/wcs]` is one of the `PredefinedNames`, which
609// is a set of libc function names.
610//
611// Note: For predefined prefix and suffix, see `LibcFunNamePrefixSuffixParser`.
612// The notation `CoreName[str/wcs]` means a new name obtained from replace
613// string "wcs" with "str" in `CoreName`.
614AST_MATCHER(FunctionDecl, isPredefinedUnsafeLibcFunc) {
615 static std::unique_ptr<std::set<StringRef>> PredefinedNames = nullptr;
616 if (!PredefinedNames)
617 PredefinedNames =
618 std::make_unique<std::set<StringRef>, std::set<StringRef>>({
619 // numeric conversion:
620 "atof",
621 "atoi",
622 "atol",
623 "atoll",
624 "strtol",
625 "strtoll",
626 "strtoul",
627 "strtoull",
628 "strtof",
629 "strtod",
630 "strtold",
631 "strtoimax",
632 "strtoumax",
633 // "strfromf", "strfromd", "strfroml", // C23?
634 // string manipulation:
635 "strcpy",
636 "strncpy",
637 "strlcpy",
638 "strcat",
639 "strncat",
640 "strlcat",
641 "strxfrm",
642 "strdup",
643 "strndup",
644 // string examination:
645 "strlen",
646 "strnlen",
647 "strcmp",
648 "strncmp",
649 "stricmp",
650 "strcasecmp",
651 "strcoll",
652 "strchr",
653 "strrchr",
654 "strspn",
655 "strcspn",
656 "strpbrk",
657 "strstr",
658 "strtok",
659 // "mem-" functions
660 "memchr",
661 "wmemchr",
662 "memcmp",
663 "wmemcmp",
664 "memcpy",
665 "memccpy",
666 "mempcpy",
667 "wmemcpy",
668 "memmove",
669 "wmemmove",
670 "memset",
671 "wmemset",
672 // IO:
673 "fread",
674 "fwrite",
675 "fgets",
676 "fgetws",
677 "gets",
678 "fputs",
679 "fputws",
680 "puts",
681 // others
682 "strerror_s",
683 "strerror_r",
684 "bcopy",
685 "bzero",
686 "bsearch",
687 "qsort",
688 });
689
690 auto *II = Node.getIdentifier();
691
692 if (!II)
693 return false;
694
695 StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
696 II->getName(), Node.getBuiltinID());
697
698 // Match predefined names:
699 if (PredefinedNames->find(Name) != PredefinedNames->end())
700 return true;
701
702 std::string NameWCS = Name.str();
703 size_t WcsPos = NameWCS.find("wcs");
704
705 while (WcsPos != std::string::npos) {
706 NameWCS[WcsPos++] = 's';
707 NameWCS[WcsPos++] = 't';
708 NameWCS[WcsPos++] = 'r';
709 WcsPos = NameWCS.find("wcs", WcsPos);
710 }
711 if (PredefinedNames->find(NameWCS) != PredefinedNames->end())
712 return true;
713 // All `scanf` functions are unsafe (including `sscanf`, `vsscanf`, etc.. They
714 // all should end with "scanf"):
715 return Name.ends_with("scanf");
716}
717
718// Match a call to one of the `v*printf` functions taking `va_list`. We cannot
719// check safety for these functions so they should be changed to their
720// non-va_list versions.
721AST_MATCHER(FunctionDecl, isUnsafeVaListPrintfFunc) {
722 auto *II = Node.getIdentifier();
723
724 if (!II)
725 return false;
726
727 StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
728 II->getName(), Node.getBuiltinID());
729
730 if (!Name.ends_with("printf"))
731 return false; // neither printf nor scanf
732 return Name.starts_with("v");
733}
734
735// Matches a call to one of the `sprintf` functions as they are always unsafe
736// and should be changed to `snprintf`.
737AST_MATCHER(FunctionDecl, isUnsafeSprintfFunc) {
738 auto *II = Node.getIdentifier();
739
740 if (!II)
741 return false;
742
743 StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
744 II->getName(), Node.getBuiltinID());
745
746 if (!Name.ends_with("printf") ||
747 // Let `isUnsafeVaListPrintfFunc` check for cases with va-list:
748 Name.starts_with("v"))
749 return false;
750
751 StringRef Prefix = Name.drop_back(6);
752
753 if (Prefix.ends_with("w"))
754 Prefix = Prefix.drop_back(1);
755 return Prefix == "s";
756}
757
758// Match function declarations of `printf`, `fprintf`, `snprintf` and their wide
759// character versions. Calls to these functions can be safe if their arguments
760// are carefully made safe.
761AST_MATCHER(FunctionDecl, isNormalPrintfFunc) {
762 auto *II = Node.getIdentifier();
763
764 if (!II)
765 return false;
766
767 StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
768 II->getName(), Node.getBuiltinID());
769
770 if (!Name.ends_with("printf") || Name.starts_with("v"))
771 return false;
772
773 StringRef Prefix = Name.drop_back(6);
774
775 if (Prefix.ends_with("w"))
776 Prefix = Prefix.drop_back(1);
777
778 return Prefix.empty() || Prefix == "k" || Prefix == "f" || Prefix == "sn";
779}
780
781// This matcher requires that it is known that the callee `isNormalPrintf`.
782// Then if the format string is a string literal, this matcher matches when at
783// least one string argument is unsafe. If the format is not a string literal,
784// this matcher matches when at least one pointer type argument is unsafe.
785AST_MATCHER_P(CallExpr, hasUnsafePrintfStringArg,
786 clang::ast_matchers::internal::Matcher<Expr>,
787 UnsafeStringArgMatcher) {
788 // Determine what printf it is by examining formal parameters:
789 const FunctionDecl *FD = Node.getDirectCallee();
790
791 assert(FD && "It should have been checked that FD is non-null.");
792
793 unsigned NumParms = FD->getNumParams();
794
795 if (NumParms < 1)
796 return false; // possibly some user-defined printf function
797
798 ASTContext &Ctx = Finder->getASTContext();
799 QualType FirstParmTy = FD->getParamDecl(0)->getType();
800
801 if (!FirstParmTy->isPointerType())
802 return false; // possibly some user-defined printf function
803
804 QualType FirstPteTy = FirstParmTy->castAs<PointerType>()->getPointeeType();
805
806 if (!Ctx.getFILEType()
807 .isNull() && //`FILE *` must be in the context if it is fprintf
808 FirstPteTy.getCanonicalType() == Ctx.getFILEType().getCanonicalType()) {
809 // It is a fprintf:
810 const Expr *UnsafeArg;
811
812 if (hasUnsafeFormatOrSArg(&Node, UnsafeArg, 1, Ctx, false))
813 return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);
814 return false;
815 }
816
817 if (FirstPteTy.isConstQualified()) {
818 // If the first parameter is a `const char *`, it is a printf/kprintf:
819 bool isKprintf = false;
820 const Expr *UnsafeArg;
821
822 if (auto *II = FD->getIdentifier())
823 isKprintf = II->getName() == "kprintf";
824 if (hasUnsafeFormatOrSArg(&Node, UnsafeArg, 0, Ctx, isKprintf))
825 return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);
826 return false;
827 }
828
829 if (NumParms > 2) {
830 QualType SecondParmTy = FD->getParamDecl(1)->getType();
831
832 if (!FirstPteTy.isConstQualified() && SecondParmTy->isIntegerType()) {
833 // If the first parameter type is non-const qualified `char *` and the
834 // second is an integer, it is a snprintf:
835 const Expr *UnsafeArg;
836
837 if (hasUnsafeFormatOrSArg(&Node, UnsafeArg, 2, Ctx, false))
838 return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);
839 return false;
840 }
841 }
842 // We don't really recognize this "normal" printf, the only thing we
843 // can do is to require all pointers to be null-terminated:
844 for (auto Arg : Node.arguments())
845 if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg))
846 if (UnsafeStringArgMatcher.matches(*Arg, Finder, Builder))
847 return true;
848 return false;
849}
850
851// This matcher requires that it is known that the callee `isNormalPrintf`.
852// Then it matches if the first two arguments of the call is a pointer and an
853// integer and they are not in a safe pattern.
854//
855// For the first two arguments: `ptr` and `size`, they are safe if in the
856// following patterns:
857//
858// Pattern 1:
859// ptr := DRE.data();
860// size:= DRE.size()/DRE.size_bytes()
861// And DRE is a hardened container or view.
862//
863// Pattern 2:
864// ptr := Constant-Array-DRE;
865// size:= any expression that has compile-time constant value equivalent to
866// sizeof (Constant-Array-DRE)
867AST_MATCHER(CallExpr, hasUnsafeSnprintfBuffer) {
868 const FunctionDecl *FD = Node.getDirectCallee();
869
870 assert(FD && "It should have been checked that FD is non-null.");
871
872 if (FD->getNumParams() < 3)
873 return false; // Not an snprint
874
875 QualType FirstParmTy = FD->getParamDecl(0)->getType();
876
877 if (!FirstParmTy->isPointerType())
878 return false; // Not an snprint
879
880 QualType FirstPteTy = FirstParmTy->castAs<PointerType>()->getPointeeType();
881 const Expr *Buf = Node.getArg(0), *Size = Node.getArg(1);
882
883 if (FirstPteTy.isConstQualified() || !Buf->getType()->isPointerType() ||
884 !Size->getType()->isIntegerType())
885 return false; // not an snprintf call
886
887 // Pattern 1:
888 static StringRef SizedObjs[] = {"span", "array", "vector",
889 "basic_string_view", "basic_string"};
890 Buf = Buf->IgnoreParenImpCasts();
891 Size = Size->IgnoreParenImpCasts();
892 if (auto *MCEPtr = dyn_cast<CXXMemberCallExpr>(Buf))
893 if (auto *MCESize = dyn_cast<CXXMemberCallExpr>(Size)) {
894 auto *DREOfPtr = dyn_cast<DeclRefExpr>(
895 MCEPtr->getImplicitObjectArgument()->IgnoreParenImpCasts());
896 auto *DREOfSize = dyn_cast<DeclRefExpr>(
897 MCESize->getImplicitObjectArgument()->IgnoreParenImpCasts());
898
899 if (!DREOfPtr || !DREOfSize)
900 return true; // not in safe pattern
901 if (DREOfPtr->getDecl() != DREOfSize->getDecl())
902 return true; // not in safe pattern
903 if (MCEPtr->getMethodDecl()->getName() != "data")
904 return true; // not in safe pattern
905
906 if (MCESize->getMethodDecl()->getName() == "size_bytes" ||
907 // Note here the pointer must be a pointer-to-char type unless there
908 // is explicit casting. If there is explicit casting, this branch
909 // is unreachable. Thus, at this branch "size" and "size_bytes" are
910 // equivalent as the pointer is a char pointer:
911 MCESize->getMethodDecl()->getName() == "size")
912 for (StringRef SizedObj : SizedObjs)
913 if (MCEPtr->getRecordDecl()->isInStdNamespace() &&
914 MCEPtr->getRecordDecl()->getCanonicalDecl()->getName() ==
915 SizedObj)
916 return false; // It is in fact safe
917 }
918
919 // Pattern 2:
920 if (auto *DRE = dyn_cast<DeclRefExpr>(Buf->IgnoreParenImpCasts())) {
921 ASTContext &Ctx = Finder->getASTContext();
922
923 if (auto *CAT = Ctx.getAsConstantArrayType(DRE->getType())) {
925 // The array element type must be compatible with `char` otherwise an
926 // explicit cast will be needed, which will make this check unreachable.
927 // Therefore, the array extent is same as its' bytewise size.
928 if (Size->EvaluateAsConstantExpr(ER, Ctx)) {
929 APSInt EVal = ER.Val.getInt(); // Size must have integer type
930
931 return APSInt::compareValues(EVal, APSInt(CAT->getSize(), true)) != 0;
932 }
933 }
934 }
935 return true; // ptr and size are not in safe pattern
936}
937} // namespace libc_func_matchers
938} // namespace clang::ast_matchers
939
940namespace {
941// Because the analysis revolves around variables and their types, we'll need to
942// track uses of variables (aka DeclRefExprs).
943using DeclUseList = SmallVector<const DeclRefExpr *, 1>;
944
945// Convenience typedef.
946using FixItList = SmallVector<FixItHint, 4>;
947} // namespace
948
949namespace {
950/// Gadget is an individual operation in the code that may be of interest to
951/// this analysis. Each (non-abstract) subclass corresponds to a specific
952/// rigid AST structure that constitutes an operation on a pointer-type object.
953/// Discovery of a gadget in the code corresponds to claiming that we understand
954/// what this part of code is doing well enough to potentially improve it.
955/// Gadgets can be warning (immediately deserving a warning) or fixable (not
956/// always deserving a warning per se, but requires our attention to identify
957/// it warrants a fixit).
958class Gadget {
959public:
960 enum class Kind {
961#define GADGET(x) x,
962#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
963 };
964
965 /// Common type of ASTMatchers used for discovering gadgets.
966 /// Useful for implementing the static matcher() methods
967 /// that are expected from all non-abstract subclasses.
968 using Matcher = decltype(stmt());
969
970 Gadget(Kind K) : K(K) {}
971
972 Kind getKind() const { return K; }
973
974#ifndef NDEBUG
975 StringRef getDebugName() const {
976 switch (K) {
977#define GADGET(x) \
978 case Kind::x: \
979 return #x;
980#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
981 }
982 llvm_unreachable("Unhandled Gadget::Kind enum");
983 }
984#endif
985
986 virtual bool isWarningGadget() const = 0;
987 // TODO remove this method from WarningGadget interface. It's only used for
988 // debug prints in FixableGadget.
989 virtual SourceLocation getSourceLoc() const = 0;
990
991 /// Returns the list of pointer-type variables on which this gadget performs
992 /// its operation. Typically, there's only one variable. This isn't a list
993 /// of all DeclRefExprs in the gadget's AST!
994 virtual DeclUseList getClaimedVarUseSites() const = 0;
995
996 virtual ~Gadget() = default;
997
998private:
999 Kind K;
1000};
1001
1002/// Warning gadgets correspond to unsafe code patterns that warrants
1003/// an immediate warning.
1004class WarningGadget : public Gadget {
1005public:
1006 WarningGadget(Kind K) : Gadget(K) {}
1007
1008 static bool classof(const Gadget *G) { return G->isWarningGadget(); }
1009 bool isWarningGadget() const final { return true; }
1010
1011 virtual void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1012 bool IsRelatedToDecl,
1013 ASTContext &Ctx) const = 0;
1014};
1015
1016/// Fixable gadgets correspond to code patterns that aren't always unsafe but
1017/// need to be properly recognized in order to emit fixes. For example, if a raw
1018/// pointer-type variable is replaced by a safe C++ container, every use of such
1019/// variable must be carefully considered and possibly updated.
1020class FixableGadget : public Gadget {
1021public:
1022 FixableGadget(Kind K) : Gadget(K) {}
1023
1024 static bool classof(const Gadget *G) { return !G->isWarningGadget(); }
1025 bool isWarningGadget() const final { return false; }
1026
1027 /// Returns a fixit that would fix the current gadget according to
1028 /// the current strategy. Returns std::nullopt if the fix cannot be produced;
1029 /// returns an empty list if no fixes are necessary.
1030 virtual std::optional<FixItList> getFixits(const FixitStrategy &) const {
1031 return std::nullopt;
1032 }
1033
1034 /// Returns a list of two elements where the first element is the LHS of a
1035 /// pointer assignment statement and the second element is the RHS. This
1036 /// two-element list represents the fact that the LHS buffer gets its bounds
1037 /// information from the RHS buffer. This information will be used later to
1038 /// group all those variables whose types must be modified together to prevent
1039 /// type mismatches.
1040 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1041 getStrategyImplications() const {
1042 return std::nullopt;
1043 }
1044};
1045
1046static auto toSupportedVariable() { return to(varDecl()); }
1047
1048using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>;
1049using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>;
1050
1051/// An increment of a pointer-type value is unsafe as it may run the pointer
1052/// out of bounds.
1053class IncrementGadget : public WarningGadget {
1054 static constexpr const char *const OpTag = "op";
1055 const UnaryOperator *Op;
1056
1057public:
1058 IncrementGadget(const MatchFinder::MatchResult &Result)
1059 : WarningGadget(Kind::Increment),
1060 Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {}
1061
1062 static bool classof(const Gadget *G) {
1063 return G->getKind() == Kind::Increment;
1064 }
1065
1066 static Matcher matcher() {
1067 return stmt(
1068 unaryOperator(hasOperatorName("++"),
1069 hasUnaryOperand(ignoringParenImpCasts(hasPointerType())))
1070 .bind(OpTag));
1071 }
1072
1073 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1074 bool IsRelatedToDecl,
1075 ASTContext &Ctx) const override {
1076 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1077 }
1078 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1079
1080 DeclUseList getClaimedVarUseSites() const override {
1082 if (const auto *DRE =
1083 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
1084 Uses.push_back(DRE);
1085 }
1086
1087 return std::move(Uses);
1088 }
1089};
1090
1091/// A decrement of a pointer-type value is unsafe as it may run the pointer
1092/// out of bounds.
1093class DecrementGadget : public WarningGadget {
1094 static constexpr const char *const OpTag = "op";
1095 const UnaryOperator *Op;
1096
1097public:
1098 DecrementGadget(const MatchFinder::MatchResult &Result)
1099 : WarningGadget(Kind::Decrement),
1100 Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {}
1101
1102 static bool classof(const Gadget *G) {
1103 return G->getKind() == Kind::Decrement;
1104 }
1105
1106 static Matcher matcher() {
1107 return stmt(
1108 unaryOperator(hasOperatorName("--"),
1109 hasUnaryOperand(ignoringParenImpCasts(hasPointerType())))
1110 .bind(OpTag));
1111 }
1112
1113 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1114 bool IsRelatedToDecl,
1115 ASTContext &Ctx) const override {
1116 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1117 }
1118 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1119
1120 DeclUseList getClaimedVarUseSites() const override {
1121 if (const auto *DRE =
1122 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
1123 return {DRE};
1124 }
1125
1126 return {};
1127 }
1128};
1129
1130/// Array subscript expressions on raw pointers as if they're arrays. Unsafe as
1131/// it doesn't have any bounds checks for the array.
1132class ArraySubscriptGadget : public WarningGadget {
1133 static constexpr const char *const ArraySubscrTag = "ArraySubscript";
1134 const ArraySubscriptExpr *ASE;
1135
1136public:
1137 ArraySubscriptGadget(const MatchFinder::MatchResult &Result)
1138 : WarningGadget(Kind::ArraySubscript),
1139 ASE(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {}
1140
1141 static bool classof(const Gadget *G) {
1142 return G->getKind() == Kind::ArraySubscript;
1143 }
1144
1145 static Matcher matcher() {
1146 // clang-format off
1147 return stmt(arraySubscriptExpr(
1148 hasBase(ignoringParenImpCasts(
1150 unless(anyOf(
1151 isSafeArraySubscript(),
1152 hasIndex(
1154 )
1155 ))).bind(ArraySubscrTag));
1156 // clang-format on
1157 }
1158
1159 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1160 bool IsRelatedToDecl,
1161 ASTContext &Ctx) const override {
1162 Handler.handleUnsafeOperation(ASE, IsRelatedToDecl, Ctx);
1163 }
1164 SourceLocation getSourceLoc() const override { return ASE->getBeginLoc(); }
1165
1166 DeclUseList getClaimedVarUseSites() const override {
1167 if (const auto *DRE =
1168 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) {
1169 return {DRE};
1170 }
1171
1172 return {};
1173 }
1174};
1175
1176/// A pointer arithmetic expression of one of the forms:
1177/// \code
1178/// ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n
1179/// \endcode
1180class PointerArithmeticGadget : public WarningGadget {
1181 static constexpr const char *const PointerArithmeticTag = "ptrAdd";
1182 static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr";
1183 const BinaryOperator *PA; // pointer arithmetic expression
1184 const Expr *Ptr; // the pointer expression in `PA`
1185
1186public:
1187 PointerArithmeticGadget(const MatchFinder::MatchResult &Result)
1188 : WarningGadget(Kind::PointerArithmetic),
1189 PA(Result.Nodes.getNodeAs<BinaryOperator>(PointerArithmeticTag)),
1190 Ptr(Result.Nodes.getNodeAs<Expr>(PointerArithmeticPointerTag)) {}
1191
1192 static bool classof(const Gadget *G) {
1193 return G->getKind() == Kind::PointerArithmetic;
1194 }
1195
1196 static Matcher matcher() {
1197 auto HasIntegerType = anyOf(hasType(isInteger()), hasType(enumType()));
1198 auto PtrAtRight =
1199 allOf(hasOperatorName("+"),
1200 hasRHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
1201 hasLHS(HasIntegerType));
1202 auto PtrAtLeft =
1203 allOf(anyOf(hasOperatorName("+"), hasOperatorName("-"),
1204 hasOperatorName("+="), hasOperatorName("-=")),
1205 hasLHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
1206 hasRHS(HasIntegerType));
1207
1208 return stmt(binaryOperator(anyOf(PtrAtLeft, PtrAtRight))
1209 .bind(PointerArithmeticTag));
1210 }
1211
1212 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1213 bool IsRelatedToDecl,
1214 ASTContext &Ctx) const override {
1215 Handler.handleUnsafeOperation(PA, IsRelatedToDecl, Ctx);
1216 }
1217 SourceLocation getSourceLoc() const override { return PA->getBeginLoc(); }
1218
1219 DeclUseList getClaimedVarUseSites() const override {
1220 if (const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
1221 return {DRE};
1222 }
1223
1224 return {};
1225 }
1226 // FIXME: pointer adding zero should be fine
1227 // FIXME: this gadge will need a fix-it
1228};
1229
1230class SpanTwoParamConstructorGadget : public WarningGadget {
1231 static constexpr const char *const SpanTwoParamConstructorTag =
1232 "spanTwoParamConstructor";
1233 const CXXConstructExpr *Ctor; // the span constructor expression
1234
1235public:
1236 SpanTwoParamConstructorGadget(const MatchFinder::MatchResult &Result)
1237 : WarningGadget(Kind::SpanTwoParamConstructor),
1238 Ctor(Result.Nodes.getNodeAs<CXXConstructExpr>(
1239 SpanTwoParamConstructorTag)) {}
1240
1241 static bool classof(const Gadget *G) {
1242 return G->getKind() == Kind::SpanTwoParamConstructor;
1243 }
1244
1245 static Matcher matcher() {
1246 auto HasTwoParamSpanCtorDecl = hasDeclaration(
1247 cxxConstructorDecl(hasDeclContext(isInStdNamespace()), hasName("span"),
1248 parameterCountIs(2)));
1249
1250 return stmt(cxxConstructExpr(HasTwoParamSpanCtorDecl,
1251 unless(isSafeSpanTwoParamConstruct()))
1252 .bind(SpanTwoParamConstructorTag));
1253 }
1254
1255 static Matcher matcher(const UnsafeBufferUsageHandler *Handler) {
1256 return stmt(unless(ignoreUnsafeBufferInContainer(Handler)), matcher());
1257 }
1258
1259 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1260 bool IsRelatedToDecl,
1261 ASTContext &Ctx) const override {
1262 Handler.handleUnsafeOperationInContainer(Ctor, IsRelatedToDecl, Ctx);
1263 }
1264 SourceLocation getSourceLoc() const override { return Ctor->getBeginLoc(); }
1265
1266 DeclUseList getClaimedVarUseSites() const override {
1267 // If the constructor call is of the form `std::span{var, n}`, `var` is
1268 // considered an unsafe variable.
1269 if (auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
1270 if (isa<VarDecl>(DRE->getDecl()))
1271 return {DRE};
1272 }
1273 return {};
1274 }
1275};
1276
1277/// A pointer initialization expression of the form:
1278/// \code
1279/// int *p = q;
1280/// \endcode
1281class PointerInitGadget : public FixableGadget {
1282private:
1283 static constexpr const char *const PointerInitLHSTag = "ptrInitLHS";
1284 static constexpr const char *const PointerInitRHSTag = "ptrInitRHS";
1285 const VarDecl *PtrInitLHS; // the LHS pointer expression in `PI`
1286 const DeclRefExpr *PtrInitRHS; // the RHS pointer expression in `PI`
1287
1288public:
1289 PointerInitGadget(const MatchFinder::MatchResult &Result)
1290 : FixableGadget(Kind::PointerInit),
1291 PtrInitLHS(Result.Nodes.getNodeAs<VarDecl>(PointerInitLHSTag)),
1292 PtrInitRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}
1293
1294 static bool classof(const Gadget *G) {
1295 return G->getKind() == Kind::PointerInit;
1296 }
1297
1298 static Matcher matcher() {
1299 auto PtrInitStmt = declStmt(hasSingleDecl(
1300 varDecl(hasInitializer(ignoringImpCasts(
1301 declRefExpr(hasPointerType(), toSupportedVariable())
1302 .bind(PointerInitRHSTag))))
1303 .bind(PointerInitLHSTag)));
1304
1305 return stmt(PtrInitStmt);
1306 }
1307
1308 virtual std::optional<FixItList>
1309 getFixits(const FixitStrategy &S) const override;
1310 SourceLocation getSourceLoc() const override {
1311 return PtrInitRHS->getBeginLoc();
1312 }
1313
1314 virtual DeclUseList getClaimedVarUseSites() const override {
1315 return DeclUseList{PtrInitRHS};
1316 }
1317
1318 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1319 getStrategyImplications() const override {
1320 return std::make_pair(PtrInitLHS, cast<VarDecl>(PtrInitRHS->getDecl()));
1321 }
1322};
1323
1324/// A pointer assignment expression of the form:
1325/// \code
1326/// p = q;
1327/// \endcode
1328/// where both `p` and `q` are pointers.
1329class PtrToPtrAssignmentGadget : public FixableGadget {
1330private:
1331 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
1332 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
1333 const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
1334 const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
1335
1336public:
1337 PtrToPtrAssignmentGadget(const MatchFinder::MatchResult &Result)
1338 : FixableGadget(Kind::PtrToPtrAssignment),
1339 PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
1340 PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
1341
1342 static bool classof(const Gadget *G) {
1343 return G->getKind() == Kind::PtrToPtrAssignment;
1344 }
1345
1346 static Matcher matcher() {
1347 auto PtrAssignExpr = binaryOperator(
1348 allOf(hasOperatorName("="),
1349 hasRHS(ignoringParenImpCasts(
1350 declRefExpr(hasPointerType(), toSupportedVariable())
1351 .bind(PointerAssignRHSTag))),
1352 hasLHS(declRefExpr(hasPointerType(), toSupportedVariable())
1353 .bind(PointerAssignLHSTag))));
1354
1355 return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr));
1356 }
1357
1358 virtual std::optional<FixItList>
1359 getFixits(const FixitStrategy &S) const override;
1360 SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
1361
1362 virtual DeclUseList getClaimedVarUseSites() const override {
1363 return DeclUseList{PtrLHS, PtrRHS};
1364 }
1365
1366 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1367 getStrategyImplications() const override {
1368 return std::make_pair(cast<VarDecl>(PtrLHS->getDecl()),
1369 cast<VarDecl>(PtrRHS->getDecl()));
1370 }
1371};
1372
1373/// An assignment expression of the form:
1374/// \code
1375/// ptr = array;
1376/// \endcode
1377/// where `p` is a pointer and `array` is a constant size array.
1378class CArrayToPtrAssignmentGadget : public FixableGadget {
1379private:
1380 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
1381 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
1382 const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
1383 const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
1384
1385public:
1386 CArrayToPtrAssignmentGadget(const MatchFinder::MatchResult &Result)
1387 : FixableGadget(Kind::CArrayToPtrAssignment),
1388 PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
1389 PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
1390
1391 static bool classof(const Gadget *G) {
1392 return G->getKind() == Kind::CArrayToPtrAssignment;
1393 }
1394
1395 static Matcher matcher() {
1396 auto PtrAssignExpr = binaryOperator(
1397 allOf(hasOperatorName("="),
1398 hasRHS(ignoringParenImpCasts(
1399 declRefExpr(hasType(hasCanonicalType(constantArrayType())),
1400 toSupportedVariable())
1401 .bind(PointerAssignRHSTag))),
1402 hasLHS(declRefExpr(hasPointerType(), toSupportedVariable())
1403 .bind(PointerAssignLHSTag))));
1404
1405 return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr));
1406 }
1407
1408 virtual std::optional<FixItList>
1409 getFixits(const FixitStrategy &S) const override;
1410 SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
1411
1412 virtual DeclUseList getClaimedVarUseSites() const override {
1413 return DeclUseList{PtrLHS, PtrRHS};
1414 }
1415
1416 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1417 getStrategyImplications() const override {
1418 return {};
1419 }
1420};
1421
1422/// A call of a function or method that performs unchecked buffer operations
1423/// over one of its pointer parameters.
1424class UnsafeBufferUsageAttrGadget : public WarningGadget {
1425 constexpr static const char *const OpTag = "attr_expr";
1426 const Expr *Op;
1427
1428public:
1429 UnsafeBufferUsageAttrGadget(const MatchFinder::MatchResult &Result)
1430 : WarningGadget(Kind::UnsafeBufferUsageAttr),
1431 Op(Result.Nodes.getNodeAs<Expr>(OpTag)) {}
1432
1433 static bool classof(const Gadget *G) {
1434 return G->getKind() == Kind::UnsafeBufferUsageAttr;
1435 }
1436
1437 static Matcher matcher() {
1438 auto HasUnsafeFieldDecl =
1439 member(fieldDecl(hasAttr(attr::UnsafeBufferUsage)));
1440
1441 auto HasUnsafeFnDecl =
1442 callee(functionDecl(hasAttr(attr::UnsafeBufferUsage)));
1443
1444 return stmt(anyOf(callExpr(HasUnsafeFnDecl).bind(OpTag),
1445 memberExpr(HasUnsafeFieldDecl).bind(OpTag)));
1446 }
1447
1448 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1449 bool IsRelatedToDecl,
1450 ASTContext &Ctx) const override {
1451 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1452 }
1453 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1454
1455 DeclUseList getClaimedVarUseSites() const override { return {}; }
1456};
1457
1458/// A call of a constructor that performs unchecked buffer operations
1459/// over one of its pointer parameters, or constructs a class object that will
1460/// perform buffer operations that depend on the correctness of the parameters.
1461class UnsafeBufferUsageCtorAttrGadget : public WarningGadget {
1462 constexpr static const char *const OpTag = "cxx_construct_expr";
1463 const CXXConstructExpr *Op;
1464
1465public:
1466 UnsafeBufferUsageCtorAttrGadget(const MatchFinder::MatchResult &Result)
1467 : WarningGadget(Kind::UnsafeBufferUsageCtorAttr),
1468 Op(Result.Nodes.getNodeAs<CXXConstructExpr>(OpTag)) {}
1469
1470 static bool classof(const Gadget *G) {
1471 return G->getKind() == Kind::UnsafeBufferUsageCtorAttr;
1472 }
1473
1474 static Matcher matcher() {
1475 auto HasUnsafeCtorDecl =
1476 hasDeclaration(cxxConstructorDecl(hasAttr(attr::UnsafeBufferUsage)));
1477 // std::span(ptr, size) ctor is handled by SpanTwoParamConstructorGadget.
1478 auto HasTwoParamSpanCtorDecl = SpanTwoParamConstructorGadget::matcher();
1479 return stmt(
1480 cxxConstructExpr(HasUnsafeCtorDecl, unless(HasTwoParamSpanCtorDecl))
1481 .bind(OpTag));
1482 }
1483
1484 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1485 bool IsRelatedToDecl,
1486 ASTContext &Ctx) const override {
1487 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1488 }
1489 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1490
1491 DeclUseList getClaimedVarUseSites() const override { return {}; }
1492};
1493
1494// Warning gadget for unsafe invocation of span::data method.
1495// Triggers when the pointer returned by the invocation is immediately
1496// cast to a larger type.
1497
1498class DataInvocationGadget : public WarningGadget {
1499 constexpr static const char *const OpTag = "data_invocation_expr";
1500 const ExplicitCastExpr *Op;
1501
1502public:
1503 DataInvocationGadget(const MatchFinder::MatchResult &Result)
1504 : WarningGadget(Kind::DataInvocation),
1505 Op(Result.Nodes.getNodeAs<ExplicitCastExpr>(OpTag)) {}
1506
1507 static bool classof(const Gadget *G) {
1508 return G->getKind() == Kind::DataInvocation;
1509 }
1510
1511 static Matcher matcher() {
1512
1513 Matcher callExpr = cxxMemberCallExpr(callee(
1514 cxxMethodDecl(hasName("data"),
1515 ofClass(anyOf(hasName("std::span"), hasName("std::array"),
1516 hasName("std::vector"))))));
1517 return stmt(
1519 .bind(OpTag));
1520 }
1521
1522 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1523 bool IsRelatedToDecl,
1524 ASTContext &Ctx) const override {
1525 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1526 }
1527 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1528
1529 DeclUseList getClaimedVarUseSites() const override { return {}; }
1530};
1531
1532class UnsafeLibcFunctionCallGadget : public WarningGadget {
1533 const CallExpr *const Call;
1534 const Expr *UnsafeArg = nullptr;
1535 constexpr static const char *const Tag = "UnsafeLibcFunctionCall";
1536 // Extra tags for additional information:
1537 constexpr static const char *const UnsafeSprintfTag =
1538 "UnsafeLibcFunctionCall_sprintf";
1539 constexpr static const char *const UnsafeSizedByTag =
1540 "UnsafeLibcFunctionCall_sized_by";
1541 constexpr static const char *const UnsafeStringTag =
1542 "UnsafeLibcFunctionCall_string";
1543 constexpr static const char *const UnsafeVaListTag =
1544 "UnsafeLibcFunctionCall_va_list";
1545
1546 enum UnsafeKind {
1547 OTHERS = 0, // no specific information, the callee function is unsafe
1548 SPRINTF = 1, // never call `-sprintf`s, call `-snprintf`s instead.
1549 SIZED_BY =
1550 2, // the first two arguments of `snprintf` function have
1551 // "__sized_by" relation but they do not conform to safe patterns
1552 STRING = 3, // an argument is a pointer-to-char-as-string but does not
1553 // guarantee null-termination
1554 VA_LIST = 4, // one of the `-printf`s function that take va_list, which is
1555 // considered unsafe as it is not compile-time check
1556 } WarnedFunKind = OTHERS;
1557
1558public:
1559 UnsafeLibcFunctionCallGadget(const MatchFinder::MatchResult &Result)
1560 : WarningGadget(Kind::UnsafeLibcFunctionCall),
1561 Call(Result.Nodes.getNodeAs<CallExpr>(Tag)) {
1562 if (Result.Nodes.getNodeAs<Decl>(UnsafeSprintfTag))
1563 WarnedFunKind = SPRINTF;
1564 else if (auto *E = Result.Nodes.getNodeAs<Expr>(UnsafeStringTag)) {
1565 WarnedFunKind = STRING;
1566 UnsafeArg = E;
1567 } else if (Result.Nodes.getNodeAs<CallExpr>(UnsafeSizedByTag)) {
1568 WarnedFunKind = SIZED_BY;
1569 UnsafeArg = Call->getArg(0);
1570 } else if (Result.Nodes.getNodeAs<Decl>(UnsafeVaListTag))
1571 WarnedFunKind = VA_LIST;
1572 }
1573
1574 static Matcher matcher(const UnsafeBufferUsageHandler *Handler) {
1575 return stmt(unless(ignoreUnsafeLibcCall(Handler)),
1576 anyOf(
1577 callExpr(
1578 callee(functionDecl(anyOf(
1579 // Match a predefined unsafe libc
1580 // function:
1581 functionDecl(libc_func_matchers::isPredefinedUnsafeLibcFunc()),
1582 // Match a call to one of the `v*printf` functions
1583 // taking va-list, which cannot be checked at
1584 // compile-time:
1585 functionDecl(libc_func_matchers::isUnsafeVaListPrintfFunc())
1586 .bind(UnsafeVaListTag),
1587 // Match a call to a `sprintf` function, which is never
1588 // safe:
1589 functionDecl(libc_func_matchers::isUnsafeSprintfFunc())
1590 .bind(UnsafeSprintfTag)))),
1591 // (unless the call has a sole string literal argument):
1592 unless(
1593 allOf(hasArgument(0, expr(stringLiteral())), hasNumArgs(1)))),
1594
1595 // The following two cases require checking against actual
1596 // arguments of the call:
1597
1598 // Match a call to an `snprintf` function. And first two
1599 // arguments of the call (that describe a buffer) are not in
1600 // safe patterns:
1601 callExpr(callee(functionDecl(libc_func_matchers::isNormalPrintfFunc())),
1602 libc_func_matchers::hasUnsafeSnprintfBuffer())
1603 .bind(UnsafeSizedByTag),
1604 // Match a call to a `printf` function, which can be safe if
1605 // all arguments are null-terminated:
1606 callExpr(callee(functionDecl(libc_func_matchers::isNormalPrintfFunc())),
1607 libc_func_matchers::hasUnsafePrintfStringArg(
1608 expr().bind(UnsafeStringTag)))));
1609 }
1610
1611 const Stmt *getBaseStmt() const { return Call; }
1612
1613 SourceLocation getSourceLoc() const override { return Call->getBeginLoc(); }
1614
1615 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1616 bool IsRelatedToDecl,
1617 ASTContext &Ctx) const override {
1618 Handler.handleUnsafeLibcCall(Call, WarnedFunKind, Ctx, UnsafeArg);
1619 }
1620
1621 DeclUseList getClaimedVarUseSites() const override { return {}; }
1622};
1623
1624// Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
1625// Context (see `isInUnspecifiedLvalueContext`).
1626// Note here `[]` is the built-in subscript operator.
1627class ULCArraySubscriptGadget : public FixableGadget {
1628private:
1629 static constexpr const char *const ULCArraySubscriptTag =
1630 "ArraySubscriptUnderULC";
1631 const ArraySubscriptExpr *Node;
1632
1633public:
1634 ULCArraySubscriptGadget(const MatchFinder::MatchResult &Result)
1635 : FixableGadget(Kind::ULCArraySubscript),
1636 Node(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) {
1637 assert(Node != nullptr && "Expecting a non-null matching result");
1638 }
1639
1640 static bool classof(const Gadget *G) {
1641 return G->getKind() == Kind::ULCArraySubscript;
1642 }
1643
1644 static Matcher matcher() {
1645 auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType());
1646 auto BaseIsArrayOrPtrDRE = hasBase(
1647 ignoringParenImpCasts(declRefExpr(ArrayOrPtr, toSupportedVariable())));
1648 auto Target =
1649 arraySubscriptExpr(BaseIsArrayOrPtrDRE).bind(ULCArraySubscriptTag);
1650
1652 }
1653
1654 virtual std::optional<FixItList>
1655 getFixits(const FixitStrategy &S) const override;
1656 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1657
1658 virtual DeclUseList getClaimedVarUseSites() const override {
1659 if (const auto *DRE =
1660 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) {
1661 return {DRE};
1662 }
1663 return {};
1664 }
1665};
1666
1667// Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the
1668// unspecified pointer context (isInUnspecifiedPointerContext). The gadget emits
1669// fixit of the form `UPC(DRE.data())`.
1670class UPCStandalonePointerGadget : public FixableGadget {
1671private:
1672 static constexpr const char *const DeclRefExprTag = "StandalonePointer";
1673 const DeclRefExpr *Node;
1674
1675public:
1676 UPCStandalonePointerGadget(const MatchFinder::MatchResult &Result)
1677 : FixableGadget(Kind::UPCStandalonePointer),
1678 Node(Result.Nodes.getNodeAs<DeclRefExpr>(DeclRefExprTag)) {
1679 assert(Node != nullptr && "Expecting a non-null matching result");
1680 }
1681
1682 static bool classof(const Gadget *G) {
1683 return G->getKind() == Kind::UPCStandalonePointer;
1684 }
1685
1686 static Matcher matcher() {
1687 auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType());
1688 auto target = expr(ignoringParenImpCasts(
1689 declRefExpr(allOf(ArrayOrPtr, toSupportedVariable()))
1690 .bind(DeclRefExprTag)));
1691 return stmt(isInUnspecifiedPointerContext(target));
1692 }
1693
1694 virtual std::optional<FixItList>
1695 getFixits(const FixitStrategy &S) const override;
1696 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1697
1698 virtual DeclUseList getClaimedVarUseSites() const override { return {Node}; }
1699};
1700
1701class PointerDereferenceGadget : public FixableGadget {
1702 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
1703 static constexpr const char *const OperatorTag = "op";
1704
1705 const DeclRefExpr *BaseDeclRefExpr = nullptr;
1706 const UnaryOperator *Op = nullptr;
1707
1708public:
1709 PointerDereferenceGadget(const MatchFinder::MatchResult &Result)
1710 : FixableGadget(Kind::PointerDereference),
1711 BaseDeclRefExpr(
1712 Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
1713 Op(Result.Nodes.getNodeAs<UnaryOperator>(OperatorTag)) {}
1714
1715 static bool classof(const Gadget *G) {
1716 return G->getKind() == Kind::PointerDereference;
1717 }
1718
1719 static Matcher matcher() {
1720 auto Target =
1722 hasOperatorName("*"),
1723 has(expr(ignoringParenImpCasts(
1724 declRefExpr(toSupportedVariable()).bind(BaseDeclRefExprTag)))))
1725 .bind(OperatorTag);
1726
1728 }
1729
1730 DeclUseList getClaimedVarUseSites() const override {
1731 return {BaseDeclRefExpr};
1732 }
1733
1734 virtual std::optional<FixItList>
1735 getFixits(const FixitStrategy &S) const override;
1736 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1737};
1738
1739// Represents expressions of the form `&DRE[any]` in the Unspecified Pointer
1740// Context (see `isInUnspecifiedPointerContext`).
1741// Note here `[]` is the built-in subscript operator.
1742class UPCAddressofArraySubscriptGadget : public FixableGadget {
1743private:
1744 static constexpr const char *const UPCAddressofArraySubscriptTag =
1745 "AddressofArraySubscriptUnderUPC";
1746 const UnaryOperator *Node; // the `&DRE[any]` node
1747
1748public:
1749 UPCAddressofArraySubscriptGadget(const MatchFinder::MatchResult &Result)
1750 : FixableGadget(Kind::ULCArraySubscript),
1751 Node(Result.Nodes.getNodeAs<UnaryOperator>(
1752 UPCAddressofArraySubscriptTag)) {
1753 assert(Node != nullptr && "Expecting a non-null matching result");
1754 }
1755
1756 static bool classof(const Gadget *G) {
1757 return G->getKind() == Kind::UPCAddressofArraySubscript;
1758 }
1759
1760 static Matcher matcher() {
1761 return expr(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
1763 hasOperatorName("&"),
1764 hasUnaryOperand(arraySubscriptExpr(hasBase(
1765 ignoringParenImpCasts(declRefExpr(toSupportedVariable()))))))
1766 .bind(UPCAddressofArraySubscriptTag)))));
1767 }
1768
1769 virtual std::optional<FixItList>
1770 getFixits(const FixitStrategy &) const override;
1771 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1772
1773 virtual DeclUseList getClaimedVarUseSites() const override {
1774 const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr());
1775 const auto *DRE =
1776 cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreParenImpCasts());
1777 return {DRE};
1778 }
1779};
1780} // namespace
1781
1782namespace {
1783// An auxiliary tracking facility for the fixit analysis. It helps connect
1784// declarations to its uses and make sure we've covered all uses with our
1785// analysis before we try to fix the declaration.
1786class DeclUseTracker {
1787 using UseSetTy = SmallSet<const DeclRefExpr *, 16>;
1788 using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>;
1789
1790 // Allocate on the heap for easier move.
1791 std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
1792 DefMapTy Defs{};
1793
1794public:
1795 DeclUseTracker() = default;
1796 DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies.
1797 DeclUseTracker &operator=(const DeclUseTracker &) = delete;
1798 DeclUseTracker(DeclUseTracker &&) = default;
1799 DeclUseTracker &operator=(DeclUseTracker &&) = default;
1800
1801 // Start tracking a freshly discovered DRE.
1802 void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }
1803
1804 // Stop tracking the DRE as it's been fully figured out.
1805 void claimUse(const DeclRefExpr *DRE) {
1806 assert(Uses->count(DRE) &&
1807 "DRE not found or claimed by multiple matchers!");
1808 Uses->erase(DRE);
1809 }
1810
1811 // A variable is unclaimed if at least one use is unclaimed.
1812 bool hasUnclaimedUses(const VarDecl *VD) const {
1813 // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs?
1814 return any_of(*Uses, [VD](const DeclRefExpr *DRE) {
1815 return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl();
1816 });
1817 }
1818
1819 UseSetTy getUnclaimedUses(const VarDecl *VD) const {
1820 UseSetTy ReturnSet;
1821 for (auto use : *Uses) {
1822 if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) {
1823 ReturnSet.insert(use);
1824 }
1825 }
1826 return ReturnSet;
1827 }
1828
1829 void discoverDecl(const DeclStmt *DS) {
1830 for (const Decl *D : DS->decls()) {
1831 if (const auto *VD = dyn_cast<VarDecl>(D)) {
1832 // FIXME: Assertion temporarily disabled due to a bug in
1833 // ASTMatcher internal behavior in presence of GNU
1834 // statement-expressions. We need to properly investigate this
1835 // because it can screw up our algorithm in other ways.
1836 // assert(Defs.count(VD) == 0 && "Definition already discovered!");
1837 Defs[VD] = DS;
1838 }
1839 }
1840 }
1841
1842 const DeclStmt *lookupDecl(const VarDecl *VD) const {
1843 return Defs.lookup(VD);
1844 }
1845};
1846} // namespace
1847
1848// Representing a pointer type expression of the form `++Ptr` in an Unspecified
1849// Pointer Context (UPC):
1850class UPCPreIncrementGadget : public FixableGadget {
1851private:
1852 static constexpr const char *const UPCPreIncrementTag =
1853 "PointerPreIncrementUnderUPC";
1854 const UnaryOperator *Node; // the `++Ptr` node
1855
1856public:
1858 : FixableGadget(Kind::UPCPreIncrement),
1859 Node(Result.Nodes.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) {
1860 assert(Node != nullptr && "Expecting a non-null matching result");
1861 }
1862
1863 static bool classof(const Gadget *G) {
1864 return G->getKind() == Kind::UPCPreIncrement;
1865 }
1866
1867 static Matcher matcher() {
1868 // Note here we match `++Ptr` for any expression `Ptr` of pointer type.
1869 // Although currently we can only provide fix-its when `Ptr` is a DRE, we
1870 // can have the matcher be general, so long as `getClaimedVarUseSites` does
1871 // things right.
1872 return stmt(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
1873 unaryOperator(isPreInc(),
1874 hasUnaryOperand(declRefExpr(toSupportedVariable())))
1875 .bind(UPCPreIncrementTag)))));
1876 }
1877
1878 virtual std::optional<FixItList>
1879 getFixits(const FixitStrategy &S) const override;
1880 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1881
1882 virtual DeclUseList getClaimedVarUseSites() const override {
1883 return {dyn_cast<DeclRefExpr>(Node->getSubExpr())};
1884 }
1885};
1886
1887// Representing a pointer type expression of the form `Ptr += n` in an
1888// Unspecified Untyped Context (UUC):
1889class UUCAddAssignGadget : public FixableGadget {
1890private:
1891 static constexpr const char *const UUCAddAssignTag =
1892 "PointerAddAssignUnderUUC";
1893 static constexpr const char *const OffsetTag = "Offset";
1894
1895 const BinaryOperator *Node; // the `Ptr += n` node
1896 const Expr *Offset = nullptr;
1897
1898public:
1900 : FixableGadget(Kind::UUCAddAssign),
1901 Node(Result.Nodes.getNodeAs<BinaryOperator>(UUCAddAssignTag)),
1902 Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) {
1903 assert(Node != nullptr && "Expecting a non-null matching result");
1904 }
1905
1906 static bool classof(const Gadget *G) {
1907 return G->getKind() == Kind::UUCAddAssign;
1908 }
1909
1910 static Matcher matcher() {
1911 // clang-format off
1912 return stmt(isInUnspecifiedUntypedContext(expr(ignoringImpCasts(
1913 binaryOperator(hasOperatorName("+="),
1914 hasLHS(
1917 toSupportedVariable())),
1918 hasRHS(expr().bind(OffsetTag)))
1919 .bind(UUCAddAssignTag)))));
1920 // clang-format on
1921 }
1922
1923 virtual std::optional<FixItList>
1924 getFixits(const FixitStrategy &S) const override;
1925 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1926
1927 virtual DeclUseList getClaimedVarUseSites() const override {
1928 return {dyn_cast<DeclRefExpr>(Node->getLHS())};
1929 }
1930};
1931
1932// Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
1933// ptr)`:
1934class DerefSimplePtrArithFixableGadget : public FixableGadget {
1935 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
1936 static constexpr const char *const DerefOpTag = "DerefOp";
1937 static constexpr const char *const AddOpTag = "AddOp";
1938 static constexpr const char *const OffsetTag = "Offset";
1939
1940 const DeclRefExpr *BaseDeclRefExpr = nullptr;
1941 const UnaryOperator *DerefOp = nullptr;
1942 const BinaryOperator *AddOp = nullptr;
1943 const IntegerLiteral *Offset = nullptr;
1944
1945public:
1947 : FixableGadget(Kind::DerefSimplePtrArithFixable),
1948 BaseDeclRefExpr(
1949 Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
1950 DerefOp(Result.Nodes.getNodeAs<UnaryOperator>(DerefOpTag)),
1951 AddOp(Result.Nodes.getNodeAs<BinaryOperator>(AddOpTag)),
1952 Offset(Result.Nodes.getNodeAs<IntegerLiteral>(OffsetTag)) {}
1953
1954 static Matcher matcher() {
1955 // clang-format off
1956 auto ThePtr = expr(hasPointerType(),
1957 ignoringImpCasts(declRefExpr(toSupportedVariable()).
1958 bind(BaseDeclRefExprTag)));
1959 auto PlusOverPtrAndInteger = expr(anyOf(
1960 binaryOperator(hasOperatorName("+"), hasLHS(ThePtr),
1961 hasRHS(integerLiteral().bind(OffsetTag)))
1962 .bind(AddOpTag),
1963 binaryOperator(hasOperatorName("+"), hasRHS(ThePtr),
1964 hasLHS(integerLiteral().bind(OffsetTag)))
1965 .bind(AddOpTag)));
1967 hasOperatorName("*"),
1968 hasUnaryOperand(ignoringParens(PlusOverPtrAndInteger)))
1969 .bind(DerefOpTag));
1970 // clang-format on
1971 }
1972
1973 virtual std::optional<FixItList>
1974 getFixits(const FixitStrategy &s) const final;
1975 SourceLocation getSourceLoc() const override {
1976 return DerefOp->getBeginLoc();
1977 }
1978
1979 virtual DeclUseList getClaimedVarUseSites() const final {
1980 return {BaseDeclRefExpr};
1981 }
1982};
1983
1984/// Scan the function and return a list of gadgets found with provided kits.
1985static void findGadgets(const Stmt *S, ASTContext &Ctx,
1986 const UnsafeBufferUsageHandler &Handler,
1987 bool EmitSuggestions, FixableGadgetList &FixableGadgets,
1988 WarningGadgetList &WarningGadgets,
1989 DeclUseTracker &Tracker) {
1990
1991 struct GadgetFinderCallback : MatchFinder::MatchCallback {
1992 GadgetFinderCallback(FixableGadgetList &FixableGadgets,
1993 WarningGadgetList &WarningGadgets,
1994 DeclUseTracker &Tracker)
1995 : FixableGadgets(FixableGadgets), WarningGadgets(WarningGadgets),
1996 Tracker(Tracker) {}
1997
1998 void run(const MatchFinder::MatchResult &Result) override {
1999 // In debug mode, assert that we've found exactly one gadget.
2000 // This helps us avoid conflicts in .bind() tags.
2001#if NDEBUG
2002#define NEXT return
2003#else
2004 [[maybe_unused]] int numFound = 0;
2005#define NEXT ++numFound
2006#endif
2007
2008 if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("any_dre")) {
2009 Tracker.discoverUse(DRE);
2010 NEXT;
2011 }
2012
2013 if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) {
2014 Tracker.discoverDecl(DS);
2015 NEXT;
2016 }
2017
2018 // Figure out which matcher we've found, and call the appropriate
2019 // subclass constructor.
2020 // FIXME: Can we do this more logarithmically?
2021#define FIXABLE_GADGET(name) \
2022 if (Result.Nodes.getNodeAs<Stmt>(#name)) { \
2023 FixableGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
2024 NEXT; \
2025 }
2026#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2027#define WARNING_GADGET(name) \
2028 if (Result.Nodes.getNodeAs<Stmt>(#name)) { \
2029 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
2030 NEXT; \
2031 }
2032#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2033
2034 assert(numFound >= 1 && "Gadgets not found in match result!");
2035 assert(numFound <= 1 && "Conflicting bind tags in gadgets!");
2036 }
2037
2038 FixableGadgetList &FixableGadgets;
2039 WarningGadgetList &WarningGadgets;
2040 DeclUseTracker &Tracker;
2041 };
2042
2043 MatchFinder M;
2044 GadgetFinderCallback CB{FixableGadgets, WarningGadgets, Tracker};
2045
2046 // clang-format off
2047 M.addMatcher(
2048 stmt(
2049 forEachDescendantEvaluatedStmt(stmt(anyOf(
2050 // Add Gadget::matcher() for every gadget in the registry.
2051#define WARNING_GADGET(x) \
2052 allOf(x ## Gadget::matcher().bind(#x), \
2053 notInSafeBufferOptOut(&Handler)),
2054#define WARNING_OPTIONAL_GADGET(x) \
2055 allOf(x ## Gadget::matcher(&Handler).bind(#x), \
2056 notInSafeBufferOptOut(&Handler)),
2057#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2058 // Avoid a hanging comma.
2059 unless(stmt())
2060 )))
2061 ),
2062 &CB
2063 );
2064 // clang-format on
2065
2066 if (EmitSuggestions) {
2067 // clang-format off
2068 M.addMatcher(
2069 stmt(
2070 forEachDescendantStmt(stmt(eachOf(
2071#define FIXABLE_GADGET(x) \
2072 x ## Gadget::matcher().bind(#x),
2073#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2074 // In parallel, match all DeclRefExprs so that to find out
2075 // whether there are any uncovered by gadgets.
2077 to(anyOf(varDecl(), bindingDecl()))).bind("any_dre"),
2078 // Also match DeclStmts because we'll need them when fixing
2079 // their underlying VarDecls that otherwise don't have
2080 // any backreferences to DeclStmts.
2081 declStmt().bind("any_ds")
2082 )))
2083 ),
2084 &CB
2085 );
2086 // clang-format on
2087 }
2088
2089 M.match(*S, Ctx);
2090}
2091
2092// Compares AST nodes by source locations.
2093template <typename NodeTy> struct CompareNode {
2094 bool operator()(const NodeTy *N1, const NodeTy *N2) const {
2095 return N1->getBeginLoc().getRawEncoding() <
2096 N2->getBeginLoc().getRawEncoding();
2097 }
2098};
2099
2101 std::map<const VarDecl *, std::set<const WarningGadget *>,
2102 // To keep keys sorted by their locations in the map so that the
2103 // order is deterministic:
2106 // These Gadgets are not related to pointer variables (e. g. temporaries).
2108};
2109
2110static WarningGadgetSets
2111groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) {
2112 WarningGadgetSets result;
2113 // If some gadgets cover more than one
2114 // variable, they'll appear more than once in the map.
2115 for (auto &G : AllUnsafeOperations) {
2116 DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
2117
2118 bool AssociatedWithVarDecl = false;
2119 for (const DeclRefExpr *DRE : ClaimedVarUseSites) {
2120 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
2121 result.byVar[VD].insert(G.get());
2122 AssociatedWithVarDecl = true;
2123 }
2124 }
2125
2126 if (!AssociatedWithVarDecl) {
2127 result.noVar.push_back(G.get());
2128 continue;
2129 }
2130 }
2131 return result;
2132}
2133
2135 std::map<const VarDecl *, std::set<const FixableGadget *>,
2136 // To keep keys sorted by their locations in the map so that the
2137 // order is deterministic:
2140};
2141
2142static FixableGadgetSets
2143groupFixablesByVar(FixableGadgetList &&AllFixableOperations) {
2144 FixableGadgetSets FixablesForUnsafeVars;
2145 for (auto &F : AllFixableOperations) {
2146 DeclUseList DREs = F->getClaimedVarUseSites();
2147
2148 for (const DeclRefExpr *DRE : DREs) {
2149 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
2150 FixablesForUnsafeVars.byVar[VD].insert(F.get());
2151 }
2152 }
2153 }
2154 return FixablesForUnsafeVars;
2155}
2156
2158 const SourceManager &SM) {
2159 // A simple interval overlap detection algorithm. Sorts all ranges by their
2160 // begin location then finds the first overlap in one pass.
2161 std::vector<const FixItHint *> All; // a copy of `FixIts`
2162
2163 for (const FixItHint &H : FixIts)
2164 All.push_back(&H);
2165 std::sort(All.begin(), All.end(),
2166 [&SM](const FixItHint *H1, const FixItHint *H2) {
2167 return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
2168 H2->RemoveRange.getBegin());
2169 });
2170
2171 const FixItHint *CurrHint = nullptr;
2172
2173 for (const FixItHint *Hint : All) {
2174 if (!CurrHint ||
2175 SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(),
2176 Hint->RemoveRange.getBegin())) {
2177 // Either to initialize `CurrHint` or `CurrHint` does not
2178 // overlap with `Hint`:
2179 CurrHint = Hint;
2180 } else
2181 // In case `Hint` overlaps the `CurrHint`, we found at least one
2182 // conflict:
2183 return true;
2184 }
2185 return false;
2186}
2187
2188std::optional<FixItList>
2189PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
2190 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
2191 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
2192 switch (S.lookup(LeftVD)) {
2193 case FixitStrategy::Kind::Span:
2194 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
2195 return FixItList{};
2196 return std::nullopt;
2197 case FixitStrategy::Kind::Wontfix:
2198 return std::nullopt;
2199 case FixitStrategy::Kind::Iterator:
2200 case FixitStrategy::Kind::Array:
2201 return std::nullopt;
2202 case FixitStrategy::Kind::Vector:
2203 llvm_unreachable("unsupported strategies for FixableGadgets");
2204 }
2205 return std::nullopt;
2206}
2207
2208/// \returns fixit that adds .data() call after \DRE.
2209static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
2210 const DeclRefExpr *DRE);
2211
2212std::optional<FixItList>
2213CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
2214 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
2215 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
2216 // TLDR: Implementing fixits for non-Wontfix strategy on both LHS and RHS is
2217 // non-trivial.
2218 //
2219 // CArrayToPtrAssignmentGadget doesn't have strategy implications because
2220 // constant size array propagates its bounds. Because of that LHS and RHS are
2221 // addressed by two different fixits.
2222 //
2223 // At the same time FixitStrategy S doesn't reflect what group a fixit belongs
2224 // to and can't be generally relied on in multi-variable Fixables!
2225 //
2226 // E. g. If an instance of this gadget is fixing variable on LHS then the
2227 // variable on RHS is fixed by a different fixit and its strategy for LHS
2228 // fixit is as if Wontfix.
2229 //
2230 // The only exception is Wontfix strategy for a given variable as that is
2231 // valid for any fixit produced for the given input source code.
2232 if (S.lookup(LeftVD) == FixitStrategy::Kind::Span) {
2233 if (S.lookup(RightVD) == FixitStrategy::Kind::Wontfix) {
2234 return FixItList{};
2235 }
2236 } else if (S.lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {
2237 if (S.lookup(RightVD) == FixitStrategy::Kind::Array) {
2238 return createDataFixit(RightVD->getASTContext(), PtrRHS);
2239 }
2240 }
2241 return std::nullopt;
2242}
2243
2244std::optional<FixItList>
2245PointerInitGadget::getFixits(const FixitStrategy &S) const {
2246 const auto *LeftVD = PtrInitLHS;
2247 const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl());
2248 switch (S.lookup(LeftVD)) {
2249 case FixitStrategy::Kind::Span:
2250 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
2251 return FixItList{};
2252 return std::nullopt;
2253 case FixitStrategy::Kind::Wontfix:
2254 return std::nullopt;
2255 case FixitStrategy::Kind::Iterator:
2256 case FixitStrategy::Kind::Array:
2257 return std::nullopt;
2258 case FixitStrategy::Kind::Vector:
2259 llvm_unreachable("unsupported strategies for FixableGadgets");
2260 }
2261 return std::nullopt;
2262}
2263
2264static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD,
2265 const ASTContext &Ctx) {
2266 if (auto ConstVal = Expr->getIntegerConstantExpr(Ctx)) {
2267 if (ConstVal->isNegative())
2268 return false;
2269 } else if (!Expr->getType()->isUnsignedIntegerType())
2270 return false;
2271 return true;
2272}
2273
2274std::optional<FixItList>
2275ULCArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
2276 if (const auto *DRE =
2277 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts()))
2278 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
2279 switch (S.lookup(VD)) {
2280 case FixitStrategy::Kind::Span: {
2281
2282 // If the index has a negative constant value, we give up as no valid
2283 // fix-it can be generated:
2284 const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
2285 VD->getASTContext();
2286 if (!isNonNegativeIntegerExpr(Node->getIdx(), VD, Ctx))
2287 return std::nullopt;
2288 // no-op is a good fix-it, otherwise
2289 return FixItList{};
2290 }
2291 case FixitStrategy::Kind::Array:
2292 return FixItList{};
2293 case FixitStrategy::Kind::Wontfix:
2294 case FixitStrategy::Kind::Iterator:
2295 case FixitStrategy::Kind::Vector:
2296 llvm_unreachable("unsupported strategies for FixableGadgets");
2297 }
2298 }
2299 return std::nullopt;
2300}
2301
2302static std::optional<FixItList> // forward declaration
2304
2305std::optional<FixItList>
2306UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
2307 auto DREs = getClaimedVarUseSites();
2308 const auto *VD = cast<VarDecl>(DREs.front()->getDecl());
2309
2310 switch (S.lookup(VD)) {
2311 case FixitStrategy::Kind::Span:
2313 case FixitStrategy::Kind::Wontfix:
2314 case FixitStrategy::Kind::Iterator:
2315 case FixitStrategy::Kind::Array:
2316 return std::nullopt;
2317 case FixitStrategy::Kind::Vector:
2318 llvm_unreachable("unsupported strategies for FixableGadgets");
2319 }
2320 return std::nullopt; // something went wrong, no fix-it
2321}
2322
2323// FIXME: this function should be customizable through format
2324static StringRef getEndOfLine() {
2325 static const char *const EOL = "\n";
2326 return EOL;
2327}
2328
2329// Returns the text indicating that the user needs to provide input there:
2330static std::string
2331getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
2332 std::string s = std::string("<# ");
2333 s += HintTextToUser;
2334 s += " #>";
2335 return s;
2336}
2337
2338// Return the source location of the last character of the AST `Node`.
2339template <typename NodeTy>
2340static std::optional<SourceLocation>
2341getEndCharLoc(const NodeTy *Node, const SourceManager &SM,
2342 const LangOptions &LangOpts) {
2343 unsigned TkLen = Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts);
2344 SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1);
2345
2346 if (Loc.isValid())
2347 return Loc;
2348
2349 return std::nullopt;
2350}
2351
2352// Return the source location just past the last character of the AST `Node`.
2353template <typename NodeTy>
2354static std::optional<SourceLocation> getPastLoc(const NodeTy *Node,
2355 const SourceManager &SM,
2356 const LangOptions &LangOpts) {
2358 Lexer::getLocForEndOfToken(Node->getEndLoc(), 0, SM, LangOpts);
2359 if (Loc.isValid())
2360 return Loc;
2361 return std::nullopt;
2362}
2363
2364// Return text representation of an `Expr`.
2365static std::optional<StringRef> getExprText(const Expr *E,
2366 const SourceManager &SM,
2367 const LangOptions &LangOpts) {
2368 std::optional<SourceLocation> LastCharLoc = getPastLoc(E, SM, LangOpts);
2369
2370 if (LastCharLoc)
2371 return Lexer::getSourceText(
2372 CharSourceRange::getCharRange(E->getBeginLoc(), *LastCharLoc), SM,
2373 LangOpts);
2374
2375 return std::nullopt;
2376}
2377
2378// Returns the literal text in `SourceRange SR`, if `SR` is a valid range.
2379static std::optional<StringRef> getRangeText(SourceRange SR,
2380 const SourceManager &SM,
2381 const LangOptions &LangOpts) {
2382 bool Invalid = false;
2384 StringRef Text = Lexer::getSourceText(CSR, SM, LangOpts, &Invalid);
2385
2386 if (!Invalid)
2387 return Text;
2388 return std::nullopt;
2389}
2390
2391// Returns the begin location of the identifier of the given variable
2392// declaration.
2394 // According to the implementation of `VarDecl`, `VD->getLocation()` actually
2395 // returns the begin location of the identifier of the declaration:
2396 return VD->getLocation();
2397}
2398
2399// Returns the literal text of the identifier of the given variable declaration.
2400static std::optional<StringRef>
2402 const LangOptions &LangOpts) {
2403 SourceLocation ParmIdentBeginLoc = getVarDeclIdentifierLoc(VD);
2404 SourceLocation ParmIdentEndLoc =
2405 Lexer::getLocForEndOfToken(ParmIdentBeginLoc, 0, SM, LangOpts);
2406
2407 if (ParmIdentEndLoc.isMacroID() &&
2408 !Lexer::isAtEndOfMacroExpansion(ParmIdentEndLoc, SM, LangOpts))
2409 return std::nullopt;
2410 return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts);
2411}
2412
2413// We cannot fix a variable declaration if it has some other specifiers than the
2414// type specifier. Because the source ranges of those specifiers could overlap
2415// with the source range that is being replaced using fix-its. Especially when
2416// we often cannot obtain accurate source ranges of cv-qualified type
2417// specifiers.
2418// FIXME: also deal with type attributes
2419static bool hasUnsupportedSpecifiers(const VarDecl *VD,
2420 const SourceManager &SM) {
2421 // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the
2422 // source range of `VD`:
2423 bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {
2424 return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
2425 VD->getBeginLoc())) &&
2426 !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
2427 At->getRange().getBegin()));
2428 });
2429 return VD->isInlineSpecified() || VD->isConstexpr() ||
2431 AttrRangeOverlapping;
2432}
2433
2434// Returns the `SourceRange` of `D`. The reason why this function exists is
2435// that `D->getSourceRange()` may return a range where the end location is the
2436// starting location of the last token. The end location of the source range
2437// returned by this function is the last location of the last token.
2439 const SourceManager &SM,
2440 const LangOptions &LangOpts) {
2443 End = // `D->getEndLoc` should always return the starting location of the
2444 // last token, so we should get the end of the token
2445 Lexer::getLocForEndOfToken(D->getEndLoc(), 0, SM, LangOpts);
2446
2447 return SourceRange(Begin, End);
2448}
2449
2450// Returns the text of the pointee type of `T` from a `VarDecl` of a pointer
2451// type. The text is obtained through from `TypeLoc`s. Since `TypeLoc` does not
2452// have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me
2453// :( ), `Qualifiers` of the pointee type is returned separately through the
2454// output parameter `QualifiersToAppend`.
2455static std::optional<std::string>
2457 const LangOptions &LangOpts,
2458 std::optional<Qualifiers> *QualifiersToAppend) {
2459 QualType Ty = VD->getType();
2460 QualType PteTy;
2461
2462 assert(Ty->isPointerType() && !Ty->isFunctionPointerType() &&
2463 "Expecting a VarDecl of type of pointer to object type");
2464 PteTy = Ty->getPointeeType();
2465
2467 TypeLoc PteTyLoc;
2468
2469 // We only deal with the cases that we know `TypeLoc::getNextTypeLoc` returns
2470 // the `TypeLoc` of the pointee type:
2471 switch (TyLoc.getTypeLocClass()) {
2472 case TypeLoc::ConstantArray:
2473 case TypeLoc::IncompleteArray:
2474 case TypeLoc::VariableArray:
2475 case TypeLoc::DependentSizedArray:
2476 case TypeLoc::Decayed:
2477 assert(isa<ParmVarDecl>(VD) && "An array type shall not be treated as a "
2478 "pointer type unless it decays.");
2479 PteTyLoc = TyLoc.getNextTypeLoc();
2480 break;
2481 case TypeLoc::Pointer:
2482 PteTyLoc = TyLoc.castAs<PointerTypeLoc>().getPointeeLoc();
2483 break;
2484 default:
2485 return std::nullopt;
2486 }
2487 if (PteTyLoc.isNull())
2488 // Sometimes we cannot get a useful `TypeLoc` for the pointee type, e.g.,
2489 // when the pointer type is `auto`.
2490 return std::nullopt;
2491
2493
2494 if (!(IdentLoc.isValid() && PteTyLoc.getSourceRange().isValid())) {
2495 // We are expecting these locations to be valid. But in some cases, they are
2496 // not all valid. It is a Clang bug to me and we are not responsible for
2497 // fixing it. So we will just give up for now when it happens.
2498 return std::nullopt;
2499 }
2500
2501 // Note that TypeLoc.getEndLoc() returns the begin location of the last token:
2502 SourceLocation PteEndOfTokenLoc =
2503 Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts);
2504
2505 if (!PteEndOfTokenLoc.isValid())
2506 // Sometimes we cannot get the end location of the pointee type, e.g., when
2507 // there are macros involved.
2508 return std::nullopt;
2509 if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, IdentLoc)) {
2510 // We only deal with the cases where the source text of the pointee type
2511 // appears on the left-hand side of the variable identifier completely,
2512 // including the following forms:
2513 // `T ident`,
2514 // `T ident[]`, where `T` is any type.
2515 // Examples of excluded cases are `T (*ident)[]` or `T ident[][n]`.
2516 return std::nullopt;
2517 }
2518 if (PteTy.hasQualifiers()) {
2519 // TypeLoc does not provide source ranges for qualifiers (it says it's
2520 // intentional but seems fishy to me), so we cannot get the full text
2521 // `PteTy` via source ranges.
2522 *QualifiersToAppend = PteTy.getQualifiers();
2523 }
2524 return getRangeText({PteTyLoc.getBeginLoc(), PteEndOfTokenLoc}, SM, LangOpts)
2525 ->str();
2526}
2527
2528// Returns the text of the name (with qualifiers) of a `FunctionDecl`.
2529static std::optional<StringRef> getFunNameText(const FunctionDecl *FD,
2530 const SourceManager &SM,
2531 const LangOptions &LangOpts) {
2532 SourceLocation BeginLoc = FD->getQualifier()
2534 : FD->getNameInfo().getBeginLoc();
2535 // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the
2536 // last token:
2538 FD->getNameInfo().getEndLoc(), 0, SM, LangOpts);
2539 SourceRange NameRange{BeginLoc, EndLoc};
2540
2541 return getRangeText(NameRange, SM, LangOpts);
2542}
2543
2544// Returns the text representing a `std::span` type where the element type is
2545// represented by `EltTyText`.
2546//
2547// Note the optional parameter `Qualifiers`: one needs to pass qualifiers
2548// explicitly if the element type needs to be qualified.
2549static std::string
2550getSpanTypeText(StringRef EltTyText,
2551 std::optional<Qualifiers> Quals = std::nullopt) {
2552 const char *const SpanOpen = "std::span<";
2553
2554 if (Quals)
2555 return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>';
2556 return SpanOpen + EltTyText.str() + '>';
2557}
2558
2559std::optional<FixItList>
2561 const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
2562
2563 if (VD && s.lookup(VD) == FixitStrategy::Kind::Span) {
2564 ASTContext &Ctx = VD->getASTContext();
2565 // std::span can't represent elements before its begin()
2566 if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
2567 if (ConstVal->isNegative())
2568 return std::nullopt;
2569
2570 // note that the expr may (oddly) has multiple layers of parens
2571 // example:
2572 // *((..(pointer + 123)..))
2573 // goal:
2574 // pointer[123]
2575 // Fix-It:
2576 // remove '*('
2577 // replace ' + ' with '['
2578 // replace ')' with ']'
2579
2580 // example:
2581 // *((..(123 + pointer)..))
2582 // goal:
2583 // 123[pointer]
2584 // Fix-It:
2585 // remove '*('
2586 // replace ' + ' with '['
2587 // replace ')' with ']'
2588
2589 const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
2590 const SourceManager &SM = Ctx.getSourceManager();
2591 const LangOptions &LangOpts = Ctx.getLangOpts();
2592 CharSourceRange StarWithTrailWhitespace =
2594 LHS->getBeginLoc());
2595
2596 std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts);
2597 if (!LHSLocation)
2598 return std::nullopt;
2599
2600 CharSourceRange PlusWithSurroundingWhitespace =
2601 clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc());
2602
2603 std::optional<SourceLocation> AddOpLocation =
2604 getPastLoc(AddOp, SM, LangOpts);
2605 std::optional<SourceLocation> DerefOpLocation =
2606 getPastLoc(DerefOp, SM, LangOpts);
2607
2608 if (!AddOpLocation || !DerefOpLocation)
2609 return std::nullopt;
2610
2611 CharSourceRange ClosingParenWithPrecWhitespace =
2612 clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation);
2613
2614 return FixItList{
2615 {FixItHint::CreateRemoval(StarWithTrailWhitespace),
2616 FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["),
2617 FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}};
2618 }
2619 return std::nullopt; // something wrong or unsupported, give up
2620}
2621
2622std::optional<FixItList>
2623PointerDereferenceGadget::getFixits(const FixitStrategy &S) const {
2624 const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl());
2625 switch (S.lookup(VD)) {
2626 case FixitStrategy::Kind::Span: {
2627 ASTContext &Ctx = VD->getASTContext();
2629 // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0]
2630 // Deletes the *operand
2632 Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1));
2633 // Inserts the [0]
2634 if (auto LocPastOperand =
2635 getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) {
2636 return FixItList{{FixItHint::CreateRemoval(derefRange),
2637 FixItHint::CreateInsertion(*LocPastOperand, "[0]")}};
2638 }
2639 break;
2640 }
2641 case FixitStrategy::Kind::Iterator:
2642 case FixitStrategy::Kind::Array:
2643 return std::nullopt;
2644 case FixitStrategy::Kind::Vector:
2645 llvm_unreachable("FixitStrategy not implemented yet!");
2646 case FixitStrategy::Kind::Wontfix:
2647 llvm_unreachable("Invalid strategy!");
2648 }
2649
2650 return std::nullopt;
2651}
2652
2653static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
2654 const DeclRefExpr *DRE) {
2655 const SourceManager &SM = Ctx.getSourceManager();
2656 // Inserts the .data() after the DRE
2657 std::optional<SourceLocation> EndOfOperand =
2658 getPastLoc(DRE, SM, Ctx.getLangOpts());
2659
2660 if (EndOfOperand)
2661 return FixItList{{FixItHint::CreateInsertion(*EndOfOperand, ".data()")}};
2662
2663 return std::nullopt;
2664}
2665
2666// Generates fix-its replacing an expression of the form UPC(DRE) with
2667// `DRE.data()`
2668std::optional<FixItList>
2669UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const {
2670 const auto VD = cast<VarDecl>(Node->getDecl());
2671 switch (S.lookup(VD)) {
2672 case FixitStrategy::Kind::Array:
2673 case FixitStrategy::Kind::Span: {
2674 return createDataFixit(VD->getASTContext(), Node);
2675 // FIXME: Points inside a macro expansion.
2676 break;
2677 }
2678 case FixitStrategy::Kind::Wontfix:
2679 case FixitStrategy::Kind::Iterator:
2680 return std::nullopt;
2681 case FixitStrategy::Kind::Vector:
2682 llvm_unreachable("unsupported strategies for FixableGadgets");
2683 }
2684
2685 return std::nullopt;
2686}
2687
2688// Generates fix-its replacing an expression of the form `&DRE[e]` with
2689// `&DRE.data()[e]`:
2690static std::optional<FixItList>
2692 const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr());
2693 const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts());
2694 // FIXME: this `getASTContext` call is costly, we should pass the
2695 // ASTContext in:
2696 const ASTContext &Ctx = DRE->getDecl()->getASTContext();
2697 const Expr *Idx = ArraySub->getIdx();
2698 const SourceManager &SM = Ctx.getSourceManager();
2699 const LangOptions &LangOpts = Ctx.getLangOpts();
2700 std::stringstream SS;
2701 bool IdxIsLitZero = false;
2702
2703 if (auto ICE = Idx->getIntegerConstantExpr(Ctx))
2704 if ((*ICE).isZero())
2705 IdxIsLitZero = true;
2706 std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts);
2707 if (!DreString)
2708 return std::nullopt;
2709
2710 if (IdxIsLitZero) {
2711 // If the index is literal zero, we produce the most concise fix-it:
2712 SS << (*DreString).str() << ".data()";
2713 } else {
2714 std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts);
2715 if (!IndexString)
2716 return std::nullopt;
2717
2718 SS << "&" << (*DreString).str() << ".data()"
2719 << "[" << (*IndexString).str() << "]";
2720 }
2721 return FixItList{
2723}
2724
2725std::optional<FixItList>
2727 DeclUseList DREs = getClaimedVarUseSites();
2728
2729 if (DREs.size() != 1)
2730 return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
2731 // give up
2732 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
2733 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
2734 FixItList Fixes;
2735
2736 const Stmt *AddAssignNode = Node;
2737 StringRef varName = VD->getName();
2738 const ASTContext &Ctx = VD->getASTContext();
2739
2740 if (!isNonNegativeIntegerExpr(Offset, VD, Ctx))
2741 return std::nullopt;
2742
2743 // To transform UUC(p += n) to UUC(p = p.subspan(..)):
2744 bool NotParenExpr =
2745 (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc());
2746 std::string SS = varName.str() + " = " + varName.str() + ".subspan";
2747 if (NotParenExpr)
2748 SS += "(";
2749
2750 std::optional<SourceLocation> AddAssignLocation = getEndCharLoc(
2751 AddAssignNode, Ctx.getSourceManager(), Ctx.getLangOpts());
2752 if (!AddAssignLocation)
2753 return std::nullopt;
2754
2755 Fixes.push_back(FixItHint::CreateReplacement(
2756 SourceRange(AddAssignNode->getBeginLoc(), Node->getOperatorLoc()),
2757 SS));
2758 if (NotParenExpr)
2759 Fixes.push_back(FixItHint::CreateInsertion(
2760 Offset->getEndLoc().getLocWithOffset(1), ")"));
2761 return Fixes;
2762 }
2763 }
2764 return std::nullopt; // Not in the cases that we can handle for now, give up.
2765}
2766
2767std::optional<FixItList>
2769 DeclUseList DREs = getClaimedVarUseSites();
2770
2771 if (DREs.size() != 1)
2772 return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we
2773 // give up
2774 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
2775 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
2776 FixItList Fixes;
2777 std::stringstream SS;
2778 StringRef varName = VD->getName();
2779 const ASTContext &Ctx = VD->getASTContext();
2780
2781 // To transform UPC(++p) to UPC((p = p.subspan(1)).data()):
2782 SS << "(" << varName.data() << " = " << varName.data()
2783 << ".subspan(1)).data()";
2784 std::optional<SourceLocation> PreIncLocation =
2786 if (!PreIncLocation)
2787 return std::nullopt;
2788
2789 Fixes.push_back(FixItHint::CreateReplacement(
2790 SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));
2791 return Fixes;
2792 }
2793 }
2794 return std::nullopt; // Not in the cases that we can handle for now, give up.
2795}
2796
2797// For a non-null initializer `Init` of `T *` type, this function returns
2798// `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it
2799// to output stream.
2800// In many cases, this function cannot figure out the actual extent `S`. It
2801// then will use a place holder to replace `S` to ask users to fill `S` in. The
2802// initializer shall be used to initialize a variable of type `std::span<T>`.
2803// In some cases (e. g. constant size array) the initializer should remain
2804// unchanged and the function returns empty list. In case the function can't
2805// provide the right fixit it will return nullopt.
2806//
2807// FIXME: Support multi-level pointers
2808//
2809// Parameters:
2810// `Init` a pointer to the initializer expression
2811// `Ctx` a reference to the ASTContext
2812static std::optional<FixItList>
2814 const StringRef UserFillPlaceHolder) {
2815 const SourceManager &SM = Ctx.getSourceManager();
2816 const LangOptions &LangOpts = Ctx.getLangOpts();
2817
2818 // If `Init` has a constant value that is (or equivalent to) a
2819 // NULL pointer, we use the default constructor to initialize the span
2820 // object, i.e., a `std:span` variable declaration with no initializer.
2821 // So the fix-it is just to remove the initializer.
2822 if (Init->isNullPointerConstant(
2823 Ctx,
2824 // FIXME: Why does this function not ask for `const ASTContext
2825 // &`? It should. Maybe worth an NFC patch later.
2827 NPC_ValueDependentIsNotNull)) {
2828 std::optional<SourceLocation> InitLocation =
2829 getEndCharLoc(Init, SM, LangOpts);
2830 if (!InitLocation)
2831 return std::nullopt;
2832
2833 SourceRange SR(Init->getBeginLoc(), *InitLocation);
2834
2835 return FixItList{FixItHint::CreateRemoval(SR)};
2836 }
2837
2838 FixItList FixIts{};
2839 std::string ExtentText = UserFillPlaceHolder.data();
2840 StringRef One = "1";
2841
2842 // Insert `{` before `Init`:
2843 FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{"));
2844 // Try to get the data extent. Break into different cases:
2845 if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) {
2846 // In cases `Init` is `new T[n]` and there is no explicit cast over
2847 // `Init`, we know that `Init` must evaluates to a pointer to `n` objects
2848 // of `T`. So the extent is `n` unless `n` has side effects. Similar but
2849 // simpler for the case where `Init` is `new T`.
2850 if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {
2851 if (!Ext->HasSideEffects(Ctx)) {
2852 std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts);
2853 if (!ExtentString)
2854 return std::nullopt;
2855 ExtentText = *ExtentString;
2856 }
2857 } else if (!CxxNew->isArray())
2858 // Although the initializer is not allocating a buffer, the pointer
2859 // variable could still be used in buffer access operations.
2860 ExtentText = One;
2861 } else if (Ctx.getAsConstantArrayType(Init->IgnoreImpCasts()->getType())) {
2862 // std::span has a single parameter constructor for initialization with
2863 // constant size array. The size is auto-deduced as the constructor is a
2864 // function template. The correct fixit is empty - no changes should happen.
2865 return FixItList{};
2866 } else {
2867 // In cases `Init` is of the form `&Var` after stripping of implicit
2868 // casts, where `&` is the built-in operator, the extent is 1.
2869 if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts()))
2870 if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
2871 isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
2872 ExtentText = One;
2873 // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`,
2874 // and explicit casting, etc. etc.
2875 }
2876
2877 SmallString<32> StrBuffer{};
2878 std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts);
2879
2880 if (!LocPassInit)
2881 return std::nullopt;
2882
2883 StrBuffer.append(", ");
2884 StrBuffer.append(ExtentText);
2885 StrBuffer.append("}");
2886 FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str()));
2887 return FixIts;
2888}
2889
2890#ifndef NDEBUG
2891#define DEBUG_NOTE_DECL_FAIL(D, Msg) \
2892 Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \
2893 "failed to produce fixit for declaration '" + \
2894 (D)->getNameAsString() + "'" + (Msg))
2895#else
2896#define DEBUG_NOTE_DECL_FAIL(D, Msg)
2897#endif
2898
2899// For the given variable declaration with a pointer-to-T type, returns the text
2900// `std::span<T>`. If it is unable to generate the text, returns
2901// `std::nullopt`.
2902static std::optional<std::string>
2904 assert(VD->getType()->isPointerType());
2905
2906 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
2907 std::optional<std::string> PteTyText = getPointeeTypeText(
2908 VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
2909
2910 if (!PteTyText)
2911 return std::nullopt;
2912
2913 std::string SpanTyText = "std::span<";
2914
2915 SpanTyText.append(*PteTyText);
2916 // Append qualifiers to span element type if any:
2917 if (PteTyQualifiers) {
2918 SpanTyText.append(" ");
2919 SpanTyText.append(PteTyQualifiers->getAsString());
2920 }
2921 SpanTyText.append(">");
2922 return SpanTyText;
2923}
2924
2925// For a `VarDecl` of the form `T * var (= Init)?`, this
2926// function generates fix-its that
2927// 1) replace `T * var` with `std::span<T> var`; and
2928// 2) change `Init` accordingly to a span constructor, if it exists.
2929//
2930// FIXME: support Multi-level pointers
2931//
2932// Parameters:
2933// `D` a pointer the variable declaration node
2934// `Ctx` a reference to the ASTContext
2935// `UserFillPlaceHolder` the user-input placeholder text
2936// Returns:
2937// the non-empty fix-it list, if fix-its are successfuly generated; empty
2938// list otherwise.
2939static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
2940 const StringRef UserFillPlaceHolder,
2941 UnsafeBufferUsageHandler &Handler) {
2943 return {};
2944
2945 FixItList FixIts{};
2946 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx);
2947
2948 if (!SpanTyText) {
2949 DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type");
2950 return {};
2951 }
2952
2953 // Will hold the text for `std::span<T> Ident`:
2954 std::stringstream SS;
2955
2956 SS << *SpanTyText;
2957 // Fix the initializer if it exists:
2958 if (const Expr *Init = D->getInit()) {
2959 std::optional<FixItList> InitFixIts =
2960 FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder);
2961 if (!InitFixIts)
2962 return {};
2963 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
2964 std::make_move_iterator(InitFixIts->end()));
2965 }
2966 // For declaration of the form `T * ident = init;`, we want to replace
2967 // `T * ` with `std::span<T>`.
2968 // We ignore CV-qualifiers so for `T * const ident;` we also want to replace
2969 // just `T *` with `std::span<T>`.
2970 const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();
2971 if (!EndLocForReplacement.isValid()) {
2972 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration");
2973 return {};
2974 }
2975 // The only exception is that for `T *ident` we'll add a single space between
2976 // "std::span<T>" and "ident".
2977 // FIXME: The condition is false for identifiers expended from macros.
2978 if (EndLocForReplacement.getLocWithOffset(1) == getVarDeclIdentifierLoc(D))
2979 SS << " ";
2980
2981 FixIts.push_back(FixItHint::CreateReplacement(
2982 SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str()));
2983 return FixIts;
2984}
2985
2986static bool hasConflictingOverload(const FunctionDecl *FD) {
2987 return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult();
2988}
2989
2990// For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new
2991// types, this function produces fix-its to make the change self-contained. Let
2992// 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the
2993// entity defined by the `FunctionDecl` after the change to the parameters.
2994// Fix-its produced by this function are
2995// 1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration
2996// of 'F';
2997// 2. Create a declaration of "NewF" next to each declaration of `F`;
2998// 3. Create a definition of "F" (as its' original definition is now belongs
2999// to "NewF") next to its original definition. The body of the creating
3000// definition calls to "NewF".
3001//
3002// Example:
3003//
3004// void f(int *p); // original declaration
3005// void f(int *p) { // original definition
3006// p[5];
3007// }
3008//
3009// To change the parameter `p` to be of `std::span<int>` type, we
3010// also add overloads:
3011//
3012// [[clang::unsafe_buffer_usage]] void f(int *p); // original decl
3013// void f(std::span<int> p); // added overload decl
3014// void f(std::span<int> p) { // original def where param is changed
3015// p[5];
3016// }
3017// [[clang::unsafe_buffer_usage]] void f(int *p) { // added def
3018// return f(std::span(p, <# size #>));
3019// }
3020//
3021static std::optional<FixItList>
3023 const ASTContext &Ctx,
3024 UnsafeBufferUsageHandler &Handler) {
3025 // FIXME: need to make this conflict checking better:
3026 if (hasConflictingOverload(FD))
3027 return std::nullopt;
3028
3029 const SourceManager &SM = Ctx.getSourceManager();
3030 const LangOptions &LangOpts = Ctx.getLangOpts();
3031 const unsigned NumParms = FD->getNumParams();
3032 std::vector<std::string> NewTysTexts(NumParms);
3033 std::vector<bool> ParmsMask(NumParms, false);
3034 bool AtLeastOneParmToFix = false;
3035
3036 for (unsigned i = 0; i < NumParms; i++) {
3037 const ParmVarDecl *PVD = FD->getParamDecl(i);
3038
3039 if (S.lookup(PVD) == FixitStrategy::Kind::Wontfix)
3040 continue;
3041 if (S.lookup(PVD) != FixitStrategy::Kind::Span)
3042 // Not supported, not suppose to happen:
3043 return std::nullopt;
3044
3045 std::optional<Qualifiers> PteTyQuals = std::nullopt;
3046 std::optional<std::string> PteTyText =
3047 getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals);
3048
3049 if (!PteTyText)
3050 // something wrong in obtaining the text of the pointee type, give up
3051 return std::nullopt;
3052 // FIXME: whether we should create std::span type depends on the
3053 // FixitStrategy.
3054 NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);
3055 ParmsMask[i] = true;
3056 AtLeastOneParmToFix = true;
3057 }
3058 if (!AtLeastOneParmToFix)
3059 // No need to create function overloads:
3060 return {};
3061 // FIXME Respect indentation of the original code.
3062
3063 // A lambda that creates the text representation of a function declaration
3064 // with the new type signatures:
3065 const auto NewOverloadSignatureCreator =
3066 [&SM, &LangOpts, &NewTysTexts,
3067 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
3068 std::stringstream SS;
3069
3070 SS << ";";
3071 SS << getEndOfLine().str();
3072 // Append: ret-type func-name "("
3073 if (auto Prefix = getRangeText(
3074 SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()),
3075 SM, LangOpts))
3076 SS << Prefix->str();
3077 else
3078 return std::nullopt; // give up
3079 // Append: parameter-type-list
3080 const unsigned NumParms = FD->getNumParams();
3081
3082 for (unsigned i = 0; i < NumParms; i++) {
3083 const ParmVarDecl *Parm = FD->getParamDecl(i);
3084
3085 if (Parm->isImplicit())
3086 continue;
3087 if (ParmsMask[i]) {
3088 // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its
3089 // new type:
3090 SS << NewTysTexts[i];
3091 // print parameter name if provided:
3092 if (IdentifierInfo *II = Parm->getIdentifier())
3093 SS << ' ' << II->getName().str();
3094 } else if (auto ParmTypeText =
3095 getRangeText(getSourceRangeToTokenEnd(Parm, SM, LangOpts),
3096 SM, LangOpts)) {
3097 // print the whole `Parm` without modification:
3098 SS << ParmTypeText->str();
3099 } else
3100 return std::nullopt; // something wrong, give up
3101 if (i != NumParms - 1)
3102 SS << ", ";
3103 }
3104 SS << ")";
3105 return SS.str();
3106 };
3107
3108 // A lambda that creates the text representation of a function definition with
3109 // the original signature:
3110 const auto OldOverloadDefCreator =
3111 [&Handler, &SM, &LangOpts, &NewTysTexts,
3112 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
3113 std::stringstream SS;
3114
3115 SS << getEndOfLine().str();
3116 // Append: attr-name ret-type func-name "(" param-list ")" "{"
3117 if (auto FDPrefix = getRangeText(
3118 SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM,
3119 LangOpts))
3120 SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ")
3121 << FDPrefix->str() << "{";
3122 else
3123 return std::nullopt;
3124 // Append: "return" func-name "("
3125 if (auto FunQualName = getFunNameText(FD, SM, LangOpts))
3126 SS << "return " << FunQualName->str() << "(";
3127 else
3128 return std::nullopt;
3129
3130 // Append: arg-list
3131 const unsigned NumParms = FD->getNumParams();
3132 for (unsigned i = 0; i < NumParms; i++) {
3133 const ParmVarDecl *Parm = FD->getParamDecl(i);
3134
3135 if (Parm->isImplicit())
3136 continue;
3137 // FIXME: If a parameter has no name, it is unused in the
3138 // definition. So we could just leave it as it is.
3139 if (!Parm->getIdentifier())
3140 // If a parameter of a function definition has no name:
3141 return std::nullopt;
3142 if (ParmsMask[i])
3143 // This is our spanified paramter!
3144 SS << NewTysTexts[i] << "(" << Parm->getIdentifier()->getName().str()
3145 << ", " << getUserFillPlaceHolder("size") << ")";
3146 else
3147 SS << Parm->getIdentifier()->getName().str();
3148 if (i != NumParms - 1)
3149 SS << ", ";
3150 }
3151 // finish call and the body
3152 SS << ");}" << getEndOfLine().str();
3153 // FIXME: 80-char line formatting?
3154 return SS.str();
3155 };
3156
3157 FixItList FixIts{};
3158 for (FunctionDecl *FReDecl : FD->redecls()) {
3159 std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts);
3160
3161 if (!Loc)
3162 return {};
3163 if (FReDecl->isThisDeclarationADefinition()) {
3164 assert(FReDecl == FD && "inconsistent function definition");
3165 // Inserts a definition with the old signature to the end of
3166 // `FReDecl`:
3167 if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
3168 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef));
3169 else
3170 return {}; // give up
3171 } else {
3172 // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`:
3173 if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
3174 FixIts.emplace_back(FixItHint::CreateInsertion(
3175 FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt(
3176 FReDecl->getBeginLoc(), " ")));
3177 }
3178 // Inserts a declaration with the new signature to the end of `FReDecl`:
3179 if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
3180 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl));
3181 else
3182 return {};
3183 }
3184 }
3185 return FixIts;
3186}
3187
3188// To fix a `ParmVarDecl` to be of `std::span` type.
3189static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
3190 UnsafeBufferUsageHandler &Handler) {
3192 DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)");
3193 return {};
3194 }
3195 if (PVD->hasDefaultArg()) {
3196 // FIXME: generate fix-its for default values:
3197 DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg");
3198 return {};
3199 }
3200
3201 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
3202 std::optional<std::string> PteTyText = getPointeeTypeText(
3203 PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
3204
3205 if (!PteTyText) {
3206 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type");
3207 return {};
3208 }
3209
3210 std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName();
3211
3212 if (!PVDNameText) {
3213 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name");
3214 return {};
3215 }
3216
3217 std::stringstream SS;
3218 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx);
3219
3220 if (PteTyQualifiers)
3221 // Append qualifiers if they exist:
3222 SS << getSpanTypeText(*PteTyText, PteTyQualifiers);
3223 else
3224 SS << getSpanTypeText(*PteTyText);
3225 // Append qualifiers to the type of the parameter:
3226 if (PVD->getType().hasQualifiers())
3227 SS << ' ' << PVD->getType().getQualifiers().getAsString();
3228 // Append parameter's name:
3229 SS << ' ' << PVDNameText->str();
3230 // Add replacement fix-it:
3231 return {FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())};
3232}
3233
3234static FixItList fixVariableWithSpan(const VarDecl *VD,
3235 const DeclUseTracker &Tracker,
3236 ASTContext &Ctx,
3237 UnsafeBufferUsageHandler &Handler) {
3238 const DeclStmt *DS = Tracker.lookupDecl(VD);
3239 if (!DS) {
3241 " : variables declared this way not implemented yet");
3242 return {};
3243 }
3244 if (!DS->isSingleDecl()) {
3245 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
3246 DEBUG_NOTE_DECL_FAIL(VD, " : multiple VarDecls");
3247 return {};
3248 }
3249 // Currently DS is an unused variable but we'll need it when
3250 // non-single decls are implemented, where the pointee type name
3251 // and the '*' are spread around the place.
3252 (void)DS;
3253
3254 // FIXME: handle cases where DS has multiple declarations
3255 return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
3256}
3257
3258static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
3259 UnsafeBufferUsageHandler &Handler) {
3260 FixItList FixIts{};
3261
3262 // Note: the code below expects the declaration to not use any type sugar like
3263 // typedef.
3264 if (auto CAT = Ctx.getAsConstantArrayType(D->getType())) {
3265 const QualType &ArrayEltT = CAT->getElementType();
3266 assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
3267 // FIXME: support multi-dimensional arrays
3268 if (isa<clang::ArrayType>(ArrayEltT.getCanonicalType()))
3269 return {};
3270
3272
3273 // Get the spelling of the element type as written in the source file
3274 // (including macros, etc.).
3275 auto MaybeElemTypeTxt =
3277 Ctx.getLangOpts());
3278 if (!MaybeElemTypeTxt)
3279 return {};
3280 const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
3281
3282 // Find the '[' token.
3283 std::optional<Token> NextTok = Lexer::findNextToken(
3285 while (NextTok && !NextTok->is(tok::l_square) &&
3286 NextTok->getLocation() <= D->getSourceRange().getEnd())
3287 NextTok = Lexer::findNextToken(NextTok->getLocation(),
3288 Ctx.getSourceManager(), Ctx.getLangOpts());
3289 if (!NextTok)
3290 return {};
3291 const SourceLocation LSqBracketLoc = NextTok->getLocation();
3292
3293 // Get the spelling of the array size as written in the source file
3294 // (including macros, etc.).
3295 auto MaybeArraySizeTxt = getRangeText(
3296 {LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()},
3297 Ctx.getSourceManager(), Ctx.getLangOpts());
3298 if (!MaybeArraySizeTxt)
3299 return {};
3300 const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
3301 if (ArraySizeTxt.empty()) {
3302 // FIXME: Support array size getting determined from the initializer.
3303 // Examples:
3304 // int arr1[] = {0, 1, 2};
3305 // int arr2{3, 4, 5};
3306 // We might be able to preserve the non-specified size with `auto` and
3307 // `std::to_array`:
3308 // auto arr1 = std::to_array<int>({0, 1, 2});
3309 return {};
3310 }
3311
3312 std::optional<StringRef> IdentText =
3314
3315 if (!IdentText) {
3316 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
3317 return {};
3318 }
3319
3320 SmallString<32> Replacement;
3321 raw_svector_ostream OS(Replacement);
3322 OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> "
3323 << IdentText->str();
3324
3325 FixIts.push_back(FixItHint::CreateReplacement(
3326 SourceRange{D->getBeginLoc(), D->getTypeSpecEndLoc()}, OS.str()));
3327 }
3328
3329 return FixIts;
3330}
3331
3332static FixItList fixVariableWithArray(const VarDecl *VD,
3333 const DeclUseTracker &Tracker,
3334 const ASTContext &Ctx,
3335 UnsafeBufferUsageHandler &Handler) {
3336 const DeclStmt *DS = Tracker.lookupDecl(VD);
3337 assert(DS && "Fixing non-local variables not implemented yet!");
3338 if (!DS->isSingleDecl()) {
3339 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
3340 return {};
3341 }
3342 // Currently DS is an unused variable but we'll need it when
3343 // non-single decls are implemented, where the pointee type name
3344 // and the '*' are spread around the place.
3345 (void)DS;
3346
3347 // FIXME: handle cases where DS has multiple declarations
3348 return fixVarDeclWithArray(VD, Ctx, Handler);
3349}
3350
3351// TODO: we should be consistent to use `std::nullopt` to represent no-fix due
3352// to any unexpected problem.
3353static FixItList
3355 /* The function decl under analysis */ const Decl *D,
3356 const DeclUseTracker &Tracker, ASTContext &Ctx,
3357 UnsafeBufferUsageHandler &Handler) {
3358 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
3359 auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
3360 if (!FD || FD != D) {
3361 // `FD != D` means that `PVD` belongs to a function that is not being
3362 // analyzed currently. Thus `FD` may not be complete.
3363 DEBUG_NOTE_DECL_FAIL(VD, " : function not currently analyzed");
3364 return {};
3365 }
3366
3367 // TODO If function has a try block we can't change params unless we check
3368 // also its catch block for their use.
3369 // FIXME We might support static class methods, some select methods,
3370 // operators and possibly lamdas.
3371 if (FD->isMain() || FD->isConstexpr() ||
3372 FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate ||
3373 FD->isVariadic() ||
3374 // also covers call-operator of lamdas
3375 isa<CXXMethodDecl>(FD) ||
3376 // skip when the function body is a try-block
3377 (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) ||
3378 FD->isOverloadedOperator()) {
3379 DEBUG_NOTE_DECL_FAIL(VD, " : unsupported function decl");
3380 return {}; // TODO test all these cases
3381 }
3382 }
3383
3384 switch (K) {
3385 case FixitStrategy::Kind::Span: {
3386 if (VD->getType()->isPointerType()) {
3387 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD))
3388 return fixParamWithSpan(PVD, Ctx, Handler);
3389
3390 if (VD->isLocalVarDecl())
3391 return fixVariableWithSpan(VD, Tracker, Ctx, Handler);
3392 }
3393 DEBUG_NOTE_DECL_FAIL(VD, " : not a pointer");
3394 return {};
3395 }
3396 case FixitStrategy::Kind::Array: {
3397 if (VD->isLocalVarDecl() && Ctx.getAsConstantArrayType(VD->getType()))
3398 return fixVariableWithArray(VD, Tracker, Ctx, Handler);
3399
3400 DEBUG_NOTE_DECL_FAIL(VD, " : not a local const-size array");
3401 return {};
3402 }
3403 case FixitStrategy::Kind::Iterator:
3404 case FixitStrategy::Kind::Vector:
3405 llvm_unreachable("FixitStrategy not implemented yet!");
3406 case FixitStrategy::Kind::Wontfix:
3407 llvm_unreachable("Invalid strategy!");
3408 }
3409 llvm_unreachable("Unknown strategy!");
3410}
3411
3412// Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the
3413// `RemoveRange` of 'h' overlaps with a macro use.
3414static bool overlapWithMacro(const FixItList &FixIts) {
3415 // FIXME: For now we only check if the range (or the first token) is (part of)
3416 // a macro expansion. Ideally, we want to check for all tokens in the range.
3417 return llvm::any_of(FixIts, [](const FixItHint &Hint) {
3418 auto Range = Hint.RemoveRange;
3420 // If the range (or the first token) is (part of) a macro expansion:
3421 return true;
3422 return false;
3423 });
3424}
3425
3426// Returns true iff `VD` is a parameter of the declaration `D`:
3427static bool isParameterOf(const VarDecl *VD, const Decl *D) {
3428 return isa<ParmVarDecl>(VD) &&
3429 VD->getDeclContext() == dyn_cast<DeclContext>(D);
3430}
3431
3432// Erases variables in `FixItsForVariable`, if such a variable has an unfixable
3433// group mate. A variable `v` is unfixable iff `FixItsForVariable` does not
3434// contain `v`.
3436 std::map<const VarDecl *, FixItList> &FixItsForVariable,
3437 const VariableGroupsManager &VarGrpMgr) {
3438 // Variables will be removed from `FixItsForVariable`:
3440
3441 for (const auto &[VD, Ignore] : FixItsForVariable) {
3442 VarGrpRef Grp = VarGrpMgr.getGroupOfVar(VD);
3443 if (llvm::any_of(Grp,
3444 [&FixItsForVariable](const VarDecl *GrpMember) -> bool {
3445 return !FixItsForVariable.count(GrpMember);
3446 })) {
3447 // At least one group member cannot be fixed, so we have to erase the
3448 // whole group:
3449 for (const VarDecl *Member : Grp)
3450 ToErase.push_back(Member);
3451 }
3452 }
3453 for (auto *VarToErase : ToErase)
3454 FixItsForVariable.erase(VarToErase);
3455}
3456
3457// Returns the fix-its that create bounds-safe function overloads for the
3458// function `D`, if `D`'s parameters will be changed to safe-types through
3459// fix-its in `FixItsForVariable`.
3460//
3461// NOTE: In case `D`'s parameters will be changed but bounds-safe function
3462// overloads cannot created, the whole group that contains the parameters will
3463// be erased from `FixItsForVariable`.
3465 std::map<const VarDecl *, FixItList> &FixItsForVariable /* mutable */,
3466 const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD,
3467 const FixitStrategy &S, ASTContext &Ctx,
3468 UnsafeBufferUsageHandler &Handler) {
3469 FixItList FixItsSharedByParms{};
3470
3471 std::optional<FixItList> OverloadFixes =
3472 createOverloadsForFixedParams(S, FD, Ctx, Handler);
3473
3474 if (OverloadFixes) {
3475 FixItsSharedByParms.append(*OverloadFixes);
3476 } else {
3477 // Something wrong in generating `OverloadFixes`, need to remove the
3478 // whole group, where parameters are in, from `FixItsForVariable` (Note
3479 // that all parameters should be in the same group):
3480 for (auto *Member : VarGrpMgr.getGroupOfParms())
3481 FixItsForVariable.erase(Member);
3482 }
3483 return FixItsSharedByParms;
3484}
3485
3486// Constructs self-contained fix-its for each variable in `FixablesForAllVars`.
3487static std::map<const VarDecl *, FixItList>
3488getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S,
3489 ASTContext &Ctx,
3490 /* The function decl under analysis */ const Decl *D,
3491 const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler,
3492 const VariableGroupsManager &VarGrpMgr) {
3493 // `FixItsForVariable` will map each variable to a set of fix-its directly
3494 // associated to the variable itself. Fix-its of distinct variables in
3495 // `FixItsForVariable` are disjoint.
3496 std::map<const VarDecl *, FixItList> FixItsForVariable;
3497
3498 // Populate `FixItsForVariable` with fix-its directly associated with each
3499 // variable. Fix-its directly associated to a variable 'v' are the ones
3500 // produced by the `FixableGadget`s whose claimed variable is 'v'.
3501 for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
3502 FixItsForVariable[VD] =
3503 fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);
3504 // If we fail to produce Fix-It for the declaration we have to skip the
3505 // variable entirely.
3506 if (FixItsForVariable[VD].empty()) {
3507 FixItsForVariable.erase(VD);
3508 continue;
3509 }
3510 for (const auto &F : Fixables) {
3511 std::optional<FixItList> Fixits = F->getFixits(S);
3512
3513 if (Fixits) {
3514 FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
3515 Fixits->begin(), Fixits->end());
3516 continue;
3517 }
3518#ifndef NDEBUG
3519 Handler.addDebugNoteForVar(
3520 VD, F->getSourceLoc(),
3521 ("gadget '" + F->getDebugName() + "' refused to produce a fix")
3522 .str());
3523#endif
3524 FixItsForVariable.erase(VD);
3525 break;
3526 }
3527 }
3528
3529 // `FixItsForVariable` now contains only variables that can be
3530 // fixed. A variable can be fixed if its' declaration and all Fixables
3531 // associated to it can all be fixed.
3532
3533 // To further remove from `FixItsForVariable` variables whose group mates
3534 // cannot be fixed...
3535 eraseVarsForUnfixableGroupMates(FixItsForVariable, VarGrpMgr);
3536 // Now `FixItsForVariable` gets further reduced: a variable is in
3537 // `FixItsForVariable` iff it can be fixed and all its group mates can be
3538 // fixed.
3539
3540 // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`.
3541 // That is, when fixing multiple parameters in one step, these fix-its will
3542 // be applied only once (instead of being applied per parameter).
3543 FixItList FixItsSharedByParms{};
3544
3545 if (auto *FD = dyn_cast<FunctionDecl>(D))
3546 FixItsSharedByParms = createFunctionOverloadsForParms(
3547 FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
3548
3549 // The map that maps each variable `v` to fix-its for the whole group where
3550 // `v` is in:
3551 std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
3552 FixItsForVariable};
3553
3554 for (auto &[Var, Ignore] : FixItsForVariable) {
3555 bool AnyParm = false;
3556 const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm);
3557
3558 for (const VarDecl *GrpMate : VarGroupForVD) {
3559 if (Var == GrpMate)
3560 continue;
3561 if (FixItsForVariable.count(GrpMate))
3562 FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
3563 }
3564 if (AnyParm) {
3565 // This assertion should never fail. Otherwise we have a bug.
3566 assert(!FixItsSharedByParms.empty() &&
3567 "Should not try to fix a parameter that does not belong to a "
3568 "FunctionDecl");
3569 FinalFixItsForVariable[Var].append(FixItsSharedByParms);
3570 }
3571 }
3572 // Fix-its that will be applied in one step shall NOT:
3573 // 1. overlap with macros or/and templates; or
3574 // 2. conflict with each other.
3575 // Otherwise, the fix-its will be dropped.
3576 for (auto Iter = FinalFixItsForVariable.begin();
3577 Iter != FinalFixItsForVariable.end();)
3578 if (overlapWithMacro(Iter->second) ||
3580 Iter = FinalFixItsForVariable.erase(Iter);
3581 } else
3582 Iter++;
3583 return FinalFixItsForVariable;
3584}
3585
3586template <typename VarDeclIterTy>
3587static FixitStrategy
3588getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) {
3589 FixitStrategy S;
3590 for (const VarDecl *VD : UnsafeVars) {
3591 if (isa<ConstantArrayType>(VD->getType().getCanonicalType()))
3592 S.set(VD, FixitStrategy::Kind::Array);
3593 else
3594 S.set(VD, FixitStrategy::Kind::Span);
3595 }
3596 return S;
3597}
3598
3599// Manages variable groups:
3601 const std::vector<VarGrpTy> Groups;
3602 const std::map<const VarDecl *, unsigned> &VarGrpMap;
3603 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
3604
3605public:
3607 const std::vector<VarGrpTy> &Groups,
3608 const std::map<const VarDecl *, unsigned> &VarGrpMap,
3609 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
3610 : Groups(Groups), VarGrpMap(VarGrpMap),
3611 GrpsUnionForParms(GrpsUnionForParms) {}
3612
3613 VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override {
3614 if (GrpsUnionForParms.contains(Var)) {
3615 if (HasParm)
3616 *HasParm = true;
3617 return GrpsUnionForParms.getArrayRef();
3618 }
3619 if (HasParm)
3620 *HasParm = false;
3621
3622 auto It = VarGrpMap.find(Var);
3623
3624 if (It == VarGrpMap.end())
3625 return {};
3626 return Groups[It->second];
3627 }
3628
3629 VarGrpRef getGroupOfParms() const override {
3630 return GrpsUnionForParms.getArrayRef();
3631 }
3632};
3633
3634void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
3635 WarningGadgetList WarningGadgets, DeclUseTracker Tracker,
3636 UnsafeBufferUsageHandler &Handler, bool EmitSuggestions) {
3637 if (!EmitSuggestions) {
3638 // Our job is very easy without suggestions. Just warn about
3639 // every problematic operation and consider it done. No need to deal
3640 // with fixable gadgets, no need to group operations by variable.
3641 for (const auto &G : WarningGadgets) {
3642 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
3643 D->getASTContext());
3644 }
3645
3646 // This return guarantees that most of the machine doesn't run when
3647 // suggestions aren't requested.
3648 assert(FixableGadgets.size() == 0 &&
3649 "Fixable gadgets found but suggestions not requested!");
3650 return;
3651 }
3652
3653 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
3654 // function under the analysis. No need to fix any Fixables.
3655 if (!WarningGadgets.empty()) {
3656 // Gadgets "claim" variables they're responsible for. Once this loop
3657 // finishes, the tracker will only track DREs that weren't claimed by any
3658 // gadgets, i.e. not understood by the analysis.
3659 for (const auto &G : FixableGadgets) {
3660 for (const auto *DRE : G->getClaimedVarUseSites()) {
3661 Tracker.claimUse(DRE);
3662 }
3663 }
3664 }
3665
3666 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
3667 // function under the analysis. Thus, it early returns here as there is
3668 // nothing needs to be fixed.
3669 //
3670 // Note this claim is based on the assumption that there is no unsafe
3671 // variable whose declaration is invisible from the analyzing function.
3672 // Otherwise, we need to consider if the uses of those unsafe varuables needs
3673 // fix.
3674 // So far, we are not fixing any global variables or class members. And,
3675 // lambdas will be analyzed along with the enclosing function. So this early
3676 // return is correct for now.
3677 if (WarningGadgets.empty())
3678 return;
3679
3680 WarningGadgetSets UnsafeOps =
3681 groupWarningGadgetsByVar(std::move(WarningGadgets));
3682 FixableGadgetSets FixablesForAllVars =
3683 groupFixablesByVar(std::move(FixableGadgets));
3684
3685 std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
3686
3687 // Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
3688 for (auto it = FixablesForAllVars.byVar.cbegin();
3689 it != FixablesForAllVars.byVar.cend();) {
3690 // FIXME: need to deal with global variables later
3691 if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first))) {
3692#ifndef NDEBUG
3693 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
3694 ("failed to produce fixit for '" +
3695 it->first->getNameAsString() +
3696 "' : neither local nor a parameter"));
3697#endif
3698 it = FixablesForAllVars.byVar.erase(it);
3699 } else if (it->first->getType().getCanonicalType()->isReferenceType()) {
3700#ifndef NDEBUG
3701 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
3702 ("failed to produce fixit for '" +
3703 it->first->getNameAsString() +
3704 "' : has a reference type"));
3705#endif
3706 it = FixablesForAllVars.byVar.erase(it);
3707 } else if (Tracker.hasUnclaimedUses(it->first)) {
3708 it = FixablesForAllVars.byVar.erase(it);
3709 } else if (it->first->isInitCapture()) {
3710#ifndef NDEBUG
3711 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
3712 ("failed to produce fixit for '" +
3713 it->first->getNameAsString() +
3714 "' : init capture"));
3715#endif
3716 it = FixablesForAllVars.byVar.erase(it);
3717 } else {
3718 ++it;
3719 }
3720 }
3721
3722#ifndef NDEBUG
3723 for (const auto &it : UnsafeOps.byVar) {
3724 const VarDecl *const UnsafeVD = it.first;
3725 auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
3726 if (UnclaimedDREs.empty())
3727 continue;
3728 const auto UnfixedVDName = UnsafeVD->getNameAsString();
3729 for (const clang::DeclRefExpr *UnclaimedDRE : UnclaimedDREs) {
3730 std::string UnclaimedUseTrace =
3731 getDREAncestorString(UnclaimedDRE, D->getASTContext());
3732
3733 Handler.addDebugNoteForVar(
3734 UnsafeVD, UnclaimedDRE->getBeginLoc(),
3735 ("failed to produce fixit for '" + UnfixedVDName +
3736 "' : has an unclaimed use\nThe unclaimed DRE trace: " +
3737 UnclaimedUseTrace));
3738 }
3739 }
3740#endif
3741
3742 // Fixpoint iteration for pointer assignments
3743 using DepMapTy = DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
3744 DepMapTy DependenciesMap{};
3745 DepMapTy PtrAssignmentGraph{};
3746
3747 for (auto it : FixablesForAllVars.byVar) {
3748 for (const FixableGadget *fixable : it.second) {
3749 std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
3750 fixable->getStrategyImplications();
3751 if (ImplPair) {
3752 std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
3753 PtrAssignmentGraph[Impl.first].insert(Impl.second);
3754 }
3755 }
3756 }
3757
3758 /*
3759 The following code does a BFS traversal of the `PtrAssignmentGraph`
3760 considering all unsafe vars as starting nodes and constructs an undirected
3761 graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner
3762 elimiates all variables that are unreachable from any unsafe var. In other
3763 words, this removes all dependencies that don't include any unsafe variable
3764 and consequently don't need any fixit generation.
3765 Note: A careful reader would observe that the code traverses
3766 `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and
3767 `Adj` and not between `CurrentVar` and `Adj`. Both approaches would
3768 achieve the same result but the one used here dramatically cuts the
3769 amount of hoops the second part of the algorithm needs to jump, given that
3770 a lot of these connections become "direct". The reader is advised not to
3771 imagine how the graph is transformed because of using `Var` instead of
3772 `CurrentVar`. The reader can continue reading as if `CurrentVar` was used,
3773 and think about why it's equivalent later.
3774 */
3775 std::set<const VarDecl *> VisitedVarsDirected{};
3776 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
3777 if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
3778
3779 std::queue<const VarDecl *> QueueDirected{};
3780 QueueDirected.push(Var);
3781 while (!QueueDirected.empty()) {
3782 const VarDecl *CurrentVar = QueueDirected.front();
3783 QueueDirected.pop();
3784 VisitedVarsDirected.insert(CurrentVar);
3785 auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
3786 for (const VarDecl *Adj : AdjacentNodes) {
3787 if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
3788 QueueDirected.push(Adj);
3789 }
3790 DependenciesMap[Var].insert(Adj);
3791 DependenciesMap[Adj].insert(Var);
3792 }
3793 }
3794 }
3795 }
3796
3797 // `Groups` stores the set of Connected Components in the graph.
3798 std::vector<VarGrpTy> Groups;
3799 // `VarGrpMap` maps variables that need fix to the groups (indexes) that the
3800 // variables belong to. Group indexes refer to the elements in `Groups`.
3801 // `VarGrpMap` is complete in that every variable that needs fix is in it.
3802 std::map<const VarDecl *, unsigned> VarGrpMap;
3803 // The union group over the ones in "Groups" that contain parameters of `D`:
3804 llvm::SetVector<const VarDecl *>
3805 GrpsUnionForParms; // these variables need to be fixed in one step
3806
3807 // Group Connected Components for Unsafe Vars
3808 // (Dependencies based on pointer assignments)
3809 std::set<const VarDecl *> VisitedVars{};
3810 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
3811 if (VisitedVars.find(Var) == VisitedVars.end()) {
3812 VarGrpTy &VarGroup = Groups.emplace_back();
3813 std::queue<const VarDecl *> Queue{};
3814
3815 Queue.push(Var);
3816 while (!Queue.empty()) {
3817 const VarDecl *CurrentVar = Queue.front();
3818 Queue.pop();
3819 VisitedVars.insert(CurrentVar);
3820 VarGroup.push_back(CurrentVar);
3821 auto AdjacentNodes = DependenciesMap[CurrentVar];
3822 for (const VarDecl *Adj : AdjacentNodes) {
3823 if (VisitedVars.find(Adj) == VisitedVars.end()) {
3824 Queue.push(Adj);
3825 }
3826 }
3827 }
3828
3829 bool HasParm = false;
3830 unsigned GrpIdx = Groups.size() - 1;
3831
3832 for (const VarDecl *V : VarGroup) {
3833 VarGrpMap[V] = GrpIdx;
3834 if (!HasParm && isParameterOf(V, D))
3835 HasParm = true;
3836 }
3837 if (HasParm)
3838 GrpsUnionForParms.insert(VarGroup.begin(), VarGroup.end());
3839 }
3840 }
3841
3842 // Remove a `FixableGadget` if the associated variable is not in the graph
3843 // computed above. We do not want to generate fix-its for such variables,
3844 // since they are neither warned nor reachable from a warned one.
3845 //
3846 // Note a variable is not warned if it is not directly used in any unsafe
3847 // operation. A variable `v` is NOT reachable from an unsafe variable, if it
3848 // does not exist another variable `u` such that `u` is warned and fixing `u`
3849 // (transitively) implicates fixing `v`.
3850 //
3851 // For example,
3852 // ```
3853 // void f(int * p) {
3854 // int * a = p; *p = 0;
3855 // }
3856 // ```
3857 // `*p = 0` is a fixable gadget associated with a variable `p` that is neither
3858 // warned nor reachable from a warned one. If we add `a[5] = 0` to the end of
3859 // the function above, `p` becomes reachable from a warned variable.
3860 for (auto I = FixablesForAllVars.byVar.begin();
3861 I != FixablesForAllVars.byVar.end();) {
3862 // Note `VisitedVars` contain all the variables in the graph:
3863 if (!VisitedVars.count((*I).first)) {
3864 // no such var in graph:
3865 I = FixablesForAllVars.byVar.erase(I);
3866 } else
3867 ++I;
3868 }
3869
3870 // We assign strategies to variables that are 1) in the graph and 2) can be
3871 // fixed. Other variables have the default "Won't fix" strategy.
3872 FixitStrategy NaiveStrategy = getNaiveStrategy(llvm::make_filter_range(
3873 VisitedVars, [&FixablesForAllVars](const VarDecl *V) {
3874 // If a warned variable has no "Fixable", it is considered unfixable:
3875 return FixablesForAllVars.byVar.count(V);
3876 }));
3877 VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms);
3878
3879 if (isa<NamedDecl>(D))
3880 // The only case where `D` is not a `NamedDecl` is when `D` is a
3881 // `BlockDecl`. Let's not fix variables in blocks for now
3882 FixItsForVariableGroup =
3883 getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D,
3884 Tracker, Handler, VarGrpMgr);
3885
3886 for (const auto &G : UnsafeOps.noVar) {
3887 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
3888 D->getASTContext());
3889 }
3890
3891 for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
3892 auto FixItsIt = FixItsForVariableGroup.find(VD);
3893 Handler.handleUnsafeVariableGroup(VD, VarGrpMgr,
3894 FixItsIt != FixItsForVariableGroup.end()
3895 ? std::move(FixItsIt->second)
3896 : FixItList{},
3897 D, NaiveStrategy);
3898 for (const auto &G : WarningGadgets) {
3899 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/true,
3900 D->getASTContext());
3901 }
3902 }
3903}
3904
3906 UnsafeBufferUsageHandler &Handler,
3907 bool EmitSuggestions) {
3908#ifndef NDEBUG
3909 Handler.clearDebugNotes();
3910#endif
3911
3912 assert(D);
3913
3914 SmallVector<Stmt *> Stmts;
3915
3916 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
3917 // We do not want to visit a Lambda expression defined inside a method
3918 // independently. Instead, it should be visited along with the outer method.
3919 // FIXME: do we want to do the same thing for `BlockDecl`s?
3920 if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
3921 if (MD->getParent()->isLambda() && MD->getParent()->isLocalClass())
3922 return;
3923 }
3924
3925 for (FunctionDecl *FReDecl : FD->redecls()) {
3926 if (FReDecl->isExternC()) {
3927 // Do not emit fixit suggestions for functions declared in an
3928 // extern "C" block.
3929 EmitSuggestions = false;
3930 break;
3931 }
3932 }
3933
3934 Stmts.push_back(FD->getBody());
3935
3936 if (const auto *ID = dyn_cast<CXXConstructorDecl>(D)) {
3937 for (const CXXCtorInitializer *CI : ID->inits()) {
3938 Stmts.push_back(CI->getInit());
3939 }
3940 }
3941 } else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
3942 Stmts.push_back(D->getBody());
3943 }
3944
3945 assert(!Stmts.empty());
3946
3947 FixableGadgetList FixableGadgets;
3948 WarningGadgetList WarningGadgets;
3949 DeclUseTracker Tracker;
3950 for (Stmt *S : Stmts) {
3951 findGadgets(S, D->getASTContext(), Handler, EmitSuggestions, FixableGadgets,
3952 WarningGadgets, Tracker);
3953 }
3954 applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
3955 std::move(Tracker), Handler, EmitSuggestions);
3956}
Defines the clang::ASTContext interface.
#define V(N, I)
Definition: ASTContext.h:3443
BoundNodesTreeBuilder Nodes
DynTypedNode Node
#define AST_MATCHER(Type, DefineMatcher)
AST_MATCHER(Type, DefineMatcher) { ... } defines a zero parameter function named DefineMatcher() that...
#define AST_MATCHER_P(Type, DefineMatcher, ParamType, Param)
AST_MATCHER_P(Type, DefineMatcher, ParamType, Param) { ... } defines a single-parameter function name...
#define SM(sm)
Definition: Cuda.cpp:84
const Decl * D
Expr * E
llvm::APSInt APSInt
Definition: Compiler.cpp:23
static Decl::Kind getKind(const Decl *D)
Definition: DeclBase.cpp:1172
StringRef Text
Definition: Format.cpp:3033
unsigned Iter
Definition: HTMLLogger.cpp:153
static const Decl * getCanonicalDecl(const Decl *D)
llvm::MachO::Target Target
Definition: MachO.h:51
Defines the clang::Preprocessor interface.
static bool hasAttr(const Decl *D, bool IgnoreImplicitAttr)
Definition: SemaCUDA.cpp:109
SourceRange Range
Definition: SemaObjC.cpp:758
SourceLocation Loc
Definition: SemaObjC.cpp:759
Defines the clang::SourceLocation class and associated facilities.
static QualType getPointeeType(const MemRegion *R)
C Language Family Type Representation.
SourceLocation Begin
static std::string getUserFillPlaceHolder(StringRef HintTextToUser="placeholder")
static FixItList fixVariableWithSpan(const VarDecl *VD, const DeclUseTracker &Tracker, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static std::optional< FixItList > fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node)
static std::optional< StringRef > getExprText(const Expr *E, const SourceManager &SM, const LangOptions &LangOpts)
static WarningGadgetSets groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations)
static StringRef getEndOfLine()
static std::optional< FixItList > FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx, const StringRef UserFillPlaceHolder)
#define NEXT
static std::optional< SourceLocation > getEndCharLoc(const NodeTy *Node, const SourceManager &SM, const LangOptions &LangOpts)
static FixItList fixVariableWithArray(const VarDecl *VD, const DeclUseTracker &Tracker, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static std::string getSpanTypeText(StringRef EltTyText, std::optional< Qualifiers > Quals=std::nullopt)
static SourceRange getSourceRangeToTokenEnd(const Decl *D, const SourceManager &SM, const LangOptions &LangOpts)
static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx, const StringRef UserFillPlaceHolder, UnsafeBufferUsageHandler &Handler)
static std::optional< FixItList > createDataFixit(const ASTContext &Ctx, const DeclRefExpr *DRE)
static FixItList createFunctionOverloadsForParms(std::map< const VarDecl *, FixItList > &FixItsForVariable, const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD, const FixitStrategy &S, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
#define FIXABLE_GADGET(name)
#define WARNING_OPTIONAL_GADGET(x)
static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets, WarningGadgetList WarningGadgets, DeclUseTracker Tracker, UnsafeBufferUsageHandler &Handler, bool EmitSuggestions)
#define WARNING_GADGET(name)
static FixItList fixVariable(const VarDecl *VD, FixitStrategy::Kind K, const Decl *D, const DeclUseTracker &Tracker, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static std::optional< StringRef > getRangeText(SourceRange SR, const SourceManager &SM, const LangOptions &LangOpts)
static FixitStrategy getNaiveStrategy(llvm::iterator_range< VarDeclIterTy > UnsafeVars)
static std::optional< std::string > createSpanTypeForVarDecl(const VarDecl *VD, const ASTContext &Ctx)
static SourceLocation getVarDeclIdentifierLoc(const VarDecl *VD)
static std::optional< SourceLocation > getPastLoc(const NodeTy *Node, const SourceManager &SM, const LangOptions &LangOpts)
static bool hasConflictingOverload(const FunctionDecl *FD)
static std::optional< StringRef > getVarDeclIdentifierText(const VarDecl *VD, const SourceManager &SM, const LangOptions &LangOpts)
static std::optional< std::string > getPointeeTypeText(const VarDecl *VD, const SourceManager &SM, const LangOptions &LangOpts, std::optional< Qualifiers > *QualifiersToAppend)
static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD, const ASTContext &Ctx)
static bool overlapWithMacro(const FixItList &FixIts)
static bool hasUnsupportedSpecifiers(const VarDecl *VD, const SourceManager &SM)
#define DEBUG_NOTE_DECL_FAIL(D, Msg)
static void findGadgets(const Stmt *S, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler, bool EmitSuggestions, FixableGadgetList &FixableGadgets, WarningGadgetList &WarningGadgets, DeclUseTracker &Tracker)
Scan the function and return a list of gadgets found with provided kits.
static std::map< const VarDecl *, FixItList > getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S, ASTContext &Ctx, const Decl *D, const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler, const VariableGroupsManager &VarGrpMgr)
static std::optional< FixItList > createOverloadsForFixedParams(const FixitStrategy &S, const FunctionDecl *FD, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static void eraseVarsForUnfixableGroupMates(std::map< const VarDecl *, FixItList > &FixItsForVariable, const VariableGroupsManager &VarGrpMgr)
static FixableGadgetSets groupFixablesByVar(FixableGadgetList &&AllFixableOperations)
static bool isParameterOf(const VarDecl *VD, const Decl *D)
static std::optional< StringRef > getFunNameText(const FunctionDecl *FD, const SourceManager &SM, const LangOptions &LangOpts)
__device__ __2f16 float __ockl_bool s
virtual std::optional< FixItList > getFixits(const FixitStrategy &s) const final
DerefSimplePtrArithFixableGadget(const MatchFinder::MatchResult &Result)
SourceLocation getSourceLoc() const override
virtual DeclUseList getClaimedVarUseSites() const final
virtual std::optional< FixItList > getFixits(const FixitStrategy &S) const override
SourceLocation getSourceLoc() const override
virtual DeclUseList getClaimedVarUseSites() const override
static bool classof(const Gadget *G)
UPCPreIncrementGadget(const MatchFinder::MatchResult &Result)
static bool classof(const Gadget *G)
static Matcher matcher()
UUCAddAssignGadget(const MatchFinder::MatchResult &Result)
virtual std::optional< FixItList > getFixits(const FixitStrategy &S) const override
virtual DeclUseList getClaimedVarUseSites() const override
SourceLocation getSourceLoc() const override
VariableGroupsManagerImpl(const std::vector< VarGrpTy > &Groups, const std::map< const VarDecl *, unsigned > &VarGrpMap, const llvm::SetVector< const VarDecl * > &GrpsUnionForParms)
VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override
Returns the set of variables (including Var) that need to be fixed together in one step.
VarGrpRef getGroupOfParms() const override
Returns the non-empty group of variables that include parameters of the analyzing function,...
APSInt & getInt()
Definition: APValue.h:465
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:188
SourceManager & getSourceManager()
Definition: ASTContext.h:741
const ConstantArrayType * getAsConstantArrayType(QualType T) const
Definition: ASTContext.h:2915
DynTypedNodeList getParents(const NodeT &Node)
Forwards to get node parents from the ParentMapContext.
QualType getFILEType() const
Retrieve the C FILE type.
Definition: ASTContext.h:2089
const LangOptions & getLangOpts() const
Definition: ASTContext.h:834
const TargetInfo & getTargetInfo() const
Definition: ASTContext.h:799
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
Definition: Expr.h:2718
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.h:2761
Attr - This represents one attribute.
Definition: Attr.h:43
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3909
Expr * getLHS() const
Definition: Expr.h:3959
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.h:3964
static StringRef getOpcodeStr(Opcode Op)
getOpcodeStr - Turn an Opcode enum value into the punctuation char it corresponds to,...
Definition: Expr.cpp:2142
Expr * getRHS() const
Definition: Expr.h:3961
Represents a call to a C++ constructor.
Definition: ExprCXX.h:1546
Expr * getArg(unsigned Arg)
Return the specified argument.
Definition: ExprCXX.h:1689
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: ExprCXX.cpp:562
Represents a C++ base or member initializer.
Definition: DeclCXX.h:2318
A use of a default initializer in a constructor or in aggregate initialization.
Definition: ExprCXX.h:1375
Represents a static or instance method of a struct/union/class.
Definition: DeclCXX.h:2078
Represents a C++11 noexcept expression (C++ [expr.unary.noexcept]).
Definition: ExprCXX.h:4126
Represents a C++ struct/union/class.
Definition: DeclCXX.h:258
CXXRecordDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition: DeclCXX.h:524
A C++ typeid expression (C++ [expr.typeid]), which gets the type_info that corresponds to the supplie...
Definition: ExprCXX.h:845
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2874
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Definition: Expr.h:3547
static const char * getCastKindName(CastKind CK)
Definition: Expr.cpp:1960
Represents a character-granular source range.
static CharSourceRange getCharRange(SourceRange R)
SourceLocation getEnd() const
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
Definition: StmtVisitor.h:195
lookup_result lookup(DeclarationName Name) const
lookup - Find the declarations (if any) with the given Name in this context.
Definition: DeclBase.cpp:1854
A reference to a declared variable, function, enum, etc.
Definition: Expr.h:1265
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.cpp:551
ValueDecl * getDecl()
Definition: Expr.h:1333
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
Definition: Stmt.h:1519
bool isSingleDecl() const
isSingleDecl - This method returns true if this DeclStmt refers to a single Decl.
Definition: Stmt.h:1532
decl_range decls()
Definition: Stmt.h:1567
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
bool isInStdNamespace() const
Definition: DeclBase.cpp:422
SourceLocation getEndLoc() const LLVM_READONLY
Definition: DeclBase.h:438
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:520
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
Definition: DeclBase.h:596
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
Definition: DeclBase.h:1076
SourceLocation getLocation() const
Definition: DeclBase.h:442
DeclContext * getDeclContext()
Definition: DeclBase.h:451
attr_range attrs() const
Definition: DeclBase.h:538
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: DeclBase.h:434
virtual SourceRange getSourceRange() const LLVM_READONLY
Source range that this declaration covers.
Definition: DeclBase.h:430
NestedNameSpecifier * getQualifier() const
Retrieve the nested-name-specifier that qualifies the name of this declaration, if it was present in ...
Definition: Decl.h:792
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Decl.h:786
NestedNameSpecifierLoc getQualifierLoc() const
Retrieve the nested-name-specifier (with source-location information) that qualifies the name of this...
Definition: Decl.h:800
TypeSourceInfo * getTypeSourceInfo() const
Definition: Decl.h:764
Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.
const DynTypedNode * begin() const
A dynamically typed AST node container.
SourceRange getSourceRange() const
For nodes which represent textual entities in the source code, return their SourceRange.
const T * get() const
Retrieve the stored node as type T.
static DynTypedNode create(const T &Node)
Creates a DynTypedNode from Node.
Recursive AST visitor that supports extension via dynamic dispatch.
virtual bool TraverseDecl(Decl *D)
Recursively visit a declaration, by dispatching to Traverse*Decl() based on the argument's dynamic ty...
virtual bool TraverseStmt(Stmt *S)
Recursively visit a statement or expression, by dispatching to Traverse*() based on the argument's dy...
bool ShouldVisitImplicitCode
Whether this visitor should recurse into implicit code, e.g.
bool ShouldVisitTemplateInstantiations
Whether this visitor should recurse into template instantiations.
ExplicitCastExpr - An explicit cast written in the source code.
Definition: Expr.h:3799
This represents one expression.
Definition: Expr.h:110
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition: Expr.cpp:3090
Expr * IgnoreImplicit() LLVM_READONLY
Skip past any implicit AST nodes which might surround this expression until reaching a fixed point.
Definition: Expr.cpp:3078
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition: Expr.cpp:3086
NullPointerConstantValueDependence
Enumeration used to describe how isNullPointerConstant() should cope with value-dependent expressions...
Definition: Expr.h:820
std::optional< llvm::APSInt > getIntegerConstantExpr(const ASTContext &Ctx, SourceLocation *Loc=nullptr) const
isIntegerConstantExpr - Return the value if this expression is a valid integer constant expression.
QualType getType() const
Definition: Expr.h:142
Annotates a diagnostic with some code that should be inserted, removed, or replaced to fix the proble...
Definition: Diagnostic.h:75
CharSourceRange RemoveRange
Code that should be replaced to correct the error.
Definition: Diagnostic.h:79
static FixItHint CreateReplacement(CharSourceRange RemoveRange, StringRef Code)
Create a code modification hint that replaces the given source range with the given code string.
Definition: Diagnostic.h:138
static FixItHint CreateRemoval(CharSourceRange RemoveRange)
Create a code modification hint that removes the given source range.
Definition: Diagnostic.h:127
static FixItHint CreateInsertion(SourceLocation InsertionLoc, StringRef Code, bool BeforePreviousInsertions=false)
Create a code modification hint that inserts the given code string at a specific location.
Definition: Diagnostic.h:101
Represents a function declaration or definition.
Definition: Decl.h:1935
const ParmVarDecl * getParamDecl(unsigned i) const
Definition: Decl.h:2672
Stmt * getBody(const FunctionDecl *&Definition) const
Retrieve the body (definition) of the function.
Definition: Decl.cpp:3243
param_iterator param_begin()
Definition: Decl.h:2661
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Definition: Decl.cpp:3702
DeclarationNameInfo getNameInfo() const
Definition: Decl.h:2146
Represents a C11 generic selection.
Definition: Expr.h:5966
One of these records is kept for each identifier that is lexed.
StringRef getName() const
Return the actual identifier string.
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition: Expr.h:3724
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:499
static StringRef getSourceText(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts, bool *Invalid=nullptr)
Returns a string for the source that the range encompasses.
Definition: Lexer.cpp:1023
static bool isAtEndOfMacroExpansion(SourceLocation loc, const SourceManager &SM, const LangOptions &LangOpts, SourceLocation *MacroEnd=nullptr)
Returns true if the given MacroID location points at the last token of the macro expansion.
Definition: Lexer.cpp:893
static unsigned MeasureTokenLength(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
MeasureTokenLength - Relex the token at the specified location and return its length in bytes in the ...
Definition: Lexer.cpp:498
static std::optional< Token > findNextToken(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
Finds the token that comes right after the given location.
Definition: Lexer.cpp:1324
static SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &LangOpts)
Computes the source location just past the end of the token at this source location.
Definition: Lexer.cpp:849
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition: Decl.h:274
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition: Decl.h:280
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition: Decl.h:319
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
Definition: Decl.h:296
SourceLocation getBeginLoc() const
Retrieve the location of the beginning of this nested-name-specifier.
Represents a parameter to a function.
Definition: Decl.h:1725
bool hasDefaultArg() const
Determines whether this parameter has a default argument, either parsed or not.
Definition: Decl.cpp:3023
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition: Decl.cpp:2945
Wrapper for source info for pointers.
Definition: TypeLoc.h:1332
PointerType - C99 6.7.5.1 - Pointer Declarators.
Definition: Type.h:3198
A (possibly-)qualified type.
Definition: Type.h:929
bool hasQualifiers() const
Determine whether this type has any qualifiers.
Definition: Type.h:8020
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition: Type.h:996
Qualifiers getQualifiers() const
Retrieve the set of qualifiers applied to this type.
Definition: Type.h:7971
QualType getCanonicalType() const
Definition: Type.h:7983
bool isConstQualified() const
Determine whether this type is const-qualified.
Definition: Type.h:8004
std::string getAsString() const
redecl_range redecls() const
Returns an iterator range for all the redeclarations of the same decl.
Definition: Redeclarable.h:295
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
SourceLocation getBegin() const
bool isValid() const
Stmt - This represents one statement.
Definition: Stmt.h:84
SourceLocation getEndLoc() const LLVM_READONLY
Definition: Stmt.cpp:357
StmtClass getStmtClass() const
Definition: Stmt.h:1380
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Stmt.cpp:345
Exposes information about the current target.
Definition: TargetInfo.h:220
Base wrapper for a particular "section" of type source info.
Definition: TypeLoc.h:59
UnqualTypeLoc getUnqualifiedLoc() const
Skips past any qualifiers, if this is qualified.
Definition: TypeLoc.h:338
TypeLoc getNextTypeLoc() const
Get the next TypeLoc pointed by this TypeLoc, e.g for "int*" the TypeLoc is a PointerLoc and next Typ...
Definition: TypeLoc.h:170
T castAs() const
Convert to the specified TypeLoc type, asserting that this TypeLoc is of the desired type.
Definition: TypeLoc.h:78
SourceRange getSourceRange() const LLVM_READONLY
Get the full source range.
Definition: TypeLoc.h:153
TypeLocClass getTypeLocClass() const
Definition: TypeLoc.h:116
bool isNull() const
Definition: TypeLoc.h:121
SourceLocation getEndLoc() const
Get the end source location.
Definition: TypeLoc.cpp:235
SourceLocation getBeginLoc() const
Get the begin source location.
Definition: TypeLoc.cpp:192
TypeLoc getTypeLoc() const
Return the TypeLoc wrapper for the type source info.
Definition: TypeLoc.h:256
bool isFunctionPointerType() const
Definition: Type.h:8226
bool isPointerType() const
Definition: Type.h:8186
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition: Type.h:8550
const T * castAs() const
Member-template castAs<specific type>.
Definition: Type.h:8800
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition: Type.cpp:738
bool isUnsignedIntegerType() const
Return true if this is an integer type that is unsigned, according to C99 6.2.5p6 [which returns true...
Definition: Type.cpp:2230
UnaryExprOrTypeTraitExpr - expression with either a type or (unevaluated) expression operand.
Definition: Expr.h:2622
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Definition: Expr.h:2232
SourceLocation getOperatorLoc() const
getOperatorLoc - Return the location of the operator.
Definition: Expr.h:2281
Expr * getSubExpr() const
Definition: Expr.h:2277
Opcode getOpcode() const
Definition: Expr.h:2272
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.h:2354
static StringRef getOpcodeStr(Opcode Op)
getOpcodeStr - Turn an Opcode enum value into the punctuation char it corresponds to,...
Definition: Expr.cpp:1401
The interface that lets the caller handle unsafe buffer usage analysis results by overriding this cla...
void addDebugNoteForVar(const VarDecl *VD, SourceLocation Loc, std::string Text)
virtual std::string getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc, StringRef WSSuffix="") const =0
virtual void handleUnsafeOperation(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx)=0
Invoked when an unsafe operation over raw pointers is found.
virtual void handleUnsafeVariableGroup(const VarDecl *Variable, const VariableGroupsManager &VarGrpMgr, FixItList &&Fixes, const Decl *D, const FixitStrategy &VarTargetTypes)=0
Invoked when a fix is suggested against a variable.
virtual void handleUnsafeOperationInContainer(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx)=0
Invoked when an unsafe operation with a std container is found.
virtual void handleUnsafeLibcCall(const CallExpr *Call, unsigned PrintfInfo, ASTContext &Ctx, const Expr *UnsafeArg=nullptr)=0
Invoked when a call to an unsafe libc function is found.
QualType getType() const
Definition: Decl.h:682
Represents a variable declaration or definition.
Definition: Decl.h:882
bool isConstexpr() const
Whether this variable is (C++11) constexpr.
Definition: Decl.h:1513
VarDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition: Decl.cpp:2246
bool isInlineSpecified() const
Definition: Decl.h:1498
bool hasConstantInitialization() const
Determine whether this variable has constant initialization.
Definition: Decl.cpp:2620
bool hasLocalStorage() const
Returns true if a variable with function scope is a non-static local variable.
Definition: Decl.h:1135
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
Definition: Decl.h:1204
virtual VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm=nullptr) const =0
Returns the set of variables (including Var) that need to be fixed together in one step.
virtual VarGrpRef getGroupOfParms() const =0
Returns the non-empty group of variables that include parameters of the analyzing function,...
bool findMatch(const DynTypedNode &DynNode)
bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) override
bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node) override
bool TraverseCXXDefaultInitExpr(CXXDefaultInitExpr *Node) override
bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) override
bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) override
bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node) override
bool TraverseStmt(Stmt *Node) override
Recursively visit a statement or expression, by dispatching to Traverse*() based on the argument's dy...
bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) override
MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher, internal::ASTMatchFinder *Finder, internal::BoundNodesTreeBuilder *Builder, internal::ASTMatchFinder::BindKind Bind, const bool ignoreUnevaluatedContext)
bool TraverseDecl(Decl *Node) override
Recursively visit a declaration, by dispatching to Traverse*Decl() based on the argument's dynamic ty...
Called when the Match registered for it was successfully found in the AST.
virtual void run(const MatchResult &Result)=0
Called on every match by the MatchFinder.
A class to allow finding matches over the Clang AST.
void addMatcher(const DeclarationMatcher &NodeMatch, MatchCallback *Action)
Adds a matcher to execute when running over the AST.
void match(const T &Node, ASTContext &Context)
Calls the registered callbacks on all matches on the given Node.
bool ParsePrintfString(FormatStringHandler &H, const char *beg, const char *end, const LangOptions &LO, const TargetInfo &Target, bool isFreeBSDKPrintf)
static bool isNullTermPointer(const Expr *Ptr)
static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg, const unsigned FmtArgIdx, ASTContext &Ctx, bool isKprintf=false)
const internal::VariadicDynCastAllOfMatcher< Decl, VarDecl > varDecl
Matches variable declarations.
const internal::VariadicDynCastAllOfMatcher< Stmt, DeclRefExpr > declRefExpr
Matches expressions that refer to declarations.
const AstTypeMatcher< EnumType > enumType
Matches enum types.
static auto isInUnspecifiedLvalueContext(internal::Matcher< Expr > innerMatcher)
const internal::VariadicOperatorMatcherFunc< 1, 1 > unless
Matches if the provided matcher does not match.
const internal::VariadicDynCastAllOfMatcher< Stmt, ImplicitCastExpr > implicitCastExpr
Matches the implicit cast nodes of Clang's AST.
const internal::VariadicDynCastAllOfMatcher< Stmt, StringLiteral > stringLiteral
Matches string literals (also matches wide string literals).
const AstTypeMatcher< PointerType > pointerType
Matches pointer types, but does not match Objective-C object pointer types.
const internal::VariadicDynCastAllOfMatcher< Decl, BindingDecl > bindingDecl
Matches binding declarations Example matches foo and bar (matcher = bindingDecl()
internal::Matcher< NamedDecl > hasName(StringRef Name)
Matches NamedDecl nodes that have the specified name.
Definition: ASTMatchers.h:3090
const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr
Matches call expressions.
const internal::VariadicDynCastAllOfMatcher< Stmt, CompoundStmt > compoundStmt
Matches compound statements.
const internal::ArgumentAdaptingMatcherFunc< internal::ForEachMatcher > forEach
Matches AST nodes that have child AST nodes that match the provided matcher.
const AstTypeMatcher< ArrayType > arrayType
Matches all kinds of arrays.
const internal::VariadicDynCastAllOfMatcher< Stmt, UnaryOperator > unaryOperator
Matches unary operator expressions.
const internal::VariadicDynCastAllOfMatcher< Stmt, ArraySubscriptExpr > arraySubscriptExpr
Matches array subscript expressions.
static internal::Matcher< Stmt > isInUnspecifiedUntypedContext(internal::Matcher< Stmt > InnerMatcher)
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXMemberCallExpr > cxxMemberCallExpr
Matches member call expressions.
const internal::VariadicDynCastAllOfMatcher< Decl, CXXConstructorDecl > cxxConstructorDecl
Matches C++ constructor declarations.
const internal::VariadicDynCastAllOfMatcher< Stmt, ArrayInitIndexExpr > arrayInitIndexExpr
The arrayInitIndexExpr consists of two subexpressions: a common expression (the source array) that is...
const internal::VariadicDynCastAllOfMatcher< Stmt, BinaryOperator > binaryOperator
Matches binary operator expressions.
const internal::VariadicDynCastAllOfMatcher< Stmt, ParenExpr > parenExpr
Matches parentheses used in expressions.
const internal::ArgumentAdaptingMatcherFunc< internal::HasMatcher > has
Matches AST nodes that have child AST nodes that match the provided matcher.
const internal::VariadicDynCastAllOfMatcher< Stmt, ExplicitCastExpr > explicitCastExpr
Matches explicit cast expressions.
internal::PolymorphicMatcher< internal::ValueEqualsMatcher, void(internal::AllNodeBaseTypes), ValueT > equals(const ValueT &Value)
Matches literals that are equal to the given value of type ValueT.
Definition: ASTMatchers.h:5859
const AstTypeMatcher< ConstantArrayType > constantArrayType
Matches C arrays with a specified constant size.
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> eachOf
Matches if any of the given matchers matches.
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXConstructExpr > cxxConstructExpr
Matches constructor call expressions (including implicit ones).
const internal::VariadicDynCastAllOfMatcher< Decl, FieldDecl > fieldDecl
Matches field declarations.
static internal::Matcher< Stmt > isInUnspecifiedPointerContext(internal::Matcher< Stmt > InnerMatcher)
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> allOf
Matches if all given matchers match.
const internal::VariadicDynCastAllOfMatcher< Decl, FunctionDecl > functionDecl
Matches function declarations.
const internal::VariadicDynCastAllOfMatcher< Stmt, MemberExpr > memberExpr
Matches member expressions.
static auto hasPointerType()
const internal::VariadicDynCastAllOfMatcher< Stmt, IntegerLiteral > integerLiteral
Matches integer literals of all sizes / encodings, e.g.
internal::PolymorphicMatcher< internal::HasDeclarationMatcher, void(internal::HasDeclarationSupportedTypes), internal::Matcher< Decl > > hasDeclaration(const internal::Matcher< Decl > &InnerMatcher)
Matches a node if the declaration associated with that node matches the given matcher.
Definition: ASTMatchers.h:3664
const internal::VariadicDynCastAllOfMatcher< Stmt, DeclStmt > declStmt
Matches declaration statements.
const internal::VariadicAllOfMatcher< Stmt > stmt
Matches statements.
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> anyOf
Matches if any of the given matchers matches.
const internal::VariadicFunction< internal::PolymorphicMatcher< internal::HasAnyOperatorNameMatcher, AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr, CXXRewrittenBinaryOperator, UnaryOperator), std::vector< std::string > >, StringRef, internal::hasAnyOperatorNameFunc > hasAnyOperatorName
Matches operator expressions (binary or unary) that have any of the specified names.
const internal::VariadicDynCastAllOfMatcher< Decl, CXXMethodDecl > cxxMethodDecl
Matches method declarations.
const internal::VariadicDynCastAllOfMatcher< Stmt, CastExpr > castExpr
Matches any cast nodes of Clang's AST.
const internal::VariadicDynCastAllOfMatcher< Stmt, IfStmt > ifStmt
Matches if statements.
bool anyConflict(const llvm::SmallVectorImpl< FixItHint > &FixIts, const SourceManager &SM)
bool Call(InterpState &S, CodePtr OpPC, const Function *Func, uint32_t VarArgSize)
Definition: Interp.cpp:1223
RangeSelector member(std::string ID)
Given a MemberExpr, selects the member token.
The JSON file list parser is used to communicate input to InstallAPI.
void checkUnsafeBufferUsage(const Decl *D, UnsafeBufferUsageHandler &Handler, bool EmitSuggestions)
std::vector< const VarDecl * > VarGrpTy
const FunctionProtoType * T
Diagnostic wrappers for TextAPI types for error reporting.
Definition: Dominators.h:30
#define false
Definition: stdbool.h:26
bool operator()(const NodeTy *N1, const NodeTy *N2) const
std::map< const VarDecl *, std::set< const FixableGadget * >, CompareNode< VarDecl > > byVar
std::map< const VarDecl *, std::set< const WarningGadget * >, CompareNode< VarDecl > > byVar
llvm::SmallVector< const WarningGadget *, 16 > noVar
SourceLocation getBeginLoc() const
getBeginLoc - Retrieve the location of the first token.
SourceLocation getEndLoc() const LLVM_READONLY
EvalResult is a struct with detailed info about an evaluated expression.
Definition: Expr.h:642
APValue Val
Val - This is the value the expression can be folded to.
Definition: Expr.h:644
Wraps an identifier and optional source location for the identifier.
Definition: ParsedAttr.h:103
Contains all information for a given match.