clang: lib/Analysis/UnsafeBufferUsage.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

23#include "llvm/ADT/APSInt.h"

24#include "llvm/ADT/SmallVector.h"

25#include "llvm/ADT/StringRef.h"

26#include "llvm/Support/Casting.h"

27#include

28#include

29#include

30#include

31

32using namespace llvm;

33using namespace clang;

34using namespace ast_matchers;

35

36#ifndef NDEBUG

37namespace {

38class StmtDebugPrinter

40public:

41 std::string VisitStmt(const Stmt *S) { return S->getStmtClassName(); }

42

43 std::string VisitBinaryOperator(const BinaryOperator *BO) {

44 return "BinaryOperator(" + BO->getOpcodeStr().str() + ")";

45 }

46

47 std::string VisitUnaryOperator(const UnaryOperator *UO) {

49 }

50

51 std::string VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {

52 return "ImplicitCastExpr(" + std::string(ICE->getCastKindName()) + ")";

53 }

54};

55

56

57

58static std::string getDREAncestorString(const DeclRefExpr *DRE,

60 std::stringstream SS;

61 const Stmt *St = DRE;

62 StmtDebugPrinter StmtPriner;

63

64 do {

65 SS << StmtPriner.Visit(St);

66

68

69 if (StParents.size() > 1)

70 return "unavailable due to multiple parents";

71 if (StParents.size() == 0)

72 break;

74 if (St)

75 SS << " ==> ";

76 } while (St);

77 return SS.str();

78}

79}

80#endif

81

83

84

86public:

87

88

89

91 internal::ASTMatchFinder *Finder,

92 internal::BoundNodesTreeBuilder *Builder,

93 internal::ASTMatchFinder::BindKind Bind,

94 const bool ignoreUnevaluatedContext)

95 : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind),

96 Matches(false), ignoreUnevaluatedContext(ignoreUnevaluatedContext) {

99 }

100

101

102

104 Matches = false;

105 if (const Stmt *StmtNode = DynNode.get<Stmt>()) {

107 *Builder = ResultBindings;

108 return Matches;

109 }

110 return false;

111 }

112

113

114

115

116

117

118

119

122 return true;

123 if (!match(*Node))

124 return false;

125

126 if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node))

127 return true;

128

130 }

131

133

134 if (ignoreUnevaluatedContext)

136 return DynamicRecursiveASTVisitor::TraverseGenericSelectionExpr(Node);

137 }

138

139 bool

141

142 if (ignoreUnevaluatedContext)

143 return true;

144 return DynamicRecursiveASTVisitor::TraverseUnaryExprOrTypeTraitExpr(Node);

145 }

146

148

149 if (ignoreUnevaluatedContext)

150 return true;

151 return DynamicRecursiveASTVisitor::TraverseTypeOfExprTypeLoc(Node);

152 }

153

155

156 if (ignoreUnevaluatedContext)

157 return true;

158 return DynamicRecursiveASTVisitor::TraverseDecltypeTypeLoc(Node);

159 }

160

162

163 if (ignoreUnevaluatedContext)

164 return true;

165 return DynamicRecursiveASTVisitor::TraverseCXXNoexceptExpr(Node);

166 }

167

169

170 if (ignoreUnevaluatedContext)

171 return true;

172 return DynamicRecursiveASTVisitor::TraverseCXXTypeidExpr(Node);

173 }

174

177 return false;

178 return DynamicRecursiveASTVisitor::TraverseCXXDefaultInitExpr(Node);

179 }

180

183 return true;

184 if (!match(*Node))

185 return false;

187 }

188

189private:

190

191

192

193

194 template bool match(const T &Node) {

195 internal::BoundNodesTreeBuilder RecursiveBuilder(*Builder);

196

198 &RecursiveBuilder)) {

199 ResultBindings.addMatch(RecursiveBuilder);

200 Matches = true;

201 if (Bind != internal::ASTMatchFinder::BK_All)

202 return false;

203 }

204 return true;

205 }

206

207 const internal::DynTypedMatcher *const Matcher;

208 internal::ASTMatchFinder *const Finder;

209 internal::BoundNodesTreeBuilder *const Builder;

210 internal::BoundNodesTreeBuilder ResultBindings;

211 const internal::ASTMatchFinder::BindKind Bind;

212 bool Matches;

213 bool ignoreUnevaluatedContext;

214};

215

216

218 return hasType(hasCanonicalType(pointerType()));

219}

220

222

224 innerMatcher) {

225 const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);

226

228 true);

230}

231

233 innerMatcher) {

234 const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);

235

237 false);

239}

240

241

243 Handler) {

244 return !Handler->isSafeBufferOptOut(Node.getBeginLoc());

245}

246

249 return Handler->ignoreUnsafeBufferInContainer(Node.getBeginLoc());

250}

251

253 Handler) {

254 if (Finder->getASTContext().getLangOpts().CPlusPlus)

255 return Handler->ignoreUnsafeBufferInLibcCall(Node.getBeginLoc());

256 return true;

257}

258

260 return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder);

261}

262

263

265 return Node.getOpcode() == UnaryOperator::Opcode::UO_PreInc;

266}

267

268

269

271

272 return

275 hasCastKind(CastKind::CK_LValueToRValue),

276 castSubExpr(innerMatcher)),

279 hasLHS(innerMatcher)

280 )

281 ));

282

283}

284

285

286

287static internal::Matcher

289

290

291

292

293

294

295

296

297

298 auto CallArgMatcher = callExpr(

299 forEachArgumentWithParamType(

300 InnerMatcher,

301 isAnyPointer() ),

304

305 auto CastOperandMatcher =

306 castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral),

307 hasCastKind(CastKind::CK_PointerToBoolean)),

309

310 auto CompOperandMatcher =

314

315

316 auto PtrSubtractionMatcher =

318

319

320

323 eachOf(hasLHS(InnerMatcher),

324 hasRHS(InnerMatcher)));

325

326

327 return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher,

328 PtrSubtractionMatcher));

329

330

331}

332

333

334

335

336

337

338

339

340

341

342

343static internal::Matcher

345

346

347

348

350 auto IfStmtThen = ifStmt(hasThen(InnerMatcher));

351 auto IfStmtElse = ifStmt(hasElse(InnerMatcher));

352

353 return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse));

354}

355

356

357

358

359

360

361

362

363

364

366 assert(Node.getNumArgs() == 2 &&

367 "expecting a two-parameter std::span constructor");

368 const Expr *Arg0 = Node.getArg(0)->IgnoreImplicit();

369 const Expr *Arg1 = Node.getArg(1)->IgnoreImplicit();

370 auto HaveEqualConstantValues = [&Finder](const Expr *E0, const Expr *E1) {

372 if (auto E1CV = E1->getIntegerConstantExpr(Finder->getASTContext())) {

373 return APSInt::compareValues(*E0CV, *E1CV) == 0;

374 }

375 return false;

376 };

377 auto AreSameDRE = [](const Expr *E0, const Expr *E1) {

378 if (auto *DRE0 = dyn_cast(E0))

379 if (auto *DRE1 = dyn_cast(E1)) {

380 return DRE0->getDecl() == DRE1->getDecl();

381 }

382 return false;

383 };

384 std::optional Arg1CV =

386

387 if (Arg1CV && Arg1CV->isZero())

388

389 return true;

391 case Stmt::CXXNewExprClass:

392 if (auto Size = cast(Arg0)->getArraySize()) {

393

394 return AreSameDRE((*Size)->IgnoreImplicit(), Arg1) ||

395 HaveEqualConstantValues(*Size, Arg1);

396 }

397

398 if (!cast(Arg0)->hasPlaceholderType()) {

399

400 return Arg1CV && Arg1CV->isOne();

401 }

402 break;

403 case Stmt::UnaryOperatorClass:

404 if (cast(Arg0)->getOpcode() ==

405 UnaryOperator::Opcode::UO_AddrOf)

406

407 return Arg1CV && Arg1CV->isOne();

408 break;

409 case Stmt::CallExprClass:

410 if (const auto *CE = dyn_cast(Arg0)) {

411 const auto FnDecl = CE->getDirectCallee();

412 if (FnDecl && FnDecl->getNameAsString() == "addressof" &&

413 FnDecl->isInStdNamespace()) {

414 return Arg1CV && Arg1CV->isOne();

415 }

416 }

417 break;

418 default:

419 break;

420 }

421

423

424 if (auto *ConstArrTy =

425 Finder->getASTContext().getAsConstantArrayType(Arg0Ty)) {

426 const APSInt ConstArrSize = APSInt(ConstArrTy->getSize());

427

428

429 return Arg1CV && APSInt::compareValues(ConstArrSize, *Arg1CV) == 0;

430 }

431 return false;

432}

433

435

436

437

438

439

440

441

442 uint64_t limit;

443 if (const auto *CATy =

444 dyn_cast(Node.getBase()

445 ->IgnoreParenImpCasts()

446 ->getType()

447 ->getUnqualifiedDesugaredType())) {

448 limit = CATy->getLimitedSize();

449 } else if (const auto *SLiteral = dyn_cast(

450 Node.getBase()->IgnoreParenImpCasts())) {

451 limit = SLiteral->getLength() + 1;

452 } else {

453 return false;

454 }

455

456 if (const auto *IdxLit = dyn_cast(Node.getIdx())) {

457 const APInt ArrIdx = IdxLit->getValue();

458 if (ArrIdx.isNonNegative() && ArrIdx.getLimitedValue() < limit)

459 return true;

460 }

461 return false;

462}

463

465 return Node.getNumArgs() == Num;

466}

467

468namespace libc_func_matchers {

469

470

471

472

473

474

475

476

477

478

479

480

481

482

484 StringRef matchName(StringRef FunName, bool isBuiltin) {

485

486 if (isBuiltin && FunName.starts_with("__builtin_"))

487

488

490 FunName.drop_front(10 ));

491

492 if (FunName.starts_with("__asan_"))

493 return matchLibcName(FunName.drop_front(7 ));

495 }

496

497

498

500 if (Name.starts_with("__") && Name.ends_with("_chk"))

502 Name.drop_front(2).drop_back(4) );

504 }

505

507 if (Name.ends_with("_s"))

508 return Name.drop_back(2 );

509 return Name;

510 }

511};

512

513

514

517 return true;

519 return true;

520 if (auto *MCE = dyn_cast(Ptr->IgnoreParenImpCasts())) {

523

525 if (MD->getName() == "c_str" && RD->getName() == "basic_string")

526 return true;

527 }

528 return false;

529}

530

531

532

533

534

535

536

537

538

540 const unsigned FmtArgIdx, ASTContext &Ctx,

541 bool isKprintf = false) {

542 class StringFormatStringHandler

545 unsigned FmtArgIdx;

546 const Expr *&UnsafeArg;

547

548 public:

549 StringFormatStringHandler(const CallExpr *Call, unsigned FmtArgIdx,

550 const Expr *&UnsafeArg)

551 : Call(Call), FmtArgIdx(FmtArgIdx), UnsafeArg(UnsafeArg) {}

552

554 const char *startSpecifier,

555 unsigned specifierLen,

557 if (FS.getConversionSpecifier().getKind() ==

558 analyze_printf::PrintfConversionSpecifier::sArg) {

559 unsigned ArgIdx = FS.getPositionalArgIndex() + FmtArgIdx;

560

561 if (0 < ArgIdx && ArgIdx < Call->getNumArgs())

563 UnsafeArg = Call->getArg(ArgIdx);

564

565 return false;

566 }

567 }

568 return true;

569 }

570 };

571

572 const Expr *Fmt = Call->getArg(FmtArgIdx);

573

575 StringRef FmtStr;

576

577 if (SL->getCharByteWidth() == 1)

578 FmtStr = SL->getString();

579 else if (auto EvaledFmtStr = SL->tryEvaluateString(Ctx))

580 FmtStr = *EvaledFmtStr;

581 else

582 goto CHECK_UNSAFE_PTR;

583

584 StringFormatStringHandler Handler(Call, FmtArgIdx, UnsafeArg);

585

587 Handler, FmtStr.begin(), FmtStr.end(), Ctx.getLangOpts(),

589 }

590CHECK_UNSAFE_PTR:

591

592

593

594 return llvm::any_of(

595 llvm::make_range(Call->arg_begin() + FmtArgIdx, Call->arg_end()),

596 [&UnsafeArg](const Expr *Arg) -> bool {

597 if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg)) {

598 UnsafeArg = Arg;

599 return true;

600 }

601 return false;

602 });

603}

604

605

606

607

608

609

610

611

612

613

615 static std::unique_ptr<std::set> PredefinedNames = nullptr;

616 if (!PredefinedNames)

617 PredefinedNames =

618 std::make_unique<std::set, std::set>({

619

620 "atof",

621 "atoi",

622 "atol",

623 "atoll",

624 "strtol",

625 "strtoll",

626 "strtoul",

627 "strtoull",

628 "strtof",

629 "strtod",

630 "strtold",

631 "strtoimax",

632 "strtoumax",

633

634

635 "strcpy",

636 "strncpy",

637 "strlcpy",

638 "strcat",

639 "strncat",

640 "strlcat",

641 "strxfrm",

642 "strdup",

643 "strndup",

644

645 "strlen",

646 "strnlen",

647 "strcmp",

648 "strncmp",

649 "stricmp",

650 "strcasecmp",

651 "strcoll",

652 "strchr",

653 "strrchr",

654 "strspn",

655 "strcspn",

656 "strpbrk",

657 "strstr",

658 "strtok",

659

660 "memchr",

661 "wmemchr",

662 "memcmp",

663 "wmemcmp",

664 "memcpy",

665 "memccpy",

666 "mempcpy",

667 "wmemcpy",

668 "memmove",

669 "wmemmove",

670 "memset",

671 "wmemset",

672

673 "fread",

674 "fwrite",

675 "fgets",

676 "fgetws",

677 "gets",

678 "fputs",

679 "fputws",

680 "puts",

681

682 "strerror_s",

683 "strerror_r",

684 "bcopy",

685 "bzero",

686 "bsearch",

687 "qsort",

688 });

