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 (SM.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 (Loc)

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

3360 const Decl *D,

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>

3495 const Decl *D,

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)