clang: lib/Analysis/UnsafeBufferUsage.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
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
28#include
29#include
30#include
31
32using namespace llvm;
33using namespace clang;
34using namespace ast_matchers;
35
36#ifndef NDEBUG
37namespace {
38class StmtDebugPrinter
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) {
49 }
50
51 std::string VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
52 return "ImplicitCastExpr(" + std::string(ICE->getCastKindName()) + ")";
53 }
54};
55
56
57
58static std::string getDREAncestorString(const DeclRefExpr *DRE,
60 std::stringstream SS;
61 const Stmt *St = DRE;
62 StmtDebugPrinter StmtPriner;
63
64 do {
65 SS << StmtPriner.Visit(St);
66
68
69 if (StParents.size() > 1)
70 return "unavailable due to multiple parents";
71 if (StParents.size() == 0)
72 break;
74 if (St)
75 SS << " ==> ";
76 } while (St);
77 return SS.str();
78}
79}
80#endif
81
83
84
86public:
87
88
89
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) {
99 }
100
101
102
104 Matches = false;
105 if (const Stmt *StmtNode = DynNode.get<Stmt>()) {
107 *Builder = ResultBindings;
108 return Matches;
109 }
110 return false;
111 }
112
113
114
115
116
117
118
119
122 return true;
123 if (!match(*Node))
124 return false;
125
126 if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node))
127 return true;
128
130 }
131
133
134 if (ignoreUnevaluatedContext)
136 return DynamicRecursiveASTVisitor::TraverseGenericSelectionExpr(Node);
137 }
138
139 bool
141
142 if (ignoreUnevaluatedContext)
143 return true;
144 return DynamicRecursiveASTVisitor::TraverseUnaryExprOrTypeTraitExpr(Node);
145 }
146
148
149 if (ignoreUnevaluatedContext)
150 return true;
151 return DynamicRecursiveASTVisitor::TraverseTypeOfExprTypeLoc(Node);
152 }
153
155
156 if (ignoreUnevaluatedContext)
157 return true;
158 return DynamicRecursiveASTVisitor::TraverseDecltypeTypeLoc(Node);
159 }
160
162
163 if (ignoreUnevaluatedContext)
164 return true;
165 return DynamicRecursiveASTVisitor::TraverseCXXNoexceptExpr(Node);
166 }
167
169
170 if (ignoreUnevaluatedContext)
171 return true;
172 return DynamicRecursiveASTVisitor::TraverseCXXTypeidExpr(Node);
173 }
174
177 return false;
178 return DynamicRecursiveASTVisitor::TraverseCXXDefaultInitExpr(Node);
179 }
180
183 return true;
184 if (!match(*Node))
185 return false;
187 }
188
189private:
190
191
192
193
194 template bool match(const T &Node) {
195 internal::BoundNodesTreeBuilder RecursiveBuilder(*Builder);
196
198 &RecursiveBuilder)) {
199 ResultBindings.addMatch(RecursiveBuilder);
200 Matches = true;
201 if (Bind != internal::ASTMatchFinder::BK_All)
202 return false;
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
218 return hasType(hasCanonicalType(pointerType()));
219}
220
222
224 innerMatcher) {
225 const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
226
228 true);
230}
231
233 innerMatcher) {
234 const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
235
237 false);
239}
240
241
243 Handler) {
244 return !Handler->isSafeBufferOptOut(Node.getBeginLoc());
245}
246
249 return Handler->ignoreUnsafeBufferInContainer(Node.getBeginLoc());
250}
251
253 Handler) {
254 if (Finder->getASTContext().getLangOpts().CPlusPlus)
255 return Handler->ignoreUnsafeBufferInLibcCall(Node.getBeginLoc());
256 return true;
257}
258
260 return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder);
261}
262
263
265 return Node.getOpcode() == UnaryOperator::Opcode::UO_PreInc;
266}
267
268
269
271
272 return
275 hasCastKind(CastKind::CK_LValueToRValue),
276 castSubExpr(innerMatcher)),
279 hasLHS(innerMatcher)
280 )
281 ));
282
283}
284
285
286
287static internal::Matcher
289
290
291
292
293
294
295
296
297
298 auto CallArgMatcher = callExpr(
299 forEachArgumentWithParamType(
300 InnerMatcher,
301 isAnyPointer() ),
304
305 auto CastOperandMatcher =
306 castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral),
307 hasCastKind(CastKind::CK_PointerToBoolean)),
309
310 auto CompOperandMatcher =
314
315
316 auto PtrSubtractionMatcher =
318
319
320
323 eachOf(hasLHS(InnerMatcher),
324 hasRHS(InnerMatcher)));
325
326
327 return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher,
328 PtrSubtractionMatcher));
329
330
331}
332
333
334
335
336
337
338
339
340
341
342
343static internal::Matcher
345
346
347
348
350 auto IfStmtThen = ifStmt(hasThen(InnerMatcher));
351 auto IfStmtElse = ifStmt(hasElse(InnerMatcher));
352
353 return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse));
354}
355
356
357
358
359
360
361
362
363
364
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) {
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(E0))
379 if (auto *DRE1 = dyn_cast(E1)) {
380 return DRE0->getDecl() == DRE1->getDecl();
381 }
382 return false;
383 };
384 std::optional Arg1CV =
386
387 if (Arg1CV && Arg1CV->isZero())
388
389 return true;
391 case Stmt::CXXNewExprClass:
392 if (auto Size = cast(Arg0)->getArraySize()) {
393
394 return AreSameDRE((*Size)->IgnoreImplicit(), Arg1) ||
395 HaveEqualConstantValues(*Size, Arg1);
396 }
397
398 if (!cast(Arg0)->hasPlaceholderType()) {
399
400 return Arg1CV && Arg1CV->isOne();
401 }
402 break;
403 case Stmt::UnaryOperatorClass:
404 if (cast(Arg0)->getOpcode() ==
405 UnaryOperator::Opcode::UO_AddrOf)
406
407 return Arg1CV && Arg1CV->isOne();
408 break;
409 case Stmt::CallExprClass:
410 if (const auto *CE = dyn_cast(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
423
424 if (auto *ConstArrTy =
425 Finder->getASTContext().getAsConstantArrayType(Arg0Ty)) {
426 const APSInt ConstArrSize = APSInt(ConstArrTy->getSize());
427
428
429 return Arg1CV && APSInt::compareValues(ConstArrSize, *Arg1CV) == 0;
430 }
431 return false;
432}
433
435
436
437
438
439
440
441
442 uint64_t limit;
443 if (const auto *CATy =
444 dyn_cast(Node.getBase()
445 ->IgnoreParenImpCasts()
446 ->getType()
447 ->getUnqualifiedDesugaredType())) {
448 limit = CATy->getLimitedSize();
449 } else if (const auto *SLiteral = dyn_cast(
450 Node.getBase()->IgnoreParenImpCasts())) {
451 limit = SLiteral->getLength() + 1;
452 } else {
453 return false;
454 }
455
456 if (const auto *IdxLit = dyn_cast(Node.getIdx())) {
457 const APInt ArrIdx = IdxLit->getValue();
458 if (ArrIdx.isNonNegative() && ArrIdx.getLimitedValue() < limit)
459 return true;
460 }
461 return false;
462}
463
465 return Node.getNumArgs() == Num;
466}
467
468namespace libc_func_matchers {
469
470
471
472
473
474
475
476
477
478
479
480
481
482
484 StringRef matchName(StringRef FunName, bool isBuiltin) {
485
486 if (isBuiltin && FunName.starts_with("__builtin_"))
487
488
490 FunName.drop_front(10 ));
491
492 if (FunName.starts_with("__asan_"))
493 return matchLibcName(FunName.drop_front(7 ));
495 }
496
497
498
500 if (Name.starts_with("__") && Name.ends_with("_chk"))
502 Name.drop_front(2).drop_back(4) );
504 }
505
507 if (Name.ends_with("_s"))
508 return Name.drop_back(2 );
509 return Name;
510 }
511};
512
513
514
517 return true;
519 return true;
520 if (auto *MCE = dyn_cast(Ptr->IgnoreParenImpCasts())) {
523
525 if (MD->getName() == "c_str" && RD->getName() == "basic_string")
526 return true;
527 }
528 return false;
529}
530
531
532
533
534
535
536
537
538
540 const unsigned FmtArgIdx, ASTContext &Ctx,
541 bool isKprintf = false) {
542 class StringFormatStringHandler
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
554 const char *startSpecifier,
555 unsigned specifierLen,
557 if (FS.getConversionSpecifier().getKind() ==
558 analyze_printf::PrintfConversionSpecifier::sArg) {
559 unsigned ArgIdx = FS.getPositionalArgIndex() + FmtArgIdx;
560
561 if (0 < ArgIdx && ArgIdx < Call->getNumArgs())
563 UnsafeArg = Call->getArg(ArgIdx);
564
565 return false;
566 }
567 }
568 return true;
569 }
570 };
571
572 const Expr *Fmt = Call->getArg(FmtArgIdx);
573
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(),
589 }
590CHECK_UNSAFE_PTR:
591
592
593
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
606
607
608
609
610
611
612
613
615 static std::unique_ptr<std::set> PredefinedNames = nullptr;
616 if (!PredefinedNames)
617 PredefinedNames =
618 std::make_unique<std::set, std::set>({
619
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
634
635 "strcpy",
636 "strncpy",
637 "strlcpy",
638 "strcat",
639 "strncat",
640 "strlcat",
641 "strxfrm",
642 "strdup",
643 "strndup",
644
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
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
673 "fread",
674 "fwrite",
675 "fgets",
676 "fgetws",
677 "gets",
678 "fputs",
679 "fputws",
680 "puts",
681
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
696 II->getName(), Node.getBuiltinID());
697
698
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:🧵: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
714
715 return Name.ends_with("scanf");
716}
717
718
719
720
722 auto *II = Node.getIdentifier();
723
724 if (!II)
725 return false;
726
728 II->getName(), Node.getBuiltinID());
729
730 if (!Name.ends_with("printf"))
731 return false;
732 return Name.starts_with("v");
733}
734
735
736
738 auto *II = Node.getIdentifier();
739
740 if (!II)
741 return false;
742
744 II->getName(), Node.getBuiltinID());
745
746 if (!Name.ends_with("printf") ||
747
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
759
760
762 auto *II = Node.getIdentifier();
763
764 if (!II)
765 return false;
766
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
782
783
784
786 clang::ast_matchers::internal::Matcher,
787 UnsafeStringArgMatcher) {
788
790
791 assert(FD && "It should have been checked that FD is non-null.");
792
794
795 if (NumParms < 1)
796 return false;
797
798 ASTContext &Ctx = Finder->getASTContext();
800
802 return false;
803
805
807 .isNull() &&
809
810 const Expr *UnsafeArg;
811
813 return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);
814 return false;
815 }
816
818
819 bool isKprintf = false;
820 const Expr *UnsafeArg;
821
823 isKprintf = II->getName() == "kprintf";
825 return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);
826 return false;
827 }
828
829 if (NumParms > 2) {
831
833
834
835 const Expr *UnsafeArg;
836
838 return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);
839 return false;
840 }
841 }
842
843
844 for (auto Arg : Node.arguments())
846 if (UnsafeStringArgMatcher.matches(*Arg, Finder, Builder))
847 return true;
848 return false;
849}
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
869
870 assert(FD && "It should have been checked that FD is non-null.");
871
873 return false;
874
876
878 return false;
879
881 const Expr *Buf = Node.getArg(0), *Size = Node.getArg(1);
882
884 !Size->getType()->isIntegerType())
885 return false;
886
887
888 static StringRef SizedObjs[] = {"span", "array", "vector",
889 "basic_string_view", "basic_string"};
891 Size = Size->IgnoreParenImpCasts();
892 if (auto *MCEPtr = dyn_cast(Buf))
893 if (auto *MCESize = dyn_cast(Size)) {
894 auto *DREOfPtr = dyn_cast(
895 MCEPtr->getImplicitObjectArgument()->IgnoreParenImpCasts());
896 auto *DREOfSize = dyn_cast(
897 MCESize->getImplicitObjectArgument()->IgnoreParenImpCasts());
898
899 if (!DREOfPtr || !DREOfSize)
900 return true;
901 if (DREOfPtr->getDecl() != DREOfSize->getDecl())
902 return true;
903 if (MCEPtr->getMethodDecl()->getName() != "data")
904 return true;
905
906 if (MCESize->getMethodDecl()->getName() == "size_bytes" ||
907
908
909
910
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;
917 }
918
919
921 ASTContext &Ctx = Finder->getASTContext();
922
925
926
927
928 if (Size->EvaluateAsConstantExpr(ER, Ctx)) {
929 APSInt EVal = ER.Val.getInt();
930
931 return APSInt::compareValues(EVal, APSInt(CAT->getSize(), true)) != 0;
932 }
933 }
934 }
935 return true;
936}
937}
938}
939
940namespace {
941
942
944
945
947}
948
949namespace {
950
951
952
953
954
955
956
957
958class Gadget {
959public:
960 enum class Kind {
961#define GADGET(x) x,
962#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
963 };
964
965
966
967
968 using Matcher = decltype(stmt());
969
970 Gadget(Kind K) : K(K) {}
971
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
988
990
991
992
993
994 virtual DeclUseList getClaimedVarUseSites() const = 0;
995
996 virtual ~Gadget() = default;
997
998private:
1000};
1001
1002
1003
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
1012 bool IsRelatedToDecl,
1014};
1015
1016
1017
1018
1019
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
1028
1029
1030 virtual std::optional getFixits(const FixitStrategy &) const {
1031 return std::nullopt;
1032 }
1033
1034
1035
1036
1037
1038
1039
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>;
1049using WarningGadgetList = std::vector<std::unique_ptr>;
1050
1051
1052
1053class IncrementGadget : public WarningGadget {
1054 static constexpr const char *const OpTag = "op";
1056
1057public:
1059 : WarningGadget(Kind::Increment),
1061
1062 static bool classof(const Gadget *G) {
1063 return G->getKind() == Kind::Increment;
1064 }
1065
1066 static Matcher matcher() {
1067 return stmt(
1069 hasUnaryOperand(ignoringParenImpCasts(hasPointerType())))
1070 .bind(OpTag));
1071 }
1072
1074 bool IsRelatedToDecl,
1077 }
1079
1080 DeclUseList getClaimedVarUseSites() const override {
1082 if (const auto *DRE =
1084 Uses.push_back(DRE);
1085 }
1086
1087 return std::move(Uses);
1088 }
1089};
1090
1091
1092
1093class DecrementGadget : public WarningGadget {
1094 static constexpr const char *const OpTag = "op";
1096
1097public:
1099 : WarningGadget(Kind::Decrement),
1101
1102 static bool classof(const Gadget *G) {
1103 return G->getKind() == Kind::Decrement;
1104 }
1105
1106 static Matcher matcher() {
1107 return stmt(
1109 hasUnaryOperand(ignoringParenImpCasts(hasPointerType())))
1110 .bind(OpTag));
1111 }
1112
1114 bool IsRelatedToDecl,
1117 }
1119
1120 DeclUseList getClaimedVarUseSites() const override {
1121 if (const auto *DRE =
1123 return {DRE};
1124 }
1125
1126 return {};
1127 }
1128};
1129
1130
1131
1132class ArraySubscriptGadget : public WarningGadget {
1133 static constexpr const char *const ArraySubscrTag = "ArraySubscript";
1135
1136public:
1138 : WarningGadget(Kind::ArraySubscript),
1140
1141 static bool classof(const Gadget *G) {
1142 return G->getKind() == Kind::ArraySubscript;
1143 }
1144
1145 static Matcher matcher() {
1146
1148 hasBase(ignoringParenImpCasts(
1151 isSafeArraySubscript(),
1152 hasIndex(
1154 )
1155 ))).bind(ArraySubscrTag));
1156
1157 }
1158
1160 bool IsRelatedToDecl,
1163 }
1165
1166 DeclUseList getClaimedVarUseSites() const override {
1167 if (const auto *DRE =
1169 return {DRE};
1170 }
1171
1172 return {};
1173 }
1174};
1175
1176
1177
1178
1179
1180class PointerArithmeticGadget : public WarningGadget {
1181 static constexpr const char *const PointerArithmeticTag = "ptrAdd";
1182 static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr";
1183 const BinaryOperator *PA;
1184 const Expr *Ptr;
1185
1186public:
1188 : WarningGadget(Kind::PointerArithmetic),
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("+"),
1201 hasLHS(HasIntegerType));
1202 auto PtrAtLeft =
1203 allOf(anyOf(hasOperatorName("+"), hasOperatorName("-"),
1204 hasOperatorName("+="), hasOperatorName("-=")),
1206 hasRHS(HasIntegerType));
1207
1209 .bind(PointerArithmeticTag));
1210 }
1211
1213 bool IsRelatedToDecl,
1216 }
1218
1219 DeclUseList getClaimedVarUseSites() const override {
1220 if (const auto *DRE = dyn_cast(Ptr->IgnoreParenImpCasts())) {
1221 return {DRE};
1222 }
1223
1224 return {};
1225 }
1226
1227
1228};
1229
1230class SpanTwoParamConstructorGadget : public WarningGadget {
1231 static constexpr const char *const SpanTwoParamConstructorTag =
1232 "spanTwoParamConstructor";
1233 const CXXConstructExpr *Ctor;
1234
1235public:
1237 : WarningGadget(Kind::SpanTwoParamConstructor),
1239 SpanTwoParamConstructorTag)) {}
1240
1241 static bool classof(const Gadget *G) {
1242 return G->getKind() == Kind::SpanTwoParamConstructor;
1243 }
1244
1245 static Matcher matcher() {
1248 parameterCountIs(2)));
1249
1251 unless(isSafeSpanTwoParamConstruct()))
1252 .bind(SpanTwoParamConstructorTag));
1253 }
1254
1256 return stmt(unless(ignoreUnsafeBufferInContainer(Handler)), matcher());
1257 }
1258
1260 bool IsRelatedToDecl,
1263 }
1265
1266 DeclUseList getClaimedVarUseSites() const override {
1267
1268
1269 if (auto *DRE = dyn_cast(Ctor->getArg(0))) {
1270 if (isa(DRE->getDecl()))
1271 return {DRE};
1272 }
1273 return {};
1274 }
1275};
1276
1277
1278
1279
1280
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;
1286 const DeclRefExpr *PtrInitRHS;
1287
1288public:
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(
1302 .bind(PointerInitRHSTag))))
1303 .bind(PointerInitLHSTag)));
1304
1305 return stmt(PtrInitStmt);
1306 }
1307
1308 virtual std::optional
1309 getFixits(const FixitStrategy &S) const override;
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(PtrInitRHS->getDecl()));
1321 }
1322};
1323
1324
1325
1326
1327
1328
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;
1334 const DeclRefExpr *PtrRHS;
1335
1336public:
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() {
1348 allOf(hasOperatorName("="),
1349 hasRHS(ignoringParenImpCasts(
1351 .bind(PointerAssignRHSTag))),
1353 .bind(PointerAssignLHSTag))));
1354
1356 }
1357
1358 virtual std::optional
1359 getFixits(const FixitStrategy &S) const override;
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(PtrLHS->getDecl()),
1369 cast(PtrRHS->getDecl()));
1370 }
1371};
1372
1373
1374
1375
1376
1377
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;
1383 const DeclRefExpr *PtrRHS;
1384
1385public:
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() {
1397 allOf(hasOperatorName("="),
1398 hasRHS(ignoringParenImpCasts(
1400 toSupportedVariable())
1401 .bind(PointerAssignRHSTag))),
1403 .bind(PointerAssignLHSTag))));
1404
1406 }
1407
1408 virtual std::optional
1409 getFixits(const FixitStrategy &S) const override;
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
1423
1424class UnsafeBufferUsageAttrGadget : public WarningGadget {
1425 constexpr static const char *const OpTag = "attr_expr";
1426 const Expr *Op;
1427
1428public:
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 =
1440
1441 auto HasUnsafeFnDecl =
1443
1445 memberExpr(HasUnsafeFieldDecl).bind(OpTag)));
1446 }
1447
1449 bool IsRelatedToDecl,
1452 }
1454
1455 DeclUseList getClaimedVarUseSites() const override { return {}; }
1456};
1457
1458
1459
1460
1461class UnsafeBufferUsageCtorAttrGadget : public WarningGadget {
1462 constexpr static const char *const OpTag = "cxx_construct_expr";
1464
1465public:
1467 : WarningGadget(Kind::UnsafeBufferUsageCtorAttr),
1469
1470 static bool classof(const Gadget *G) {
1471 return G->getKind() == Kind::UnsafeBufferUsageCtorAttr;
1472 }
1473
1474 static Matcher matcher() {
1475 auto HasUnsafeCtorDecl =
1477
1478 auto HasTwoParamSpanCtorDecl = SpanTwoParamConstructorGadget::matcher();
1479 return stmt(
1481 .bind(OpTag));
1482 }
1483
1485 bool IsRelatedToDecl,
1488 }
1490
1491 DeclUseList getClaimedVarUseSites() const override { return {}; }
1492};
1493
1494
1495
1496
1497
1498class DataInvocationGadget : public WarningGadget {
1499 constexpr static const char *const OpTag = "data_invocation_expr";
1501
1502public:
1504 : WarningGadget(Kind::DataInvocation),
1506
1507 static bool classof(const Gadget *G) {
1508 return G->getKind() == Kind::DataInvocation;
1509 }
1510
1511 static Matcher matcher() {
1512
1516 hasName("std::vector"))))));
1517 return stmt(
1519 .bind(OpTag));
1520 }
1521
1523 bool IsRelatedToDecl,
1526 }
1528
1529 DeclUseList getClaimedVarUseSites() const override { return {}; }
1530};
1531
1532class UnsafeLibcFunctionCallGadget : public WarningGadget {
1534 const Expr *UnsafeArg = nullptr;
1535 constexpr static const char *const Tag = "UnsafeLibcFunctionCall";
1536
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,
1548 SPRINTF = 1,
1549 SIZED_BY =
1550 2,
1551
1552 STRING = 3,
1553
1554 VA_LIST = 4,
1555
1556 } WarnedFunKind = OTHERS;
1557
1558public:
1560 : WarningGadget(Kind::UnsafeLibcFunctionCall),
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
1575 return stmt(unless(ignoreUnsafeLibcCall(Handler)),
1579
1580
1581 functionDecl(libc_func_matchers::isPredefinedUnsafeLibcFunc()),
1582
1583
1584
1585 functionDecl(libc_func_matchers::isUnsafeVaListPrintfFunc())
1586 .bind(UnsafeVaListTag),
1587
1588
1589 functionDecl(libc_func_matchers::isUnsafeSprintfFunc())
1590 .bind(UnsafeSprintfTag)))),
1591
1594
1595
1596
1597
1598
1599
1600
1602 libc_func_matchers::hasUnsafeSnprintfBuffer())
1603 .bind(UnsafeSizedByTag),
1604
1605
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
1616 bool IsRelatedToDecl,
1619 }
1620
1621 DeclUseList getClaimedVarUseSites() const override { return {}; }
1622};
1623
1624
1625
1626
1627class ULCArraySubscriptGadget : public FixableGadget {
1628private:
1629 static constexpr const char *const ULCArraySubscriptTag =
1630 "ArraySubscriptUnderULC";
1632
1633public:
1635 : FixableGadget(Kind::ULCArraySubscript),
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() {
1646 auto BaseIsArrayOrPtrDRE = hasBase(
1647 ignoringParenImpCasts(declRefExpr(ArrayOrPtr, toSupportedVariable())));
1650
1652 }
1653
1654 virtual std::optional
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(Node->getBase()->IgnoreImpCasts())) {
1661 return {DRE};
1662 }
1663 return {};
1664 }
1665};
1666
1667
1668
1669
1670class UPCStandalonePointerGadget : public FixableGadget {
1671private:
1672 static constexpr const char *const DeclRefExprTag = "StandalonePointer";
1674
1675public:
1677 : FixableGadget(Kind::UPCStandalonePointer),
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() {
1688 auto target = expr(ignoringParenImpCasts(
1690 .bind(DeclRefExprTag)));
1692 }
1693
1694 virtual std::optional
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;
1707
1708public:
1710 : FixableGadget(Kind::PointerDereference),
1711 BaseDeclRefExpr(
1714
1715 static bool classof(const Gadget *G) {
1716 return G->getKind() == Kind::PointerDereference;
1717 }
1718
1719 static Matcher matcher() {
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
1735 getFixits(const FixitStrategy &S) const override;
1737};
1738
1739
1740
1741
1742class UPCAddressofArraySubscriptGadget : public FixableGadget {
1743private:
1744 static constexpr const char *const UPCAddressofArraySubscriptTag =
1745 "AddressofArraySubscriptUnderUPC";
1747
1748public:
1750 : FixableGadget(Kind::ULCArraySubscript),
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() {
1763 hasOperatorName("&"),
1765 ignoringParenImpCasts(declRefExpr(toSupportedVariable()))))))
1766 .bind(UPCAddressofArraySubscriptTag)))));
1767 }
1768
1769 virtual std::optional
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(Node->getSubExpr());
1775 const auto *DRE =
1776 cast(ArraySubst->getBase()->IgnoreParenImpCasts());
1777 return {DRE};
1778 }
1779};
1780}
1781
1782namespace {
1783
1784
1785
1786class DeclUseTracker {
1787 using UseSetTy = SmallSet<const DeclRefExpr *, 16>;
1788 using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>;
1789
1790
1791 std::unique_ptr Uses{std::make_unique()};
1792 DefMapTy Defs{};
1793
1794public:
1795 DeclUseTracker() = default;
1796 DeclUseTracker(const DeclUseTracker &) = delete;
1797 DeclUseTracker &operator=(const DeclUseTracker &) = delete;
1798 DeclUseTracker(DeclUseTracker &&) = default;
1799 DeclUseTracker &operator=(DeclUseTracker &&) = default;
1800
1801
1802 void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }
1803
1804
1806 assert(Uses->count(DRE) &&
1807 "DRE not found or claimed by multiple matchers!");
1808 Uses->erase(DRE);
1809 }
1810
1811
1812 bool hasUnclaimedUses(const VarDecl *VD) const {
1813
1814 return any_of(*Uses, [VD](const DeclRefExpr *DRE) {
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) {
1831 if (const auto *VD = dyn_cast(D)) {
1832
1833
1834
1835
1836
1837 Defs[VD] = DS;
1838 }
1839 }
1840 }
1841
1843 return Defs.lookup(VD);
1844 }
1845};
1846}
1847
1848
1849
1851private:
1852 static constexpr const char *const UPCPreIncrementTag =
1853 "PointerPreIncrementUnderUPC";
1855
1856public:
1858 : FixableGadget(Kind::UPCPreIncrement),
1860 assert(Node != nullptr && "Expecting a non-null matching result");
1861 }
1862
1864 return G->getKind() == Kind::UPCPreIncrement;
1865 }
1866
1868
1869
1870
1871
1874 hasUnaryOperand(declRefExpr(toSupportedVariable())))
1875 .bind(UPCPreIncrementTag)))));
1876 }
1877
1878 virtual std::optional
1881
1883 return {dyn_cast(Node->getSubExpr())};
1884 }
1885};
1886
1887
1888
1890private:
1891 static constexpr const char *const UUCAddAssignTag =
1892 "PointerAddAssignUnderUUC";
1893 static constexpr const char *const OffsetTag = "Offset";
1894
1896 const Expr *Offset = nullptr;
1897
1898public:
1900 : FixableGadget(Kind::UUCAddAssign),
1902 Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) {
1903 assert(Node != nullptr && "Expecting a non-null matching result");
1904 }
1905
1907 return G->getKind() == Kind::UUCAddAssign;
1908 }
1909
1911
1914 hasLHS(
1917 toSupportedVariable())),
1918 hasRHS(expr().bind(OffsetTag)))
1919 .bind(UUCAddAssignTag)))));
1920
1921 }
1922
1923 virtual std::optional
1926
1928 return {dyn_cast(Node->getLHS())};
1929 }
1930};
1931
1932
1933
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;
1944
1945public:
1947 : FixableGadget(Kind::DerefSimplePtrArithFixable),
1948 BaseDeclRefExpr(
1953
1955
1957 ignoringImpCasts(declRefExpr(toSupportedVariable()).
1958 bind(BaseDeclRefExprTag)));
1959 auto PlusOverPtrAndInteger = expr(anyOf(
1960 binaryOperator(hasOperatorName("+"), hasLHS(ThePtr),
1962 .bind(AddOpTag),
1963 binaryOperator(hasOperatorName("+"), hasRHS(ThePtr),
1965 .bind(AddOpTag)));
1967 hasOperatorName("*"),
1968 hasUnaryOperand(ignoringParens(PlusOverPtrAndInteger)))
1969 .bind(DerefOpTag));
1970
1971 }
1972
1973 virtual std::optional
1977 }
1978
1980 return {BaseDeclRefExpr};
1981 }
1982};
1983
1984
1987 bool EmitSuggestions, FixableGadgetList &FixableGadgets,
1988 WarningGadgetList &WarningGadgets,
1989 DeclUseTracker &Tracker) {
1990
1992 GadgetFinderCallback(FixableGadgetList &FixableGadgets,
1993 WarningGadgetList &WarningGadgets,
1994 DeclUseTracker &Tracker)
1995 : FixableGadgets(FixableGadgets), WarningGadgets(WarningGadgets),
1996 Tracker(Tracker) {}
1997
1999
2000
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);
2011 }
2012
2013 if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) {
2014 Tracker.discoverDecl(DS);
2016 }
2017
2018
2019
2020
2021#define FIXABLE_GADGET(name) \
2022 if (Result.Nodes.getNodeAs(#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(#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
2044 GadgetFinderCallback CB{FixableGadgets, WarningGadgets, Tracker};
2045
2046
2049 forEachDescendantEvaluatedStmt(stmt(anyOf(
2050
2052 allOf(x ## Gadget::matcher().bind(#x), \
2053 notInSafeBufferOptOut(&Handler)),
2055 allOf(x ## Gadget::matcher(&Handler).bind(#x), \
2056 notInSafeBufferOptOut(&Handler)),
2057#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2058
2060 )))
2061 ),
2062 &CB
2063 );
2064
2065
2066 if (EmitSuggestions) {
2067
2072 x ## Gadget::matcher().bind(#x),
2073#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2074
2075
2078
2079
2080
2082 )))
2083 ),
2084 &CB
2085 );
2086
2087 }
2088
2089 M.match(*S, Ctx);
2090}
2091
2092
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
2103
2106
2108};
2109
2113
2114
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(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
2137
2140};
2141
2145 for (auto &F : AllFixableOperations) {
2146 DeclUseList DREs = F->getClaimedVarUseSites();
2147
2149 if (const auto *VD = dyn_cast(DRE->getDecl())) {
2150 FixablesForUnsafeVars.byVar[VD].insert(F.get());
2151 }
2152 }
2153 }
2154 return FixablesForUnsafeVars;
2155}
2156
2159
2160
2161 std::vector<const FixItHint *> All;
2162
2163 for (const FixItHint &H : FixIts)
2164 All.push_back(&H);
2165 std::sort(All.begin(), All.end(),
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 ||
2176 Hint->RemoveRange.getBegin())) {
2177
2178
2179 CurrHint = Hint;
2180 } else
2181
2182
2183 return true;
2184 }
2185 return false;
2186}
2187
2188std::optional
2189PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
2190 const auto *LeftVD = cast(PtrLHS->getDecl());
2191 const auto *RightVD = cast(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
2211
2212std::optional
2213CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
2214 const auto *LeftVD = cast(PtrLHS->getDecl());
2215 const auto *RightVD = cast(PtrRHS->getDecl());
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
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
2245PointerInitGadget::getFixits(const FixitStrategy &S) const {
2246 const auto *LeftVD = PtrInitLHS;
2247 const auto *RightVD = cast(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
2267 if (ConstVal->isNegative())
2268 return false;
2270 return false;
2271 return true;
2272}
2273
2274std::optional
2275ULCArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
2276 if (const auto *DRE =
2277 dyn_cast(Node->getBase()->IgnoreImpCasts()))
2278 if (const auto *VD = dyn_cast(DRE->getDecl())) {
2279 switch (S.lookup(VD)) {
2280 case FixitStrategy::Kind::Span: {
2281
2282
2283
2284 const ASTContext &Ctx =
2287 return std::nullopt;
2288
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
2304
2305std::optional
2306UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
2307 auto DREs = getClaimedVarUseSites();
2308 const auto *VD = cast(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;
2321}
2322
2323
2325 static const char *const EOL = "\n";
2326 return EOL;
2327}
2328
2329
2330static std::string
2332 std::string s = std::string("<# ");
2333 s += HintTextToUser;
2334 s += " #>";
2335 return s;
2336}
2337
2338
2339template
2340static std::optional
2345
2347 return Loc;
2348
2349 return std::nullopt;
2350}
2351
2352
2353template
2360 return Loc;
2361 return std::nullopt;
2362}
2363
2364
2368 std::optional LastCharLoc = getPastLoc(E, SM, LangOpts);
2369
2370 if (LastCharLoc)
2373 LangOpts);
2374
2375 return std::nullopt;
2376}
2377
2378
2385
2387 return Text;
2388 return std::nullopt;
2389}
2390
2391
2392
2394
2395
2397}
2398
2399
2400static std::optional
2406
2407 if (ParmIdentEndLoc.isMacroID() &&
2409 return std::nullopt;
2410 return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts);
2411}
2412
2413
2414
2415
2416
2417
2418
2421
2422
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 });
2431 AttrRangeOverlapping;
2432}
2433
2434
2435
2436
2437
2443 End =
2444
2446
2448}
2449
2450
2451
2452
2453
2454
2455static std::optionalstd::string
2458 std::optional *QualifiersToAppend) {
2461
2463 "Expecting a VarDecl of type of pointer to object type");
2465
2468
2469
2470
2472 case TypeLoc::ConstantArray:
2473 case TypeLoc::IncompleteArray:
2474 case TypeLoc::VariableArray:
2475 case TypeLoc::DependentSizedArray:
2476 case TypeLoc::Decayed:
2477 assert(isa(VD) && "An array type shall not be treated as a "
2478 "pointer type unless it decays.");
2480 break;
2481 case TypeLoc::Pointer:
2483 break;
2484 default:
2485 return std::nullopt;
2486 }
2487 if (PteTyLoc.isNull())
2488
2489
2490 return std::nullopt;
2491
2493
2495
2496
2497
2498 return std::nullopt;
2499 }
2500
2501
2504
2505 if (!PteEndOfTokenLoc.isValid())
2506
2507
2508 return std::nullopt;
2509 if (.isBeforeInTranslationUnit(PteEndOfTokenLoc, IdentLoc)) {
2510
2511
2512
2513
2514
2515
2516 return std::nullopt;
2517 }
2519
2520
2521
2523 }
2525 ->str();
2526}
2527
2528
2535
2536
2539 SourceRange NameRange{BeginLoc, EndLoc};
2540
2542}
2543
2544
2545
2546
2547
2548
2549static std::string
2551 std::optional 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
2561 const VarDecl *VD = dyn_cast(BaseDeclRefExpr->getDecl());
2562
2563 if (VD && s.lookup(VD) == FixitStrategy::Kind::Span) {
2565
2567 if (ConstVal->isNegative())
2568 return std::nullopt;
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2595
2596 std::optional LHSLocation = getPastLoc(LHS, SM, LangOpts);
2597 if (!LHSLocation)
2598 return std::nullopt;
2599
2602
2603 std::optional AddOpLocation =
2605 std::optional DerefOpLocation =
2607
2608 if (!AddOpLocation || !DerefOpLocation)
2609 return std::nullopt;
2610
2613
2614 return FixItList{
2618 }
2619 return std::nullopt;
2620}
2621
2622std::optional
2623PointerDereferenceGadget::getFixits(const FixitStrategy &S) const {
2624 const VarDecl *VD = cast(BaseDeclRefExpr->getDecl());
2625 switch (S.lookup(VD)) {
2626 case FixitStrategy::Kind::Span: {
2629
2630
2633
2634 if (auto LocPastOperand =
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
2656
2657 std::optional EndOfOperand =
2659
2660 if (EndOfOperand)
2662
2663 return std::nullopt;
2664}
2665
2666
2667
2668std::optional
2669UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const {
2670 const auto VD = cast(Node->getDecl());
2671 switch (S.lookup(VD)) {
2672 case FixitStrategy::Kind::Array:
2673 case FixitStrategy::Kind::Span: {
2675
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
2689
2690static std::optional
2692 const auto *ArraySub = cast(Node->getSubExpr());
2693 const auto *DRE = cast(ArraySub->getBase()->IgnoreImpCasts());
2694
2695
2697 const Expr *Idx = ArraySub->getIdx();
2700 std::stringstream SS;
2701 bool IdxIsLitZero = false;
2702
2704 if ((*ICE).isZero())
2705 IdxIsLitZero = true;
2706 std::optional DreString = getExprText(DRE, SM, LangOpts);
2707 if (!DreString)
2708 return std::nullopt;
2709
2710 if (IdxIsLitZero) {
2711
2712 SS << (*DreString).str() << ".data()";
2713 } else {
2714 std::optional 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
2728
2729 if (DREs.size() != 1)
2730 return std::nullopt;
2731
2732 if (const VarDecl *VD = dyn_cast(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();
2739
2741 return std::nullopt;
2742
2743
2744 bool NotParenExpr =
2746 std::string SS = varName.str() + " = " + varName.str() + ".subspan";
2747 if (NotParenExpr)
2748 SS += "(";
2749
2750 std::optional AddAssignLocation = getEndCharLoc(
2752 if (!AddAssignLocation)
2753 return std::nullopt;
2754
2757 SS));
2758 if (NotParenExpr)
2761 return Fixes;
2762 }
2763 }
2764 return std::nullopt;
2765}
2766
2767std::optional
2770
2771 if (DREs.size() != 1)
2772 return std::nullopt;
2773
2774 if (const VarDecl *VD = dyn_cast(DREs.front()->getDecl())) {
2775 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
2776 FixItList Fixes;
2777 std::stringstream SS;
2778 StringRef varName = VD->getName();
2780
2781
2782 SS << "(" << varName.data() << " = " << varName.data()
2783 << ".subspan(1)).data()";
2784 std::optional PreIncLocation =
2786 if (!PreIncLocation)
2787 return std::nullopt;
2788
2790 SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));
2791 return Fixes;
2792 }
2793 }
2794 return std::nullopt;
2795}
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812static std::optional
2814 const StringRef UserFillPlaceHolder) {
2817
2818
2819
2820
2821
2822 if (Init->isNullPointerConstant(
2823 Ctx,
2824
2825
2827 NPC_ValueDependentIsNotNull)) {
2828 std::optional InitLocation =
2830 if (!InitLocation)
2831 return std::nullopt;
2832
2834
2836 }
2837
2838 FixItList FixIts{};
2839 std::string ExtentText = UserFillPlaceHolder.data();
2840 StringRef One = "1";
2841
2842
2844
2845 if (auto CxxNew = dyn_cast(Init->IgnoreImpCasts())) {
2846
2847
2848
2849
2850 if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {
2851 if (!Ext->HasSideEffects(Ctx)) {
2852 std::optional ExtentString = getExprText(Ext, SM, LangOpts);
2853 if (!ExtentString)
2854 return std::nullopt;
2855 ExtentText = *ExtentString;
2856 }
2857 } else if (!CxxNew->isArray())
2858
2859
2860 ExtentText = One;
2862
2863
2864
2865 return FixItList{};
2866 } else {
2867
2868
2869 if (auto AddrOfExpr = dyn_cast(Init->IgnoreImpCasts()))
2870 if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
2871 isa_and_present(AddrOfExpr->getSubExpr()))
2872 ExtentText = One;
2873
2874
2875 }
2876
2878 std::optional LocPassInit = getPastLoc(Init, SM, LangOpts);
2879
2880 if (!LocPassInit)
2881 return std::nullopt;
2882
2883 StrBuffer.append(", ");
2884 StrBuffer.append(ExtentText);
2885 StrBuffer.append("}");
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
2900
2901
2902static std::optionalstd::string
2905
2906 std::optional PteTyQualifiers = std::nullopt;
2909
2910 if (!PteTyText)
2911 return std::nullopt;
2912
2913 std::string SpanTyText = "std::span<";
2914
2915 SpanTyText.append(*PteTyText);
2916
2917 if (PteTyQualifiers) {
2918 SpanTyText.append(" ");
2919 SpanTyText.append(PteTyQualifiers->getAsString());
2920 }
2921 SpanTyText.append(">");
2922 return SpanTyText;
2923}
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2940 const StringRef UserFillPlaceHolder,
2943 return {};
2944
2945 FixItList FixIts{};
2947
2948 if (!SpanTyText) {
2950 return {};
2951 }
2952
2953
2954 std::stringstream SS;
2955
2956 SS << *SpanTyText;
2957
2958 if (const Expr *Init = D->getInit()) {
2959 std::optional InitFixIts =
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
2967
2968
2969
2970 const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();
2971 if (!EndLocForReplacement.isValid()) {
2973 return {};
2974 }
2975
2976
2977
2979 SS << " ";
2980
2983 return FixIts;
2984}
2985
2988}
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021static std::optional
3025
3027 return std::nullopt;
3028
3031 const unsigned NumParms = FD->getNumParams();
3032 std::vectorstd::string NewTysTexts(NumParms);
3033 std::vector ParmsMask(NumParms, false);
3034 bool AtLeastOneParmToFix = false;
3035
3036 for (unsigned i = 0; i < NumParms; i++) {
3038
3039 if (S.lookup(PVD) == FixitStrategy::Kind::Wontfix)
3040 continue;
3041 if (S.lookup(PVD) != FixitStrategy::Kind::Span)
3042
3043 return std::nullopt;
3044
3045 std::optional PteTyQuals = std::nullopt;
3046 std::optionalstd::string PteTyText =
3048
3049 if (!PteTyText)
3050
3051 return std::nullopt;
3052
3053
3054 NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);
3055 ParmsMask[i] = true;
3056 AtLeastOneParmToFix = true;
3057 }
3058 if (!AtLeastOneParmToFix)
3059
3060 return {};
3061
3062
3063
3064
3065 const auto NewOverloadSignatureCreator =
3066 [&SM, &LangOpts, &NewTysTexts,
3067 &ParmsMask](const FunctionDecl *FD) -> std::optionalstd::string {
3068 std::stringstream SS;
3069
3070 SS << ";";
3072
3075 SM, LangOpts))
3076 SS << Prefix->str();
3077 else
3078 return std::nullopt;
3079
3080 const unsigned NumParms = FD->getNumParams();
3081
3082 for (unsigned i = 0; i < NumParms; i++) {
3084
3086 continue;
3087 if (ParmsMask[i]) {
3088
3089
3090 SS << NewTysTexts[i];
3091
3093 SS << ' ' << II->getName().str();
3094 } else if (auto ParmTypeText =
3096 SM, LangOpts)) {
3097
3098 SS << ParmTypeText->str();
3099 } else
3100 return std::nullopt;
3101 if (i != NumParms - 1)
3102 SS << ", ";
3103 }
3104 SS << ")";
3105 return SS.str();
3106 };
3107
3108
3109
3110 const auto OldOverloadDefCreator =
3111 [&Handler, &SM, &LangOpts, &NewTysTexts,
3112 &ParmsMask](const FunctionDecl *FD) -> std::optionalstd::string {
3113 std::stringstream SS;
3114
3116
3119 LangOpts))
3121 << FDPrefix->str() << "{";
3122 else
3123 return std::nullopt;
3124
3126 SS << "return " << FunQualName->str() << "(";
3127 else
3128 return std::nullopt;
3129
3130
3131 const unsigned NumParms = FD->getNumParams();
3132 for (unsigned i = 0; i < NumParms; i++) {
3134
3136 continue;
3137
3138
3140
3141 return std::nullopt;
3142 if (ParmsMask[i])
3143
3146 else
3148 if (i != NumParms - 1)
3149 SS << ", ";
3150 }
3151
3153
3154 return SS.str();
3155 };
3156
3157 FixItList FixIts{};
3159 std::optional Loc = getPastLoc(FReDecl, SM, LangOpts);
3160
3161 if ()
3162 return {};
3163 if (FReDecl->isThisDeclarationADefinition()) {
3164 assert(FReDecl == FD && "inconsistent function definition");
3165
3166
3167 if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
3169 else
3170 return {};
3171 } else {
3172
3173 if (!FReDecl->hasAttr()) {
3176 FReDecl->getBeginLoc(), " ")));
3177 }
3178
3179 if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
3181 else
3182 return {};
3183 }
3184 }
3185 return FixIts;
3186}
3187
3188
3193 return {};
3194 }
3196
3198 return {};
3199 }
3200
3201 std::optional PteTyQualifiers = std::nullopt;
3204
3205 if (!PteTyText) {
3207 return {};
3208 }
3209
3211
3212 if (!PVDNameText) {
3214 return {};
3215 }
3216
3217 std::stringstream SS;
3219
3220 if (PteTyQualifiers)
3221
3223 else
3225
3228
3229 SS << ' ' << PVDNameText->str();
3230
3232}
3233
3235 const DeclUseTracker &Tracker,
3238 const DeclStmt *DS = Tracker.lookupDecl(VD);
3239 if (!DS) {
3241 " : variables declared this way not implemented yet");
3242 return {};
3243 }
3245
3247 return {};
3248 }
3249
3250
3251
3252 (void)DS;
3253
3254
3256}
3257
3260 FixItList FixIts{};
3261
3262
3263
3265 const QualType &ArrayEltT = CAT->getElementType();
3266 assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
3267
3269 return {};
3270
3272
3273
3274
3275 auto MaybeElemTypeTxt =
3278 if (!MaybeElemTypeTxt)
3279 return {};
3280 const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
3281
3282
3285 while (NextTok && !NextTok->is(tok::l_square) &&
3289 if (!NextTok)
3290 return {};
3291 const SourceLocation LSqBracketLoc = NextTok->getLocation();
3292
3293
3294
3298 if (!MaybeArraySizeTxt)
3299 return {};
3300 const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
3301 if (ArraySizeTxt.empty()) {
3302
3303
3304
3305
3306
3307
3308
3309 return {};
3310 }
3311
3312 std::optional IdentText =
3314
3315 if (!IdentText) {
3317 return {};
3318 }
3319
3321 raw_svector_ostream OS(Replacement);
3322 OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> "
3323 << IdentText->str();
3324
3327 }
3328
3329 return FixIts;
3330}
3331
3333 const DeclUseTracker &Tracker,
3336 const DeclStmt *DS = Tracker.lookupDecl(VD);
3337 assert(DS && "Fixing non-local variables not implemented yet!");
3339
3340 return {};
3341 }
3342
3343
3344
3345 (void)DS;
3346
3347
3349}
3350
3351
3352
3353static FixItList
3356 const DeclUseTracker &Tracker, ASTContext &Ctx,
3358 if (const auto *PVD = dyn_cast(VD)) {
3359 auto *FD = dyn_castclang::FunctionDecl(PVD->getDeclContext());
3360 if (!FD || FD != D) {
3361
3362
3364 return {};
3365 }
3366
3367
3368
3369
3370
3371 if (FD->isMain() || FD->isConstexpr() ||
3372 FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate ||
3373 FD->isVariadic() ||
3374
3375 isa(FD) ||
3376
3377 (FD->hasBody() && isa(FD->getBody())) ||
3378 FD->isOverloadedOperator()) {
3380 return {};
3381 }
3382 }
3383
3384 switch (K) {
3385 case FixitStrategy::Kind::Span: {
3387 if (const auto *PVD = dyn_cast(VD))
3389
3392 }
3394 return {};
3395 }
3396 case FixitStrategy::Kind::Array: {
3399
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
3413
3415
3416
3417 return llvm::any_of(FixIts, [](const FixItHint &Hint) {
3420
3421 return true;
3422 return false;
3423 });
3424}
3425
3426
3428 return isa(VD) &&
3430}
3431
3432
3433
3434
3436 std::map<const VarDecl *, FixItList> &FixItsForVariable,
3438
3440
3441 for (const auto &[VD, Ignore] : FixItsForVariable) {
3443 if (llvm::any_of(Grp,
3444 [&FixItsForVariable](const VarDecl *GrpMember) -> bool {
3445 return !FixItsForVariable.count(GrpMember);
3446 })) {
3447
3448
3450 ToErase.push_back(Member);
3451 }
3452 }
3453 for (auto *VarToErase : ToErase)
3454 FixItsForVariable.erase(VarToErase);
3455}
3456
3457
3458
3459
3460
3461
3462
3463
3465 std::map<const VarDecl *, FixItList> &FixItsForVariable ,
3469 FixItList FixItsSharedByParms{};
3470
3471 std::optional OverloadFixes =
3473
3474 if (OverloadFixes) {
3475 FixItsSharedByParms.append(*OverloadFixes);
3476 } else {
3477
3478
3479
3481 FixItsForVariable.erase(Member);
3482 }
3483 return FixItsSharedByParms;
3484}
3485
3486
3487static std::map<const VarDecl *, FixItList>
3493
3494
3495
3496 std::map<const VarDecl *, FixItList> FixItsForVariable;
3497
3498
3499
3500
3501 for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
3502 FixItsForVariable[VD] =
3503 fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);
3504
3505
3506 if (FixItsForVariable[VD].empty()) {
3507 FixItsForVariable.erase(VD);
3508 continue;
3509 }
3510 for (const auto &F : Fixables) {
3511 std::optional 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
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
3530
3531
3532
3533
3534
3536
3537
3538
3539
3540
3541
3542
3543 FixItList FixItsSharedByParms{};
3544
3545 if (auto *FD = dyn_cast(D))
3547 FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
3548
3549
3550
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
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
3573
3574
3575
3576 for (auto Iter = FinalFixItsForVariable.begin();
3577 Iter != FinalFixItsForVariable.end();)
3580 Iter = FinalFixItsForVariable.erase(Iter);
3581 } else
3583 return FinalFixItsForVariable;
3584}
3585
3586template
3590 for (const VarDecl *VD : UnsafeVars) {
3592 S.set(VD, FixitStrategy::Kind::Array);
3593 else
3594 S.set(VD, FixitStrategy::Kind::Span);
3595 }
3596 return S;
3597}
3598
3599
3601 const std::vector Groups;
3602 const std::map<const VarDecl *, unsigned> &VarGrpMap;
3603 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
3604
3605public:
3607 const std::vector &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
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
3630 return GrpsUnionForParms.getArrayRef();
3631 }
3632};
3633
3635 WarningGadgetList WarningGadgets, DeclUseTracker Tracker,
3637 if (!EmitSuggestions) {
3638
3639
3640
3641 for (const auto &G : WarningGadgets) {
3642 G->handleUnsafeOperation(Handler, false,
3644 }
3645
3646
3647
3648 assert(FixableGadgets.size() == 0 &&
3649 "Fixable gadgets found but suggestions not requested!");
3650 return;
3651 }
3652
3653
3654
3655 if (!WarningGadgets.empty()) {
3656
3657
3658
3659 for (const auto &G : FixableGadgets) {
3660 for (const auto *DRE : G->getClaimedVarUseSites()) {
3661 Tracker.claimUse(DRE);
3662 }
3663 }
3664 }
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677 if (WarningGadgets.empty())
3678 return;
3679
3684
3685 std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
3686
3687
3688 for (auto it = FixablesForAllVars.byVar.cbegin();
3689 it != FixablesForAllVars.byVar.cend();) {
3690
3691 if ((!it->first->isLocalVarDecl() && !isa(it->first))) {
3692#ifndef NDEBUG
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
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
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;
3730 std::string UnclaimedUseTrace =
3731 getDREAncestorString(UnclaimedDRE, D->getASTContext());
3732
3735 ("failed to produce fixit for '" + UnfixedVDName +
3736 "' : has an unclaimed use\nThe unclaimed DRE trace: " +
3737 UnclaimedUseTrace));
3738 }
3739 }
3740#endif
3741
3742
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
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
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
3798 std::vector Groups;
3799
3800
3801
3802 std::map<const VarDecl *, unsigned> VarGrpMap;
3803
3804 llvm::SetVector<const VarDecl *>
3805 GrpsUnionForParms;
3806
3807
3808
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;
3835 HasParm = true;
3836 }
3837 if (HasParm)
3838 GrpsUnionForParms.insert(VarGroup.begin(), VarGroup.end());
3839 }
3840 }
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860 for (auto I = FixablesForAllVars.byVar.begin();
3861 I != FixablesForAllVars.byVar.end();) {
3862
3863 if (!VisitedVars.count((*I).first)) {
3864
3865 I = FixablesForAllVars.byVar.erase(I);
3866 } else
3867 ++I;
3868 }
3869
3870
3871
3873 VisitedVars, [&FixablesForAllVars](const VarDecl *V) {
3874
3875 return FixablesForAllVars.byVar.count(V);
3876 }));
3878
3879 if (isa(D))
3880
3881
3882 FixItsForVariableGroup =
3884 Tracker, Handler, VarGrpMgr);
3885
3886 for (const auto &G : UnsafeOps.noVar) {
3887 G->handleUnsafeOperation(Handler, false,
3889 }
3890
3891 for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
3892 auto FixItsIt = FixItsForVariableGroup.find(VD);
3894 FixItsIt != FixItsForVariableGroup.end()
3895 ? std::move(FixItsIt->second)
3896 : FixItList{},
3897 D, NaiveStrategy);
3898 for (const auto &G : WarningGadgets) {
3899 G->handleUnsafeOperation(Handler, true,
3901 }
3902 }
3903}
3904
3907 bool EmitSuggestions) {
3908#ifndef NDEBUG
3910#endif
3911
3912 assert(D);
3913
3915
3916 if (const auto *FD = dyn_cast(D)) {
3917
3918
3919
3920 if (const auto *MD = dyn_cast(D)) {
3921 if (MD->getParent()->isLambda() && MD->getParent()->isLocalClass())
3922 return;
3923 }
3924
3926 if (FReDecl->isExternC()) {
3927
3928
3929 EmitSuggestions = false;
3930 break;
3931 }
3932 }
3933
3934 Stmts.push_back(FD->getBody());
3935
3936 if (const auto *ID = dyn_cast(D)) {
3938 Stmts.push_back(CI->getInit());
3939 }
3940 }
3941 } else if (isa(D) || isa(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) {
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.
BoundNodesTreeBuilder Nodes
#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...
static Decl::Kind getKind(const Decl *D)
static const Decl * getCanonicalDecl(const Decl *D)
llvm::MachO::Target Target
Defines the clang::Preprocessor interface.
static bool hasAttr(const Decl *D, bool IgnoreImplicitAttr)
Defines the clang::SourceLocation class and associated facilities.
static QualType getPointeeType(const MemRegion *R)
C Language Family Type Representation.
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)
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)
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,...
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
SourceManager & getSourceManager()
const ConstantArrayType * getAsConstantArrayType(QualType T) const
DynTypedNodeList getParents(const NodeT &Node)
Forwards to get node parents from the ParentMapContext.
QualType getFILEType() const
Retrieve the C FILE type.
const LangOptions & getLangOpts() const
const TargetInfo & getTargetInfo() const
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
SourceLocation getBeginLoc() const LLVM_READONLY
Attr - This represents one attribute.
A builtin binary operation expression such as "x + y" or "x <= y".
SourceLocation getBeginLoc() const LLVM_READONLY
static StringRef getOpcodeStr(Opcode Op)
getOpcodeStr - Turn an Opcode enum value into the punctuation char it corresponds to,...
Represents a call to a C++ constructor.
Expr * getArg(unsigned Arg)
Return the specified argument.
SourceLocation getBeginLoc() const LLVM_READONLY
Represents a C++ base or member initializer.
A use of a default initializer in a constructor or in aggregate initialization.
Represents a static or instance method of a struct/union/class.
Represents a C++11 noexcept expression (C++ [expr.unary.noexcept]).
Represents a C++ struct/union/class.
CXXRecordDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
A C++ typeid expression (C++ [expr.typeid]), which gets the type_info that corresponds to the supplie...
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
static const char * getCastKindName(CastKind CK)
Represents a character-granular source range.
static CharSourceRange getCharRange(SourceRange R)
SourceLocation getEnd() const
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
bool isSingleResult() const
lookup_result lookup(DeclarationName Name) const
lookup - Find the declarations (if any) with the given Name in this context.
A reference to a declared variable, function, enum, etc.
SourceLocation getBeginLoc() const LLVM_READONLY
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
bool isSingleDecl() const
isSingleDecl - This method returns true if this DeclStmt refers to a single Decl.
Decl - This represents one declaration (or definition), e.g.
bool isInStdNamespace() const
SourceLocation getEndLoc() const LLVM_READONLY
ASTContext & getASTContext() const LLVM_READONLY
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
SourceLocation getLocation() const
DeclContext * getDeclContext()
SourceLocation getBeginLoc() const LLVM_READONLY
virtual SourceRange getSourceRange() const LLVM_READONLY
Source range that this declaration covers.
NestedNameSpecifier * getQualifier() const
Retrieve the nested-name-specifier that qualifies the name of this declaration, if it was present in ...
SourceLocation getBeginLoc() const LLVM_READONLY
NestedNameSpecifierLoc getQualifierLoc() const
Retrieve the nested-name-specifier (with source-location information) that qualifies the name of this...
TypeSourceInfo * getTypeSourceInfo() const
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.
This represents one expression.
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Expr * IgnoreImplicit() LLVM_READONLY
Skip past any implicit AST nodes which might surround this expression until reaching a fixed point.
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
NullPointerConstantValueDependence
Enumeration used to describe how isNullPointerConstant() should cope with value-dependent expressions...
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.
Annotates a diagnostic with some code that should be inserted, removed, or replaced to fix the proble...
CharSourceRange RemoveRange
Code that should be replaced to correct the error.
static FixItHint CreateReplacement(CharSourceRange RemoveRange, StringRef Code)
Create a code modification hint that replaces the given source range with the given code string.
static FixItHint CreateRemoval(CharSourceRange RemoveRange)
Create a code modification hint that removes the given source range.
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.
Represents a function declaration or definition.
const ParmVarDecl * getParamDecl(unsigned i) const
Stmt * getBody(const FunctionDecl *&Definition) const
Retrieve the body (definition) of the function.
param_iterator param_begin()
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
DeclarationNameInfo getNameInfo() const
Represents a C11 generic selection.
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 ...
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
static StringRef getSourceText(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts, bool *Invalid=nullptr)
Returns a string for the source that the range encompasses.
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.
static std::optional< Token > findNextToken(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts, bool IncludeComments=false)
Finds the token that comes right after the given location.
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 ...
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.
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
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...
SourceLocation getBeginLoc() const
Retrieve the location of the beginning of this nested-name-specifier.
Represents a parameter to a function.
bool hasDefaultArg() const
Determines whether this parameter has a default argument, either parsed or not.
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Wrapper for source info for pointers.
PointerType - C99 6.7.5.1 - Pointer Declarators.
A (possibly-)qualified type.
bool hasQualifiers() const
Determine whether this type has any qualifiers.
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Qualifiers getQualifiers() const
Retrieve the set of qualifiers applied to this type.
QualType getCanonicalType() const
bool isConstQualified() const
Determine whether this type is const-qualified.
std::string getAsString() const
redecl_range redecls() const
Returns an iterator range for all the redeclarations of the same decl.
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
Stmt - This represents one statement.
SourceLocation getEndLoc() const LLVM_READONLY
StmtClass getStmtClass() const
SourceLocation getBeginLoc() const LLVM_READONLY
Exposes information about the current target.
Base wrapper for a particular "section" of type source info.
UnqualTypeLoc getUnqualifiedLoc() const
Skips past any qualifiers, if this is qualified.
TypeLoc getNextTypeLoc() const
Get the next TypeLoc pointed by this TypeLoc, e.g for "int*" the TypeLoc is a PointerLoc and next Typ...
T castAs() const
Convert to the specified TypeLoc type, asserting that this TypeLoc is of the desired type.
SourceRange getSourceRange() const LLVM_READONLY
Get the full source range.
TypeLocClass getTypeLocClass() const
SourceLocation getEndLoc() const
Get the end source location.
SourceLocation getBeginLoc() const
Get the begin source location.
TypeLoc getTypeLoc() const
Return the TypeLoc wrapper for the type source info.
bool isFunctionPointerType() const
bool isPointerType() const
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
const T * castAs() const
Member-template castAs.
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
bool isUnsignedIntegerType() const
Return true if this is an integer type that is unsigned, according to C99 6.2.5p6 [which returns true...
UnaryExprOrTypeTraitExpr - expression with either a type or (unevaluated) expression operand.
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
SourceLocation getOperatorLoc() const
getOperatorLoc - Return the location of the operator.
Expr * getSubExpr() const
SourceLocation getBeginLoc() const LLVM_READONLY
static StringRef getOpcodeStr(Opcode Op)
getOpcodeStr - Turn an Opcode enum value into the punctuation char it corresponds to,...
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.
Represents a variable declaration or definition.
bool isConstexpr() const
Whether this variable is (C++11) constexpr.
VarDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
bool isInlineSpecified() const
bool hasConstantInitialization() const
Determine whether this variable has constant initialization.
bool hasLocalStorage() const
Returns true if a variable with function scope is a non-static local variable.
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
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.
const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr
Matches call expressions.
const internal::VariadicDynCastAllOfMatcher< Stmt, CompoundStmt > compoundStmt
Matches compound statements.
static auto hasArrayType()
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.
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.
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)
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.
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.
APValue Val
Val - This is the value the expression can be folded to.
Wraps an identifier and optional source location for the identifier.
Contains all information for a given match.
StringRef matchLibcName(StringRef Name)
StringRef matchLibcNameOrBuiltinChk(StringRef Name)
StringRef matchName(StringRef FunName, bool isBuiltin)