689

690 auto *II = Node.getIdentifier();

691

692 if (!II)

693 return false;

694

696 II->getName(), Node.getBuiltinID());

697

698

699 if (PredefinedNames->find(Name) != PredefinedNames->end())

700 return true;

701

702 std::string NameWCS = Name.str();

703 size_t WcsPos = NameWCS.find("wcs");

704

705 while (WcsPos != std:🧵:npos) {

706 NameWCS[WcsPos++] = 's';

707 NameWCS[WcsPos++] = 't';

708 NameWCS[WcsPos++] = 'r';

709 WcsPos = NameWCS.find("wcs", WcsPos);

710 }

711 if (PredefinedNames->find(NameWCS) != PredefinedNames->end())

712 return true;

713

714

715 return Name.ends_with("scanf");

716}

717

718

719

720

722 auto *II = Node.getIdentifier();

723

724 if (!II)

725 return false;

726

728 II->getName(), Node.getBuiltinID());

729

730 if (!Name.ends_with("printf"))

731 return false;

732 return Name.starts_with("v");

733}

734

735

736

738 auto *II = Node.getIdentifier();

739

740 if (!II)

741 return false;

742

744 II->getName(), Node.getBuiltinID());

745

746 if (!Name.ends_with("printf") ||

747

748 Name.starts_with("v"))

749 return false;

750

751 StringRef Prefix = Name.drop_back(6);

752

753 if (Prefix.ends_with("w"))

754 Prefix = Prefix.drop_back(1);

755 return Prefix == "s";

756}

757

758

759

760

762 auto *II = Node.getIdentifier();

763

764 if (!II)

765 return false;

766

768 II->getName(), Node.getBuiltinID());

769

770 if (!Name.ends_with("printf") || Name.starts_with("v"))

771 return false;

772

773 StringRef Prefix = Name.drop_back(6);

774

775 if (Prefix.ends_with("w"))

776 Prefix = Prefix.drop_back(1);

777

778 return Prefix.empty() || Prefix == "k" || Prefix == "f" || Prefix == "sn";

779}

780

781

782

783

784

786 clang::ast_matchers::internal::Matcher,

787 UnsafeStringArgMatcher) {

788

790

791 assert(FD && "It should have been checked that FD is non-null.");

792

794

795 if (NumParms < 1)

796 return false;

797

798 ASTContext &Ctx = Finder->getASTContext();

800

802 return false;

803

805

807 .isNull() &&

809

810 const Expr *UnsafeArg;

811

813 return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);

814 return false;

815 }

816

818

819 bool isKprintf = false;

820 const Expr *UnsafeArg;

821

823 isKprintf = II->getName() == "kprintf";

825 return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);

826 return false;

827 }

828

829 if (NumParms > 2) {

831

833

834

835 const Expr *UnsafeArg;

836

838 return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);

839 return false;

840 }

841 }

842

843

844 for (auto Arg : Node.arguments())

846 if (UnsafeStringArgMatcher.matches(*Arg, Finder, Builder))

847 return true;

848 return false;

849}

850

851

852

853

854

855

856

857

858

859

860

861

862

863

864

865

866

869

870 assert(FD && "It should have been checked that FD is non-null.");

871

873 return false;

874

876

878 return false;

879

881 const Expr *Buf = Node.getArg(0), *Size = Node.getArg(1);

882

884 !Size->getType()->isIntegerType())

885 return false;

886

887

888 static StringRef SizedObjs[] = {"span", "array", "vector",

889 "basic_string_view", "basic_string"};

891 Size = Size->IgnoreParenImpCasts();

892 if (auto *MCEPtr = dyn_cast(Buf))

893 if (auto *MCESize = dyn_cast(Size)) {

894 auto *DREOfPtr = dyn_cast(

895 MCEPtr->getImplicitObjectArgument()->IgnoreParenImpCasts());

896 auto *DREOfSize = dyn_cast(

897 MCESize->getImplicitObjectArgument()->IgnoreParenImpCasts());

898

899 if (!DREOfPtr || !DREOfSize)

900 return true;

901 if (DREOfPtr->getDecl() != DREOfSize->getDecl())

902 return true;

903 if (MCEPtr->getMethodDecl()->getName() != "data")

904 return true;

905

906 if (MCESize->getMethodDecl()->getName() == "size_bytes" ||

907

908

909

910

911 MCESize->getMethodDecl()->getName() == "size")

912 for (StringRef SizedObj : SizedObjs)

913 if (MCEPtr->getRecordDecl()->isInStdNamespace() &&

914 MCEPtr->getRecordDecl()->getCanonicalDecl()->getName() ==

915 SizedObj)

916 return false;

917 }

918

919

921 ASTContext &Ctx = Finder->getASTContext();

922

925

926

927

928 if (Size->EvaluateAsConstantExpr(ER, Ctx)) {

929 APSInt EVal = ER.Val.getInt();

930

931 return APSInt::compareValues(EVal, APSInt(CAT->getSize(), true)) != 0;

932 }

933 }

934 }

935 return true;

936}

937}

938}

939

940namespace {

941

942

944

945

947}

948

949namespace {

950

951

952

953

954

955

956

957

958class Gadget {

959public:

960 enum class Kind {

961#define GADGET(x) x,

962#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"

963 };

964

965

966

967

968 using Matcher = decltype(stmt());

969

970 Gadget(Kind K) : K(K) {}

971

973

974#ifndef NDEBUG

975 StringRef getDebugName() const {

976 switch (K) {

977#define GADGET(x) \

978 case Kind::x: \

979 return #x;

980#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"

981 }

982 llvm_unreachable("Unhandled Gadget::Kind enum");

983 }

984#endif

985

986 virtual bool isWarningGadget() const = 0;

987

988

990

991

992

993

994 virtual DeclUseList getClaimedVarUseSites() const = 0;

995

996 virtual ~Gadget() = default;

997

998private:

1000};

1001

1002

1003

1004class WarningGadget : public Gadget {

1005public:

1006 WarningGadget(Kind K) : Gadget(K) {}

1007

1008 static bool classof(const Gadget *G) { return G->isWarningGadget(); }

1009 bool isWarningGadget() const final { return true; }

1010

1012 bool IsRelatedToDecl,

1014};

1015

1016

1017

1018

1019

1020class FixableGadget : public Gadget {

1021public:

1022 FixableGadget(Kind K) : Gadget(K) {}

1023

1024 static bool classof(const Gadget *G) { return !G->isWarningGadget(); }

1025 bool isWarningGadget() const final { return false; }

1026

1027

1028

1029

1030 virtual std::optional getFixits(const FixitStrategy &) const {

1031 return std::nullopt;

1032 }

1033

1034

1035

1036

1037

1038

1039

1040 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>

1041 getStrategyImplications() const {

1042 return std::nullopt;

1043 }

1044};

1045

1046static auto toSupportedVariable() { return to(varDecl()); }

1047

1048using FixableGadgetList = std::vector<std::unique_ptr>;

1049using WarningGadgetList = std::vector<std::unique_ptr>;

1050

1051

1052

1053class IncrementGadget : public WarningGadget {

1054 static constexpr const char *const OpTag = "op";

1056

1057public:

1059 : WarningGadget(Kind::Increment),

1061

1062 static bool classof(const Gadget *G) {

1063 return G->getKind() == Kind::Increment;

1064 }

1065

1066 static Matcher matcher() {

1067 return stmt(

1069 hasUnaryOperand(ignoringParenImpCasts(hasPointerType())))

1070 .bind(OpTag));

1071 }

1072

1074 bool IsRelatedToDecl,

1077 }

1079

1080 DeclUseList getClaimedVarUseSites() const override {

1082 if (const auto *DRE =

1084 Uses.push_back(DRE);

1085 }

1086

1087 return std::move(Uses);

1088 }

1089};

1090

1091

1092

1093class DecrementGadget : public WarningGadget {

1094 static constexpr const char *const OpTag = "op";

1096

1097public:

1099 : WarningGadget(Kind::Decrement),

1101

1102 static bool classof(const Gadget *G) {

1103 return G->getKind() == Kind::Decrement;

1104 }

1105

1106 static Matcher matcher() {

1107 return stmt(

1109 hasUnaryOperand(ignoringParenImpCasts(hasPointerType())))

1110 .bind(OpTag));

1111 }

1112

1114 bool IsRelatedToDecl,

1117 }

1119

1120 DeclUseList getClaimedVarUseSites() const override {

1121 if (const auto *DRE =

1123 return {DRE};

1124 }

1125

1126 return {};

1127 }

1128};

1129

1130

1131

1132class ArraySubscriptGadget : public WarningGadget {

1133 static constexpr const char *const ArraySubscrTag = "ArraySubscript";

1135

1136public:

1138 : WarningGadget(Kind::ArraySubscript),

1140

1141 static bool classof(const Gadget *G) {

1142 return G->getKind() == Kind::ArraySubscript;

1143 }

1144

1145 static Matcher matcher() {

1146

1148 hasBase(ignoringParenImpCasts(

1151 isSafeArraySubscript(),

1152 hasIndex(

1154 )

1155 ))).bind(ArraySubscrTag));

1156

1157 }

1158

1160 bool IsRelatedToDecl,

1163 }

1165

1166 DeclUseList getClaimedVarUseSites() const override {

1167 if (const auto *DRE =

1169 return {DRE};

1170 }

1171

1172 return {};

1173 }

1174};

1175

1176

1177

1178

1179

1180class PointerArithmeticGadget : public WarningGadget {

1181 static constexpr const char *const PointerArithmeticTag = "ptrAdd";

1182 static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr";

1183 const BinaryOperator *PA;

1184 const Expr *Ptr;

1185

1186public:

1188 : WarningGadget(Kind::PointerArithmetic),

1190 Ptr(Result.Nodes.getNodeAs<Expr>(PointerArithmeticPointerTag)) {}

1191

1192 static bool classof(const Gadget *G) {

1193 return G->getKind() == Kind::PointerArithmetic;

1194 }

1195

1196 static Matcher matcher() {

1197 auto HasIntegerType = anyOf(hasType(isInteger()), hasType(enumType()));

1198 auto PtrAtRight =

1199 allOf(hasOperatorName("+"),

1201 hasLHS(HasIntegerType));

1202 auto PtrAtLeft =

1203 allOf(anyOf(hasOperatorName("+"), hasOperatorName("-"),

1204 hasOperatorName("+="), hasOperatorName("-=")),

1206 hasRHS(HasIntegerType));

1207

1209 .bind(PointerArithmeticTag));

1210 }

1211

1213 bool IsRelatedToDecl,

1216 }

1218

1219 DeclUseList getClaimedVarUseSites() const override {

1220 if (const auto *DRE = dyn_cast(Ptr->IgnoreParenImpCasts())) {

1221 return {DRE};

1222 }

1223

1224 return {};

1225 }

1226

1227

1228};

1229

1230class SpanTwoParamConstructorGadget : public WarningGadget {

1231 static constexpr const char *const SpanTwoParamConstructorTag =

1232 "spanTwoParamConstructor";

1233 const CXXConstructExpr *Ctor;

1234

1235public:

1237 : WarningGadget(Kind::SpanTwoParamConstructor),

1239 SpanTwoParamConstructorTag)) {}

1240

1241 static bool classof(const Gadget *G) {

1242 return G->getKind() == Kind::SpanTwoParamConstructor;

1243 }

1244

1245 static Matcher matcher() {

1248 parameterCountIs(2)));

1249

1251 unless(isSafeSpanTwoParamConstruct()))

1252 .bind(SpanTwoParamConstructorTag));

1253 }

1254

1256 return stmt(unless(ignoreUnsafeBufferInContainer(Handler)), matcher());

1257 }

1258

1260 bool IsRelatedToDecl,

1263 }

1265

1266 DeclUseList getClaimedVarUseSites() const override {

1267

1268

1269 if (auto *DRE = dyn_cast(Ctor->getArg(0))) {

1270 if (isa(DRE->getDecl()))

1271 return {DRE};

1272 }

1273 return {};

1274 }

1275};

1276

1277

1278

1279

1280

1281class PointerInitGadget : public FixableGadget {

1282private:

1283 static constexpr const char *const PointerInitLHSTag = "ptrInitLHS";

1284 static constexpr const char *const PointerInitRHSTag = "ptrInitRHS";

1285 const VarDecl *PtrInitLHS;

1286 const DeclRefExpr *PtrInitRHS;

1287

1288public:

1290 : FixableGadget(Kind::PointerInit),

1291 PtrInitLHS(Result.Nodes.getNodeAs<VarDecl>(PointerInitLHSTag)),

1292 PtrInitRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}

1293

1294 static bool classof(const Gadget *G) {

1295 return G->getKind() == Kind::PointerInit;

1296 }

1297

1298 static Matcher matcher() {

1299 auto PtrInitStmt = declStmt(hasSingleDecl(

1300 varDecl(hasInitializer(ignoringImpCasts(

1302 .bind(PointerInitRHSTag))))

1303 .bind(PointerInitLHSTag)));

1304

1305 return stmt(PtrInitStmt);

1306 }

1307

1308 virtual std::optional

1309 getFixits(const FixitStrategy &S) const override;

1312 }

1313

1314 virtual DeclUseList getClaimedVarUseSites() const override {

1315 return DeclUseList{PtrInitRHS};

1316 }

1317

1318 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>

1319 getStrategyImplications() const override {

1320 return std::make_pair(PtrInitLHS, cast(PtrInitRHS->getDecl()));

1321 }

1322};

1323

1324

1325

1326

1327

