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
457 const Expr *IndexExpr = Node.getIdx();
459 IndexExpr->EvaluateAsInt(EVResult, Finder->getASTContext())) {
460 llvm::APSInt ArrIdx = EVResult.Val.getInt();
461
462
463 if (ArrIdx.isNonNegative() && ArrIdx.getLimitedValue() < limit)
464 return true;
465 }
466 return false;
467}
468
470 return Node.getNumArgs() == Num;
471}
472
473namespace libc_func_matchers {
474
475
476
477
478
479
480
481
482
483
484
485
486
487
489 StringRef matchName(StringRef FunName, bool isBuiltin) {
490
491 if (isBuiltin && FunName.starts_with("__builtin_"))
492
493
495 FunName.drop_front(10 ));
496
497 if (FunName.starts_with("__asan_"))
498 return matchLibcName(FunName.drop_front(7 ));
500 }
501
502
503
505 if (Name.starts_with("__") && Name.ends_with("_chk"))
507 Name.drop_front(2).drop_back(4) );
509 }
510
512 if (Name.ends_with("_s"))
513 return Name.drop_back(2 );
514 return Name;
515 }
516};
517
518
519
522 return true;
524 return true;
525 if (auto *MCE = dyn_cast(Ptr->IgnoreParenImpCasts())) {
528
530 if (MD->getName() == "c_str" && RD->getName() == "basic_string")
531 return true;
532 }
533 return false;
534}
535
536
537
538
539
540
541
542
543
545 const unsigned FmtArgIdx, ASTContext &Ctx,
546 bool isKprintf = false) {
547 class StringFormatStringHandler
550 unsigned FmtArgIdx;
551 const Expr *&UnsafeArg;
552
553 public:
554 StringFormatStringHandler(const CallExpr *Call, unsigned FmtArgIdx,
555 const Expr *&UnsafeArg)
556 : Call(Call), FmtArgIdx(FmtArgIdx), UnsafeArg(UnsafeArg) {}
557
559 const char *startSpecifier,
560 unsigned specifierLen,
562 if (FS.getConversionSpecifier().getKind() ==
563 analyze_printf::PrintfConversionSpecifier::sArg) {
564 unsigned ArgIdx = FS.getPositionalArgIndex() + FmtArgIdx;
565
566 if (0 < ArgIdx && ArgIdx < Call->getNumArgs())
568 UnsafeArg = Call->getArg(ArgIdx);
569
570 return false;
571 }
572 }
573 return true;
574 }
575 };
576
577 const Expr *Fmt = Call->getArg(FmtArgIdx);
578
580 StringRef FmtStr;
581
582 if (SL->getCharByteWidth() == 1)
583 FmtStr = SL->getString();
584 else if (auto EvaledFmtStr = SL->tryEvaluateString(Ctx))
585 FmtStr = *EvaledFmtStr;
586 else
587 goto CHECK_UNSAFE_PTR;
588
589 StringFormatStringHandler Handler(Call, FmtArgIdx, UnsafeArg);
590
592 Handler, FmtStr.begin(), FmtStr.end(), Ctx.getLangOpts(),
594 }
595CHECK_UNSAFE_PTR:
596
597
598
599 return llvm::any_of(
600 llvm::make_range(Call->arg_begin() + FmtArgIdx, Call->arg_end()),
601 [&UnsafeArg](const Expr *Arg) -> bool {
602 if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg)) {
603 UnsafeArg = Arg;
604 return true;
605 }
606 return false;
607 });
608}
609
610
611
612
613
614
615
616
617
618
620 static std::unique_ptr<std::set> PredefinedNames = nullptr;
621 if (!PredefinedNames)
622 PredefinedNames =
623 std::make_unique<std::set, std::set>({
624
625 "atof",
626 "atoi",
627 "atol",
628 "atoll",
629 "strtol",
630 "strtoll",
631 "strtoul",
632 "strtoull",
633 "strtof",
634 "strtod",
635 "strtold",
636 "strtoimax",
637 "strtoumax",
638
639
640 "strcpy",
641 "strncpy",
642 "strlcpy",
643 "strcat",
644 "strncat",
645 "strlcat",
646 "strxfrm",
647 "strdup",
648 "strndup",
649
650 "strlen",
651 "strnlen",
652 "strcmp",
653 "strncmp",
654 "stricmp",
655 "strcasecmp",
656 "strcoll",
657 "strchr",
658 "strrchr",
659 "strspn",
660 "strcspn",
661 "strpbrk",
662 "strstr",
663 "strtok",
664
665 "memchr",
666 "wmemchr",
667 "memcmp",
668 "wmemcmp",
669 "memcpy",
670 "memccpy",
671 "mempcpy",
672 "wmemcpy",
673 "memmove",
674 "wmemmove",
675 "memset",
676 "wmemset",
677
678 "fread",
679 "fwrite",
680 "fgets",
681 "fgetws",
682 "gets",
683 "fputs",
684 "fputws",
685 "puts",
686
687 "strerror_s",
688 "strerror_r",
689 "bcopy",
690 "bzero",
691 "bsearch",
692 "qsort",
693 });
694
695 auto *II = Node.getIdentifier();
696
697 if (!II)
698 return false;
699
701 II->getName(), Node.getBuiltinID());
702
703
704 if (PredefinedNames->find(Name) != PredefinedNames->end())
705 return true;
706
707 std::string NameWCS = Name.str();
708 size_t WcsPos = NameWCS.find("wcs");
709
710 while (WcsPos != std:🧵:npos) {
711 NameWCS[WcsPos++] = 's';
712 NameWCS[WcsPos++] = 't';
713 NameWCS[WcsPos++] = 'r';
714 WcsPos = NameWCS.find("wcs", WcsPos);
715 }
716 if (PredefinedNames->find(NameWCS) != PredefinedNames->end())
717 return true;
718
719
720 return Name.ends_with("scanf");
721}
722
723
724
725
727 auto *II = Node.getIdentifier();
728
729 if (!II)
730 return false;
731
733 II->getName(), Node.getBuiltinID());
734
735 if (!Name.ends_with("printf"))
736 return false;
737 return Name.starts_with("v");
738}
739
740
741
743 auto *II = Node.getIdentifier();
744
745 if (!II)
746 return false;
747
749 II->getName(), Node.getBuiltinID());
750
751 if (!Name.ends_with("printf") ||
752
753 Name.starts_with("v"))
754 return false;
755
756 StringRef Prefix = Name.drop_back(6);
757
758 if (Prefix.ends_with("w"))
759 Prefix = Prefix.drop_back(1);
760 return Prefix == "s";
761}
762
763
764
765
767 auto *II = Node.getIdentifier();
768
769 if (!II)
770 return false;
771
773 II->getName(), Node.getBuiltinID());
774
775 if (!Name.ends_with("printf") || Name.starts_with("v"))
776 return false;
777
778 StringRef Prefix = Name.drop_back(6);
779
780 if (Prefix.ends_with("w"))
781 Prefix = Prefix.drop_back(1);
782
783 return Prefix.empty() || Prefix == "k" || Prefix == "f" || Prefix == "sn";
784}
785
786
787
788
789
791 clang::ast_matchers::internal::Matcher,
792 UnsafeStringArgMatcher) {
793
795
796 assert(FD && "It should have been checked that FD is non-null.");
797
799
800 if (NumParms < 1)
801 return false;
802
803 ASTContext &Ctx = Finder->getASTContext();
805
807 return false;
808
810
812 .isNull() &&
814
815 const Expr *UnsafeArg;
816
818 return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);
819 return false;
820 }
821
823
824 bool isKprintf = false;
825 const Expr *UnsafeArg;
826
828 isKprintf = II->getName() == "kprintf";
830 return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);
831 return false;
832 }
833
834 if (NumParms > 2) {
836
838
839
840 const Expr *UnsafeArg;
841
843 return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);
844 return false;
845 }
846 }
847
848
849 for (auto Arg : Node.arguments())
851 if (UnsafeStringArgMatcher.matches(*Arg, Finder, Builder))
852 return true;
853 return false;
854}
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
874
875 assert(FD && "It should have been checked that FD is non-null.");
876
878 return false;
879
881
883 return false;
884
886 const Expr *Buf = Node.getArg(0), *Size = Node.getArg(1);
887
889 !Size->getType()->isIntegerType())
890 return false;
891
892
893 static StringRef SizedObjs[] = {"span", "array", "vector",
894 "basic_string_view", "basic_string"};
896 Size = Size->IgnoreParenImpCasts();
897 if (auto *MCEPtr = dyn_cast(Buf))
898 if (auto *MCESize = dyn_cast(Size)) {
899 auto *DREOfPtr = dyn_cast(
900 MCEPtr->getImplicitObjectArgument()->IgnoreParenImpCasts());
901 auto *DREOfSize = dyn_cast(
902 MCESize->getImplicitObjectArgument()->IgnoreParenImpCasts());
903
904 if (!DREOfPtr || !DREOfSize)
905 return true;
906 if (DREOfPtr->getDecl() != DREOfSize->getDecl())
907 return true;
908 if (MCEPtr->getMethodDecl()->getName() != "data")
909 return true;
910
911 if (MCESize->getMethodDecl()->getName() == "size_bytes" ||
912
913
914
915
916 MCESize->getMethodDecl()->getName() == "size")
917 for (StringRef SizedObj : SizedObjs)
918 if (MCEPtr->getRecordDecl()->isInStdNamespace() &&
919 MCEPtr->getRecordDecl()->getCanonicalDecl()->getName() ==
920 SizedObj)
921 return false;
922 }
923
924
926 ASTContext &Ctx = Finder->getASTContext();
927
930
931
932
933 if (Size->EvaluateAsConstantExpr(ER, Ctx)) {
934 APSInt EVal = ER.Val.getInt();
935
936 return APSInt::compareValues(EVal, APSInt(CAT->getSize(), true)) != 0;
937 }
938 }
939 }
940 return true;
941}
942}
943}
944
945namespace {
946
947
949
950
952}
953
954namespace {
955
956
957
958
959
960
961
962
963class Gadget {
964public:
965 enum class Kind {
966#define GADGET(x) x,
967#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
968 };
969
970
971
972
973 using Matcher = decltype(stmt());
974
975 Gadget(Kind K) : K(K) {}
976
978
979#ifndef NDEBUG
980 StringRef getDebugName() const {
981 switch (K) {
982#define GADGET(x) \
983 case Kind::x: \
984 return #x;
985#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
986 }
987 llvm_unreachable("Unhandled Gadget::Kind enum");
988 }
989#endif
990
991 virtual bool isWarningGadget() const = 0;
992
993
995
996
997
998
999 virtual DeclUseList getClaimedVarUseSites() const = 0;
1000
1001 virtual ~Gadget() = default;
1002
1003private:
1005};
1006
1007
1008
1009class WarningGadget : public Gadget {
1010public:
1011 WarningGadget(Kind K) : Gadget(K) {}
1012
1013 static bool classof(const Gadget *G) { return G->isWarningGadget(); }
1014 bool isWarningGadget() const final { return true; }
1015
1017 bool IsRelatedToDecl,
1019};
1020
1021
1022
1023
1024
1025class FixableGadget : public Gadget {
1026public:
1027 FixableGadget(Kind K) : Gadget(K) {}
1028
1029 static bool classof(const Gadget *G) { return !G->isWarningGadget(); }
1030 bool isWarningGadget() const final { return false; }
1031
1032
1033
1034
1035 virtual std::optional getFixits(const FixitStrategy &) const {
1036 return std::nullopt;
1037 }
1038
1039
1040
1041
1042
1043
1044
1045 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1046 getStrategyImplications() const {
1047 return std::nullopt;
1048 }
1049};
1050
1051static auto toSupportedVariable() { return to(varDecl()); }
1052
1053using FixableGadgetList = std::vector<std::unique_ptr>;
1054using WarningGadgetList = std::vector<std::unique_ptr>;
1055
1056
1057
1058class IncrementGadget : public WarningGadget {
1059 static constexpr const char *const OpTag = "op";
1061
1062public:
1064 : WarningGadget(Kind::Increment),
1066
1067 static bool classof(const Gadget *G) {
1068 return G->getKind() == Kind::Increment;
1069 }
1070
1071 static Matcher matcher() {
1072 return stmt(
1074 hasUnaryOperand(ignoringParenImpCasts(hasPointerType())))
1075 .bind(OpTag));
1076 }
1077
1079 bool IsRelatedToDecl,
1082 }
1084
1085 DeclUseList getClaimedVarUseSites() const override {
1087 if (const auto *DRE =
1089 Uses.push_back(DRE);
1090 }
1091
1092 return std::move(Uses);
1093 }
1094};
1095
1096
1097
1098class DecrementGadget : public WarningGadget {
1099 static constexpr const char *const OpTag = "op";
1101
1102public:
1104 : WarningGadget(Kind::Decrement),
1106
1107 static bool classof(const Gadget *G) {
1108 return G->getKind() == Kind::Decrement;
1109 }
1110
1111 static Matcher matcher() {
1112 return stmt(
1114 hasUnaryOperand(ignoringParenImpCasts(hasPointerType())))
1115 .bind(OpTag));
1116 }
1117
1119 bool IsRelatedToDecl,
1122 }
1124
1125 DeclUseList getClaimedVarUseSites() const override {
1126 if (const auto *DRE =
1128 return {DRE};
1129 }
1130
1131 return {};
1132 }
1133};
1134
1135
1136
1137class ArraySubscriptGadget : public WarningGadget {
1138 static constexpr const char *const ArraySubscrTag = "ArraySubscript";
1140
1141public:
1143 : WarningGadget(Kind::ArraySubscript),
1145
1146 static bool classof(const Gadget *G) {
1147 return G->getKind() == Kind::ArraySubscript;
1148 }
1149
1150 static Matcher matcher() {
1151
1153 hasBase(ignoringParenImpCasts(
1156 isSafeArraySubscript(),
1157 hasIndex(
1159 )
1160 ))).bind(ArraySubscrTag));
1161
1162 }
1163
1165 bool IsRelatedToDecl,
1168 }
1170
1171 DeclUseList getClaimedVarUseSites() const override {
1172 if (const auto *DRE =
1174 return {DRE};
1175 }
1176
1177 return {};
1178 }
1179};
1180
1181
1182
1183
1184
1185class PointerArithmeticGadget : public WarningGadget {
1186 static constexpr const char *const PointerArithmeticTag = "ptrAdd";
1187 static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr";
1188 const BinaryOperator *PA;
1189 const Expr *Ptr;
1190
1191public:
1193 : WarningGadget(Kind::PointerArithmetic),
1195 Ptr(Result.Nodes.getNodeAs<Expr>(PointerArithmeticPointerTag)) {}
1196
1197 static bool classof(const Gadget *G) {
1198 return G->getKind() == Kind::PointerArithmetic;
1199 }
1200
1201 static Matcher matcher() {
1202 auto HasIntegerType = anyOf(hasType(isInteger()), hasType(enumType()));
1203 auto PtrAtRight =
1204 allOf(hasOperatorName("+"),
1206 hasLHS(HasIntegerType));
1207 auto PtrAtLeft =
1208 allOf(anyOf(hasOperatorName("+"), hasOperatorName("-"),
1209 hasOperatorName("+="), hasOperatorName("-=")),
1211 hasRHS(HasIntegerType));
1212
1214 .bind(PointerArithmeticTag));
1215 }
1216
1218 bool IsRelatedToDecl,
1221 }
1223
1224 DeclUseList getClaimedVarUseSites() const override {
1225 if (const auto *DRE = dyn_cast(Ptr->IgnoreParenImpCasts())) {
1226 return {DRE};
1227 }
1228
1229 return {};
1230 }
1231
1232
1233};
1234
1235class SpanTwoParamConstructorGadget : public WarningGadget {
1236 static constexpr const char *const SpanTwoParamConstructorTag =
1237 "spanTwoParamConstructor";
1238 const CXXConstructExpr *Ctor;
1239
1240public:
1242 : WarningGadget(Kind::SpanTwoParamConstructor),
1244 SpanTwoParamConstructorTag)) {}
1245
1246 static bool classof(const Gadget *G) {
1247 return G->getKind() == Kind::SpanTwoParamConstructor;
1248 }
1249
1250 static Matcher matcher() {
1253 parameterCountIs(2)));
1254
1256 unless(isSafeSpanTwoParamConstruct()))
1257 .bind(SpanTwoParamConstructorTag));
1258 }
1259
1261 return stmt(unless(ignoreUnsafeBufferInContainer(Handler)), matcher());
1262 }
1263
1265 bool IsRelatedToDecl,
1268 }
1270
1271 DeclUseList getClaimedVarUseSites() const override {
1272
1273
1274 if (auto *DRE = dyn_cast(Ctor->getArg(0))) {
1275 if (isa(DRE->getDecl()))
1276 return {DRE};
1277 }
1278 return {};
1279 }
1280};
1281
1282
1283
1284
1285
1286class PointerInitGadget : public FixableGadget {
1287private:
1288 static constexpr const char *const PointerInitLHSTag = "ptrInitLHS";
1289 static constexpr const char *const PointerInitRHSTag = "ptrInitRHS";
1290 const VarDecl *PtrInitLHS;
1291 const DeclRefExpr *PtrInitRHS;
1292
1293public:
1295 : FixableGadget(Kind::PointerInit),
1296 PtrInitLHS(Result.Nodes.getNodeAs<VarDecl>(PointerInitLHSTag)),
1297 PtrInitRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}
1298
1299 static bool classof(const Gadget *G) {
1300 return G->getKind() == Kind::PointerInit;
1301 }
1302
1303 static Matcher matcher() {
1304 auto PtrInitStmt = declStmt(hasSingleDecl(
1305 varDecl(hasInitializer(ignoringImpCasts(
1307 .bind(PointerInitRHSTag))))
1308 .bind(PointerInitLHSTag)));
1309
1310 return stmt(PtrInitStmt);
1311 }
1312
1313 virtual std::optional
1314 getFixits(const FixitStrategy &S) const override;
1317 }
1318
1319 virtual DeclUseList getClaimedVarUseSites() const override {
1320 return DeclUseList{PtrInitRHS};
1321 }
1322
1323 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1324 getStrategyImplications() const override {
1325 return std::make_pair(PtrInitLHS, cast(PtrInitRHS->getDecl()));
1326 }
1327};
1328
1329
1330
1331
1332
1333
1334class PtrToPtrAssignmentGadget : public FixableGadget {
1335private:
1336 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
1337 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
1338 const DeclRefExpr *PtrLHS;
1339 const DeclRefExpr *PtrRHS;
1340
1341public:
1343 : FixableGadget(Kind::PtrToPtrAssignment),
1344 PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
1345 PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
1346
1347 static bool classof(const Gadget *G) {
1348 return G->getKind() == Kind::PtrToPtrAssignment;
1349 }
1350
1351 static Matcher matcher() {
1353 allOf(hasOperatorName("="),
1354 hasRHS(ignoringParenImpCasts(
1356 .bind(PointerAssignRHSTag))),
1358 .bind(PointerAssignLHSTag))));
1359
1361 }
1362
1363 virtual std::optional
1364 getFixits(const FixitStrategy &S) const override;
1366
1367 virtual DeclUseList getClaimedVarUseSites() const override {
1368 return DeclUseList{PtrLHS, PtrRHS};
1369 }
1370
1371 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1372 getStrategyImplications() const override {
1373 return std::make_pair(cast(PtrLHS->getDecl()),
1374 cast(PtrRHS->getDecl()));
1375 }
1376};
1377
1378
1379
1380
1381
1382
1383class CArrayToPtrAssignmentGadget : public FixableGadget {
1384private:
1385 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
1386 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
1387 const DeclRefExpr *PtrLHS;
1388 const DeclRefExpr *PtrRHS;
1389
1390public:
1392 : FixableGadget(Kind::CArrayToPtrAssignment),
1393 PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
1394 PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
1395
1396 static bool classof(const Gadget *G) {
1397 return G->getKind() == Kind::CArrayToPtrAssignment;
1398 }
1399
1400 static Matcher matcher() {
1402 allOf(hasOperatorName("="),
1403 hasRHS(ignoringParenImpCasts(
1405 toSupportedVariable())
1406 .bind(PointerAssignRHSTag))),
1408 .bind(PointerAssignLHSTag))));
1409
1411 }
1412
1413 virtual std::optional
1414 getFixits(const FixitStrategy &S) const override;
1416
1417 virtual DeclUseList getClaimedVarUseSites() const override {
1418 return DeclUseList{PtrLHS, PtrRHS};
1419 }
1420
1421 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1422 getStrategyImplications() const override {
1423 return {};
1424 }
1425};
1426
1427
1428
1429class UnsafeBufferUsageAttrGadget : public WarningGadget {
1430 constexpr static const char *const OpTag = "attr_expr";
1431 const Expr *Op;
1432
1433public:
1435 : WarningGadget(Kind::UnsafeBufferUsageAttr),
1436 Op(Result.Nodes.getNodeAs<Expr>(OpTag)) {}
1437
1438 static bool classof(const Gadget *G) {
1439 return G->getKind() == Kind::UnsafeBufferUsageAttr;
1440 }
1441
1442 static Matcher matcher() {
1443 auto HasUnsafeFieldDecl =
1445
1446 auto HasUnsafeFnDecl =
1448
1450 memberExpr(HasUnsafeFieldDecl).bind(OpTag)));
1451 }
1452
1454 bool IsRelatedToDecl,
1457 }
1459
1460 DeclUseList getClaimedVarUseSites() const override { return {}; }
1461};
1462
1463
1464
1465
1466class UnsafeBufferUsageCtorAttrGadget : public WarningGadget {
1467 constexpr static const char *const OpTag = "cxx_construct_expr";
1469
1470public:
1472 : WarningGadget(Kind::UnsafeBufferUsageCtorAttr),
1474
1475 static bool classof(const Gadget *G) {
1476 return G->getKind() == Kind::UnsafeBufferUsageCtorAttr;
1477 }
1478
1479 static Matcher matcher() {
1480 auto HasUnsafeCtorDecl =
1482
1483 auto HasTwoParamSpanCtorDecl = SpanTwoParamConstructorGadget::matcher();
1484 return stmt(
1486 .bind(OpTag));
1487 }
1488
1490 bool IsRelatedToDecl,
1493 }
1495
1496 DeclUseList getClaimedVarUseSites() const override { return {}; }
1497};
1498
1499
1500
1501
1502
1503class DataInvocationGadget : public WarningGadget {
1504 constexpr static const char *const OpTag = "data_invocation_expr";
1506
1507public:
1509 : WarningGadget(Kind::DataInvocation),
1511
1512 static bool classof(const Gadget *G) {
1513 return G->getKind() == Kind::DataInvocation;
1514 }
1515
1516 static Matcher matcher() {
1517
1521 hasName("std::vector"))))));
1522 return stmt(
1524 .bind(OpTag));
1525 }
1526
1528 bool IsRelatedToDecl,
1531 }
1533
1534 DeclUseList getClaimedVarUseSites() const override { return {}; }
1535};
1536
1537class UnsafeLibcFunctionCallGadget : public WarningGadget {
1539 const Expr *UnsafeArg = nullptr;
1540 constexpr static const char *const Tag = "UnsafeLibcFunctionCall";
1541
1542 constexpr static const char *const UnsafeSprintfTag =
1543 "UnsafeLibcFunctionCall_sprintf";
1544 constexpr static const char *const UnsafeSizedByTag =
1545 "UnsafeLibcFunctionCall_sized_by";
1546 constexpr static const char *const UnsafeStringTag =
1547 "UnsafeLibcFunctionCall_string";
1548 constexpr static const char *const UnsafeVaListTag =
1549 "UnsafeLibcFunctionCall_va_list";
1550
1551 enum UnsafeKind {
1552 OTHERS = 0,
1553 SPRINTF = 1,
1554 SIZED_BY =
1555 2,
1556
1557 STRING = 3,
1558
1559 VA_LIST = 4,
1560
1561 } WarnedFunKind = OTHERS;
1562
1563public:
1565 : WarningGadget(Kind::UnsafeLibcFunctionCall),
1567 if (Result.Nodes.getNodeAs<Decl>(UnsafeSprintfTag))
1568 WarnedFunKind = SPRINTF;
1569 else if (auto *E = Result.Nodes.getNodeAs<Expr>(UnsafeStringTag)) {
1570 WarnedFunKind = STRING;
1571 UnsafeArg = E;
1572 } else if (Result.Nodes.getNodeAs<CallExpr>(UnsafeSizedByTag)) {
1573 WarnedFunKind = SIZED_BY;
1574 UnsafeArg = Call->getArg(0);
1575 } else if (Result.Nodes.getNodeAs<Decl>(UnsafeVaListTag))
1576 WarnedFunKind = VA_LIST;
1577 }
1578
1580 return stmt(unless(ignoreUnsafeLibcCall(Handler)),
1584
1585
1586 functionDecl(libc_func_matchers::isPredefinedUnsafeLibcFunc()),
1587
1588
1589
1590 functionDecl(libc_func_matchers::isUnsafeVaListPrintfFunc())
1591 .bind(UnsafeVaListTag),
1592
1593
1594 functionDecl(libc_func_matchers::isUnsafeSprintfFunc())
1595 .bind(UnsafeSprintfTag)))),
1596
1599
1600
1601
1602
1603
1604
1605
1607 libc_func_matchers::hasUnsafeSnprintfBuffer())
1608 .bind(UnsafeSizedByTag),
1609
1610
1612 libc_func_matchers::hasUnsafePrintfStringArg(
1613 expr().bind(UnsafeStringTag)))));
1614 }
1615
1616 const Stmt *getBaseStmt() const { return Call; }
1617
1618 SourceLocation getSourceLoc() const override { return Call->getBeginLoc(); }
1619
1621 bool IsRelatedToDecl,
1624 }
1625
1626 DeclUseList getClaimedVarUseSites() const override { return {}; }
1627};
1628
1629
1630
1631
1632class ULCArraySubscriptGadget : public FixableGadget {
1633private:
1634 static constexpr const char *const ULCArraySubscriptTag =
1635 "ArraySubscriptUnderULC";
1637
1638public:
1640 : FixableGadget(Kind::ULCArraySubscript),
1642 assert(Node != nullptr && "Expecting a non-null matching result");
1643 }
1644
1645 static bool classof(const Gadget *G) {
1646 return G->getKind() == Kind::ULCArraySubscript;
1647 }
1648
1649 static Matcher matcher() {
1651 auto BaseIsArrayOrPtrDRE = hasBase(
1652 ignoringParenImpCasts(declRefExpr(ArrayOrPtr, toSupportedVariable())));
1655
1657 }
1658
1659 virtual std::optional
1660 getFixits(const FixitStrategy &S) const override;
1661 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1662
1663 virtual DeclUseList getClaimedVarUseSites() const override {
1664 if (const auto *DRE =
1665 dyn_cast(Node->getBase()->IgnoreImpCasts())) {
1666 return {DRE};
1667 }
1668 return {};
1669 }
1670};
1671
1672
1673
1674
1675class UPCStandalonePointerGadget : public FixableGadget {
1676private:
1677 static constexpr const char *const DeclRefExprTag = "StandalonePointer";
1679
1680public:
1682 : FixableGadget(Kind::UPCStandalonePointer),
1684 assert(Node != nullptr && "Expecting a non-null matching result");
1685 }
1686
1687 static bool classof(const Gadget *G) {
1688 return G->getKind() == Kind::UPCStandalonePointer;
1689 }
1690
1691 static Matcher matcher() {
1693 auto target = expr(ignoringParenImpCasts(
1695 .bind(DeclRefExprTag)));
1697 }
1698
1699 virtual std::optional
1700 getFixits(const FixitStrategy &S) const override;
1701 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1702
1703 virtual DeclUseList getClaimedVarUseSites() const override { return {Node}; }
1704};
1705
1706class PointerDereferenceGadget : public FixableGadget {
1707 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
1708 static constexpr const char *const OperatorTag = "op";
1709
1710 const DeclRefExpr *BaseDeclRefExpr = nullptr;
1712
1713public:
1715 : FixableGadget(Kind::PointerDereference),
1716 BaseDeclRefExpr(
1719
1720 static bool classof(const Gadget *G) {
1721 return G->getKind() == Kind::PointerDereference;
1722 }
1723
1724 static Matcher matcher() {
1727 hasOperatorName("*"),
1728 has(expr(ignoringParenImpCasts(
1729 declRefExpr(toSupportedVariable()).bind(BaseDeclRefExprTag)))))
1730 .bind(OperatorTag);
1731
1733 }
1734
1735 DeclUseList getClaimedVarUseSites() const override {
1736 return {BaseDeclRefExpr};
1737 }
1738
1739 virtual std::optional
1740 getFixits(const FixitStrategy &S) const override;
1742};
1743
1744
1745
1746
1747class UPCAddressofArraySubscriptGadget : public FixableGadget {
1748private:
1749 static constexpr const char *const UPCAddressofArraySubscriptTag =
1750 "AddressofArraySubscriptUnderUPC";
1752
1753public:
1755 : FixableGadget(Kind::ULCArraySubscript),
1757 UPCAddressofArraySubscriptTag)) {
1758 assert(Node != nullptr && "Expecting a non-null matching result");
1759 }
1760
1761 static bool classof(const Gadget *G) {
1762 return G->getKind() == Kind::UPCAddressofArraySubscript;
1763 }
1764
1765 static Matcher matcher() {
1768 hasOperatorName("&"),
1770 ignoringParenImpCasts(declRefExpr(toSupportedVariable()))))))
1771 .bind(UPCAddressofArraySubscriptTag)))));
1772 }
1773
1774 virtual std::optional
1775 getFixits(const FixitStrategy &) const override;
1776 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1777
1778 virtual DeclUseList getClaimedVarUseSites() const override {
1779 const auto *ArraySubst = cast(Node->getSubExpr());
1780 const auto *DRE =
1781 cast(ArraySubst->getBase()->IgnoreParenImpCasts());
1782 return {DRE};
1783 }
1784};
1785}
1786
1787namespace {
1788
1789
1790
1791class DeclUseTracker {
1792 using UseSetTy = SmallSet<const DeclRefExpr *, 16>;
1793 using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>;
1794
1795
1796 std::unique_ptr Uses{std::make_unique()};
1797 DefMapTy Defs{};
1798
1799public:
1800 DeclUseTracker() = default;
1801 DeclUseTracker(const DeclUseTracker &) = delete;
1802 DeclUseTracker &operator=(const DeclUseTracker &) = delete;
1803 DeclUseTracker(DeclUseTracker &&) = default;
1804 DeclUseTracker &operator=(DeclUseTracker &&) = default;
1805
1806
1807 void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }
1808
1809
1811 assert(Uses->count(DRE) &&
1812 "DRE not found or claimed by multiple matchers!");
1813 Uses->erase(DRE);
1814 }
1815
1816
1817 bool hasUnclaimedUses(const VarDecl *VD) const {
1818
1819 return any_of(*Uses, [VD](const DeclRefExpr *DRE) {
1821 });
1822 }
1823
1824 UseSetTy getUnclaimedUses(const VarDecl *VD) const {
1825 UseSetTy ReturnSet;
1826 for (auto use : *Uses) {
1827 if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) {
1828 ReturnSet.insert(use);
1829 }
1830 }
1831 return ReturnSet;
1832 }
1833
1834 void discoverDecl(const DeclStmt *DS) {
1836 if (const auto *VD = dyn_cast(D)) {
1837
1838
1839
1840
1841
1842 Defs[VD] = DS;
1843 }
1844 }
1845 }
1846
1848 return Defs.lookup(VD);
1849 }
1850};
1851}
1852
1853
1854
1856private:
1857 static constexpr const char *const UPCPreIncrementTag =
1858 "PointerPreIncrementUnderUPC";
1860
1861public:
1863 : FixableGadget(Kind::UPCPreIncrement),
1865 assert(Node != nullptr && "Expecting a non-null matching result");
1866 }
1867
1869 return G->getKind() == Kind::UPCPreIncrement;
1870 }
1871
1873
1874
1875
1876
1879 hasUnaryOperand(declRefExpr(toSupportedVariable())))
1880 .bind(UPCPreIncrementTag)))));
1881 }
1882
1883 virtual std::optional
1886
1888 return {dyn_cast(Node->getSubExpr())};
1889 }
1890};
1891
1892
1893
1895private:
1896 static constexpr const char *const UUCAddAssignTag =
1897 "PointerAddAssignUnderUUC";
1898 static constexpr const char *const OffsetTag = "Offset";
1899
1901 const Expr *Offset = nullptr;
1902
1903public:
1905 : FixableGadget(Kind::UUCAddAssign),
1907 Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) {
1908 assert(Node != nullptr && "Expecting a non-null matching result");
1909 }
1910
1912 return G->getKind() == Kind::UUCAddAssign;
1913 }
1914
1916
1919 hasLHS(
1922 toSupportedVariable())),
1923 hasRHS(expr().bind(OffsetTag)))
1924 .bind(UUCAddAssignTag)))));
1925
1926 }
1927
1928 virtual std::optional
1931
1933 return {dyn_cast(Node->getLHS())};
1934 }
1935};
1936
1937
1938
1940 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
1941 static constexpr const char *const DerefOpTag = "DerefOp";
1942 static constexpr const char *const AddOpTag = "AddOp";
1943 static constexpr const char *const OffsetTag = "Offset";
1944
1945 const DeclRefExpr *BaseDeclRefExpr = nullptr;
1949
1950public:
1952 : FixableGadget(Kind::DerefSimplePtrArithFixable),
1953 BaseDeclRefExpr(
1958
1960
1962 ignoringImpCasts(declRefExpr(toSupportedVariable()).
1963 bind(BaseDeclRefExprTag)));
1964 auto PlusOverPtrAndInteger = expr(anyOf(
1965 binaryOperator(hasOperatorName("+"), hasLHS(ThePtr),
1967 .bind(AddOpTag),
1968 binaryOperator(hasOperatorName("+"), hasRHS(ThePtr),
1970 .bind(AddOpTag)));
1972 hasOperatorName("*"),
1973 hasUnaryOperand(ignoringParens(PlusOverPtrAndInteger)))
1974 .bind(DerefOpTag));
1975
1976 }
1977
1978 virtual std::optional
1982 }
1983
1985 return {BaseDeclRefExpr};
1986 }
1987};
1988
1989
1992 bool EmitSuggestions, FixableGadgetList &FixableGadgets,
1993 WarningGadgetList &WarningGadgets,
1994 DeclUseTracker &Tracker) {
1995
1997 GadgetFinderCallback(FixableGadgetList &FixableGadgets,
1998 WarningGadgetList &WarningGadgets,
1999 DeclUseTracker &Tracker)
2000 : FixableGadgets(FixableGadgets), WarningGadgets(WarningGadgets),
2001 Tracker(Tracker) {}
2002
2004
2005
2006#if NDEBUG
2007#define NEXT return
2008#else
2009 [[maybe_unused]] int numFound = 0;
2010#define NEXT ++numFound
2011#endif
2012
2013 if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("any_dre")) {
2014 Tracker.discoverUse(DRE);
2016 }
2017
2018 if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) {
2019 Tracker.discoverDecl(DS);
2021 }
2022
2023
2024
2025
2026#define FIXABLE_GADGET(name) \
2027 if (Result.Nodes.getNodeAs(#name)) { \
2028 FixableGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
2029 NEXT; \
2030 }
2031#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2032#define WARNING_GADGET(name) \
2033 if (Result.Nodes.getNodeAs(#name)) { \
2034 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
2035 NEXT; \
2036 }
2037#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2038
2039 assert(numFound >= 1 && "Gadgets not found in match result!");
2040 assert(numFound <= 1 && "Conflicting bind tags in gadgets!");
2041 }
2042
2043 FixableGadgetList &FixableGadgets;
2044 WarningGadgetList &WarningGadgets;
2045 DeclUseTracker &Tracker;
2046 };
2047
2049 GadgetFinderCallback CB{FixableGadgets, WarningGadgets, Tracker};
2050
2051
2054 forEachDescendantEvaluatedStmt(stmt(anyOf(
2055
2057 allOf(x ## Gadget::matcher().bind(#x), \
2058 notInSafeBufferOptOut(&Handler)),
2060 allOf(x ## Gadget::matcher(&Handler).bind(#x), \
2061 notInSafeBufferOptOut(&Handler)),
2062#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2063
2065 )))
2066 ),
2067 &CB
2068 );
2069
2070
2071 if (EmitSuggestions) {
2072
2077 x ## Gadget::matcher().bind(#x),
2078#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2079
2080
2083
2084
2085
2087 )))
2088 ),
2089 &CB
2090 );
2091
2092 }
2093
2094 M.match(*S, Ctx);
2095}
2096
2097
2099 bool operator()(const NodeTy *N1, const NodeTy *N2) const {
2100 return N1->getBeginLoc().getRawEncoding() <
2101 N2->getBeginLoc().getRawEncoding();
2102 }
2103};
2104
2106 std::map<const VarDecl *, std::set<const WarningGadget *>,
2107
2108
2111
2113};
2114
2118
2119
2120 for (auto &G : AllUnsafeOperations) {
2121 DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
2122
2123 bool AssociatedWithVarDecl = false;
2124 for (const DeclRefExpr *DRE : ClaimedVarUseSites) {
2125 if (const auto *VD = dyn_cast(DRE->getDecl())) {
2126 result.byVar[VD].insert(G.get());
2127 AssociatedWithVarDecl = true;
2128 }
2129 }
2130
2131 if (!AssociatedWithVarDecl) {
2132 result.noVar.push_back(G.get());
2133 continue;
2134 }
2135 }
2136 return result;
2137}
2138
2140 std::map<const VarDecl *, std::set<const FixableGadget *>,
2141
2142
2145};
2146
2150 for (auto &F : AllFixableOperations) {
2151 DeclUseList DREs = F->getClaimedVarUseSites();
2152
2154 if (const auto *VD = dyn_cast(DRE->getDecl())) {
2155 FixablesForUnsafeVars.byVar[VD].insert(F.get());
2156 }
2157 }
2158 }
2159 return FixablesForUnsafeVars;
2160}
2161
2164
2165
2166 std::vector<const FixItHint *> All;
2167
2168 for (const FixItHint &H : FixIts)
2169 All.push_back(&H);
2170 std::sort(All.begin(), All.end(),
2172 return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
2173 H2->RemoveRange.getBegin());
2174 });
2175
2176 const FixItHint *CurrHint = nullptr;
2177
2178 for (const FixItHint *Hint : All) {
2179 if (!CurrHint ||
2181 Hint->RemoveRange.getBegin())) {
2182
2183
2184 CurrHint = Hint;
2185 } else
2186
2187
2188 return true;
2189 }
2190 return false;
2191}
2192
2193std::optional
2194PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
2195 const auto *LeftVD = cast(PtrLHS->getDecl());
2196 const auto *RightVD = cast(PtrRHS->getDecl());
2197 switch (S.lookup(LeftVD)) {
2198 case FixitStrategy::Kind::Span:
2199 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
2200 return FixItList{};
2201 return std::nullopt;
2202 case FixitStrategy::Kind::Wontfix:
2203 return std::nullopt;
2204 case FixitStrategy::Kind::Iterator:
2205 case FixitStrategy::Kind::Array:
2206 return std::nullopt;
2207 case FixitStrategy::Kind::Vector:
2208 llvm_unreachable("unsupported strategies for FixableGadgets");
2209 }
2210 return std::nullopt;
2211}
2212
2213
2216
2217std::optional
2218CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
2219 const auto *LeftVD = cast(PtrLHS->getDecl());
2220 const auto *RightVD = cast(PtrRHS->getDecl());
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237 if (S.lookup(LeftVD) == FixitStrategy::Kind::Span) {
2238 if (S.lookup(RightVD) == FixitStrategy::Kind::Wontfix) {
2239 return FixItList{};
2240 }
2241 } else if (S.lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {
2242 if (S.lookup(RightVD) == FixitStrategy::Kind::Array) {
2243 return createDataFixit(RightVD->getASTContext(), PtrRHS);
2244 }
2245 }
2246 return std::nullopt;
2247}
2248
2249std::optional
2250PointerInitGadget::getFixits(const FixitStrategy &S) const {
2251 const auto *LeftVD = PtrInitLHS;
2252 const auto *RightVD = cast(PtrInitRHS->getDecl());
2253 switch (S.lookup(LeftVD)) {
2254 case FixitStrategy::Kind::Span:
2255 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
2256 return FixItList{};
2257 return std::nullopt;
2258 case FixitStrategy::Kind::Wontfix:
2259 return std::nullopt;
2260 case FixitStrategy::Kind::Iterator:
2261 case FixitStrategy::Kind::Array:
2262 return std::nullopt;
2263 case FixitStrategy::Kind::Vector:
2264 llvm_unreachable("unsupported strategies for FixableGadgets");
2265 }
2266 return std::nullopt;
2267}
2268
2272 if (ConstVal->isNegative())
2273 return false;
2275 return false;
2276 return true;
2277}
2278
2279std::optional
2280ULCArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
2281 if (const auto *DRE =
2282 dyn_cast(Node->getBase()->IgnoreImpCasts()))
2283 if (const auto *VD = dyn_cast(DRE->getDecl())) {
2284 switch (S.lookup(VD)) {
2285 case FixitStrategy::Kind::Span: {
2286
2287
2288
2289 const ASTContext &Ctx =
2292 return std::nullopt;
2293
2294 return FixItList{};
2295 }
2296 case FixitStrategy::Kind::Array:
2297 return FixItList{};
2298 case FixitStrategy::Kind::Wontfix:
2299 case FixitStrategy::Kind::Iterator:
2300 case FixitStrategy::Kind::Vector:
2301 llvm_unreachable("unsupported strategies for FixableGadgets");
2302 }
2303 }
2304 return std::nullopt;
2305}
2306
2307static std::optional
2309
2310std::optional
2311UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
2312 auto DREs = getClaimedVarUseSites();
2313 const auto *VD = cast(DREs.front()->getDecl());
2314
2315 switch (S.lookup(VD)) {
2316 case FixitStrategy::Kind::Span:
2318 case FixitStrategy::Kind::Wontfix:
2319 case FixitStrategy::Kind::Iterator:
2320 case FixitStrategy::Kind::Array:
2321 return std::nullopt;
2322 case FixitStrategy::Kind::Vector:
2323 llvm_unreachable("unsupported strategies for FixableGadgets");
2324 }
2325 return std::nullopt;
2326}
2327
2328
2330 static const char *const EOL = "\n";
2331 return EOL;
2332}
2333
2334
2335static std::string
2337 std::string s = std::string("<# ");
2338 s += HintTextToUser;
2339 s += " #>";
2340 return s;
2341}
2342
2343
2344template
2345static std::optional
2350
2352 return Loc;
2353
2354 return std::nullopt;
2355}
2356
2357
2358template
2365 return Loc;
2366 return std::nullopt;
2367}
2368
2369
2373 std::optional LastCharLoc = getPastLoc(E, SM, LangOpts);
2374
2375 if (LastCharLoc)
2378 LangOpts);
2379
2380 return std::nullopt;
2381}
2382
2383
2390
2392 return Text;
2393 return std::nullopt;
2394}
2395
2396
2397
2399
2400
2402}
2403
2404
2405static std::optional
2411
2412 if (ParmIdentEndLoc.isMacroID() &&
2414 return std::nullopt;
2415 return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts);
2416}
2417
2418
2419
2420
2421
2422
2423
2426
2427
2428 bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {
2429 return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
2430 VD->getBeginLoc())) &&
2431 !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
2432 At->getRange().getBegin()));
2433 });
2436 AttrRangeOverlapping;
2437}
2438
2439
2440
2441
2442
2448 End =
2449
2451
2453}
2454
2455
2456
2457
2458
2459
2460static std::optionalstd::string
2463 std::optional *QualifiersToAppend) {
2466
2468 "Expecting a VarDecl of type of pointer to object type");
2470
2473
2474
2475
2477 case TypeLoc::ConstantArray:
2478 case TypeLoc::IncompleteArray:
2479 case TypeLoc::VariableArray:
2480 case TypeLoc::DependentSizedArray:
2481 case TypeLoc::Decayed:
2482 assert(isa(VD) && "An array type shall not be treated as a "
2483 "pointer type unless it decays.");
2485 break;
2486 case TypeLoc::Pointer:
2488 break;
2489 default:
2490 return std::nullopt;
2491 }
2492 if (PteTyLoc.isNull())
2493
2494
2495 return std::nullopt;
2496
2498
2500
2501
2502
2503 return std::nullopt;
2504 }
2505
2506
2509
2510 if (!PteEndOfTokenLoc.isValid())
2511
2512
2513 return std::nullopt;
2514 if (.isBeforeInTranslationUnit(PteEndOfTokenLoc, IdentLoc)) {
2515
2516
2517
2518
2519
2520
2521 return std::nullopt;
2522 }
2524
2525
2526
2528 }
2530 ->str();
2531}
2532
2533
2540
2541
2544 SourceRange NameRange{BeginLoc, EndLoc};
2545
2547}
2548
2549
2550
2551
2552
2553
2554static std::string
2556 std::optional Quals = std::nullopt) {
2557 const char *const SpanOpen = "std::span<";
2558
2559 if (Quals)
2560 return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>';
2561 return SpanOpen + EltTyText.str() + '>';
2562}
2563
2564std::optional
2566 const VarDecl *VD = dyn_cast(BaseDeclRefExpr->getDecl());
2567
2568 if (VD && s.lookup(VD) == FixitStrategy::Kind::Span) {
2570
2572 if (ConstVal->isNegative())
2573 return std::nullopt;
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2600
2601 std::optional LHSLocation = getPastLoc(LHS, SM, LangOpts);
2602 if (!LHSLocation)
2603 return std::nullopt;
2604
2607
2608 std::optional AddOpLocation =
2610 std::optional DerefOpLocation =
2612
2613 if (!AddOpLocation || !DerefOpLocation)
2614 return std::nullopt;
2615
2618
2619 return FixItList{
2623 }
2624 return std::nullopt;
2625}
2626
2627std::optional
2628PointerDereferenceGadget::getFixits(const FixitStrategy &S) const {
2629 const VarDecl *VD = cast(BaseDeclRefExpr->getDecl());
2630 switch (S.lookup(VD)) {
2631 case FixitStrategy::Kind::Span: {
2634
2635
2638
2639 if (auto LocPastOperand =
2643 }
2644 break;
2645 }
2646 case FixitStrategy::Kind::Iterator:
2647 case FixitStrategy::Kind::Array:
2648 return std::nullopt;
2649 case FixitStrategy::Kind::Vector:
2650 llvm_unreachable("FixitStrategy not implemented yet!");
2651 case FixitStrategy::Kind::Wontfix:
2652 llvm_unreachable("Invalid strategy!");
2653 }
2654
2655 return std::nullopt;
2656}
2657
2661
2662 std::optional EndOfOperand =
2664
2665 if (EndOfOperand)
2667
2668 return std::nullopt;
2669}
2670
2671
2672
2673std::optional
2674UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const {
2675 const auto VD = cast(Node->getDecl());
2676 switch (S.lookup(VD)) {
2677 case FixitStrategy::Kind::Array:
2678 case FixitStrategy::Kind::Span: {
2680
2681 break;
2682 }
2683 case FixitStrategy::Kind::Wontfix:
2684 case FixitStrategy::Kind::Iterator:
2685 return std::nullopt;
2686 case FixitStrategy::Kind::Vector:
2687 llvm_unreachable("unsupported strategies for FixableGadgets");
2688 }
2689
2690 return std::nullopt;
2691}
2692
2693
2694
2695static std::optional
2697 const auto *ArraySub = cast(Node->getSubExpr());
2698 const auto *DRE = cast(ArraySub->getBase()->IgnoreImpCasts());
2699
2700
2702 const Expr *Idx = ArraySub->getIdx();
2705 std::stringstream SS;
2706 bool IdxIsLitZero = false;
2707
2709 if ((*ICE).isZero())
2710 IdxIsLitZero = true;
2711 std::optional DreString = getExprText(DRE, SM, LangOpts);
2712 if (!DreString)
2713 return std::nullopt;
2714
2715 if (IdxIsLitZero) {
2716
2717 SS << (*DreString).str() << ".data()";
2718 } else {
2719 std::optional IndexString = getExprText(Idx, SM, LangOpts);
2720 if (!IndexString)
2721 return std::nullopt;
2722
2723 SS << "&" << (*DreString).str() << ".data()"
2724 << "[" << (*IndexString).str() << "]";
2725 }
2726 return FixItList{
2728}
2729
2730std::optional
2733
2734 if (DREs.size() != 1)
2735 return std::nullopt;
2736
2737 if (const VarDecl *VD = dyn_cast(DREs.front()->getDecl())) {
2738 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
2739 FixItList Fixes;
2740
2741 const Stmt *AddAssignNode = Node;
2742 StringRef varName = VD->getName();
2744
2746 return std::nullopt;
2747
2748
2749 bool NotParenExpr =
2751 std::string SS = varName.str() + " = " + varName.str() + ".subspan";
2752 if (NotParenExpr)
2753 SS += "(";
2754
2755 std::optional AddAssignLocation = getEndCharLoc(
2757 if (!AddAssignLocation)
2758 return std::nullopt;
2759
2762 SS));
2763 if (NotParenExpr)
2766 return Fixes;
2767 }
2768 }
2769 return std::nullopt;
2770}
2771
2772std::optional
2775
2776 if (DREs.size() != 1)
2777 return std::nullopt;
2778
2779 if (const VarDecl *VD = dyn_cast(DREs.front()->getDecl())) {
2780 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
2781 FixItList Fixes;
2782 std::stringstream SS;
2783 StringRef varName = VD->getName();
2785
2786
2787 SS << "(" << varName.data() << " = " << varName.data()
2788 << ".subspan(1)).data()";
2789 std::optional PreIncLocation =
2791 if (!PreIncLocation)
2792 return std::nullopt;
2793
2795 SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));
2796 return Fixes;
2797 }
2798 }
2799 return std::nullopt;
2800}
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817static std::optional
2819 const StringRef UserFillPlaceHolder) {
2822
2823
2824
2825
2826
2827 if (Init->isNullPointerConstant(
2828 Ctx,
2829
2830
2832 NPC_ValueDependentIsNotNull)) {
2833 std::optional InitLocation =
2835 if (!InitLocation)
2836 return std::nullopt;
2837
2839
2841 }
2842
2843 FixItList FixIts{};
2844 std::string ExtentText = UserFillPlaceHolder.data();
2845 StringRef One = "1";
2846
2847
2849
2850 if (auto CxxNew = dyn_cast(Init->IgnoreImpCasts())) {
2851
2852
2853
2854
2855 if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {
2856 if (!Ext->HasSideEffects(Ctx)) {
2857 std::optional ExtentString = getExprText(Ext, SM, LangOpts);
2858 if (!ExtentString)
2859 return std::nullopt;
2860 ExtentText = *ExtentString;
2861 }
2862 } else if (!CxxNew->isArray())
2863
2864
2865 ExtentText = One;
2867
2868
2869
2870 return FixItList{};
2871 } else {
2872
2873
2874 if (auto AddrOfExpr = dyn_cast(Init->IgnoreImpCasts()))
2875 if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
2876 isa_and_present(AddrOfExpr->getSubExpr()))
2877 ExtentText = One;
2878
2879
2880 }
2881
2883 std::optional LocPassInit = getPastLoc(Init, SM, LangOpts);
2884
2885 if (!LocPassInit)
2886 return std::nullopt;
2887
2888 StrBuffer.append(", ");
2889 StrBuffer.append(ExtentText);
2890 StrBuffer.append("}");
2892 return FixIts;
2893}
2894
2895#ifndef NDEBUG
2896#define DEBUG_NOTE_DECL_FAIL(D, Msg) \
2897 Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \
2898 "failed to produce fixit for declaration '" + \
2899 (D)->getNameAsString() + "'" + (Msg))
2900#else
2901#define DEBUG_NOTE_DECL_FAIL(D, Msg)
2902#endif
2903
2904
2905
2906
2907static std::optionalstd::string
2910
2911 std::optional PteTyQualifiers = std::nullopt;
2914
2915 if (!PteTyText)
2916 return std::nullopt;
2917
2918 std::string SpanTyText = "std::span<";
2919
2920 SpanTyText.append(*PteTyText);
2921
2922 if (PteTyQualifiers) {
2923 SpanTyText.append(" ");
2924 SpanTyText.append(PteTyQualifiers->getAsString());
2925 }
2926 SpanTyText.append(">");
2927 return SpanTyText;
2928}
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2945 const StringRef UserFillPlaceHolder,
2948 return {};
2949
2950 FixItList FixIts{};
2952
2953 if (!SpanTyText) {
2955 return {};
2956 }
2957
2958
2959 std::stringstream SS;
2960
2961 SS << *SpanTyText;
2962
2963 if (const Expr *Init = D->getInit()) {
2964 std::optional InitFixIts =
2966 if (!InitFixIts)
2967 return {};
2968 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
2969 std::make_move_iterator(InitFixIts->end()));
2970 }
2971
2972
2973
2974
2975 const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();
2976 if (!EndLocForReplacement.isValid()) {
2978 return {};
2979 }
2980
2981
2982
2984 SS << " ";
2985
2988 return FixIts;
2989}
2990
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
3021
3022
3023
3024
3025
3026static std::optional
3030
3032 return std::nullopt;
3033
3036 const unsigned NumParms = FD->getNumParams();
3037 std::vectorstd::string NewTysTexts(NumParms);
3038 std::vector ParmsMask(NumParms, false);
3039 bool AtLeastOneParmToFix = false;
3040
3041 for (unsigned i = 0; i < NumParms; i++) {
3043
3044 if (S.lookup(PVD) == FixitStrategy::Kind::Wontfix)
3045 continue;
3046 if (S.lookup(PVD) != FixitStrategy::Kind::Span)
3047
3048 return std::nullopt;
3049
3050 std::optional PteTyQuals = std::nullopt;
3051 std::optionalstd::string PteTyText =
3053
3054 if (!PteTyText)
3055
3056 return std::nullopt;
3057
3058
3059 NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);
3060 ParmsMask[i] = true;
3061 AtLeastOneParmToFix = true;
3062 }
3063 if (!AtLeastOneParmToFix)
3064
3065 return {};
3066
3067
3068
3069
3070 const auto NewOverloadSignatureCreator =
3071 [&SM, &LangOpts, &NewTysTexts,
3072 &ParmsMask](const FunctionDecl *FD) -> std::optionalstd::string {
3073 std::stringstream SS;
3074
3075 SS << ";";
3077
3080 SM, LangOpts))
3081 SS << Prefix->str();
3082 else
3083 return std::nullopt;
3084
3085 const unsigned NumParms = FD->getNumParams();
3086
3087 for (unsigned i = 0; i < NumParms; i++) {
3089
3091 continue;
3092 if (ParmsMask[i]) {
3093
3094
3095 SS << NewTysTexts[i];
3096
3098 SS << ' ' << II->getName().str();
3099 } else if (auto ParmTypeText =
3101 SM, LangOpts)) {
3102
3103 SS << ParmTypeText->str();
3104 } else
3105 return std::nullopt;
3106 if (i != NumParms - 1)
3107 SS << ", ";
3108 }
3109 SS << ")";
3110 return SS.str();
3111 };
3112
3113
3114
3115 const auto OldOverloadDefCreator =
3116 [&Handler, &SM, &LangOpts, &NewTysTexts,
3117 &ParmsMask](const FunctionDecl *FD) -> std::optionalstd::string {
3118 std::stringstream SS;
3119
3121
3124 LangOpts))
3126 << FDPrefix->str() << "{";
3127 else
3128 return std::nullopt;
3129
3131 SS << "return " << FunQualName->str() << "(";
3132 else
3133 return std::nullopt;
3134
3135
3136 const unsigned NumParms = FD->getNumParams();
3137 for (unsigned i = 0; i < NumParms; i++) {
3139
3141 continue;
3142
3143
3145
3146 return std::nullopt;
3147 if (ParmsMask[i])
3148
3151 else
3153 if (i != NumParms - 1)
3154 SS << ", ";
3155 }
3156
3158
3159 return SS.str();
3160 };
3161
3162 FixItList FixIts{};
3164 std::optional Loc = getPastLoc(FReDecl, SM, LangOpts);
3165
3166 if ()
3167 return {};
3168 if (FReDecl->isThisDeclarationADefinition()) {
3169 assert(FReDecl == FD && "inconsistent function definition");
3170
3171
3172 if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
3174 else
3175 return {};
3176 } else {
3177
3178 if (!FReDecl->hasAttr()) {
3181 FReDecl->getBeginLoc(), " ")));
3182 }
3183
3184 if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
3186 else
3187 return {};
3188 }
3189 }
3190 return FixIts;
3191}
3192
3193
3198 return {};
3199 }
3201
3203 return {};
3204 }
3205
3206 std::optional PteTyQualifiers = std::nullopt;
3209
3210 if (!PteTyText) {
3212 return {};
3213 }
3214
3216
3217 if (!PVDNameText) {
3219 return {};
3220 }
3221
3222 std::stringstream SS;
3224
3225 if (PteTyQualifiers)
3226
3228 else
3230
3233
3234 SS << ' ' << PVDNameText->str();
3235
3237}
3238
3240 const DeclUseTracker &Tracker,
3243 const DeclStmt *DS = Tracker.lookupDecl(VD);
3244 if (!DS) {
3246 " : variables declared this way not implemented yet");
3247 return {};
3248 }
3250
3252 return {};
3253 }
3254
3255
3256
3257 (void)DS;
3258
3259
3261}
3262
3265 FixItList FixIts{};
3266
3267
3268
3270 const QualType &ArrayEltT = CAT->getElementType();
3271 assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
3272
3274 return {};
3275
3277
3278
3279
3280 auto MaybeElemTypeTxt =
3283 if (!MaybeElemTypeTxt)
3284 return {};
3285 const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
3286
3287
3290 while (NextTok && !NextTok->is(tok::l_square) &&
3294 if (!NextTok)
3295 return {};
3296 const SourceLocation LSqBracketLoc = NextTok->getLocation();
3297
3298
3299
3303 if (!MaybeArraySizeTxt)
3304 return {};
3305 const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
3306 if (ArraySizeTxt.empty()) {
3307
3308
3309
3310
3311
3312
3313
3314 return {};
3315 }
3316
3317 std::optional IdentText =
3319
3320 if (!IdentText) {
3322 return {};
3323 }
3324
3326 raw_svector_ostream OS(Replacement);
3327 OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> "
3328 << IdentText->str();
3329
3332 }
3333
3334 return FixIts;
3335}
3336
3338 const DeclUseTracker &Tracker,
3341 const DeclStmt *DS = Tracker.lookupDecl(VD);
3342 assert(DS && "Fixing non-local variables not implemented yet!");
3344
3345 return {};
3346 }
3347
3348
3349
3350 (void)DS;
3351
3352
3354}
3355
3356
3357
3358static FixItList
3361 const DeclUseTracker &Tracker, ASTContext &Ctx,
3363 if (const auto *PVD = dyn_cast(VD)) {
3364 auto *FD = dyn_castclang::FunctionDecl(PVD->getDeclContext());
3365 if (!FD || FD != D) {
3366
3367
3369 return {};
3370 }
3371
3372
3373
3374
3375
3376 if (FD->isMain() || FD->isConstexpr() ||
3377 FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate ||
3378 FD->isVariadic() ||
3379
3380 isa(FD) ||
3381
3382 (FD->hasBody() && isa(FD->getBody())) ||
3383 FD->isOverloadedOperator()) {
3385 return {};
3386 }
3387 }
3388
3389 switch (K) {
3390 case FixitStrategy::Kind::Span: {
3392 if (const auto *PVD = dyn_cast(VD))
3394
3397 }
3399 return {};
3400 }
3401 case FixitStrategy::Kind::Array: {
3404
3406 return {};
3407 }
3408 case FixitStrategy::Kind::Iterator:
3409 case FixitStrategy::Kind::Vector:
3410 llvm_unreachable("FixitStrategy not implemented yet!");
3411 case FixitStrategy::Kind::Wontfix:
3412 llvm_unreachable("Invalid strategy!");
3413 }
3414 llvm_unreachable("Unknown strategy!");
3415}
3416
3417
3418
3420
3421
3422 return llvm::any_of(FixIts, [](const FixItHint &Hint) {
3425
3426 return true;
3427 return false;
3428 });
3429}
3430
3431
3433 return isa(VD) &&
3435}
3436
3437
3438
3439
3441 std::map<const VarDecl *, FixItList> &FixItsForVariable,
3443
3445
3446 for (const auto &[VD, Ignore] : FixItsForVariable) {
3448 if (llvm::any_of(Grp,
3449 [&FixItsForVariable](const VarDecl *GrpMember) -> bool {
3450 return !FixItsForVariable.count(GrpMember);
3451 })) {
3452
3453
3455 ToErase.push_back(Member);
3456 }
3457 }
3458 for (auto *VarToErase : ToErase)
3459 FixItsForVariable.erase(VarToErase);
3460}
3461
3462
3463
3464
3465
3466
3467
3468
3470 std::map<const VarDecl *, FixItList> &FixItsForVariable ,
3474 FixItList FixItsSharedByParms{};
3475
3476 std::optional OverloadFixes =
3478
3479 if (OverloadFixes) {
3480 FixItsSharedByParms.append(*OverloadFixes);
3481 } else {
3482
3483
3484
3486 FixItsForVariable.erase(Member);
3487 }
3488 return FixItsSharedByParms;
3489}
3490
3491
3492static std::map<const VarDecl *, FixItList>
3498
3499
3500
3501 std::map<const VarDecl *, FixItList> FixItsForVariable;
3502
3503
3504
3505
3506 for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
3507 FixItsForVariable[VD] =
3508 fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);
3509
3510
3511 if (FixItsForVariable[VD].empty()) {
3512 FixItsForVariable.erase(VD);
3513 continue;
3514 }
3515 for (const auto &F : Fixables) {
3516 std::optional Fixits = F->getFixits(S);
3517
3518 if (Fixits) {
3519 FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
3520 Fixits->begin(), Fixits->end());
3521 continue;
3522 }
3523#ifndef NDEBUG
3525 VD, F->getSourceLoc(),
3526 ("gadget '" + F->getDebugName() + "' refused to produce a fix")
3527 .str());
3528#endif
3529 FixItsForVariable.erase(VD);
3530 break;
3531 }
3532 }
3533
3534
3535
3536
3537
3538
3539
3541
3542
3543
3544
3545
3546
3547
3548 FixItList FixItsSharedByParms{};
3549
3550 if (auto *FD = dyn_cast(D))
3552 FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
3553
3554
3555
3556 std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
3557 FixItsForVariable};
3558
3559 for (auto &[Var, Ignore] : FixItsForVariable) {
3560 bool AnyParm = false;
3561 const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm);
3562
3563 for (const VarDecl *GrpMate : VarGroupForVD) {
3564 if (Var == GrpMate)
3565 continue;
3566 if (FixItsForVariable.count(GrpMate))
3567 FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
3568 }
3569 if (AnyParm) {
3570
3571 assert(!FixItsSharedByParms.empty() &&
3572 "Should not try to fix a parameter that does not belong to a "
3573 "FunctionDecl");
3574 FinalFixItsForVariable[Var].append(FixItsSharedByParms);
3575 }
3576 }
3577
3578
3579
3580
3581 for (auto Iter = FinalFixItsForVariable.begin();
3582 Iter != FinalFixItsForVariable.end();)
3585 Iter = FinalFixItsForVariable.erase(Iter);
3586 } else
3588 return FinalFixItsForVariable;
3589}
3590
3591template
3595 for (const VarDecl *VD : UnsafeVars) {
3597 S.set(VD, FixitStrategy::Kind::Array);
3598 else
3599 S.set(VD, FixitStrategy::Kind::Span);
3600 }
3601 return S;
3602}
3603
3604
3606 const std::vector Groups;
3607 const std::map<const VarDecl *, unsigned> &VarGrpMap;
3608 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
3609
3610public:
3612 const std::vector &Groups,
3613 const std::map<const VarDecl *, unsigned> &VarGrpMap,
3614 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
3615 : Groups(Groups), VarGrpMap(VarGrpMap),
3616 GrpsUnionForParms(GrpsUnionForParms) {}
3617
3619 if (GrpsUnionForParms.contains(Var)) {
3620 if (HasParm)
3621 *HasParm = true;
3622 return GrpsUnionForParms.getArrayRef();
3623 }
3624 if (HasParm)
3625 *HasParm = false;
3626
3627 auto It = VarGrpMap.find(Var);
3628
3629 if (It == VarGrpMap.end())
3630 return {};
3631 return Groups[It->second];
3632 }
3633
3635 return GrpsUnionForParms.getArrayRef();
3636 }
3637};
3638
3640 WarningGadgetList WarningGadgets, DeclUseTracker Tracker,
3642 if (!EmitSuggestions) {
3643
3644
3645
3646 for (const auto &G : WarningGadgets) {
3647 G->handleUnsafeOperation(Handler, false,
3649 }
3650
3651
3652
3653 assert(FixableGadgets.size() == 0 &&
3654 "Fixable gadgets found but suggestions not requested!");
3655 return;
3656 }
3657
3658
3659
3660 if (!WarningGadgets.empty()) {
3661
3662
3663
3664 for (const auto &G : FixableGadgets) {
3665 for (const auto *DRE : G->getClaimedVarUseSites()) {
3666 Tracker.claimUse(DRE);
3667 }
3668 }
3669 }
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682 if (WarningGadgets.empty())
3683 return;
3684
3689
3690 std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
3691
3692
3693 for (auto it = FixablesForAllVars.byVar.cbegin();
3694 it != FixablesForAllVars.byVar.cend();) {
3695
3696 if ((!it->first->isLocalVarDecl() && !isa(it->first))) {
3697#ifndef NDEBUG
3699 ("failed to produce fixit for '" +
3700 it->first->getNameAsString() +
3701 "' : neither local nor a parameter"));
3702#endif
3703 it = FixablesForAllVars.byVar.erase(it);
3704 } else if (it->first->getType().getCanonicalType()->isReferenceType()) {
3705#ifndef NDEBUG
3707 ("failed to produce fixit for '" +
3708 it->first->getNameAsString() +
3709 "' : has a reference type"));
3710#endif
3711 it = FixablesForAllVars.byVar.erase(it);
3712 } else if (Tracker.hasUnclaimedUses(it->first)) {
3713 it = FixablesForAllVars.byVar.erase(it);
3714 } else if (it->first->isInitCapture()) {
3715#ifndef NDEBUG
3717 ("failed to produce fixit for '" +
3718 it->first->getNameAsString() +
3719 "' : init capture"));
3720#endif
3721 it = FixablesForAllVars.byVar.erase(it);
3722 } else {
3723 ++it;
3724 }
3725 }
3726
3727#ifndef NDEBUG
3728 for (const auto &it : UnsafeOps.byVar) {
3729 const VarDecl *const UnsafeVD = it.first;
3730 auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
3731 if (UnclaimedDREs.empty())
3732 continue;
3735 std::string UnclaimedUseTrace =
3736 getDREAncestorString(UnclaimedDRE, D->getASTContext());
3737
3740 ("failed to produce fixit for '" + UnfixedVDName +
3741 "' : has an unclaimed use\nThe unclaimed DRE trace: " +
3742 UnclaimedUseTrace));
3743 }
3744 }
3745#endif
3746
3747
3748 using DepMapTy = DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
3749 DepMapTy DependenciesMap{};
3750 DepMapTy PtrAssignmentGraph{};
3751
3752 for (auto it : FixablesForAllVars.byVar) {
3753 for (const FixableGadget *fixable : it.second) {
3754 std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
3755 fixable->getStrategyImplications();
3756 if (ImplPair) {
3757 std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
3758 PtrAssignmentGraph[Impl.first].insert(Impl.second);
3759 }
3760 }
3761 }
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780 std::set<const VarDecl *> VisitedVarsDirected{};
3781 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
3782 if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
3783
3784 std::queue<const VarDecl *> QueueDirected{};
3785 QueueDirected.push(Var);
3786 while (!QueueDirected.empty()) {
3787 const VarDecl *CurrentVar = QueueDirected.front();
3788 QueueDirected.pop();
3789 VisitedVarsDirected.insert(CurrentVar);
3790 auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
3791 for (const VarDecl *Adj : AdjacentNodes) {
3792 if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
3793 QueueDirected.push(Adj);
3794 }
3795 DependenciesMap[Var].insert(Adj);
3796 DependenciesMap[Adj].insert(Var);
3797 }
3798 }
3799 }
3800 }
3801
3802
3803 std::vector Groups;
3804
3805
3806
3807 std::map<const VarDecl *, unsigned> VarGrpMap;
3808
3809 llvm::SetVector<const VarDecl *>
3810 GrpsUnionForParms;
3811
3812
3813
3814 std::set<const VarDecl *> VisitedVars{};
3815 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
3816 if (VisitedVars.find(Var) == VisitedVars.end()) {
3817 VarGrpTy &VarGroup = Groups.emplace_back();
3818 std::queue<const VarDecl *> Queue{};
3819
3820 Queue.push(Var);
3821 while (!Queue.empty()) {
3822 const VarDecl *CurrentVar = Queue.front();
3823 Queue.pop();
3824 VisitedVars.insert(CurrentVar);
3825 VarGroup.push_back(CurrentVar);
3826 auto AdjacentNodes = DependenciesMap[CurrentVar];
3827 for (const VarDecl *Adj : AdjacentNodes) {
3828 if (VisitedVars.find(Adj) == VisitedVars.end()) {
3829 Queue.push(Adj);
3830 }
3831 }
3832 }
3833
3834 bool HasParm = false;
3835 unsigned GrpIdx = Groups.size() - 1;
3836
3837 for (const VarDecl *V : VarGroup) {
3838 VarGrpMap[V] = GrpIdx;
3840 HasParm = true;
3841 }
3842 if (HasParm)
3843 GrpsUnionForParms.insert(VarGroup.begin(), VarGroup.end());
3844 }
3845 }
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865 for (auto I = FixablesForAllVars.byVar.begin();
3866 I != FixablesForAllVars.byVar.end();) {
3867
3868 if (!VisitedVars.count((*I).first)) {
3869
3870 I = FixablesForAllVars.byVar.erase(I);
3871 } else
3872 ++I;
3873 }
3874
3875
3876
3878 VisitedVars, [&FixablesForAllVars](const VarDecl *V) {
3879
3880 return FixablesForAllVars.byVar.count(V);
3881 }));
3883
3884 if (isa(D))
3885
3886
3887 FixItsForVariableGroup =
3889 Tracker, Handler, VarGrpMgr);
3890
3891 for (const auto &G : UnsafeOps.noVar) {
3892 G->handleUnsafeOperation(Handler, false,
3894 }
3895
3896 for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
3897 auto FixItsIt = FixItsForVariableGroup.find(VD);
3899 FixItsIt != FixItsForVariableGroup.end()
3900 ? std::move(FixItsIt->second)
3901 : FixItList{},
3902 D, NaiveStrategy);
3903 for (const auto &G : WarningGadgets) {
3904 G->handleUnsafeOperation(Handler, true,
3906 }
3907 }
3908}
3909
3912 bool EmitSuggestions) {
3913#ifndef NDEBUG
3915#endif
3916
3917 assert(D);
3918
3920
3921 if (const auto *FD = dyn_cast(D)) {
3922
3923
3924
3925 if (const auto *MD = dyn_cast(D)) {
3926 if (MD->getParent()->isLambda() && MD->getParent()->isLocalClass())
3927 return;
3928 }
3929
3931 if (FReDecl->isExternC()) {
3932
3933
3934 EmitSuggestions = false;
3935 break;
3936 }
3937 }
3938
3939 Stmts.push_back(FD->getBody());
3940
3941 if (const auto *ID = dyn_cast(D)) {
3943 Stmts.push_back(CI->getInit());
3944 }
3945 }
3946 } else if (isa(D) || isa(D)) {
3947 Stmts.push_back(D->getBody());
3948 }
3949
3950 assert(!Stmts.empty());
3951
3952 FixableGadgetList FixableGadgets;
3953 WarningGadgetList WarningGadgets;
3954 DeclUseTracker Tracker;
3955 for (Stmt *S : Stmts) {
3957 WarningGadgets, Tracker);
3958 }
3959 applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
3960 std::move(Tracker), Handler, EmitSuggestions);
3961}
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.
bool EvaluateAsInt(EvalResult &Result, const ASTContext &Ctx, SideEffectsKind AllowSideEffects=SE_NoSideEffects, bool InConstantContext=false) const
EvaluateAsInt - Return true if this is a constant which we can fold and convert to an integer,...
bool isValueDependent() const
Determines whether the value of this expression depends on.
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)