1328

1329class PtrToPtrAssignmentGadget : public FixableGadget {

1330private:

1331 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";

1332 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";

1333 const DeclRefExpr *PtrLHS;

1334 const DeclRefExpr *PtrRHS;

1335

1336public:

1338 : FixableGadget(Kind::PtrToPtrAssignment),

1339 PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),

1340 PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}

1341

1342 static bool classof(const Gadget *G) {

1343 return G->getKind() == Kind::PtrToPtrAssignment;

1344 }

1345

1346 static Matcher matcher() {

1348 allOf(hasOperatorName("="),

1349 hasRHS(ignoringParenImpCasts(

1351 .bind(PointerAssignRHSTag))),

1353 .bind(PointerAssignLHSTag))));

1354

1356 }

1357

1358 virtual std::optional

1359 getFixits(const FixitStrategy &S) const override;

1361

1362 virtual DeclUseList getClaimedVarUseSites() const override {

1363 return DeclUseList{PtrLHS, PtrRHS};

1364 }

1365

1366 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>

1367 getStrategyImplications() const override {

1368 return std::make_pair(cast(PtrLHS->getDecl()),

1369 cast(PtrRHS->getDecl()));

1370 }

1371};

1372

1373

1374

1375

1376

1377

1378class CArrayToPtrAssignmentGadget : public FixableGadget {

1379private:

1380 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";

1381 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";

1382 const DeclRefExpr *PtrLHS;

1383 const DeclRefExpr *PtrRHS;

1384

1385public:

1387 : FixableGadget(Kind::CArrayToPtrAssignment),

1388 PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),

1389 PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}

1390

1391 static bool classof(const Gadget *G) {

1392 return G->getKind() == Kind::CArrayToPtrAssignment;

1393 }

1394

1395 static Matcher matcher() {

1397 allOf(hasOperatorName("="),

1398 hasRHS(ignoringParenImpCasts(

1400 toSupportedVariable())

1401 .bind(PointerAssignRHSTag))),

1403 .bind(PointerAssignLHSTag))));

1404

1406 }

1407

1408 virtual std::optional

1409 getFixits(const FixitStrategy &S) const override;

1411

1412 virtual DeclUseList getClaimedVarUseSites() const override {

1413 return DeclUseList{PtrLHS, PtrRHS};

1414 }

1415

1416 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>

1417 getStrategyImplications() const override {

1418 return {};

1419 }

1420};

1421

1422

1423

1424class UnsafeBufferUsageAttrGadget : public WarningGadget {

1425 constexpr static const char *const OpTag = "attr_expr";

1426 const Expr *Op;

1427

1428public:

1430 : WarningGadget(Kind::UnsafeBufferUsageAttr),

1431 Op(Result.Nodes.getNodeAs<Expr>(OpTag)) {}

1432

1433 static bool classof(const Gadget *G) {

1434 return G->getKind() == Kind::UnsafeBufferUsageAttr;

1435 }

1436

1437 static Matcher matcher() {

1438 auto HasUnsafeFieldDecl =

1440

1441 auto HasUnsafeFnDecl =

1443

1445 memberExpr(HasUnsafeFieldDecl).bind(OpTag)));

1446 }

1447

1449 bool IsRelatedToDecl,

1452 }

1454

1455 DeclUseList getClaimedVarUseSites() const override { return {}; }

1456};

1457

1458

1459

1460

1461class UnsafeBufferUsageCtorAttrGadget : public WarningGadget {

1462 constexpr static const char *const OpTag = "cxx_construct_expr";

1464

1465public:

1467 : WarningGadget(Kind::UnsafeBufferUsageCtorAttr),

1469

1470 static bool classof(const Gadget *G) {

1471 return G->getKind() == Kind::UnsafeBufferUsageCtorAttr;

1472 }

1473

1474 static Matcher matcher() {

1475 auto HasUnsafeCtorDecl =

1477

1478 auto HasTwoParamSpanCtorDecl = SpanTwoParamConstructorGadget::matcher();

1479 return stmt(

1481 .bind(OpTag));

1482 }

1483

1485 bool IsRelatedToDecl,

1488 }

1490

1491 DeclUseList getClaimedVarUseSites() const override { return {}; }

1492};

1493

1494

1495

1496

1497

1498class DataInvocationGadget : public WarningGadget {

1499 constexpr static const char *const OpTag = "data_invocation_expr";

1501

1502public:

1504 : WarningGadget(Kind::DataInvocation),

1506

1507 static bool classof(const Gadget *G) {

1508 return G->getKind() == Kind::DataInvocation;

1509 }

1510

1511 static Matcher matcher() {

1512

1516 hasName("std::vector"))))));

1517 return stmt(

1519 .bind(OpTag));

1520 }

1521

1523 bool IsRelatedToDecl,

1526 }

1528

1529 DeclUseList getClaimedVarUseSites() const override { return {}; }

1530};

1531

1532class UnsafeLibcFunctionCallGadget : public WarningGadget {

1534 const Expr *UnsafeArg = nullptr;

1535 constexpr static const char *const Tag = "UnsafeLibcFunctionCall";

1536

1537 constexpr static const char *const UnsafeSprintfTag =

1538 "UnsafeLibcFunctionCall_sprintf";

1539 constexpr static const char *const UnsafeSizedByTag =

1540 "UnsafeLibcFunctionCall_sized_by";

1541 constexpr static const char *const UnsafeStringTag =

1542 "UnsafeLibcFunctionCall_string";

1543 constexpr static const char *const UnsafeVaListTag =

1544 "UnsafeLibcFunctionCall_va_list";

1545

1546 enum UnsafeKind {

1547 OTHERS = 0,

1548 SPRINTF = 1,

1549 SIZED_BY =

1550 2,

1551

1552 STRING = 3,

1553

1554 VA_LIST = 4,

1555

1556 } WarnedFunKind = OTHERS;

1557

1558public:

1560 : WarningGadget(Kind::UnsafeLibcFunctionCall),

1562 if (Result.Nodes.getNodeAs<Decl>(UnsafeSprintfTag))

1563 WarnedFunKind = SPRINTF;

1564 else if (auto *E = Result.Nodes.getNodeAs<Expr>(UnsafeStringTag)) {

1565 WarnedFunKind = STRING;

1566 UnsafeArg = E;

1567 } else if (Result.Nodes.getNodeAs<CallExpr>(UnsafeSizedByTag)) {

1568 WarnedFunKind = SIZED_BY;

1569 UnsafeArg = Call->getArg(0);

1570 } else if (Result.Nodes.getNodeAs<Decl>(UnsafeVaListTag))

1571 WarnedFunKind = VA_LIST;

1572 }

1573

1575 return stmt(unless(ignoreUnsafeLibcCall(Handler)),

1579

1580

1581 functionDecl(libc_func_matchers::isPredefinedUnsafeLibcFunc()),

1582

1583

1584

1585 functionDecl(libc_func_matchers::isUnsafeVaListPrintfFunc())

1586 .bind(UnsafeVaListTag),

1587

1588

1589 functionDecl(libc_func_matchers::isUnsafeSprintfFunc())

1590 .bind(UnsafeSprintfTag)))),

1591

1594

1595

1596

1597

1598

1599

1600

1602 libc_func_matchers::hasUnsafeSnprintfBuffer())

1603 .bind(UnsafeSizedByTag),

1604

1605

1607 libc_func_matchers::hasUnsafePrintfStringArg(

1608 expr().bind(UnsafeStringTag)))));

1609 }

1610

1611 const Stmt *getBaseStmt() const { return Call; }

1612

1613 SourceLocation getSourceLoc() const override { return Call->getBeginLoc(); }

1614

1616 bool IsRelatedToDecl,

1619 }

1620

1621 DeclUseList getClaimedVarUseSites() const override { return {}; }

1622};

1623

1624

1625

1626

1627class ULCArraySubscriptGadget : public FixableGadget {

1628private:

1629 static constexpr const char *const ULCArraySubscriptTag =

1630 "ArraySubscriptUnderULC";

1632

1633public:

1635 : FixableGadget(Kind::ULCArraySubscript),

1637 assert(Node != nullptr && "Expecting a non-null matching result");

1638 }

1639

1640 static bool classof(const Gadget *G) {

1641 return G->getKind() == Kind::ULCArraySubscript;

1642 }

1643

1644 static Matcher matcher() {

1646 auto BaseIsArrayOrPtrDRE = hasBase(

1647 ignoringParenImpCasts(declRefExpr(ArrayOrPtr, toSupportedVariable())));

1650

1652 }

1653

1654 virtual std::optional

1655 getFixits(const FixitStrategy &S) const override;

1656 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }

1657

1658 virtual DeclUseList getClaimedVarUseSites() const override {

1659 if (const auto *DRE =

1660 dyn_cast(Node->getBase()->IgnoreImpCasts())) {

1661 return {DRE};

1662 }

1663 return {};

1664 }

1665};

1666

1667

1668

1669

1670class UPCStandalonePointerGadget : public FixableGadget {

1671private:

1672 static constexpr const char *const DeclRefExprTag = "StandalonePointer";

1674

1675public:

1677 : FixableGadget(Kind::UPCStandalonePointer),

1679 assert(Node != nullptr && "Expecting a non-null matching result");

1680 }

1681

1682 static bool classof(const Gadget *G) {

1683 return G->getKind() == Kind::UPCStandalonePointer;

1684 }

1685

1686 static Matcher matcher() {

1688 auto target = expr(ignoringParenImpCasts(

1690 .bind(DeclRefExprTag)));

1692 }

1693

1694 virtual std::optional

1695 getFixits(const FixitStrategy &S) const override;

1696 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }

1697

1698 virtual DeclUseList getClaimedVarUseSites() const override { return {Node}; }

1699};

1700

1701class PointerDereferenceGadget : public FixableGadget {

1702 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";

1703 static constexpr const char *const OperatorTag = "op";

1704

1705 const DeclRefExpr *BaseDeclRefExpr = nullptr;

1707

1708public:

1710 : FixableGadget(Kind::PointerDereference),

1711 BaseDeclRefExpr(

1714

1715 static bool classof(const Gadget *G) {

1716 return G->getKind() == Kind::PointerDereference;

1717 }

1718

1719 static Matcher matcher() {

1722 hasOperatorName("*"),

1723 has(expr(ignoringParenImpCasts(

1724 declRefExpr(toSupportedVariable()).bind(BaseDeclRefExprTag)))))

1725 .bind(OperatorTag);

1726

1728 }

1729

1730 DeclUseList getClaimedVarUseSites() const override {

1731 return {BaseDeclRefExpr};

1732 }

1733

1734 virtual std::optional

1735 getFixits(const FixitStrategy &S) const override;

1737};

1738

1739

1740

1741

1742class UPCAddressofArraySubscriptGadget : public FixableGadget {

1743private:

1744 static constexpr const char *const UPCAddressofArraySubscriptTag =

1745 "AddressofArraySubscriptUnderUPC";

1747

1748public:

1750 : FixableGadget(Kind::ULCArraySubscript),

1752 UPCAddressofArraySubscriptTag)) {

1753 assert(Node != nullptr && "Expecting a non-null matching result");

1754 }

1755

1756 static bool classof(const Gadget *G) {

1757 return G->getKind() == Kind::UPCAddressofArraySubscript;

1758 }

1759

1760 static Matcher matcher() {

1763 hasOperatorName("&"),

1765 ignoringParenImpCasts(declRefExpr(toSupportedVariable()))))))

1766 .bind(UPCAddressofArraySubscriptTag)))));

1767 }

1768

1769 virtual std::optional

1770 getFixits(const FixitStrategy &) const override;

1771 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }

1772

1773 virtual DeclUseList getClaimedVarUseSites() const override {

1774 const auto *ArraySubst = cast(Node->getSubExpr());

1775 const auto *DRE =

1776 cast(ArraySubst->getBase()->IgnoreParenImpCasts());

1777 return {DRE};

1778 }

1779};

1780}

1781

1782namespace {

1783

1784

1785

1786class DeclUseTracker {

1787 using UseSetTy = SmallSet<const DeclRefExpr *, 16>;

1788 using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>;

1789

1790

1791 std::unique_ptr Uses{std::make_unique()};

1792 DefMapTy Defs{};

1793

1794public:

1795 DeclUseTracker() = default;

1796 DeclUseTracker(const DeclUseTracker &) = delete;

1797 DeclUseTracker &operator=(const DeclUseTracker &) = delete;

1798 DeclUseTracker(DeclUseTracker &&) = default;

1799 DeclUseTracker &operator=(DeclUseTracker &&) = default;

1800

1801

1802 void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }

1803

1804

1806 assert(Uses->count(DRE) &&

1807 "DRE not found or claimed by multiple matchers!");

1808 Uses->erase(DRE);

1809 }

1810

1811

1812 bool hasUnclaimedUses(const VarDecl *VD) const {

1813

1814 return any_of(*Uses, [VD](const DeclRefExpr *DRE) {

1816 });

1817 }

1818

1819 UseSetTy getUnclaimedUses(const VarDecl *VD) const {

1820 UseSetTy ReturnSet;

1821 for (auto use : *Uses) {

1822 if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) {

1823 ReturnSet.insert(use);

1824 }

1825 }

1826 return ReturnSet;

1827 }

1828

1829 void discoverDecl(const DeclStmt *DS) {

1831 if (const auto *VD = dyn_cast(D)) {

1832

1833

1834

1835

1836

1837 Defs[VD] = DS;

1838 }

1839 }

1840 }

1841

1843 return Defs.lookup(VD);

1844 }

1845};

1846}

1847

1848

1849

1851private:

1852 static constexpr const char *const UPCPreIncrementTag =

1853 "PointerPreIncrementUnderUPC";

1855

1856public:

1858 : FixableGadget(Kind::UPCPreIncrement),

1860 assert(Node != nullptr && "Expecting a non-null matching result");

1861 }

1862

1864 return G->getKind() == Kind::UPCPreIncrement;

1865 }

1866

1868

1869

1870

1871

1874 hasUnaryOperand(declRefExpr(toSupportedVariable())))

1875 .bind(UPCPreIncrementTag)))));

1876 }

1877

1878 virtual std::optional

1881

1883 return {dyn_cast(Node->getSubExpr())};

1884 }

1885};

1886

1887

1888

1890private:

1891 static constexpr const char *const UUCAddAssignTag =

1892 "PointerAddAssignUnderUUC";

1893 static constexpr const char *const OffsetTag = "Offset";

1894

1896 const Expr *Offset = nullptr;

1897

1898public:

1900 : FixableGadget(Kind::UUCAddAssign),

1902 Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) {

1903 assert(Node != nullptr && "Expecting a non-null matching result");

1904 }

1905

1907 return G->getKind() == Kind::UUCAddAssign;

1908 }

1909

1911

1914 hasLHS(

1917 toSupportedVariable())),

1918 hasRHS(expr().bind(OffsetTag)))

1919 .bind(UUCAddAssignTag)))));

1920

1921 }

1922

1923 virtual std::optional

1926

1928 return {dyn_cast(Node->getLHS())};

1929 }

1930};

1931

1932

1933

1935 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";

1936 static constexpr const char *const DerefOpTag = "DerefOp";

1937 static constexpr const char *const AddOpTag = "AddOp";

1938 static constexpr const char *const OffsetTag = "Offset";

1939

1940 const DeclRefExpr *BaseDeclRefExpr = nullptr;

1944

1945public:

1947 : FixableGadget(Kind::DerefSimplePtrArithFixable),

1948 BaseDeclRefExpr(

1953

1955

1957 ignoringImpCasts(declRefExpr(toSupportedVariable()).

1958 bind(BaseDeclRefExprTag)));

1959 auto PlusOverPtrAndInteger = expr(anyOf(

1960 binaryOperator(hasOperatorName("+"), hasLHS(ThePtr),

1962 .bind(AddOpTag),

1963 binaryOperator(hasOperatorName("+"), hasRHS(ThePtr),

1965 .bind(AddOpTag)));

1967 hasOperatorName("*"),

1968 hasUnaryOperand(ignoringParens(PlusOverPtrAndInteger)))

1969 .bind(DerefOpTag));

1970

1971 }

1972

1973 virtual std::optional

1977 }

1978

1980 return {BaseDeclRefExpr};

1981 }

1982};

1983

1984

1987 bool EmitSuggestions, FixableGadgetList &FixableGadgets,

1988 WarningGadgetList &WarningGadgets,

1989 DeclUseTracker &Tracker) {

1990

1992 GadgetFinderCallback(FixableGadgetList &FixableGadgets,

1993 WarningGadgetList &WarningGadgets,

1994 DeclUseTracker &Tracker)

1995 : FixableGadgets(FixableGadgets), WarningGadgets(WarningGadgets),

1996 Tracker(Tracker) {}

1997

1999

2000

2001#if NDEBUG

2002#define NEXT return

2003#else

2004 [[maybe_unused]] int numFound = 0;

2005#define NEXT ++numFound

2006#endif

2007

2008 if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("any_dre")) {

2009 Tracker.discoverUse(DRE);

2011 }

2012

2013 if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) {

2014 Tracker.discoverDecl(DS);

2016 }

2017

2018

2019

2020

2021#define FIXABLE_GADGET(name) \

2022 if (Result.Nodes.getNodeAs(#name)) { \

2023 FixableGadgets.push_back(std::make_unique<name##Gadget>(Result)); \

2024 NEXT; \

2025 }

2026#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"

2027#define WARNING_GADGET(name) \

2028 if (Result.Nodes.getNodeAs(#name)) { \

2029 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \

2030 NEXT; \

2031 }

2032#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"

2033

2034 assert(numFound >= 1 && "Gadgets not found in match result!");

2035 assert(numFound <= 1 && "Conflicting bind tags in gadgets!");

2036 }

2037

2038 FixableGadgetList &FixableGadgets;

2039 WarningGadgetList &WarningGadgets;

2040 DeclUseTracker &Tracker;

2041 };

2042

2044 GadgetFinderCallback CB{FixableGadgets, WarningGadgets, Tracker};

2045

2046

2049 forEachDescendantEvaluatedStmt(stmt(anyOf(

2050

2052 allOf(x ## Gadget::matcher().bind(#x), \

2053 notInSafeBufferOptOut(&Handler)),

2055 allOf(x ## Gadget::matcher(&Handler).bind(#x), \

2056 notInSafeBufferOptOut(&Handler)),

2057#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"

2058

2060 )))

2061 ),

2062 &CB

2063 );

2064

2065

2066 if (EmitSuggestions) {

2067

2072 x ## Gadget::matcher().bind(#x),

2073#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"

2074

2075

2078

2079

2080

2082 )))

2083 ),

2084 &CB

2085 );

2086

2087 }

2088

2089 M.match(*S, Ctx);

2090}

2091

2092

2094 bool operator()(const NodeTy *N1, const NodeTy *N2) const {

2095 return N1->getBeginLoc().getRawEncoding() <

2096 N2->getBeginLoc().getRawEncoding();

2097 }

2098};

2099

2101 std::map<const VarDecl *, std::set<const WarningGadget *>,

2102

2103

2106

2108};

2109

2113

2114

2115 for (auto &G : AllUnsafeOperations) {

2116 DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();

2117

2118 bool AssociatedWithVarDecl = false;

2119 for (const DeclRefExpr *DRE : ClaimedVarUseSites) {

2120 if (const auto *VD = dyn_cast(DRE->getDecl())) {

2121 result.byVar[VD].insert(G.get());

2122 AssociatedWithVarDecl = true;

2123 }

2124 }

2125

2126 if (!AssociatedWithVarDecl) {

2127 result.noVar.push_back(G.get());

2128 continue;

2129 }

2130 }

2131 return result;

2132}

2133

2135 std::map<const VarDecl *, std::set<const FixableGadget *>,

2136

2137

2140};

2141

2145 for (auto &F : AllFixableOperations) {

2146 DeclUseList DREs = F->getClaimedVarUseSites();

2147

2149 if (const auto *VD = dyn_cast(DRE->getDecl())) {

2150 FixablesForUnsafeVars.byVar[VD].insert(F.get());

2151 }

2152 }

2153 }

2154 return FixablesForUnsafeVars;

2155}

2156

2159

2160

2161 std::vector<const FixItHint *> All;

2162

2163 for (const FixItHint &H : FixIts)

2164 All.push_back(&H);

2165 std::sort(All.begin(), All.end(),

2167 return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),

2168 H2->RemoveRange.getBegin());

2169 });

2170

2171 const FixItHint *CurrHint = nullptr;

2172

2173 for (const FixItHint *Hint : All) {

2174 if (!CurrHint ||

2176 Hint->RemoveRange.getBegin())) {

2177

2178

2179 CurrHint = Hint;

2180 } else

2181

2182

2183 return true;

2184 }

2185 return false;

2186}

2187

2188std::optional

2189PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {

2190 const auto *LeftVD = cast(PtrLHS->getDecl());

2191 const auto *RightVD = cast(PtrRHS->getDecl());

2192 switch (S.lookup(LeftVD)) {

2193 case FixitStrategy::Kind::Span:

2194 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)

2195 return FixItList{};

2196 return std::nullopt;

2197 case FixitStrategy::Kind::Wontfix:

2198 return std::nullopt;

2199 case FixitStrategy::Kind::Iterator:

2200 case FixitStrategy::Kind::Array:

2201 return std::nullopt;

2202 case FixitStrategy::Kind::Vector:

2203 llvm_unreachable("unsupported strategies for FixableGadgets");

2204 }

2205 return std::nullopt;

2206}

2207

2208

2211

2212std::optional

2213CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {

2214 const auto *LeftVD = cast(PtrLHS->getDecl());

2215 const auto *RightVD = cast(PtrRHS->getDecl());

2216

2217

2218

2219

2220

2221

2222

2223

2224

2225

2226

2227

2228

2229

2230

2231

2232 if (S.lookup(LeftVD) == FixitStrategy::Kind::Span) {

2233 if (S.lookup(RightVD) == FixitStrategy::Kind::Wontfix) {

2234 return FixItList{};

2235 }

2236 } else if (S.lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {

2237 if (S.lookup(RightVD) == FixitStrategy::Kind::Array) {

2238 return createDataFixit(RightVD->getASTContext(), PtrRHS);

2239 }

2240 }

2241 return std::nullopt;

2242}

2243

2244std::optional

2245PointerInitGadget::getFixits(const FixitStrategy &S) const {

2246 const auto *LeftVD = PtrInitLHS;

2247 const auto *RightVD = cast(PtrInitRHS->getDecl());

2248 switch (S.lookup(LeftVD)) {

2249 case FixitStrategy::Kind::Span:

2250 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)

2251 return FixItList{};

2252 return std::nullopt;

2253 case FixitStrategy::Kind::Wontfix:

2254 return std::nullopt;

2255 case FixitStrategy::Kind::Iterator:

2256 case FixitStrategy::Kind::Array:

2257 return std::nullopt;

2258 case FixitStrategy::Kind::Vector:

2259 llvm_unreachable("unsupported strategies for FixableGadgets");

2260 }

2261 return std::nullopt;

2262}

2263

2267 if (ConstVal->isNegative())

2268 return false;

2270 return false;

2271 return true;

2272}

2273

2274std::optional

2275ULCArraySubscriptGadget::getFixits(const FixitStrategy &S) const {

2276 if (const auto *DRE =

2277 dyn_cast(Node->getBase()->IgnoreImpCasts()))

2278 if (const auto *VD = dyn_cast(DRE->getDecl())) {

2279 switch (S.lookup(VD)) {

2280 case FixitStrategy::Kind::Span: {

2281

2282

2283

2284 const ASTContext &Ctx =

2287 return std::nullopt;

2288

2289 return FixItList{};

2290 }

2291 case FixitStrategy::Kind::Array:

2292 return FixItList{};

2293 case FixitStrategy::Kind::Wontfix:

2294 case FixitStrategy::Kind::Iterator:

2295 case FixitStrategy::Kind::Vector:

2296 llvm_unreachable("unsupported strategies for FixableGadgets");

2297 }

2298 }

2299 return std::nullopt;

2300}

2301

2302static std::optional

2304

2305std::optional

2306UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy &S) const {

2307 auto DREs = getClaimedVarUseSites();

2308 const auto *VD = cast(DREs.front()->getDecl());

2309

2310 switch (S.lookup(VD)) {

2311 case FixitStrategy::Kind::Span:

2313 case FixitStrategy::Kind::Wontfix:

2314 case FixitStrategy::Kind::Iterator:

2315 case FixitStrategy::Kind::Array:

2316 return std::nullopt;

2317 case FixitStrategy::Kind::Vector:

2318 llvm_unreachable("unsupported strategies for FixableGadgets");

2319 }

2320 return std::nullopt;

2321}

2322

2323

2325 static const char *const EOL = "\n";

2326 return EOL;

2327}

2328

2329

2330static std::string

2332 std::string s = std::string("<# ");

2333 s += HintTextToUser;

2334 s += " #>";

2335 return s;

2336}

2337

2338

2339template

2340static std::optional

2345

2347 return Loc;

2348

2349 return std::nullopt;

2350}

2351

2352

2353template

2360 return Loc;

2361 return std::nullopt;

2362}

2363

2364

2368 std::optional LastCharLoc = getPastLoc(E, SM, LangOpts);

2369

2370 if (LastCharLoc)

2373 LangOpts);

2374

2375 return std::nullopt;

2376}

2377

2378

2385

2387 return Text;

2388 return std::nullopt;

2389}

2390

2391

2392

2394

2395

2397}

2398

2399

2400static std::optional

2406

2407 if (ParmIdentEndLoc.isMacroID() &&

2409 return std::nullopt;

2410 return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts);

2411}

2412

2413

2414

2415

2416

2417

2418

2421

2422

2423 bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {

2424 return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),

2425 VD->getBeginLoc())) &&

2426 !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),

2427 At->getRange().getBegin()));

2428 });

2431 AttrRangeOverlapping;

2432}

2433

2434

2435

2436

2437

2443 End =

2444

2446

2448}

2449

2450

2451

2452

2453

2454

2455static std::optionalstd::string

2458 std::optional *QualifiersToAppend) {

2461

2463 "Expecting a VarDecl of type of pointer to object type");

2465

2468

2469

2470

2472 case TypeLoc::ConstantArray:

2473 case TypeLoc::IncompleteArray:

2474 case TypeLoc::VariableArray:

2475 case TypeLoc::DependentSizedArray:

2476 case TypeLoc::Decayed:

2477 assert(isa(VD) && "An array type shall not be treated as a "

2478 "pointer type unless it decays.");

2480 break;

2481 case TypeLoc::Pointer:

2483 break;

2484 default:

2485 return std::nullopt;

2486 }

2487 if (PteTyLoc.isNull())

2488

2489

2490 return std::nullopt;

2491

2493

2495

2496

2497

2498 return std::nullopt;

2499 }

2500

2501

2504

2505 if (!PteEndOfTokenLoc.isValid())

2506

2507

2508 return std::nullopt;

2509 if (SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, IdentLoc)) {

2510

2511

2512

2513

2514

2515

2516 return std::nullopt;

2517 }

2519

2520

2521

2523 }

2525 ->str();

2526}

2527

2528

2535

2536

2539 SourceRange NameRange{BeginLoc, EndLoc};

2540

2542}

2543

2544

2545

2546

2547

2548

2549static std::string

2551 std::optional Quals = std::nullopt) {

2552 const char *const SpanOpen = "std::span<";

2553

2554 if (Quals)

2555 return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>';

2556 return SpanOpen + EltTyText.str() + '>';

2557}

2558

2559std::optional

2561 const VarDecl *VD = dyn_cast(BaseDeclRefExpr->getDecl());

2562

2563 if (VD && s.lookup(VD) == FixitStrategy::Kind::Span) {

2565

2567 if (ConstVal->isNegative())

2568 return std::nullopt;

2569

2570

2571

2572

2573

2574

2575

2576

2577

2578

2579

2580

2581

2582

2583

2584

2585

2586

2587

2588

2595

2596 std::optional LHSLocation = getPastLoc(LHS, SM, LangOpts);

2597 if (!LHSLocation)

2598 return std::nullopt;

2599

2602

2603 std::optional AddOpLocation =

2605 std::optional DerefOpLocation =

2607

2608 if (!AddOpLocation || !DerefOpLocation)

2609 return std::nullopt;

2610

2613

2614 return FixItList{

2618 }

2619 return std::nullopt;

2620}

2621

2622std::optional

2623PointerDereferenceGadget::getFixits(const FixitStrategy &S) const {

2624 const VarDecl *VD = cast(BaseDeclRefExpr->getDecl());

2625 switch (S.lookup(VD)) {

2626 case FixitStrategy::Kind::Span: {

2629

2630

2633

2634 if (auto LocPastOperand =

2638 }

2639 break;

2640 }

2641 case FixitStrategy::Kind::Iterator:

2642 case FixitStrategy::Kind::Array:

2643 return std::nullopt;

2644 case FixitStrategy::Kind::Vector:

2645 llvm_unreachable("FixitStrategy not implemented yet!");

2646 case FixitStrategy::Kind::Wontfix:

2647 llvm_unreachable("Invalid strategy!");

2648 }

2649

2650 return std::nullopt;

2651}

2652

2656

2657 std::optional EndOfOperand =

2659

2660 if (EndOfOperand)

2662

2663 return std::nullopt;

2664}

2665

2666

2667

2668std::optional

2669UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const {

2670 const auto VD = cast(Node->getDecl());

2671 switch (S.lookup(VD)) {

2672 case FixitStrategy::Kind::Array:

2673 case FixitStrategy::Kind::Span: {

2675

2676 break;

2677 }

2678 case FixitStrategy::Kind::Wontfix:

2679 case FixitStrategy::Kind::Iterator:

2680 return std::nullopt;

2681 case FixitStrategy::Kind::Vector:

2682 llvm_unreachable("unsupported strategies for FixableGadgets");

2683 }

2684

2685 return std::nullopt;

2686}

2687

2688

2689

2690static std::optional

2692 const auto *ArraySub = cast(Node->getSubExpr());

2693 const auto *DRE = cast(ArraySub->getBase()->IgnoreImpCasts());

2694

2695

2697 const Expr *Idx = ArraySub->getIdx();

2700 std::stringstream SS;

2701 bool IdxIsLitZero = false;

2702

2704 if ((*ICE).isZero())

2705 IdxIsLitZero = true;

2706 std::optional DreString = getExprText(DRE, SM, LangOpts);

2707 if (!DreString)

2708 return std::nullopt;

2709

2710 if (IdxIsLitZero) {

2711

2712 SS << (*DreString).str() << ".data()";

2713 } else {

2714 std::optional IndexString = getExprText(Idx, SM, LangOpts);

2715 if (!IndexString)

2716 return std::nullopt;

2717

2718 SS << "&" << (*DreString).str() << ".data()"

2719 << "[" << (*IndexString).str() << "]";

2720 }

2721 return FixItList{

2723}

2724

2725std::optional

2728

2729 if (DREs.size() != 1)

2730 return std::nullopt;

2731

2732 if (const VarDecl *VD = dyn_cast(DREs.front()->getDecl())) {

2733 if (S.lookup(VD) == FixitStrategy::Kind::Span) {

2734 FixItList Fixes;

2735

2736 const Stmt *AddAssignNode = Node;

2737 StringRef varName = VD->getName();

2739

2741 return std::nullopt;

2742

2743

2744 bool NotParenExpr =

2746 std::string SS = varName.str() + " = " + varName.str() + ".subspan";

2747 if (NotParenExpr)

2748 SS += "(";

2749

2750 std::optional AddAssignLocation = getEndCharLoc(

2752 if (!AddAssignLocation)

2753 return std::nullopt;

2754

2757 SS));

2758 if (NotParenExpr)

2761 return Fixes;

2762 }

2763 }

2764 return std::nullopt;

2765}

2766

2767std::optional

2770

2771 if (DREs.size() != 1)

2772 return std::nullopt;

2773

2774 if (const VarDecl *VD = dyn_cast(DREs.front()->getDecl())) {

2775 if (S.lookup(VD) == FixitStrategy::Kind::Span) {

2776 FixItList Fixes;

2777 std::stringstream SS;

2778 StringRef varName = VD->getName();

2780

2781

2782 SS << "(" << varName.data() << " = " << varName.data()

2783 << ".subspan(1)).data()";

2784 std::optional PreIncLocation =

2786 if (!PreIncLocation)

2787 return std::nullopt;

2788

2790 SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));

2791 return Fixes;

2792 }

2793 }

2794 return std::nullopt;

2795}

2796

2797

2798

2799

2800

2801

2802

2803

2804

2805

2806

2807

2808

2809

2810

2811

2812static std::optional

2814 const StringRef UserFillPlaceHolder) {

2817

2818

2819

2820

2821

2822 if (Init->isNullPointerConstant(

2823 Ctx,

2824

2825

2827 NPC_ValueDependentIsNotNull)) {

2828 std::optional InitLocation =

2830 if (!InitLocation)

2831 return std::nullopt;

2832

2834

2836 }

2837

2838 FixItList FixIts{};

2839 std::string ExtentText = UserFillPlaceHolder.data();

2840 StringRef One = "1";

2841

2842

2844

2845 if (auto CxxNew = dyn_cast(Init->IgnoreImpCasts())) {

2846

2847

2848

2849

2850 if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {

2851 if (!Ext->HasSideEffects(Ctx)) {

2852 std::optional ExtentString = getExprText(Ext, SM, LangOpts);

2853 if (!ExtentString)

2854 return std::nullopt;

2855 ExtentText = *ExtentString;

2856 }

2857 } else if (!CxxNew->isArray())

2858

2859

2860 ExtentText = One;

2862

2863

2864

2865 return FixItList{};

2866 } else {

2867

2868

2869 if (auto AddrOfExpr = dyn_cast(Init->IgnoreImpCasts()))

2870 if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&

2871 isa_and_present(AddrOfExpr->getSubExpr()))

2872 ExtentText = One;

2873

2874

2875 }

2876

2878 std::optional LocPassInit = getPastLoc(Init, SM, LangOpts);

2879

2880 if (!LocPassInit)

2881 return std::nullopt;

2882

2883 StrBuffer.append(", ");

2884 StrBuffer.append(ExtentText);

2885 StrBuffer.append("}");

2887 return FixIts;

2888}

2889

2890#ifndef NDEBUG

2891#define DEBUG_NOTE_DECL_FAIL(D, Msg) \

2892 Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \

2893 "failed to produce fixit for declaration '" + \

2894 (D)->getNameAsString() + "'" + (Msg))

2895#else

2896#define DEBUG_NOTE_DECL_FAIL(D, Msg)

2897#endif

2898

2899

2900

2901

2902static std::optionalstd::string

2905

2906 std::optional PteTyQualifiers = std::nullopt;

2909

2910 if (!PteTyText)

2911 return std::nullopt;

2912

2913 std::string SpanTyText = "std::span<";

2914

2915 SpanTyText.append(*PteTyText);

2916

2917 if (PteTyQualifiers) {

2918 SpanTyText.append(" ");

2919 SpanTyText.append(PteTyQualifiers->getAsString());

2920 }

2921 SpanTyText.append(">");

2922 return SpanTyText;

2923}

2924

2925

2926

2927

2928

2929

2930

2931

2932

2933

2934

2935

2936

2937

2938

2940 const StringRef UserFillPlaceHolder,

2943 return {};

2944

2945 FixItList FixIts{};

2947

2948 if (!SpanTyText) {

2950 return {};

2951 }

2952

2953

2954 std::stringstream SS;

2955

2956 SS << *SpanTyText;

2957

2958 if (const Expr *Init = D->getInit()) {

2959 std::optional InitFixIts =

2961 if (!InitFixIts)

2962 return {};

2963 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),

2964 std::make_move_iterator(InitFixIts->end()));

2965 }

2966

2967

2968

2969

2970 const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();

2971 if (!EndLocForReplacement.isValid()) {

2973 return {};

2974 }

2975

2976

2977

2979 SS << " ";

2980

2983 return FixIts;

2984}

2985

2988}

2989

2990

2991

2992

2993

2994

2995

2996

2997

2998

2999

3000

3001

3002

3003

3004

3005

3006

3007

3008

3009

3010

3011

3012

3013

3014

3015

3016

3017

3018

3019

3020

3021static std::optional

3025

3027 return std::nullopt;

3028

3031 const unsigned NumParms = FD->getNumParams();

3032 std::vectorstd::string NewTysTexts(NumParms);

3033 std::vector ParmsMask(NumParms, false);

3034 bool AtLeastOneParmToFix = false;

3035

3036 for (unsigned i = 0; i < NumParms; i++) {

3038

3039 if (S.lookup(PVD) == FixitStrategy::Kind::Wontfix)

3040 continue;

3041 if (S.lookup(PVD) != FixitStrategy::Kind::Span)

3042

3043 return std::nullopt;

3044

3045 std::optional PteTyQuals = std::nullopt;

3046 std::optionalstd::string PteTyText =

3048

3049 if (!PteTyText)

3050

3051 return std::nullopt;

3052

3053

3054 NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);

3055 ParmsMask[i] = true;

3056 AtLeastOneParmToFix = true;

3057 }

3058 if (!AtLeastOneParmToFix)

3059

3060 return {};

3061

3062

3063

3064

3065 const auto NewOverloadSignatureCreator =

3066 [&SM, &LangOpts, &NewTysTexts,

3067 &ParmsMask](const FunctionDecl *FD) -> std::optionalstd::string {

3068 std::stringstream SS;

3069

3070 SS << ";";

3072

3075 SM, LangOpts))

3076 SS << Prefix->str();

3077 else

3078 return std::nullopt;

3079

3080 const unsigned NumParms = FD->getNumParams();

3081

3082 for (unsigned i = 0; i < NumParms; i++) {

3084

3086 continue;

3087 if (ParmsMask[i]) {

3088

3089

3090 SS << NewTysTexts[i];

3091

3093 SS << ' ' << II->getName().str();

3094 } else if (auto ParmTypeText =

3096 SM, LangOpts)) {

3097

3098 SS << ParmTypeText->str();

3099 } else

3100 return std::nullopt;

3101 if (i != NumParms - 1)

3102 SS << ", ";

3103 }

3104 SS << ")";

3105 return SS.str();

3106 };

3107

3108

3109

3110 const auto OldOverloadDefCreator =

3111 [&Handler, &SM, &LangOpts, &NewTysTexts,

3112 &ParmsMask](const FunctionDecl *FD) -> std::optionalstd::string {

3113 std::stringstream SS;

3114

3116

3119 LangOpts))

3121 << FDPrefix->str() << "{";

3122 else

3123 return std::nullopt;

3124

3126 SS << "return " << FunQualName->str() << "(";

3127 else

3128 return std::nullopt;

3129

3130

3131 const unsigned NumParms = FD->getNumParams();

3132 for (unsigned i = 0; i < NumParms; i++) {

3134

3136 continue;

3137

3138

3140

3141 return std::nullopt;

3142 if (ParmsMask[i])

3143

3146 else

3148 if (i != NumParms - 1)

3149 SS << ", ";

3150 }

3151

3153

3154 return SS.str();

3155 };

3156

3157 FixItList FixIts{};

3159 std::optional Loc = getPastLoc(FReDecl, SM, LangOpts);

3160

3161 if (Loc)

3162 return {};

3163 if (FReDecl->isThisDeclarationADefinition()) {

3164 assert(FReDecl == FD && "inconsistent function definition");

3165

3166

3167 if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl))

3169 else

3170 return {};

3171 } else {

3172

3173 if (!FReDecl->hasAttr()) {

3176 FReDecl->getBeginLoc(), " ")));

3177 }

3178

3179 if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))

3181 else

3182 return {};

3183 }

3184 }

3185 return FixIts;

3186}

3187

3188

3193 return {};

3194 }

3196

3198 return {};

3199 }

3200

3201 std::optional PteTyQualifiers = std::nullopt;

3204

3205 if (!PteTyText) {

3207 return {};

3208 }

3209

3211

3212 if (!PVDNameText) {

3214 return {};

3215 }

3216

3217 std::stringstream SS;

3219

3220 if (PteTyQualifiers)

3221

3223 else

3225

3228

3229 SS << ' ' << PVDNameText->str();

3230

3232}

3233

3235 const DeclUseTracker &Tracker,

3238 const DeclStmt *DS = Tracker.lookupDecl(VD);

3239 if (!DS) {

3241 " : variables declared this way not implemented yet");

3242 return {};

3243 }

3245

3247 return {};

3248 }

3249

3250

3251

3252 (void)DS;

3253

3254

3256}

3257

3260 FixItList FixIts{};

3261

3262

3263

3265 const QualType &ArrayEltT = CAT->getElementType();

3266 assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");

3267

3269 return {};

3270

3272

3273

3274

3275 auto MaybeElemTypeTxt =

3278 if (!MaybeElemTypeTxt)

3279 return {};

3280 const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();

3281

3282

3285 while (NextTok && !NextTok->is(tok::l_square) &&

3289 if (!NextTok)

3290 return {};

3291 const SourceLocation LSqBracketLoc = NextTok->getLocation();

3292

3293

3294

3298 if (!MaybeArraySizeTxt)

3299 return {};

3300 const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();

3301 if (ArraySizeTxt.empty()) {

3302

3303

3304

3305

3306

3307

3308

3309 return {};

3310 }

3311

3312 std::optional IdentText =

3314

3315 if (!IdentText) {

3317 return {};

3318 }

3319

3321 raw_svector_ostream OS(Replacement);

3322 OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> "

3323 << IdentText->str();

3324

3327 }

3328

3329 return FixIts;

3330}

3331

3333 const DeclUseTracker &Tracker,

3336 const DeclStmt *DS = Tracker.lookupDecl(VD);

3337 assert(DS && "Fixing non-local variables not implemented yet!");

3339

3340 return {};

3341 }

3342

3343

3344

3345 (void)DS;

3346

3347

3349}

3350

3351

3352

3353static FixItList

3355 const Decl *D,

3356 const DeclUseTracker &Tracker, ASTContext &Ctx,

3358 if (const auto *PVD = dyn_cast(VD)) {

3359 auto *FD = dyn_castclang::FunctionDecl(PVD->getDeclContext());

3360 if (!FD || FD != D) {

3361

3362

3364 return {};

3365 }

3366

3367

3368

3369

3370

3371 if (FD->isMain() || FD->isConstexpr() ||

3372 FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate ||

3373 FD->isVariadic() ||

3374

3375 isa(FD) ||

3376

3377 (FD->hasBody() && isa(FD->getBody())) ||

3378 FD->isOverloadedOperator()) {

3380 return {};

3381 }

3382 }

3383

3384 switch (K) {

3385 case FixitStrategy::Kind::Span: {

3387 if (const auto *PVD = dyn_cast(VD))

3389

3392 }

3394 return {};

3395 }

3396 case FixitStrategy::Kind::Array: {

3399

3401 return {};

3402 }

3403 case FixitStrategy::Kind::Iterator:

3404 case FixitStrategy::Kind::Vector:

3405 llvm_unreachable("FixitStrategy not implemented yet!");

3406 case FixitStrategy::Kind::Wontfix:

3407 llvm_unreachable("Invalid strategy!");

3408 }

3409 llvm_unreachable("Unknown strategy!");

3410}

3411

3412

3413

3415

3416

3417 return llvm::any_of(FixIts, [](const FixItHint &Hint) {

3420

3421 return true;

3422 return false;

3423 });

3424}

3425

3426

3428 return isa(VD) &&

3430}

3431

3432

3433

3434

3436 std::map<const VarDecl *, FixItList> &FixItsForVariable,

3438

3440

3441 for (const auto &[VD, Ignore] : FixItsForVariable) {

3443 if (llvm::any_of(Grp,

3444 [&FixItsForVariable](const VarDecl *GrpMember) -> bool {

3445 return !FixItsForVariable.count(GrpMember);

3446 })) {

3447

3448

3450 ToErase.push_back(Member);

3451 }

3452 }

3453 for (auto *VarToErase : ToErase)

3454 FixItsForVariable.erase(VarToErase);

3455}

3456

3457

3458

3459

3460

3461

3462

3463

3465 std::map<const VarDecl *, FixItList> &FixItsForVariable ,

3469 FixItList FixItsSharedByParms{};

3470

3471 std::optional OverloadFixes =

3473

3474 if (OverloadFixes) {

3475 FixItsSharedByParms.append(*OverloadFixes);

3476 } else {

3477

3478

3479

3481 FixItsForVariable.erase(Member);

3482 }

3483 return FixItsSharedByParms;

3484}

3485

3486

3487static std::map<const VarDecl *, FixItList>

3490 const Decl *D,

3493

3494

3495

3496 std::map<const VarDecl *, FixItList> FixItsForVariable;

3497

3498

3499

3500

3501 for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {

3502 FixItsForVariable[VD] =

3503 fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);

3504

3505

3506 if (FixItsForVariable[VD].empty()) {

3507 FixItsForVariable.erase(VD);

3508 continue;

3509 }

3510 for (const auto &F : Fixables) {

3511 std::optional Fixits = F->getFixits(S);

3512

3513 if (Fixits) {

3514 FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),

3515 Fixits->begin(), Fixits->end());

3516 continue;

3517 }

3518#ifndef NDEBUG

3520 VD, F->getSourceLoc(),

3521 ("gadget '" + F->getDebugName() + "' refused to produce a fix")

3522 .str());

3523#endif

3524 FixItsForVariable.erase(VD);

3525 break;

3526 }

3527 }

3528

3529

3530

3531

3532

3533

3534

3536

3537

3538

3539

3540

3541

3542

3543 FixItList FixItsSharedByParms{};

3544

3545 if (auto *FD = dyn_cast(D))

3547 FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);

3548

3549

3550

3551 std::map<const VarDecl *, FixItList> FinalFixItsForVariable{

3552 FixItsForVariable};

3553

3554 for (auto &[Var, Ignore] : FixItsForVariable) {

3555 bool AnyParm = false;

3556 const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm);

3557

3558 for (const VarDecl *GrpMate : VarGroupForVD) {

3559 if (Var == GrpMate)

3560 continue;

3561 if (FixItsForVariable.count(GrpMate))

3562 FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);

3563 }

3564 if (AnyParm) {

3565

3566 assert(!FixItsSharedByParms.empty() &&

3567 "Should not try to fix a parameter that does not belong to a "

3568 "FunctionDecl");

3569 FinalFixItsForVariable[Var].append(FixItsSharedByParms);

3570 }

3571 }

3572

3573

3574

3575

3576 for (auto Iter = FinalFixItsForVariable.begin();

3577 Iter != FinalFixItsForVariable.end();)

3580 Iter = FinalFixItsForVariable.erase(Iter);

3581 } else

3583 return FinalFixItsForVariable;

3584}

3585

3586template

3590 for (const VarDecl *VD : UnsafeVars) {

3592 S.set(VD, FixitStrategy::Kind::Array);

3593 else

3594 S.set(VD, FixitStrategy::Kind::Span);

3595 }

3596 return S;

3597}

3598

3599

3601 const std::vector Groups;

3602 const std::map<const VarDecl *, unsigned> &VarGrpMap;

3603 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;

3604

3605public:

3607 const std::vector &Groups,

3608 const std::map<const VarDecl *, unsigned> &VarGrpMap,

3609 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)

3610 : Groups(Groups), VarGrpMap(VarGrpMap),

3611 GrpsUnionForParms(GrpsUnionForParms) {}

3612

3614 if (GrpsUnionForParms.contains(Var)) {

3615 if (HasParm)

3616 *HasParm = true;

3617 return GrpsUnionForParms.getArrayRef();

3618 }

3619 if (HasParm)

3620 *HasParm = false;

3621

3622 auto It = VarGrpMap.find(Var);

3623

3624 if (It == VarGrpMap.end())

3625 return {};

3626 return Groups[It->second];

3627 }

3628

3630 return GrpsUnionForParms.getArrayRef();

3631 }

3632};

3633

3635 WarningGadgetList WarningGadgets, DeclUseTracker Tracker,

3637 if (!EmitSuggestions) {

3638

3639

3640

3641 for (const auto &G : WarningGadgets) {

3642 G->handleUnsafeOperation(Handler, false,

3644 }

3645

3646

3647

3648 assert(FixableGadgets.size() == 0 &&

3649 "Fixable gadgets found but suggestions not requested!");

3650 return;

3651 }

3652

3653

3654

3655 if (!WarningGadgets.empty()) {

3656

3657

3658

3659 for (const auto &G : FixableGadgets) {

3660 for (const auto *DRE : G->getClaimedVarUseSites()) {

3661 Tracker.claimUse(DRE);

3662 }

3663 }

3664 }

3665

3666

3667

3668

3669

3670

3671

3672

3673

3674

3675

3676

3677 if (WarningGadgets.empty())

3678 return;

3679

3684

3685 std::map<const VarDecl *, FixItList> FixItsForVariableGroup;

3686

3687

3688 for (auto it = FixablesForAllVars.byVar.cbegin();

3689 it != FixablesForAllVars.byVar.cend();) {

3690

3691 if ((!it->first->isLocalVarDecl() && !isa(it->first))) {

3692#ifndef NDEBUG

3694 ("failed to produce fixit for '" +

3695 it->first->getNameAsString() +

3696 "' : neither local nor a parameter"));

3697#endif

3698 it = FixablesForAllVars.byVar.erase(it);

3699 } else if (it->first->getType().getCanonicalType()->isReferenceType()) {

3700#ifndef NDEBUG

3702 ("failed to produce fixit for '" +

3703 it->first->getNameAsString() +

3704 "' : has a reference type"));

3705#endif

3706 it = FixablesForAllVars.byVar.erase(it);

3707 } else if (Tracker.hasUnclaimedUses(it->first)) {

3708 it = FixablesForAllVars.byVar.erase(it);

3709 } else if (it->first->isInitCapture()) {

3710#ifndef NDEBUG

3712 ("failed to produce fixit for '" +

3713 it->first->getNameAsString() +

3714 "' : init capture"));

3715#endif

3716 it = FixablesForAllVars.byVar.erase(it);

3717 } else {

3718 ++it;

3719 }

3720 }

3721

3722#ifndef NDEBUG

3723 for (const auto &it : UnsafeOps.byVar) {

3724 const VarDecl *const UnsafeVD = it.first;

3725 auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);

3726 if (UnclaimedDREs.empty())

3727 continue;

3730 std::string UnclaimedUseTrace =

3731 getDREAncestorString(UnclaimedDRE, D->getASTContext());

3732

3735 ("failed to produce fixit for '" + UnfixedVDName +

3736 "' : has an unclaimed use\nThe unclaimed DRE trace: " +

3737 UnclaimedUseTrace));

3738 }

3739 }

3740#endif

3741

3742

3743 using DepMapTy = DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;

3744 DepMapTy DependenciesMap{};

3745 DepMapTy PtrAssignmentGraph{};

3746

3747 for (auto it : FixablesForAllVars.byVar) {

3748 for (const FixableGadget *fixable : it.second) {

3749 std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =

3750 fixable->getStrategyImplications();

3751 if (ImplPair) {

3752 std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);

3753 PtrAssignmentGraph[Impl.first].insert(Impl.second);

3754 }

3755 }

3756 }

3757

3758

3759

3760

3761

3762

3763

3764

3765

3766

3767

3768

3769

3770

3771

3772

3773

3774

3775 std::set<const VarDecl *> VisitedVarsDirected{};

3776 for (const auto &[Var, ignore] : UnsafeOps.byVar) {

3777 if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {

3778

3779 std::queue<const VarDecl *> QueueDirected{};

3780 QueueDirected.push(Var);

3781 while (!QueueDirected.empty()) {

3782 const VarDecl *CurrentVar = QueueDirected.front();

3783 QueueDirected.pop();

3784 VisitedVarsDirected.insert(CurrentVar);

3785 auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];

3786 for (const VarDecl *Adj : AdjacentNodes) {

3787 if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {

3788 QueueDirected.push(Adj);

3789 }

3790 DependenciesMap[Var].insert(Adj);

3791 DependenciesMap[Adj].insert(Var);

3792 }

3793 }

3794 }

3795 }

3796

3797

3798 std::vector Groups;

3799

3800

3801

3802 std::map<const VarDecl *, unsigned> VarGrpMap;

3803

3804 llvm::SetVector<const VarDecl *>

3805 GrpsUnionForParms;

3806

3807

3808

3809 std::set<const VarDecl *> VisitedVars{};

3810 for (const auto &[Var, ignore] : UnsafeOps.byVar) {

3811 if (VisitedVars.find(Var) == VisitedVars.end()) {

3812 VarGrpTy &VarGroup = Groups.emplace_back();

3813 std::queue<const VarDecl *> Queue{};

3814

3815 Queue.push(Var);

3816 while (!Queue.empty()) {

3817 const VarDecl *CurrentVar = Queue.front();

3818 Queue.pop();

3819 VisitedVars.insert(CurrentVar);

3820 VarGroup.push_back(CurrentVar);

3821 auto AdjacentNodes = DependenciesMap[CurrentVar];

3822 for (const VarDecl *Adj : AdjacentNodes) {

3823 if (VisitedVars.find(Adj) == VisitedVars.end()) {

3824 Queue.push(Adj);

3825 }

3826 }

3827 }

3828

3829 bool HasParm = false;

3830 unsigned GrpIdx = Groups.size() - 1;

3831

3832 for (const VarDecl *V : VarGroup) {

3833 VarGrpMap[V] = GrpIdx;

3835 HasParm = true;

3836 }

3837 if (HasParm)

3838 GrpsUnionForParms.insert(VarGroup.begin(), VarGroup.end());

3839 }

3840 }

3841

3842

3843

3844

3845

3846

3847

3848

3849

3850

3851

3852

3853

3854

3855

3856

3857

3858

3859

3860 for (auto I = FixablesForAllVars.byVar.begin();

3861 I != FixablesForAllVars.byVar.end();) {

3862

3863 if (!VisitedVars.count((*I).first)) {

3864

3865 I = FixablesForAllVars.byVar.erase(I);

3866 } else

3867 ++I;

3868 }

3869

3870

3871

3873 VisitedVars, [&FixablesForAllVars](const VarDecl *V) {

3874

3875 return FixablesForAllVars.byVar.count(V);

3876 }));

3878

3879 if (isa(D))

3880

3881

3882 FixItsForVariableGroup =

3884 Tracker, Handler, VarGrpMgr);

3885

3886 for (const auto &G : UnsafeOps.noVar) {

3887 G->handleUnsafeOperation(Handler, false,

3889 }

3890

3891 for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {

3892 auto FixItsIt = FixItsForVariableGroup.find(VD);

3894 FixItsIt != FixItsForVariableGroup.end()

3895 ? std::move(FixItsIt->second)

3896 : FixItList{},

3897 D, NaiveStrategy);

3898 for (const auto &G : WarningGadgets) {

3899 G->handleUnsafeOperation(Handler, true,

3901 }

3902 }

3903}

3904

3907 bool EmitSuggestions) {

3908#ifndef NDEBUG

3910#endif

3911

3912 assert(D);

3913

3915

3916 if (const auto *FD = dyn_cast(D)) {

3917

3918

3919

3920 if (const auto *MD = dyn_cast(D)) {

3921 if (MD->getParent()->isLambda() && MD->getParent()->isLocalClass())

3922 return;

3923 }

3924

3926 if (FReDecl->isExternC()) {

3927

3928

3929 EmitSuggestions = false;

3930 break;

3931 }

3932 }

3933

3934 Stmts.push_back(FD->getBody());

3935

3936 if (const auto *ID = dyn_cast(D)) {

3938 Stmts.push_back(CI->getInit());

3939 }

3940 }

3941 } else if (isa(D) || isa(D)) {

3942 Stmts.push_back(D->getBody());

3943 }

3944

3945 assert(!Stmts.empty());

3946

3947 FixableGadgetList FixableGadgets;

3948 WarningGadgetList WarningGadgets;

3949 DeclUseTracker Tracker;

3950 for (Stmt *S : Stmts) {

3952 WarningGadgets, Tracker);

3953 }

3954 applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),

3955 std::move(Tracker), Handler, EmitSuggestions);

3956}

Defines the clang::ASTContext interface.

BoundNodesTreeBuilder Nodes

#define AST_MATCHER(Type, DefineMatcher)

AST_MATCHER(Type, DefineMatcher) { ... } defines a zero parameter function named DefineMatcher() that...

#define AST_MATCHER_P(Type, DefineMatcher, ParamType, Param)

AST_MATCHER_P(Type, DefineMatcher, ParamType, Param) { ... } defines a single-parameter function name...

static Decl::Kind getKind(const Decl *D)

static const Decl * getCanonicalDecl(const Decl *D)

llvm::MachO::Target Target

Defines the clang::Preprocessor interface.

static bool hasAttr(const Decl *D, bool IgnoreImplicitAttr)

Defines the clang::SourceLocation class and associated facilities.

static QualType getPointeeType(const MemRegion *R)

C Language Family Type Representation.

static std::string getUserFillPlaceHolder(StringRef HintTextToUser="placeholder")

static FixItList fixVariableWithSpan(const VarDecl *VD, const DeclUseTracker &Tracker, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)

static std::optional< FixItList > fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node)

static std::optional< StringRef > getExprText(const Expr *E, const SourceManager &SM, const LangOptions &LangOpts)

static WarningGadgetSets groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations)

static StringRef getEndOfLine()

static std::optional< FixItList > FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx, const StringRef UserFillPlaceHolder)

static std::optional< SourceLocation > getEndCharLoc(const NodeTy *Node, const SourceManager &SM, const LangOptions &LangOpts)

static FixItList fixVariableWithArray(const VarDecl *VD, const DeclUseTracker &Tracker, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)

static std::string getSpanTypeText(StringRef EltTyText, std::optional< Qualifiers > Quals=std::nullopt)

static SourceRange getSourceRangeToTokenEnd(const Decl *D, const SourceManager &SM, const LangOptions &LangOpts)

static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx, const StringRef UserFillPlaceHolder, UnsafeBufferUsageHandler &Handler)

static std::optional< FixItList > createDataFixit(const ASTContext &Ctx, const DeclRefExpr *DRE)

static FixItList createFunctionOverloadsForParms(std::map< const VarDecl *, FixItList > &FixItsForVariable, const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD, const FixitStrategy &S, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)

#define FIXABLE_GADGET(name)

#define WARNING_OPTIONAL_GADGET(x)

static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)

void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets, WarningGadgetList WarningGadgets, DeclUseTracker Tracker, UnsafeBufferUsageHandler &Handler, bool EmitSuggestions)

#define WARNING_GADGET(name)

static FixItList fixVariable(const VarDecl *VD, FixitStrategy::Kind K, const Decl *D, const DeclUseTracker &Tracker, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)

static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)

static std::optional< StringRef > getRangeText(SourceRange SR, const SourceManager &SM, const LangOptions &LangOpts)

static FixitStrategy getNaiveStrategy(llvm::iterator_range< VarDeclIterTy > UnsafeVars)

static std::optional< std::string > createSpanTypeForVarDecl(const VarDecl *VD, const ASTContext &Ctx)

static SourceLocation getVarDeclIdentifierLoc(const VarDecl *VD)

static std::optional< SourceLocation > getPastLoc(const NodeTy *Node, const SourceManager &SM, const LangOptions &LangOpts)

static bool hasConflictingOverload(const FunctionDecl *FD)

static std::optional< StringRef > getVarDeclIdentifierText(const VarDecl *VD, const SourceManager &SM, const LangOptions &LangOpts)

static std::optional< std::string > getPointeeTypeText(const VarDecl *VD, const SourceManager &SM, const LangOptions &LangOpts, std::optional< Qualifiers > *QualifiersToAppend)

static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD, const ASTContext &Ctx)

static bool overlapWithMacro(const FixItList &FixIts)

static bool hasUnsupportedSpecifiers(const VarDecl *VD, const SourceManager &SM)

#define DEBUG_NOTE_DECL_FAIL(D, Msg)

static void findGadgets(const Stmt *S, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler, bool EmitSuggestions, FixableGadgetList &FixableGadgets, WarningGadgetList &WarningGadgets, DeclUseTracker &Tracker)

Scan the function and return a list of gadgets found with provided kits.

static std::map< const VarDecl *, FixItList > getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S, ASTContext &Ctx, const Decl *D, const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler, const VariableGroupsManager &VarGrpMgr)

static std::optional< FixItList > createOverloadsForFixedParams(const FixitStrategy &S, const FunctionDecl *FD, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)

static void eraseVarsForUnfixableGroupMates(std::map< const VarDecl *, FixItList > &FixItsForVariable, const VariableGroupsManager &VarGrpMgr)

static FixableGadgetSets groupFixablesByVar(FixableGadgetList &&AllFixableOperations)

static bool isParameterOf(const VarDecl *VD, const Decl *D)

static std::optional< StringRef > getFunNameText(const FunctionDecl *FD, const SourceManager &SM, const LangOptions &LangOpts)

__device__ __2f16 float __ockl_bool s

virtual std::optional< FixItList > getFixits(const FixitStrategy &s) const final

DerefSimplePtrArithFixableGadget(const MatchFinder::MatchResult &Result)

SourceLocation getSourceLoc() const override

virtual DeclUseList getClaimedVarUseSites() const final

virtual std::optional< FixItList > getFixits(const FixitStrategy &S) const override

SourceLocation getSourceLoc() const override

virtual DeclUseList getClaimedVarUseSites() const override

static bool classof(const Gadget *G)

UPCPreIncrementGadget(const MatchFinder::MatchResult &Result)

static bool classof(const Gadget *G)

UUCAddAssignGadget(const MatchFinder::MatchResult &Result)

virtual std::optional< FixItList > getFixits(const FixitStrategy &S) const override

virtual DeclUseList getClaimedVarUseSites() const override

SourceLocation getSourceLoc() const override

VariableGroupsManagerImpl(const std::vector< VarGrpTy > &Groups, const std::map< const VarDecl *, unsigned > &VarGrpMap, const llvm::SetVector< const VarDecl * > &GrpsUnionForParms)

VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override

Returns the set of variables (including Var) that need to be fixed together in one step.

VarGrpRef getGroupOfParms() const override

Returns the non-empty group of variables that include parameters of the analyzing function,...

Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...

SourceManager & getSourceManager()

const ConstantArrayType * getAsConstantArrayType(QualType T) const

DynTypedNodeList getParents(const NodeT &Node)

Forwards to get node parents from the ParentMapContext.

QualType getFILEType() const

Retrieve the C FILE type.

const LangOptions & getLangOpts() const

const TargetInfo & getTargetInfo() const

ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.

SourceLocation getBeginLoc() const LLVM_READONLY

Attr - This represents one attribute.

A builtin binary operation expression such as "x + y" or "x <= y".

SourceLocation getBeginLoc() const LLVM_READONLY

static StringRef getOpcodeStr(Opcode Op)

getOpcodeStr - Turn an Opcode enum value into the punctuation char it corresponds to,...

Represents a call to a C++ constructor.

Expr * getArg(unsigned Arg)

Return the specified argument.

SourceLocation getBeginLoc() const LLVM_READONLY

Represents a C++ base or member initializer.

A use of a default initializer in a constructor or in aggregate initialization.

Represents a static or instance method of a struct/union/class.

Represents a C++11 noexcept expression (C++ [expr.unary.noexcept]).

Represents a C++ struct/union/class.

CXXRecordDecl * getCanonicalDecl() override

Retrieves the "canonical" declaration of the given declaration.

A C++ typeid expression (C++ [expr.typeid]), which gets the type_info that corresponds to the supplie...

CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).

CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...

static const char * getCastKindName(CastKind CK)

Represents a character-granular source range.

static CharSourceRange getCharRange(SourceRange R)

SourceLocation getEnd() const

ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.

bool isSingleResult() const

lookup_result lookup(DeclarationName Name) const

lookup - Find the declarations (if any) with the given Name in this context.

A reference to a declared variable, function, enum, etc.

SourceLocation getBeginLoc() const LLVM_READONLY

DeclStmt - Adaptor class for mixing declarations with statements and expressions.

bool isSingleDecl() const

isSingleDecl - This method returns true if this DeclStmt refers to a single Decl.

Decl - This represents one declaration (or definition), e.g.

bool isInStdNamespace() const

SourceLocation getEndLoc() const LLVM_READONLY

ASTContext & getASTContext() const LLVM_READONLY

bool isImplicit() const

isImplicit - Indicates whether the declaration was implicitly generated by the implementation.

virtual Stmt * getBody() const

getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...

SourceLocation getLocation() const

DeclContext * getDeclContext()

SourceLocation getBeginLoc() const LLVM_READONLY

virtual SourceRange getSourceRange() const LLVM_READONLY

Source range that this declaration covers.

NestedNameSpecifier * getQualifier() const

Retrieve the nested-name-specifier that qualifies the name of this declaration, if it was present in ...

SourceLocation getBeginLoc() const LLVM_READONLY

NestedNameSpecifierLoc getQualifierLoc() const

Retrieve the nested-name-specifier (with source-location information) that qualifies the name of this...

TypeSourceInfo * getTypeSourceInfo() const

Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.

const DynTypedNode * begin() const

A dynamically typed AST node container.

SourceRange getSourceRange() const

For nodes which represent textual entities in the source code, return their SourceRange.

const T * get() const

Retrieve the stored node as type T.

static DynTypedNode create(const T &Node)

Creates a DynTypedNode from Node.

Recursive AST visitor that supports extension via dynamic dispatch.

virtual bool TraverseDecl(Decl *D)

Recursively visit a declaration, by dispatching to Traverse*Decl() based on the argument's dynamic ty...

virtual bool TraverseStmt(Stmt *S)

Recursively visit a statement or expression, by dispatching to Traverse*() based on the argument's dy...

bool ShouldVisitImplicitCode

Whether this visitor should recurse into implicit code, e.g.

bool ShouldVisitTemplateInstantiations

Whether this visitor should recurse into template instantiations.

ExplicitCastExpr - An explicit cast written in the source code.

This represents one expression.

Expr * IgnoreParenImpCasts() LLVM_READONLY

Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...

Expr * IgnoreImplicit() LLVM_READONLY

Skip past any implicit AST nodes which might surround this expression until reaching a fixed point.

Expr * IgnoreParens() LLVM_READONLY

Skip past any parentheses which might surround this expression until reaching a fixed point.

NullPointerConstantValueDependence

Enumeration used to describe how isNullPointerConstant() should cope with value-dependent expressions...

std::optional< llvm::APSInt > getIntegerConstantExpr(const ASTContext &Ctx, SourceLocation *Loc=nullptr) const

isIntegerConstantExpr - Return the value if this expression is a valid integer constant expression.

Annotates a diagnostic with some code that should be inserted, removed, or replaced to fix the proble...

CharSourceRange RemoveRange

Code that should be replaced to correct the error.

static FixItHint CreateReplacement(CharSourceRange RemoveRange, StringRef Code)

Create a code modification hint that replaces the given source range with the given code string.

static FixItHint CreateRemoval(CharSourceRange RemoveRange)

Create a code modification hint that removes the given source range.

static FixItHint CreateInsertion(SourceLocation InsertionLoc, StringRef Code, bool BeforePreviousInsertions=false)

Create a code modification hint that inserts the given code string at a specific location.

Represents a function declaration or definition.

const ParmVarDecl * getParamDecl(unsigned i) const

Stmt * getBody(const FunctionDecl *&Definition) const

Retrieve the body (definition) of the function.

param_iterator param_begin()

unsigned getNumParams() const

Return the number of parameters this function must have based on its FunctionType.

DeclarationNameInfo getNameInfo() const

Represents a C11 generic selection.

One of these records is kept for each identifier that is lexed.

StringRef getName() const

Return the actual identifier string.

ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...

Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...

static StringRef getSourceText(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts, bool *Invalid=nullptr)

Returns a string for the source that the range encompasses.

static bool isAtEndOfMacroExpansion(SourceLocation loc, const SourceManager &SM, const LangOptions &LangOpts, SourceLocation *MacroEnd=nullptr)

Returns true if the given MacroID location points at the last token of the macro expansion.

static std::optional< Token > findNextToken(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts, bool IncludeComments=false)

Finds the token that comes right after the given location.

static unsigned MeasureTokenLength(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)

MeasureTokenLength - Relex the token at the specified location and return its length in bytes in the ...

static SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &LangOpts)

Computes the source location just past the end of the token at this source location.

IdentifierInfo * getIdentifier() const

Get the identifier that names this declaration, if there is one.

StringRef getName() const

Get the name of identifier for this declaration as a StringRef.

DeclarationName getDeclName() const

Get the actual, stored name of the declaration, which may be a special name.

std::string getNameAsString() const

Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...

SourceLocation getBeginLoc() const

Retrieve the location of the beginning of this nested-name-specifier.

Represents a parameter to a function.

bool hasDefaultArg() const

Determines whether this parameter has a default argument, either parsed or not.

SourceRange getSourceRange() const override LLVM_READONLY

Source range that this declaration covers.

Wrapper for source info for pointers.

PointerType - C99 6.7.5.1 - Pointer Declarators.

A (possibly-)qualified type.

bool hasQualifiers() const

Determine whether this type has any qualifiers.

bool isNull() const

Return true if this QualType doesn't point to a type yet.

Qualifiers getQualifiers() const

Retrieve the set of qualifiers applied to this type.

QualType getCanonicalType() const

bool isConstQualified() const

Determine whether this type is const-qualified.

std::string getAsString() const

redecl_range redecls() const

Returns an iterator range for all the redeclarations of the same decl.

Encodes a location in the source.

bool isValid() const

Return true if this is a valid SourceLocation object.

SourceLocation getLocWithOffset(IntTy Offset) const

Return a source location with the specified offset from this SourceLocation.

This class handles loading and caching of source files into memory.

A trivial tuple used to represent a source range.

SourceLocation getEnd() const

SourceLocation getBegin() const

Stmt - This represents one statement.

SourceLocation getEndLoc() const LLVM_READONLY

StmtClass getStmtClass() const

SourceLocation getBeginLoc() const LLVM_READONLY

Exposes information about the current target.

Base wrapper for a particular "section" of type source info.

UnqualTypeLoc getUnqualifiedLoc() const

Skips past any qualifiers, if this is qualified.

TypeLoc getNextTypeLoc() const

Get the next TypeLoc pointed by this TypeLoc, e.g for "int*" the TypeLoc is a PointerLoc and next Typ...

T castAs() const

Convert to the specified TypeLoc type, asserting that this TypeLoc is of the desired type.

SourceRange getSourceRange() const LLVM_READONLY

Get the full source range.

TypeLocClass getTypeLocClass() const

SourceLocation getEndLoc() const

Get the end source location.

SourceLocation getBeginLoc() const

Get the begin source location.

TypeLoc getTypeLoc() const

Return the TypeLoc wrapper for the type source info.

bool isFunctionPointerType() const

bool isPointerType() const

bool isIntegerType() const

isIntegerType() does not include complex integers (a GCC extension).

const T * castAs() const

Member-template castAs.

QualType getPointeeType() const

If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.

bool isUnsignedIntegerType() const

Return true if this is an integer type that is unsigned, according to C99 6.2.5p6 [which returns true...

UnaryExprOrTypeTraitExpr - expression with either a type or (unevaluated) expression operand.

UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...

SourceLocation getOperatorLoc() const

getOperatorLoc - Return the location of the operator.

Expr * getSubExpr() const

SourceLocation getBeginLoc() const LLVM_READONLY

static StringRef getOpcodeStr(Opcode Op)

getOpcodeStr - Turn an Opcode enum value into the punctuation char it corresponds to,...

The interface that lets the caller handle unsafe buffer usage analysis results by overriding this cla...

void addDebugNoteForVar(const VarDecl *VD, SourceLocation Loc, std::string Text)

virtual std::string getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc, StringRef WSSuffix="") const =0

virtual void handleUnsafeOperation(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx)=0

Invoked when an unsafe operation over raw pointers is found.

virtual void handleUnsafeVariableGroup(const VarDecl *Variable, const VariableGroupsManager &VarGrpMgr, FixItList &&Fixes, const Decl *D, const FixitStrategy &VarTargetTypes)=0

Invoked when a fix is suggested against a variable.

virtual void handleUnsafeOperationInContainer(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx)=0

Invoked when an unsafe operation with a std container is found.

virtual void handleUnsafeLibcCall(const CallExpr *Call, unsigned PrintfInfo, ASTContext &Ctx, const Expr *UnsafeArg=nullptr)=0

Invoked when a call to an unsafe libc function is found.

Represents a variable declaration or definition.

bool isConstexpr() const

Whether this variable is (C++11) constexpr.

VarDecl * getCanonicalDecl() override

Retrieves the "canonical" declaration of the given declaration.

bool isInlineSpecified() const

bool hasConstantInitialization() const

Determine whether this variable has constant initialization.

bool hasLocalStorage() const

Returns true if a variable with function scope is a non-static local variable.

bool isLocalVarDecl() const

Returns true for local variable declarations other than parameters.

virtual VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm=nullptr) const =0

Returns the set of variables (including Var) that need to be fixed together in one step.

virtual VarGrpRef getGroupOfParms() const =0

Returns the non-empty group of variables that include parameters of the analyzing function,...

bool findMatch(const DynTypedNode &DynNode)

bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) override

bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node) override

bool TraverseCXXDefaultInitExpr(CXXDefaultInitExpr *Node) override

bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) override

bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) override

bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node) override

bool TraverseStmt(Stmt *Node) override

Recursively visit a statement or expression, by dispatching to Traverse*() based on the argument's dy...

bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) override

MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher, internal::ASTMatchFinder *Finder, internal::BoundNodesTreeBuilder *Builder, internal::ASTMatchFinder::BindKind Bind, const bool ignoreUnevaluatedContext)

bool TraverseDecl(Decl *Node) override

Recursively visit a declaration, by dispatching to Traverse*Decl() based on the argument's dynamic ty...

Called when the Match registered for it was successfully found in the AST.

virtual void run(const MatchResult &Result)=0

Called on every match by the MatchFinder.

A class to allow finding matches over the Clang AST.

void addMatcher(const DeclarationMatcher &NodeMatch, MatchCallback *Action)

Adds a matcher to execute when running over the AST.

void match(const T &Node, ASTContext &Context)

Calls the registered callbacks on all matches on the given Node.

bool ParsePrintfString(FormatStringHandler &H, const char *beg, const char *end, const LangOptions &LO, const TargetInfo &Target, bool isFreeBSDKPrintf)

static bool isNullTermPointer(const Expr *Ptr)

static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg, const unsigned FmtArgIdx, ASTContext &Ctx, bool isKprintf=false)

const internal::VariadicDynCastAllOfMatcher< Decl, VarDecl > varDecl

Matches variable declarations.

const internal::VariadicDynCastAllOfMatcher< Stmt, DeclRefExpr > declRefExpr

Matches expressions that refer to declarations.

const AstTypeMatcher< EnumType > enumType

Matches enum types.

static auto isInUnspecifiedLvalueContext(internal::Matcher< Expr > innerMatcher)

const internal::VariadicOperatorMatcherFunc< 1, 1 > unless

Matches if the provided matcher does not match.

const internal::VariadicDynCastAllOfMatcher< Stmt, ImplicitCastExpr > implicitCastExpr

Matches the implicit cast nodes of Clang's AST.

const internal::VariadicDynCastAllOfMatcher< Stmt, StringLiteral > stringLiteral

Matches string literals (also matches wide string literals).

const AstTypeMatcher< PointerType > pointerType

Matches pointer types, but does not match Objective-C object pointer types.

const internal::VariadicDynCastAllOfMatcher< Decl, BindingDecl > bindingDecl

Matches binding declarations Example matches foo and bar (matcher = bindingDecl()

internal::Matcher< NamedDecl > hasName(StringRef Name)

Matches NamedDecl nodes that have the specified name.

const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr

Matches call expressions.

const internal::VariadicDynCastAllOfMatcher< Stmt, CompoundStmt > compoundStmt

Matches compound statements.

static auto hasArrayType()

const internal::ArgumentAdaptingMatcherFunc< internal::ForEachMatcher > forEach

Matches AST nodes that have child AST nodes that match the provided matcher.

const AstTypeMatcher< ArrayType > arrayType

Matches all kinds of arrays.

const internal::VariadicDynCastAllOfMatcher< Stmt, UnaryOperator > unaryOperator

Matches unary operator expressions.

const internal::VariadicDynCastAllOfMatcher< Stmt, ArraySubscriptExpr > arraySubscriptExpr

Matches array subscript expressions.

static internal::Matcher< Stmt > isInUnspecifiedUntypedContext(internal::Matcher< Stmt > InnerMatcher)

const internal::VariadicDynCastAllOfMatcher< Stmt, CXXMemberCallExpr > cxxMemberCallExpr

Matches member call expressions.

const internal::VariadicDynCastAllOfMatcher< Decl, CXXConstructorDecl > cxxConstructorDecl

Matches C++ constructor declarations.

const internal::VariadicDynCastAllOfMatcher< Stmt, ArrayInitIndexExpr > arrayInitIndexExpr

The arrayInitIndexExpr consists of two subexpressions: a common expression (the source array) that is...

const internal::VariadicDynCastAllOfMatcher< Stmt, BinaryOperator > binaryOperator

Matches binary operator expressions.

const internal::VariadicDynCastAllOfMatcher< Stmt, ParenExpr > parenExpr

Matches parentheses used in expressions.

const internal::ArgumentAdaptingMatcherFunc< internal::HasMatcher > has

Matches AST nodes that have child AST nodes that match the provided matcher.

const internal::VariadicDynCastAllOfMatcher< Stmt, ExplicitCastExpr > explicitCastExpr

Matches explicit cast expressions.

internal::PolymorphicMatcher< internal::ValueEqualsMatcher, void(internal::AllNodeBaseTypes), ValueT > equals(const ValueT &Value)

Matches literals that are equal to the given value of type ValueT.

const AstTypeMatcher< ConstantArrayType > constantArrayType

Matches C arrays with a specified constant size.

const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> eachOf

Matches if any of the given matchers matches.

const internal::VariadicDynCastAllOfMatcher< Stmt, CXXConstructExpr > cxxConstructExpr

Matches constructor call expressions (including implicit ones).

const internal::VariadicDynCastAllOfMatcher< Decl, FieldDecl > fieldDecl

Matches field declarations.

static internal::Matcher< Stmt > isInUnspecifiedPointerContext(internal::Matcher< Stmt > InnerMatcher)

const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> allOf

Matches if all given matchers match.

const internal::VariadicDynCastAllOfMatcher< Decl, FunctionDecl > functionDecl

Matches function declarations.

const internal::VariadicDynCastAllOfMatcher< Stmt, MemberExpr > memberExpr

Matches member expressions.

static auto hasPointerType()

const internal::VariadicDynCastAllOfMatcher< Stmt, IntegerLiteral > integerLiteral

Matches integer literals of all sizes / encodings, e.g.

internal::PolymorphicMatcher< internal::HasDeclarationMatcher, void(internal::HasDeclarationSupportedTypes), internal::Matcher< Decl > > hasDeclaration(const internal::Matcher< Decl > &InnerMatcher)

Matches a node if the declaration associated with that node matches the given matcher.

const internal::VariadicDynCastAllOfMatcher< Stmt, DeclStmt > declStmt

Matches declaration statements.

const internal::VariadicAllOfMatcher< Stmt > stmt

Matches statements.

const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr

Matches expressions.

const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> anyOf

Matches if any of the given matchers matches.

const internal::VariadicFunction< internal::PolymorphicMatcher< internal::HasAnyOperatorNameMatcher, AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr, CXXRewrittenBinaryOperator, UnaryOperator), std::vector< std::string > >, StringRef, internal::hasAnyOperatorNameFunc > hasAnyOperatorName

Matches operator expressions (binary or unary) that have any of the specified names.

const internal::VariadicDynCastAllOfMatcher< Decl, CXXMethodDecl > cxxMethodDecl

Matches method declarations.

const internal::VariadicDynCastAllOfMatcher< Stmt, CastExpr > castExpr

Matches any cast nodes of Clang's AST.

const internal::VariadicDynCastAllOfMatcher< Stmt, IfStmt > ifStmt

Matches if statements.

bool anyConflict(const llvm::SmallVectorImpl< FixItHint > &FixIts, const SourceManager &SM)

bool Call(InterpState &S, CodePtr OpPC, const Function *Func, uint32_t VarArgSize)

RangeSelector member(std::string ID)

Given a MemberExpr, selects the member token.

The JSON file list parser is used to communicate input to InstallAPI.

void checkUnsafeBufferUsage(const Decl *D, UnsafeBufferUsageHandler &Handler, bool EmitSuggestions)

std::vector< const VarDecl * > VarGrpTy

const FunctionProtoType * T

Diagnostic wrappers for TextAPI types for error reporting.

bool operator()(const NodeTy *N1, const NodeTy *N2) const

std::map< const VarDecl *, std::set< const FixableGadget * >, CompareNode< VarDecl > > byVar

std::map< const VarDecl *, std::set< const WarningGadget * >, CompareNode< VarDecl > > byVar

llvm::SmallVector< const WarningGadget *, 16 > noVar

SourceLocation getBeginLoc() const

getBeginLoc - Retrieve the location of the first token.

SourceLocation getEndLoc() const LLVM_READONLY

EvalResult is a struct with detailed info about an evaluated expression.

APValue Val

Val - This is the value the expression can be folded to.

Wraps an identifier and optional source location for the identifier.

Contains all information for a given match.

StringRef matchLibcName(StringRef Name)

StringRef matchLibcNameOrBuiltinChk(StringRef Name)

StringRef matchName(StringRef FunName, bool isBuiltin)