29#include "llvm/ADT/APInt.h"
30#include "llvm/ADT/APSInt.h"
31#include "llvm/ADT/STLExtras.h"
32#include "llvm/ADT/STLFunctionalExtras.h"
33#include "llvm/ADT/SmallVector.h"
34#include "llvm/ADT/StringRef.h"
51 std::string VisitBinaryOperator(
const BinaryOperator *BO) {
52 return "BinaryOperator(" + BO->
getOpcodeStr().str() +
")";
55 std::string VisitUnaryOperator(
const UnaryOperator *UO) {
59 std::string VisitImplicitCastExpr(
const ImplicitCastExpr *ICE) {
66static std::string getDREAncestorString(
const DeclRefExpr *DRE,
70 StmtDebugPrinter StmtPriner;
73 SS << StmtPriner.Visit(St);
77 if (StParents.
size() > 1)
78 return "unavailable due to multiple parents";
79 if (StParents.
empty())
98 virtual bool matches(
const DynTypedNode &DynNode, ASTContext &Ctx,
99 const UnsafeBufferUsageHandler &Handler) = 0;
100 virtual ~FastMatcher() =
default;
106 template <
typename T>
const T *getNodeAs(StringRef ID)
const {
107 auto It =
Nodes.find(ID);
108 if (It ==
Nodes.end()) {
111 return It->second.get<
T>();
114 void addNode(StringRef ID,
const DynTypedNode &Node) {
Nodes[
ID] = Node; }
117 llvm::StringMap<DynTypedNode>
Nodes;
121#define SIZED_CONTAINER_OR_VIEW_LIST \
122 "span", "array", "vector", "basic_string_view", "basic_string", \
133 bool FindAll,
bool IgnoreUnevaluatedContext,
135 : Matcher(&Matcher), FindAll(FindAll), Matches(
false),
136 IgnoreUnevaluatedContext(IgnoreUnevaluatedContext),
137 ActiveASTContext(&Context), Handler(&NewHandler) {
174 if (IgnoreUnevaluatedContext)
176 return DynamicRecursiveASTVisitor::TraverseGenericSelectionExpr(Node);
182 if (IgnoreUnevaluatedContext)
184 return DynamicRecursiveASTVisitor::TraverseUnaryExprOrTypeTraitExpr(Node);
188 bool TraverseQualifier)
override {
190 if (IgnoreUnevaluatedContext)
192 return DynamicRecursiveASTVisitor::TraverseTypeOfExprTypeLoc(
193 Node, TraverseQualifier);
197 bool TraverseQualifier)
override {
199 if (IgnoreUnevaluatedContext)
201 return DynamicRecursiveASTVisitor::TraverseDecltypeTypeLoc(
202 Node, TraverseQualifier);
207 if (IgnoreUnevaluatedContext)
209 return DynamicRecursiveASTVisitor::TraverseCXXNoexceptExpr(Node);
214 if (IgnoreUnevaluatedContext)
216 return DynamicRecursiveASTVisitor::TraverseCXXTypeidExpr(Node);
222 return DynamicRecursiveASTVisitor::TraverseCXXDefaultInitExpr(Node);
238 template <
typename T>
bool match(
const T &Node) {
248 FastMatcher *
const Matcher;
252 bool IgnoreUnevaluatedContext;
253 ASTContext *ActiveASTContext;
254 const UnsafeBufferUsageHandler *Handler;
269 FastMatcher &Matcher) {
277 FastMatcher &Matcher) {
305 const Stmt *S,
const llvm::function_ref<
void(
const Expr *)> OnResult) {
306 if (
const auto *CE = dyn_cast<ImplicitCastExpr>(S);
307 CE && CE->getCastKind() == CastKind::CK_LValueToRValue)
308 OnResult(CE->getSubExpr());
309 if (
const auto *BO = dyn_cast<BinaryOperator>(S);
317 const Stmt *S, llvm::function_ref<
void(
const Stmt *)> InnerMatcher) {
326 if (
auto *CE = dyn_cast<CallExpr>(S)) {
327 if (
const auto *FnDecl = CE->getDirectCallee();
328 FnDecl && FnDecl->hasAttr<UnsafeBufferUsageAttr>())
337 if (
auto *CE = dyn_cast<CastExpr>(S)) {
338 if (CE->getCastKind() != CastKind::CK_PointerToIntegral &&
339 CE->getCastKind() != CastKind::CK_PointerToBoolean)
343 InnerMatcher(CE->getSubExpr());
347 if (
const auto *BO = dyn_cast<BinaryOperator>(S);
361 if (
const auto *BO = dyn_cast<BinaryOperator>(S);
367 InnerMatcher(BO->
getLHS());
368 InnerMatcher(BO->
getRHS());
386 const Stmt *S, llvm::function_ref<
void(
const Stmt *)> InnerMatcher) {
391 if (
auto *CS = dyn_cast<CompoundStmt>(S)) {
392 for (
auto *Child : CS->body())
395 if (
auto *IfS = dyn_cast<IfStmt>(S)) {
397 InnerMatcher(IfS->getThen());
399 InnerMatcher(IfS->getElse());
450 case Stmt::DeclRefExprClass:
452 case Stmt::BinaryOperatorClass: {
455 BO2->getLHS(), BO2->getOpcode(),
472 if (
const auto *UO = dyn_cast<UnaryOperator>(Ptr)) {
473 if (UO->
getOpcode() != UnaryOperator::Opcode::UO_AddrOf)
479 if (
const auto *CE = dyn_cast<CallExpr>(Ptr)) {
484 return CE->getArg(0)->IgnoreParenImpCasts();
494 const auto *SizeOfExpr =
496 if (!SizeOfExpr || SizeOfExpr->getKind() != UETT_SizeOf)
498 if (SizeOfExpr->isArgumentType())
500 return SizeOfExpr->getArgumentExpr()->IgnoreParenImpCasts();
519 if (
auto *MCEPtr = dyn_cast<CXXMemberCallExpr>(Ptr->IgnoreParenImpCasts()))
521 dyn_cast<CXXMemberCallExpr>(Size->IgnoreParenImpCasts())) {
522 auto *DREOfPtr = dyn_cast<DeclRefExpr>(
523 MCEPtr->getImplicitObjectArgument()->IgnoreParenImpCasts());
524 auto *DREOfSize = dyn_cast<DeclRefExpr>(
525 MCESize->getImplicitObjectArgument()->IgnoreParenImpCasts());
527 if (!DREOfPtr || !DREOfSize)
531 if (DREOfPtr->getDecl() != DREOfSize->getDecl())
533 if (MCEPtr->getMethodDecl()->getName() !=
"data")
536 if (!MCEPtr->getRecordDecl()->isInStdNamespace())
539 auto *ObjII = MCEPtr->getRecordDecl()->getIdentifier();
544 bool AcceptSizeBytes = Ptr->getType()->getPointeeType()->isCharType();
546 if (!((AcceptSizeBytes &&
547 MCESize->getMethodDecl()->getName() ==
"size_bytes") ||
552 MCESize->getMethodDecl()->getName() ==
"size"))
562 if (Size->EvaluateAsInt(ER, Ctx)) {
564 if (
auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
568 return llvm::APSInt::compareValues(
569 SizeInt, llvm::APSInt(CAT->getSize(),
true)) == 0;
603 "expecting a two-parameter std::span constructor");
606 auto HaveEqualConstantValues = [&Ctx](
const Expr *E0,
const Expr *E1) {
608 if (
auto E1CV = E1->getIntegerConstantExpr(Ctx)) {
609 return llvm::APSInt::compareValues(*E0CV, *E1CV) == 0;
613 auto AreSameDRE = [](
const Expr *E0,
const Expr *E1) {
614 if (
auto *DRE0 = dyn_cast<DeclRefExpr>(E0))
615 if (
auto *DRE1 = dyn_cast<DeclRefExpr>(E1)) {
616 return DRE0->getDecl() == DRE1->getDecl();
622 if (Arg1CV && Arg1CV->isZero())
628 case Stmt::CXXNewExprClass:
631 return AreSameDRE((*Size)->IgnoreImplicit(), Arg1) ||
632 HaveEqualConstantValues(*Size, Arg1);
637 return Arg1CV && Arg1CV->isOne();
645 if (
auto CCast = dyn_cast<CStyleCastExpr>(Arg0)) {
646 if (!CCast->getType()->isPointerType())
654 if (
const auto *
Call = dyn_cast<CallExpr>(CCast->getSubExpr())) {
656 if (
auto *AllocAttr = FD->getAttr<AllocSizeAttr>()) {
657 const Expr *EleSizeExpr =
658 Call->getArg(AllocAttr->getElemSizeParam().getASTIndex());
660 ParamIdx NumElemIdx = AllocAttr->getNumElemsParam();
667 if (
auto BO = dyn_cast<BinaryOperator>(Arg1))
674 auto IsMethodCallToSizedObject = [](
const Stmt *Node, StringRef MethodName) {
675 if (
const auto *MC = dyn_cast<CXXMemberCallExpr>(Node)) {
676 const auto *MD = MC->getMethodDecl();
677 const auto *RD = MC->getRecordDecl();
680 if (
auto *II = RD->getDeclName().getAsIdentifierInfo();
681 II && RD->isInStdNamespace())
684 MD->getName() == MethodName;
689 if (IsMethodCallToSizedObject(Arg0,
"begin") &&
690 IsMethodCallToSizedObject(Arg1,
"end"))
694 ->getImplicitObjectArgument()
695 ->IgnoreParenImpCasts(),
697 ->getImplicitObjectArgument()
698 ->IgnoreParenImpCasts());
710 if (
const auto *SL = dyn_cast<StringLiteral>(Arg0)) {
712 if (llvm::APSInt::compareValues(
713 llvm::APSInt::getUnsigned(SL->getLength()), *ArgSize) >= 0)
722 if (llvm::APSInt::compareValues(llvm::APSInt(CAT->getSize(),
true),
737 auto GetContainerObj = [](
const Expr *E) ->
const Expr * {
738 E = E->IgnoreParenImpCasts();
739 if (
const auto *MCE = dyn_cast<CXXMemberCallExpr>(E)) {
740 const auto *MD = MCE->getMethodDecl();
741 if (MD && MD->getIdentifier())
742 if (MD->getName() ==
"begin" || MD->getName() ==
"end")
743 return MCE->getImplicitObjectArgument()->IgnoreParenImpCasts();
748 const Expr *Obj0 = GetContainerObj(Arg0);
749 const Expr *Obj1 = GetContainerObj(Arg1);
752 const auto *DRE0 = dyn_cast<DeclRefExpr>(Obj0);
753 const auto *DRE1 = dyn_cast<DeclRefExpr>(Obj1);
758 if (DRE0->getDecl()->getCanonicalDecl() ==
759 DRE1->getDecl()->getCanonicalDecl())
772 const bool IgnoreStaticSizedArrays) {
781 if (
const auto *CATy =
782 dyn_cast<ConstantArrayType>(Node.
getBase()
786 limit = CATy->getLimitedSize();
787 }
else if (
const auto *SLiteral = dyn_cast<clang::StringLiteral>(
789 limit = SLiteral->getLength() + 1;
794 if (IgnoreStaticSizedArrays) {
804 llvm::APSInt ArrIdx = EVResult.
Val.
getInt();
807 if (ArrIdx.isNonNegative() && ArrIdx.getLimitedValue() < limit)
809 }
else if (
const auto *BE = dyn_cast<BinaryOperator>(IndexExpr)) {
812 if (BE->getOpcode() != BO_And && BE->getOpcode() != BO_Rem)
815 const Expr *LHS = BE->getLHS();
816 const Expr *RHS = BE->getRHS();
818 if (BE->getOpcode() == BO_Rem) {
825 llvm::APSInt result = EVResult.
Val.
getInt();
826 if (result.isNonNegative() && result.getLimitedValue() <= limit)
837 llvm::APSInt result = EVResult.
Val.
getInt();
838 if (result.isNonNegative() && result.getLimitedValue() < limit)
858 const Expr *
Base = Ptr->IgnoreParenImpCasts();
860 if (
const auto *CATy = dyn_cast<ConstantArrayType>(
861 Base->getType()->getUnqualifiedDesugaredType())) {
862 limit = CATy->getLimitedSize();
863 }
else if (
const auto *SLiteral = dyn_cast<clang::StringLiteral>(
Base)) {
864 limit = SLiteral->getLength() + 1;
869 llvm::APSInt OffsetVal = EVResult.
Val.
getInt();
870 if (Opcode == BO_Sub)
871 OffsetVal = -OffsetVal;
875 return OffsetVal.isNonNegative() && OffsetVal.getLimitedValue() < limit;
884 if (
const auto *CE = dyn_cast<ConditionalOperator>(E)) {
886 const auto *Cond = CE->getCond();
888 if (!Cond->isValueDependent() &&
889 Cond->EvaluateAsBooleanCondition(CondEval, Ctx))
890 return CondEval ? CE->getLHS() : CE->getRHS();
899 Ptr = Ptr->IgnoreParenImpCasts();
900 if (
const auto *DefaultArgE = dyn_cast<CXXDefaultArgExpr>(Ptr))
901 Ptr = DefaultArgE->getExpr()->IgnoreParenImpCasts();
908 if (
const auto *CondE = dyn_cast<ConditionalOperator>(Ptr)) {
917 if (
auto *MCE = dyn_cast<CXXMemberCallExpr>(Ptr)) {
927 static const llvm::StringSet<> NullTermFunctions = {
"strerror"};
928 if (
auto *CE = dyn_cast<CallExpr>(Ptr)) {
951 if (Name.ends_with(
"_s"))
952 return Name.drop_back(2 );
959 if (Name.starts_with(
"__") && Name.ends_with(
"_chk"))
961 Name.drop_front(2).drop_back(4) );
965static StringRef
matchName(StringRef FunName,
bool isBuiltin) {
967 if (isBuiltin && FunName.starts_with(
"__builtin_"))
971 FunName.drop_front(10 ));
973 if (FunName.starts_with(
"__asan_"))
990 const Expr *&UnsafeArg,
const unsigned FmtIdx,
991 std::optional<const unsigned> FmtArgIdx = std::nullopt,
992 bool isKprintf =
false) {
993 class StringFormatStringHandler
997 const Expr *&UnsafeArg;
1013 unsigned PArgIdx = Precision.
getArgIndex() + FmtArgIdx;
1015 if (PArgIdx < Call->getNumArgs()) {
1016 const Expr *PArg =
Call->getArg(PArgIdx);
1019 if (
auto *CE = dyn_cast<CastExpr>(PArg);
1020 CE && CE->getType()->isSignedIntegerType())
1021 PArg = CE->getSubExpr();
1026 analyze_printf::OptionalAmount::HowSpecified::Constant) {
1028 llvm::APSInt PArgVal = llvm::APSInt(
1038 StringFormatStringHandler(
const CallExpr *
Call,
unsigned FmtArgIdx,
1040 :
Call(
Call), FmtArgIdx(FmtArgIdx), UnsafeArg(UnsafeArg), Ctx(Ctx),
1041 UnsafeArgSet(
false) {}
1044 const char *startSpecifier,
1045 unsigned specifierLen,
1053 if (ArgIdx >=
Call->getNumArgs())
1057 const Expr *Arg =
Call->getArg(ArgIdx);
1067 bool IsArgTypeValid =
1070 ?
ArgType->getPointeeType()->isWideCharType()
1071 :
ArgType->getPointeeType()->isCharType());
1074 Precision && IsArgTypeValid)
1078 UnsafeArg =
Call->getArg(ArgIdx);
1079 UnsafeArgSet =
true;
1083 bool isUnsafeArgSet() {
return UnsafeArgSet; }
1086 const Expr *Fmt =
Call->getArg(FmtIdx);
1087 unsigned FmtArgStartingIdx =
1088 FmtArgIdx.has_value() ?
static_cast<unsigned>(*FmtArgIdx) : FmtIdx + 1;
1091 if (SL->getCharByteWidth() == 1) {
1092 StringRef FmtStr = SL->getString();
1093 StringFormatStringHandler Handler(
Call, FmtArgStartingIdx, UnsafeArg,
1097 Handler, FmtStr.begin(), FmtStr.end(), Ctx.
getLangOpts(),
1099 Handler.isUnsafeArgSet();
1102 if (
auto FmtStr = SL->tryEvaluateString(Ctx)) {
1103 StringFormatStringHandler Handler(
Call, FmtArgStartingIdx, UnsafeArg,
1106 Handler, FmtStr->data(), FmtStr->data() + FmtStr->size(),
1108 Handler.isUnsafeArgSet();
1114 return llvm::any_of(
1115 llvm::make_range(
Call->arg_begin() + FmtIdx,
Call->arg_end()),
1116 [&UnsafeArg, &Ctx](
const Expr *Arg) ->
bool {
1117 if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg, Ctx)) {
1135 static const std::set<StringRef> PredefinedNames = {
1214 if (PredefinedNames.count(Name))
1217 std::string NameWCS = Name.str();
1218 size_t WcsPos = NameWCS.find(
"wcs");
1220 while (WcsPos != std::string::npos) {
1221 NameWCS[WcsPos++] =
's';
1222 NameWCS[WcsPos++] =
't';
1223 NameWCS[WcsPos++] =
'r';
1224 WcsPos = NameWCS.find(
"wcs", WcsPos);
1226 if (PredefinedNames.count(NameWCS))
1230 return Name.ends_with(
"scanf");
1238 assert(FD &&
"It should have been checked that FD is non-null.");
1245 if (Name !=
"memset")
1257 const auto *AddressOfVar = dyn_cast_if_present<DeclRefExpr>(
1262 const auto *SizeOfVar =
1267 return AddressOfVar->getDecl() != SizeOfVar->getDecl();
1281 return Name.starts_with(
"v") && Name.ends_with(
"printf");
1294 return Name ==
"sprintf" || Name ==
"swprintf";
1308 if (!Name.ends_with(
"printf"))
1311 StringRef Prefix = Name.drop_back(6);
1313 if (Prefix.ends_with(
"w"))
1314 Prefix = Prefix.drop_back(1);
1316 return Prefix.empty() || Prefix ==
"k" || Prefix ==
"f" || Prefix ==
"sn";
1324 MatchResult &Result, llvm::StringRef Tag) {
1328 assert(FD &&
"It should have been checked that FD is non-null.");
1346 const Expr *UnsafeArg;
1357 bool isKprintf =
false;
1358 const Expr *UnsafeArg;
1361 isKprintf = II->getName() ==
"kprintf";
1363 std::nullopt, isKprintf)) {
1376 const Expr *UnsafeArg;
1387 for (
const auto *Arg : Node.
arguments())
1402 assert(FD &&
"It should have been checked that FD is non-null.");
1417 !Size->getType()->isUnsignedIntegerType())
1446#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1449 Gadget(Kind K) : K(K) {}
1451 Kind
getKind()
const {
return K; }
1454 StringRef getDebugName()
const {
1459#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1461 llvm_unreachable(
"Unhandled Gadget::Kind enum");
1465 virtual bool isWarningGadget()
const = 0;
1468 virtual SourceLocation getSourceLoc()
const = 0;
1473 virtual DeclUseList getClaimedVarUseSites()
const = 0;
1475 virtual ~Gadget() =
default;
1483class WarningGadget :
public Gadget {
1485 WarningGadget(Kind K) : Gadget(K) {}
1487 static bool classof(
const Gadget *G) {
return G->isWarningGadget(); }
1488 bool isWarningGadget() const final {
return true; }
1490 virtual void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1491 bool IsRelatedToDecl,
1492 ASTContext &Ctx)
const = 0;
1494 virtual SmallVector<const Expr *, 1> getUnsafePtrs()
const = 0;
1501class FixableGadget :
public Gadget {
1503 FixableGadget(Kind K) : Gadget(K) {}
1505 static bool classof(
const Gadget *G) {
return !G->isWarningGadget(); }
1506 bool isWarningGadget() const final {
return false; }
1511 virtual std::optional<FixItList> getFixits(
const FixitStrategy &)
const {
1512 return std::nullopt;
1521 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1522 getStrategyImplications()
const {
1523 return std::nullopt;
1527static bool isSupportedVariable(
const DeclRefExpr &Node) {
1539 dyn_cast<ClassTemplateSpecializationDecl>(
RecordDecl);
1540 if (!class_template_specialization_decl)
1545 if (template_args.
size() == 0)
1556class UniquePtrArrayAccessGadget :
public WarningGadget {
1558 static constexpr const char *
const AccessorTag =
"unique_ptr_array_access";
1559 const CXXOperatorCallExpr *AccessorExpr;
1563 : WarningGadget(
Kind::UniquePtrArrayAccess),
1564 AccessorExpr(
Result.getNodeAs<CXXOperatorCallExpr>(AccessorTag)) {
1565 assert(AccessorExpr &&
1566 "UniquePtrArrayAccessGadget requires a matched CXXOperatorCallExpr");
1569 static bool classof(
const Gadget *G) {
1570 return G->getKind() == Kind::UniquePtrArrayAccess;
1573 static bool matches(
const Stmt *S,
const ASTContext &Ctx,
1576 const CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(S);
1577 if (!OpCall || OpCall->
getOperator() != OO_Subscript)
1584 const CXXMethodDecl *
Method =
1589 if (
Method->getOverloadedOperator() != OO_Subscript)
1593 if (!isUniquePtrArray(RecordDecl))
1596 const Expr *IndexExpr = OpCall->
getArg(1);
1597 clang::Expr::EvalResult Eval;
1603 Result.addNode(AccessorTag, DynTypedNode::create(*OpCall));
1606 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1607 bool IsRelatedToDecl,
1608 ASTContext &Ctx)
const override {
1610 DynTypedNode::create(*AccessorExpr), IsRelatedToDecl, Ctx);
1613 SourceLocation getSourceLoc()
const override {
1615 return AccessorExpr->getOperatorLoc();
1616 return SourceLocation();
1619 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
1620 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
1623using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>;
1624using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>;
1628class IncrementGadget :
public WarningGadget {
1629 static constexpr const char *
const OpTag =
"op";
1630 const UnaryOperator *Op;
1634 : WarningGadget(
Kind::Increment),
1635 Op(
Result.getNodeAs<UnaryOperator>(OpTag)) {}
1637 static bool classof(
const Gadget *G) {
1638 return G->getKind() == Kind::Increment;
1641 static bool matches(
const Stmt *S,
const ASTContext &Ctx,
1643 const auto *UO = dyn_cast<UnaryOperator>(S);
1648 Result.addNode(OpTag, DynTypedNode::create(*UO));
1652 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1653 bool IsRelatedToDecl,
1654 ASTContext &Ctx)
const override {
1657 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
1659 DeclUseList getClaimedVarUseSites()
const override {
1660 SmallVector<const DeclRefExpr *, 2> Uses;
1661 if (
const auto *DRE =
1662 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
1663 Uses.push_back(DRE);
1666 return std::move(Uses);
1669 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
1670 return {Op->getSubExpr()->IgnoreParenImpCasts()};
1676class DecrementGadget :
public WarningGadget {
1677 static constexpr const char *
const OpTag =
"op";
1678 const UnaryOperator *Op;
1682 : WarningGadget(
Kind::Decrement),
1683 Op(
Result.getNodeAs<UnaryOperator>(OpTag)) {}
1685 static bool classof(
const Gadget *G) {
1686 return G->getKind() == Kind::Decrement;
1689 static bool matches(
const Stmt *S,
const ASTContext &Ctx,
1691 const auto *UO = dyn_cast<UnaryOperator>(S);
1696 Result.addNode(OpTag, DynTypedNode::create(*UO));
1700 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1701 bool IsRelatedToDecl,
1702 ASTContext &Ctx)
const override {
1705 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
1707 DeclUseList getClaimedVarUseSites()
const override {
1708 if (
const auto *DRE =
1709 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
1716 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
1717 return {Op->getSubExpr()->IgnoreParenImpCasts()};
1723class ArraySubscriptGadget :
public WarningGadget {
1724 static constexpr const char *
const ArraySubscrTag =
"ArraySubscript";
1725 const ArraySubscriptExpr *ASE;
1730 ASE(
Result.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {}
1732 static bool classof(
const Gadget *G) {
1733 return G->getKind() == Kind::ArraySubscript;
1736 static bool matches(
const Stmt *S,
const ASTContext &Ctx,
1737 const UnsafeBufferUsageHandler *Handler,
1739 const auto *ASE = dyn_cast<ArraySubscriptExpr>(S);
1742 const auto *
const Base = ASE->getBase()->IgnoreParenImpCasts();
1745 const auto *Idx = dyn_cast<IntegerLiteral>(ASE->getIdx());
1746 bool IsSafeIndex = (Idx && Idx->getValue().isZero()) ||
1753 Result.addNode(ArraySubscrTag, DynTypedNode::create(*ASE));
1757 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1758 bool IsRelatedToDecl,
1759 ASTContext &Ctx)
const override {
1762 SourceLocation getSourceLoc()
const override {
return ASE->getBeginLoc(); }
1764 DeclUseList getClaimedVarUseSites()
const override {
1765 if (
const auto *DRE =
1766 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) {
1773 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
1774 return {ASE->getBase()->IgnoreParenImpCasts()};
1782class PointerArithmeticGadget :
public WarningGadget {
1783 static constexpr const char *
const PointerArithmeticTag =
"ptrAdd";
1784 static constexpr const char *
const PointerArithmeticPointerTag =
"ptrAddPtr";
1785 const BinaryOperator *PA;
1790 : WarningGadget(
Kind::PointerArithmetic),
1791 PA(
Result.getNodeAs<BinaryOperator>(PointerArithmeticTag)),
1792 Ptr(
Result.getNodeAs<Expr>(PointerArithmeticPointerTag)) {}
1794 static bool classof(
const Gadget *G) {
1795 return G->getKind() == Kind::PointerArithmetic;
1798 static bool matches(
const Stmt *S,
const ASTContext &Ctx,
1799 const UnsafeBufferUsageHandler *Handler,
1801 const auto *BO = dyn_cast<BinaryOperator>(S);
1804 const auto *LHS = BO->
getLHS();
1805 const auto *RHS = BO->
getRHS();
1807 const Expr *Ptr =
nullptr;
1808 const Expr *OffsetExpr =
nullptr;
1814 RHS->getType()->isEnumeralType())) {
1821 (LHS->getType()->isIntegerType() || LHS->getType()->isEnumeralType())) {
1826 if (!Ptr || !OffsetExpr)
1837 Result.addNode(PointerArithmeticPointerTag, DynTypedNode::create(*Ptr));
1838 Result.addNode(PointerArithmeticTag, DynTypedNode::create(*BO));
1842 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1843 bool IsRelatedToDecl,
1844 ASTContext &Ctx)
const override {
1847 SourceLocation getSourceLoc()
const override {
return PA->getBeginLoc(); }
1849 DeclUseList getClaimedVarUseSites()
const override {
1850 if (
const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
1857 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
1858 return {Ptr->IgnoreParenImpCasts()};
1865class SpanTwoParamConstructorGadget :
public WarningGadget {
1866 static constexpr const char *
const SpanTwoParamConstructorTag =
1867 "spanTwoParamConstructor";
1868 const CXXConstructExpr *Ctor;
1872 : WarningGadget(
Kind::SpanTwoParamConstructor),
1873 Ctor(
Result.getNodeAs<CXXConstructExpr>(SpanTwoParamConstructorTag)) {}
1875 static bool classof(
const Gadget *G) {
1876 return G->getKind() == Kind::SpanTwoParamConstructor;
1879 static bool matches(
const CXXConstructExpr *CE, ASTContext &Ctx,
1882 const auto *CRecordDecl = CDecl->
getParent();
1883 auto HasTwoParamSpanCtorDecl =
1885 CDecl->getDeclName().getAsString() ==
"span" && CE->
getNumArgs() == 2;
1888 Result.addNode(SpanTwoParamConstructorTag, DynTypedNode::create(*CE));
1892 static bool matches(
const Stmt *S, ASTContext &Ctx,
1893 const UnsafeBufferUsageHandler *Handler,
1895 const auto *CE = dyn_cast<CXXConstructExpr>(S);
1903 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1904 bool IsRelatedToDecl,
1905 ASTContext &Ctx)
const override {
1908 SourceLocation getSourceLoc()
const override {
return Ctor->getBeginLoc(); }
1910 DeclUseList getClaimedVarUseSites()
const override {
1913 if (
auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
1920 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
1923class StringViewTwoParamConstructorGadget :
public WarningGadget {
1924 static constexpr const char *
const StringViewTwoParamConstructorTag =
1925 "stringViewTwoParamConstructor";
1926 const CXXConstructExpr *Ctor;
1930 : WarningGadget(
Kind::StringViewTwoParamConstructor),
1931 Ctor(
Result.getNodeAs<CXXConstructExpr>(
1932 StringViewTwoParamConstructorTag)) {}
1934 static bool classof(
const Gadget *G) {
1935 return G->getKind() == Kind::StringViewTwoParamConstructor;
1938 static bool matches(
const CXXConstructExpr *CE, ASTContext &Ctx,
1941 const auto *CRecordDecl = CDecl->
getParent();
1946 CDecl->getDeclName().getAsString() ==
"basic_string_view" &&
1952 Result.addNode(StringViewTwoParamConstructorTag, DynTypedNode::create(*CE));
1956 static bool matches(
const Stmt *S, ASTContext &Ctx,
1957 const UnsafeBufferUsageHandler *Handler,
1959 const auto *CE = dyn_cast<CXXConstructExpr>(S);
1967 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1968 bool IsRelatedToDecl,
1969 ASTContext &Ctx)
const override {
1973 SourceLocation getSourceLoc()
const override {
return Ctor->getBeginLoc(); }
1975 DeclUseList getClaimedVarUseSites()
const override {
1978 if (
auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
1985 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
1992class PointerInitGadget :
public FixableGadget {
1994 static constexpr const char *
const PointerInitLHSTag =
"ptrInitLHS";
1995 static constexpr const char *
const PointerInitRHSTag =
"ptrInitRHS";
1996 const VarDecl *PtrInitLHS;
1997 const DeclRefExpr *PtrInitRHS;
2001 : FixableGadget(
Kind::PointerInit),
2002 PtrInitLHS(
Result.getNodeAs<VarDecl>(PointerInitLHSTag)),
2003 PtrInitRHS(
Result.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}
2005 static bool classof(
const Gadget *G) {
2006 return G->getKind() == Kind::PointerInit;
2009 static bool matches(
const Stmt *S,
2010 llvm::SmallVectorImpl<MatchResult> &Results) {
2011 const DeclStmt *DS = dyn_cast<DeclStmt>(S);
2020 const auto *DRE = dyn_cast<DeclRefExpr>(
Init->IgnoreImpCasts());
2021 if (!DRE || !
hasPointerType(*DRE) || !isSupportedVariable(*DRE)) {
2025 R.addNode(PointerInitLHSTag, DynTypedNode::create(*VD));
2026 R.addNode(PointerInitRHSTag, DynTypedNode::create(*DRE));
2027 Results.emplace_back(std::move(R));
2031 virtual std::optional<FixItList>
2032 getFixits(
const FixitStrategy &S)
const override;
2033 SourceLocation getSourceLoc()
const override {
2034 return PtrInitRHS->getBeginLoc();
2037 virtual DeclUseList getClaimedVarUseSites()
const override {
2038 return DeclUseList{PtrInitRHS};
2041 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
2042 getStrategyImplications()
const override {
2043 return std::make_pair(PtrInitLHS,
cast<VarDecl>(PtrInitRHS->getDecl()));
2052class PtrToPtrAssignmentGadget :
public FixableGadget {
2054 static constexpr const char *
const PointerAssignLHSTag =
"ptrLHS";
2055 static constexpr const char *
const PointerAssignRHSTag =
"ptrRHS";
2056 const DeclRefExpr *PtrLHS;
2057 const DeclRefExpr *PtrRHS;
2061 : FixableGadget(
Kind::PtrToPtrAssignment),
2062 PtrLHS(
Result.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
2063 PtrRHS(
Result.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
2065 static bool classof(
const Gadget *G) {
2066 return G->getKind() == Kind::PtrToPtrAssignment;
2069 static bool matches(
const Stmt *S,
2070 llvm::SmallVectorImpl<MatchResult> &Results) {
2071 size_t SizeBefore = Results.size();
2073 const auto *BO = dyn_cast<BinaryOperator>(S);
2074 if (!BO || BO->
getOpcode() != BO_Assign)
2077 if (
const auto *RHSRef = dyn_cast<DeclRefExpr>(RHS);
2079 !isSupportedVariable(*RHSRef)) {
2082 const auto *LHS = BO->
getLHS();
2083 if (
const auto *LHSRef = dyn_cast<DeclRefExpr>(LHS);
2085 !isSupportedVariable(*LHSRef)) {
2089 R.addNode(PointerAssignLHSTag, DynTypedNode::create(*LHS));
2090 R.addNode(PointerAssignRHSTag, DynTypedNode::create(*RHS));
2091 Results.emplace_back(std::move(R));
2093 return SizeBefore != Results.size();
2096 virtual std::optional<FixItList>
2097 getFixits(
const FixitStrategy &S)
const override;
2098 SourceLocation getSourceLoc()
const override {
return PtrLHS->getBeginLoc(); }
2100 virtual DeclUseList getClaimedVarUseSites()
const override {
2101 return DeclUseList{PtrLHS, PtrRHS};
2104 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
2105 getStrategyImplications()
const override {
2116class CArrayToPtrAssignmentGadget :
public FixableGadget {
2118 static constexpr const char *
const PointerAssignLHSTag =
"ptrLHS";
2119 static constexpr const char *
const PointerAssignRHSTag =
"ptrRHS";
2120 const DeclRefExpr *PtrLHS;
2121 const DeclRefExpr *PtrRHS;
2125 : FixableGadget(
Kind::CArrayToPtrAssignment),
2126 PtrLHS(
Result.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
2127 PtrRHS(
Result.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
2129 static bool classof(
const Gadget *G) {
2130 return G->getKind() == Kind::CArrayToPtrAssignment;
2133 static bool matches(
const Stmt *S,
2134 llvm::SmallVectorImpl<MatchResult> &Results) {
2135 size_t SizeBefore = Results.size();
2137 const auto *BO = dyn_cast<BinaryOperator>(S);
2138 if (!BO || BO->
getOpcode() != BO_Assign)
2141 if (
const auto *RHSRef = dyn_cast<DeclRefExpr>(RHS);
2144 !isSupportedVariable(*RHSRef)) {
2147 const auto *LHS = BO->
getLHS();
2148 if (
const auto *LHSRef = dyn_cast<DeclRefExpr>(LHS);
2150 !isSupportedVariable(*LHSRef)) {
2154 R.addNode(PointerAssignLHSTag, DynTypedNode::create(*LHS));
2155 R.addNode(PointerAssignRHSTag, DynTypedNode::create(*RHS));
2156 Results.emplace_back(std::move(R));
2158 return SizeBefore != Results.size();
2161 virtual std::optional<FixItList>
2162 getFixits(
const FixitStrategy &S)
const override;
2163 SourceLocation getSourceLoc()
const override {
return PtrLHS->getBeginLoc(); }
2165 virtual DeclUseList getClaimedVarUseSites()
const override {
2166 return DeclUseList{PtrLHS, PtrRHS};
2169 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
2170 getStrategyImplications()
const override {
2177class UnsafeBufferUsageAttrGadget :
public WarningGadget {
2178 constexpr static const char *
const OpTag =
"attr_expr";
2183 : WarningGadget(
Kind::UnsafeBufferUsageAttr),
2184 Op(
Result.getNodeAs<Expr>(OpTag)) {}
2186 static bool classof(
const Gadget *G) {
2187 return G->getKind() == Kind::UnsafeBufferUsageAttr;
2190 static bool matches(
const Stmt *S,
const ASTContext &Ctx,
2192 if (
auto *CE = dyn_cast<CallExpr>(S)) {
2193 if (CE->getDirectCallee() &&
2194 CE->getDirectCallee()->hasAttr<UnsafeBufferUsageAttr>()) {
2195 Result.addNode(OpTag, DynTypedNode::create(*CE));
2199 if (
auto *ME = dyn_cast<MemberExpr>(S)) {
2202 if (ME->getMemberDecl()->hasAttr<UnsafeBufferUsageAttr>()) {
2203 Result.addNode(OpTag, DynTypedNode::create(*ME));
2210 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2211 bool IsRelatedToDecl,
2212 ASTContext &Ctx)
const override {
2215 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
2217 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
2219 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
2225class UnsafeBufferUsageCtorAttrGadget :
public WarningGadget {
2226 constexpr static const char *
const OpTag =
"cxx_construct_expr";
2227 const CXXConstructExpr *Op;
2231 : WarningGadget(
Kind::UnsafeBufferUsageCtorAttr),
2232 Op(
Result.getNodeAs<CXXConstructExpr>(OpTag)) {}
2234 static bool classof(
const Gadget *G) {
2235 return G->getKind() == Kind::UnsafeBufferUsageCtorAttr;
2239 const auto *CE = dyn_cast<CXXConstructExpr>(S);
2244 if (SpanTwoParamConstructorGadget::matches(CE, Ctx, Tmp))
2246 Result.addNode(OpTag, DynTypedNode::create(*CE));
2250 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2251 bool IsRelatedToDecl,
2252 ASTContext &Ctx)
const override {
2255 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
2257 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
2259 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
2266class DataInvocationGadget :
public WarningGadget {
2267 constexpr static const char *
const OpTag =
"data_invocation_expr";
2268 const ExplicitCastExpr *Op;
2272 : WarningGadget(
Kind::DataInvocation),
2273 Op(
Result.getNodeAs<ExplicitCastExpr>(OpTag)) {}
2275 static bool classof(
const Gadget *G) {
2276 return G->getKind() == Kind::DataInvocation;
2279 static bool matches(
const Stmt *S,
const ASTContext &Ctx,
2281 auto *CE = dyn_cast<ExplicitCastExpr>(S);
2284 for (
auto *Child : CE->
children()) {
2285 if (
auto *MCE = dyn_cast<CXXMemberCallExpr>(Child);
2286 MCE && isDataFunction(MCE)) {
2287 Result.addNode(OpTag, DynTypedNode::create(*CE));
2290 if (
auto *
Paren = dyn_cast<ParenExpr>(Child)) {
2291 if (
auto *MCE = dyn_cast<CXXMemberCallExpr>(
Paren->getSubExpr());
2292 MCE && isDataFunction(MCE)) {
2293 Result.addNode(OpTag, DynTypedNode::create(*CE));
2301 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2302 bool IsRelatedToDecl,
2303 ASTContext &Ctx)
const override {
2306 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
2308 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
2311 static bool isDataFunction(
const CXXMemberCallExpr *call) {
2318 if (method->getNameAsString() ==
"data" &&
2319 method->getParent()->isInStdNamespace() &&
2320 llvm::is_contained({SIZED_CONTAINER_OR_VIEW_LIST},
2321 method->getParent()->getName()))
2326 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
2329class UnsafeLibcFunctionCallGadget :
public WarningGadget {
2330 const CallExpr *
const Call;
2331 const Expr *UnsafeArg =
nullptr;
2332 constexpr static const char *
const Tag =
"UnsafeLibcFunctionCall";
2334 constexpr static const char *
const UnsafeSprintfTag =
2335 "UnsafeLibcFunctionCall_sprintf";
2336 constexpr static const char *
const UnsafeSizedByTag =
2337 "UnsafeLibcFunctionCall_sized_by";
2338 constexpr static const char *
const UnsafeStringTag =
2339 "UnsafeLibcFunctionCall_string";
2340 constexpr static const char *
const UnsafeVaListTag =
2341 "UnsafeLibcFunctionCall_va_list";
2355 } WarnedFunKind = OTHERS;
2358 : WarningGadget(
Kind::UnsafeLibcFunctionCall),
2359 Call(
Result.getNodeAs<CallExpr>(Tag)) {
2360 if (
Result.getNodeAs<Decl>(UnsafeSprintfTag))
2361 WarnedFunKind = SPRINTF;
2362 else if (
auto *E =
Result.getNodeAs<Expr>(UnsafeStringTag)) {
2363 WarnedFunKind = STRING;
2365 }
else if (
Result.getNodeAs<CallExpr>(UnsafeSizedByTag)) {
2366 WarnedFunKind = SIZED_BY;
2367 UnsafeArg = Call->getArg(0);
2368 }
else if (
Result.getNodeAs<Decl>(UnsafeVaListTag))
2369 WarnedFunKind = VA_LIST;
2372 static bool matches(
const Stmt *S, ASTContext &Ctx,
2373 const UnsafeBufferUsageHandler *Handler,
2375 const auto *CE = dyn_cast<CallExpr>(S);
2378 const auto *FD = CE->getDirectCallee();
2384 const bool IsGlobalAndNotInAnyNamespace =
2385 FD->isGlobal() && !FD->getEnclosingNamespaceContext()->isNamespace();
2389 if (!FD->isInStdNamespace() && !IsGlobalAndNotInAnyNamespace)
2396 const bool isSingleStringLiteralArg =
2399 if (!isSingleStringLiteralArg) {
2402 Result.addNode(Tag, DynTypedNode::create(*CE));
2406 Result.addNode(Tag, DynTypedNode::create(*CE));
2410 Result.addNode(Tag, DynTypedNode::create(*CE));
2411 Result.addNode(UnsafeVaListTag, DynTypedNode::create(*FD));
2415 Result.addNode(Tag, DynTypedNode::create(*CE));
2416 Result.addNode(UnsafeSprintfTag, DynTypedNode::create(*FD));
2422 Result.addNode(Tag, DynTypedNode::create(*CE));
2423 Result.addNode(UnsafeSizedByTag, DynTypedNode::create(*CE));
2428 Result.addNode(Tag, DynTypedNode::create(*CE));
2435 const Stmt *getBaseStmt()
const {
return Call; }
2437 SourceLocation getSourceLoc()
const override {
return Call->getBeginLoc(); }
2439 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2440 bool IsRelatedToDecl,
2441 ASTContext &Ctx)
const override {
2445 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
2447 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
2450class UnsafeFormatAttributedFunctionCallGadget :
public WarningGadget {
2451 const CallExpr *
const Call;
2452 const Expr *UnsafeArg =
nullptr;
2453 constexpr static const char *
const Tag =
"UnsafeFormatAttributedFunctionCall";
2454 constexpr static const char *
const UnsafeStringTag =
2455 "UnsafeFormatAttributedFunctionCall_string";
2459 : WarningGadget(
Kind::UnsafeLibcFunctionCall),
2460 Call(
Result.getNodeAs<CallExpr>(Tag)),
2461 UnsafeArg(
Result.getNodeAs<Expr>(UnsafeStringTag)) {}
2463 static bool matches(
const Stmt *S, ASTContext &Ctx,
2464 const UnsafeBufferUsageHandler *Handler,
2466 auto *CE = dyn_cast<CallExpr>(S);
2467 if (!CE || !CE->getDirectCallee())
2469 const FunctionDecl *FD = CE->getDirectCallee();
2475 const FormatAttr *Attr =
nullptr;
2476 bool IsPrintf =
false;
2477 bool AnyAttr = llvm::any_of(
2479 [&Attr, &IsPrintf](
const FormatAttr *FA) ->
bool {
2480 if (const auto *II = FA->getType()) {
2481 if (II->getName() ==
"printf" || II->getName() ==
"scanf") {
2483 IsPrintf = II->getName() ==
"printf";
2489 const Expr *UnsafeArg;
2495 unsigned FmtIdx = Attr->getFormatIdx() - 1;
2496 std::optional<unsigned> FmtArgIdx = Attr->getFirstArg() - 1;
2506 }
else if (CE->
getStmtClass() != Stmt::CallExprClass &&
2516 FmtArgIdx = std::nullopt;
2518 if (AnyAttr && !IsPrintf && FmtArgIdx) {
2520 Result.addNode(Tag, DynTypedNode::create(*CE));
2525 Ctx, CE, UnsafeArg, FmtIdx, FmtArgIdx)) {
2526 Result.addNode(Tag, DynTypedNode::create(*CE));
2527 Result.addNode(UnsafeStringTag, DynTypedNode::create(*UnsafeArg));
2533 const Stmt *getBaseStmt()
const {
return Call; }
2538 bool IsRelatedToDecl,
2543 UnsafeLibcFunctionCallGadget::UnsafeKind::STRING |
2544 UnsafeLibcFunctionCallGadget::UnsafeKind::FORMAT_ATTR,
2549 UnsafeLibcFunctionCallGadget::UnsafeKind::OTHERS |
2550 UnsafeLibcFunctionCallGadget::UnsafeKind::FORMAT_ATTR,
2554 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
2562class ULCArraySubscriptGadget :
public FixableGadget {
2564 static constexpr const char *
const ULCArraySubscriptTag =
2565 "ArraySubscriptUnderULC";
2566 const ArraySubscriptExpr *Node;
2570 : FixableGadget(
Kind::ULCArraySubscript),
2571 Node(
Result.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) {
2572 assert(Node !=
nullptr &&
"Expecting a non-null matching result");
2575 static bool classof(
const Gadget *G) {
2576 return G->getKind() == Kind::ULCArraySubscript;
2579 static bool matches(
const Stmt *S,
2580 llvm::SmallVectorImpl<MatchResult> &Results) {
2581 size_t SizeBefore = Results.size();
2583 const auto *ASE = dyn_cast<ArraySubscriptExpr>(E);
2589 !isSupportedVariable(*DRE))
2592 R.addNode(ULCArraySubscriptTag, DynTypedNode::create(*ASE));
2593 Results.emplace_back(std::move(R));
2595 return SizeBefore != Results.size();
2598 virtual std::optional<FixItList>
2599 getFixits(
const FixitStrategy &S)
const override;
2600 SourceLocation getSourceLoc()
const override {
return Node->getBeginLoc(); }
2602 virtual DeclUseList getClaimedVarUseSites()
const override {
2603 if (
const auto *DRE =
2604 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) {
2614class UPCStandalonePointerGadget :
public FixableGadget {
2616 static constexpr const char *
const DeclRefExprTag =
"StandalonePointer";
2617 const DeclRefExpr *Node;
2621 : FixableGadget(
Kind::UPCStandalonePointer),
2622 Node(
Result.getNodeAs<DeclRefExpr>(DeclRefExprTag)) {
2623 assert(Node !=
nullptr &&
"Expecting a non-null matching result");
2626 static bool classof(
const Gadget *G) {
2627 return G->getKind() == Kind::UPCStandalonePointer;
2630 static bool matches(
const Stmt *S,
2631 llvm::SmallVectorImpl<MatchResult> &Results) {
2632 size_t SizeBefore = Results.size();
2634 auto *E = dyn_cast<Expr>(S);
2639 !isSupportedVariable(*DRE))
2642 R.addNode(DeclRefExprTag, DynTypedNode::create(*DRE));
2643 Results.emplace_back(std::move(R));
2645 return SizeBefore != Results.size();
2648 virtual std::optional<FixItList>
2649 getFixits(
const FixitStrategy &S)
const override;
2650 SourceLocation getSourceLoc()
const override {
return Node->getBeginLoc(); }
2652 virtual DeclUseList getClaimedVarUseSites()
const override {
return {Node}; }
2655class PointerDereferenceGadget :
public FixableGadget {
2656 static constexpr const char *
const BaseDeclRefExprTag =
"BaseDRE";
2657 static constexpr const char *
const OperatorTag =
"op";
2659 const DeclRefExpr *BaseDeclRefExpr =
nullptr;
2660 const UnaryOperator *Op =
nullptr;
2664 : FixableGadget(
Kind::PointerDereference),
2665 BaseDeclRefExpr(
Result.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
2666 Op(
Result.getNodeAs<UnaryOperator>(OperatorTag)) {}
2668 static bool classof(
const Gadget *G) {
2669 return G->getKind() == Kind::PointerDereference;
2672 static bool matches(
const Stmt *S,
2673 llvm::SmallVectorImpl<MatchResult> &Results) {
2674 size_t SizeBefore = Results.size();
2676 const auto *UO = dyn_cast<UnaryOperator>(S);
2683 const auto *DRE = dyn_cast<DeclRefExpr>(CE);
2684 if (!DRE || !isSupportedVariable(*DRE))
2687 R.addNode(BaseDeclRefExprTag, DynTypedNode::create(*DRE));
2688 R.addNode(OperatorTag, DynTypedNode::create(*UO));
2689 Results.emplace_back(std::move(R));
2691 return SizeBefore != Results.size();
2694 DeclUseList getClaimedVarUseSites()
const override {
2695 return {BaseDeclRefExpr};
2698 virtual std::optional<FixItList>
2699 getFixits(
const FixitStrategy &S)
const override;
2700 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
2706class UPCAddressofArraySubscriptGadget :
public FixableGadget {
2708 static constexpr const char *
const UPCAddressofArraySubscriptTag =
2709 "AddressofArraySubscriptUnderUPC";
2710 const UnaryOperator *Node;
2714 : FixableGadget(
Kind::ULCArraySubscript),
2715 Node(
Result.getNodeAs<UnaryOperator>(UPCAddressofArraySubscriptTag)) {
2716 assert(Node !=
nullptr &&
"Expecting a non-null matching result");
2719 static bool classof(
const Gadget *G) {
2720 return G->getKind() == Kind::UPCAddressofArraySubscript;
2723 static bool matches(
const Stmt *S,
2724 llvm::SmallVectorImpl<MatchResult> &Results) {
2725 size_t SizeBefore = Results.size();
2727 auto *E = dyn_cast<Expr>(S);
2731 if (!UO || UO->
getOpcode() != UO_AddrOf)
2733 const auto *ASE = dyn_cast<ArraySubscriptExpr>(UO->
getSubExpr());
2738 if (!DRE || !isSupportedVariable(*DRE))
2741 R.addNode(UPCAddressofArraySubscriptTag, DynTypedNode::create(*UO));
2742 Results.emplace_back(std::move(R));
2744 return SizeBefore != Results.size();
2747 virtual std::optional<FixItList>
2748 getFixits(
const FixitStrategy &)
const override;
2749 SourceLocation getSourceLoc()
const override {
return Node->getBeginLoc(); }
2751 virtual DeclUseList getClaimedVarUseSites()
const override {
2764class DeclUseTracker {
2765 using UseSetTy = llvm::SmallPtrSet<const DeclRefExpr *, 16>;
2766 using DefMapTy = llvm::DenseMap<const VarDecl *, const DeclStmt *>;
2769 std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
2773 DeclUseTracker() =
default;
2774 DeclUseTracker(
const DeclUseTracker &) =
delete;
2775 DeclUseTracker &operator=(
const DeclUseTracker &) =
delete;
2776 DeclUseTracker(DeclUseTracker &&) =
default;
2777 DeclUseTracker &operator=(DeclUseTracker &&) =
default;
2780 void discoverUse(
const DeclRefExpr *DRE) { Uses->insert(DRE); }
2783 void claimUse(
const DeclRefExpr *DRE) {
2784 assert(Uses->count(DRE) &&
2785 "DRE not found or claimed by multiple matchers!");
2790 bool hasUnclaimedUses(
const VarDecl *VD)
const {
2792 return any_of(*Uses, [VD](
const DeclRefExpr *DRE) {
2797 UseSetTy getUnclaimedUses(
const VarDecl *VD)
const {
2799 for (
auto use : *Uses) {
2801 ReturnSet.insert(use);
2807 void discoverDecl(
const DeclStmt *DS) {
2808 for (
const Decl *D : DS->
decls()) {
2809 if (
const auto *VD = dyn_cast<VarDecl>(D)) {
2820 const DeclStmt *lookupDecl(
const VarDecl *VD)
const {
2821 return Defs.lookup(VD);
2830 static constexpr const char *
const UPCPreIncrementTag =
2831 "PointerPreIncrementUnderUPC";
2836 : FixableGadget(Kind::UPCPreIncrement),
2838 assert(Node !=
nullptr &&
"Expecting a non-null matching result");
2842 return G->getKind() == Kind::UPCPreIncrement;
2851 size_t SizeBefore = Results.size();
2853 auto *E = dyn_cast<Expr>(S);
2857 if (!UO || UO->
getOpcode() != UO_PreInc)
2859 const auto *DRE = dyn_cast<DeclRefExpr>(UO->
getSubExpr());
2860 if (!DRE || !isSupportedVariable(*DRE))
2864 Results.emplace_back(std::move(R));
2866 return SizeBefore != Results.size();
2869 virtual std::optional<FixItList>
2874 return {dyn_cast<DeclRefExpr>(Node->getSubExpr())};
2882 static constexpr const char *
const UUCAddAssignTag =
2883 "PointerAddAssignUnderUUC";
2884 static constexpr const char *
const OffsetTag =
"Offset";
2887 const Expr *Offset =
nullptr;
2891 : FixableGadget(Kind::UUCAddAssign),
2894 assert(Node !=
nullptr &&
"Expecting a non-null matching result");
2898 return G->getKind() == Kind::UUCAddAssign;
2903 size_t SizeBefore = Results.size();
2905 const auto *E = dyn_cast<Expr>(S);
2909 if (!BO || BO->
getOpcode() != BO_AddAssign)
2911 const auto *DRE = dyn_cast<DeclRefExpr>(BO->
getLHS());
2917 Results.emplace_back(std::move(R));
2919 return SizeBefore != Results.size();
2922 virtual std::optional<FixItList>
2927 return {dyn_cast<DeclRefExpr>(Node->getLHS())};
2934 static constexpr const char *
const BaseDeclRefExprTag =
"BaseDRE";
2935 static constexpr const char *
const DerefOpTag =
"DerefOp";
2936 static constexpr const char *
const AddOpTag =
"AddOp";
2937 static constexpr const char *
const OffsetTag =
"Offset";
2946 : FixableGadget(Kind::DerefSimplePtrArithFixable),
2954 auto IsPtr = [](
const Expr *E, MatchResult &R) {
2958 if (!DRE || !isSupportedVariable(*DRE))
2963 const auto IsPlusOverPtrAndInteger = [&IsPtr](
const Expr *E,
2965 const auto *BO = dyn_cast<BinaryOperator>(E);
2969 const auto *LHS = BO->
getLHS();
2970 const auto *RHS = BO->
getRHS();
2983 size_t SizeBefore = Results.size();
2984 const auto InnerMatcher = [&IsPlusOverPtrAndInteger,
2985 &Results](
const Expr *E) {
2986 const auto *UO = dyn_cast<UnaryOperator>(E);
2992 if (IsPlusOverPtrAndInteger(Operand, R)) {
2994 Results.emplace_back(std::move(R));
2998 return SizeBefore != Results.size();
3001 virtual std::optional<FixItList>
3004 return DerefOp->getBeginLoc();
3008 return {BaseDeclRefExpr};
3016 : WarningGadgets(WarningGadgets) {}
3025#define WARNING_GADGET(name) \
3026 if (name##Gadget::matches(S, Ctx, Result) && \
3027 notInSafeBufferOptOut(*S, &Handler)) { \
3028 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
3031#define WARNING_OPTIONAL_GADGET(name) \
3032 if (name##Gadget::matches(S, Ctx, &Handler, Result) && \
3033 notInSafeBufferOptOut(*S, &Handler)) { \
3034 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
3037#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
3042 WarningGadgetList &WarningGadgets;
3049 DeclUseTracker &Tracker)
3050 : FixableGadgets(FixableGadgets), Tracker(Tracker) {}
3054 bool matchFound =
false;
3061#define FIXABLE_GADGET(name) \
3062 if (name##Gadget::matches(S, Results)) { \
3063 for (const auto &R : Results) { \
3064 FixableGadgets.push_back(std::make_unique<name##Gadget>(R)); \
3065 matchFound = true; \
3069#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
3072 if (
auto *DRE = findDeclRefExpr(S); DRE) {
3073 Tracker.discoverUse(DRE);
3079 if (
auto *DS = findDeclStmt(S); DS) {
3080 Tracker.discoverDecl(DS);
3088 const auto *DRE = dyn_cast<DeclRefExpr>(S);
3096 const DeclStmt *findDeclStmt(
const Stmt *S) {
3097 const auto *DS = dyn_cast<DeclStmt>(S);
3102 FixableGadgetList &FixableGadgets;
3103 DeclUseTracker &Tracker;
3109 bool EmitSuggestions, FixableGadgetList &FixableGadgets,
3110 WarningGadgetList &WarningGadgets,
3111 DeclUseTracker &Tracker) {
3114 if (EmitSuggestions) {
3123 return N1->getBeginLoc().getRawEncoding() <
3124 N2->getBeginLoc().getRawEncoding();
3137 auto AddStmt = [&Stmts](
const Stmt *S) {
3141 if (
const auto *FD = dyn_cast<FunctionDecl>(D)) {
3144 if (PD->hasDefaultArg() && !PD->hasUninstantiatedDefaultArg())
3145 AddStmt(PD->getDefaultArg());
3146 if (
const auto *CtorD = dyn_cast<CXXConstructorDecl>(FD))
3148 Stmts, llvm::map_range(CtorD->inits(),
3152 }
else if (
const auto *VD = dyn_cast<VarDecl>(D)) {
3154 }
else if (
const auto *FD = dyn_cast<FieldDecl>(D)) {
3155 AddStmt(FD->getInClassInitializer());
3160 std::map<const VarDecl *, std::set<const WarningGadget *>,
3174 for (
auto &G : AllUnsafeOperations) {
3175 DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
3177 bool AssociatedWithVarDecl =
false;
3178 for (
const DeclRefExpr *DRE : ClaimedVarUseSites) {
3179 if (
const auto *VD = dyn_cast<VarDecl>(DRE->
getDecl())) {
3180 result.
byVar[VD].insert(G.get());
3181 AssociatedWithVarDecl =
true;
3185 if (!AssociatedWithVarDecl) {
3186 result.
noVar.push_back(G.get());
3194 std::map<const VarDecl *, std::set<const FixableGadget *>,
3204 for (
auto &F : AllFixableOperations) {
3205 DeclUseList DREs = F->getClaimedVarUseSites();
3208 if (
const auto *VD = dyn_cast<VarDecl>(DRE->
getDecl())) {
3209 FixablesForUnsafeVars.
byVar[VD].insert(F.get());
3213 return FixablesForUnsafeVars;
3220 std::vector<const FixItHint *>
All;
3224 std::sort(
All.begin(),
All.end(),
3226 return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
3227 H2->RemoveRange.getBegin());
3235 Hint->RemoveRange.getBegin())) {
3247std::optional<FixItList>
3248PtrToPtrAssignmentGadget::getFixits(
const FixitStrategy &S)
const {
3251 switch (S.
lookup(LeftVD)) {
3255 return std::nullopt;
3257 return std::nullopt;
3260 return std::nullopt;
3262 llvm_unreachable(
"unsupported strategies for FixableGadgets");
3264 return std::nullopt;
3268static inline std::optional<FixItList>
createDataFixit(
const ASTContext &Ctx,
3269 const DeclRefExpr *DRE);
3271std::optional<FixItList>
3272CArrayToPtrAssignmentGadget::getFixits(
const FixitStrategy &S)
const {
3291 if (S.
lookup(LeftVD) == FixitStrategy::Kind::Span) {
3292 if (S.
lookup(RightVD) == FixitStrategy::Kind::Wontfix) {
3295 }
else if (S.
lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {
3296 if (S.
lookup(RightVD) == FixitStrategy::Kind::Array) {
3300 return std::nullopt;
3303std::optional<FixItList>
3304PointerInitGadget::getFixits(
const FixitStrategy &S)
const {
3305 const auto *LeftVD = PtrInitLHS;
3307 switch (S.
lookup(LeftVD)) {
3308 case FixitStrategy::Kind::Span:
3309 if (S.
lookup(RightVD) == FixitStrategy::Kind::Span)
3311 return std::nullopt;
3312 case FixitStrategy::Kind::Wontfix:
3313 return std::nullopt;
3314 case FixitStrategy::Kind::Iterator:
3315 case FixitStrategy::Kind::Array:
3316 return std::nullopt;
3317 case FixitStrategy::Kind::Vector:
3318 llvm_unreachable(
"unsupported strategies for FixableGadgets");
3320 return std::nullopt;
3326 if (ConstVal->isNegative())
3333std::optional<FixItList>
3334ULCArraySubscriptGadget::getFixits(
const FixitStrategy &S)
const {
3335 if (
const auto *DRE =
3337 if (
const auto *VD = dyn_cast<VarDecl>(DRE->
getDecl())) {
3339 case FixitStrategy::Kind::Span: {
3343 const ASTContext &Ctx =
3346 return std::nullopt;
3350 case FixitStrategy::Kind::Array:
3352 case FixitStrategy::Kind::Wontfix:
3353 case FixitStrategy::Kind::Iterator:
3354 case FixitStrategy::Kind::Vector:
3355 llvm_unreachable(
"unsupported strategies for FixableGadgets");
3358 return std::nullopt;
3361static std::optional<FixItList>
3364std::optional<FixItList>
3365UPCAddressofArraySubscriptGadget::getFixits(
const FixitStrategy &S)
const {
3366 auto DREs = getClaimedVarUseSites();
3370 case FixitStrategy::Kind::Span:
3372 case FixitStrategy::Kind::Wontfix:
3373 case FixitStrategy::Kind::Iterator:
3374 case FixitStrategy::Kind::Array:
3375 return std::nullopt;
3376 case FixitStrategy::Kind::Vector:
3377 llvm_unreachable(
"unsupported strategies for FixableGadgets");
3379 return std::nullopt;
3384 static const char *
const EOL =
"\n";
3391 std::string s = std::string(
"<# ");
3392 s += HintTextToUser;
3398template <
typename NodeTy>
3399static std::optional<SourceLocation>
3402 if (
unsigned TkLen =
3409 return std::nullopt;
3422 bool AttrRangeOverlapping = llvm::any_of(VD->
attrs(), [&](
Attr *At) ->
bool {
3423 return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
3424 VD->getBeginLoc())) &&
3425 !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
3426 At->getRange().getBegin()));
3430 AttrRangeOverlapping;
3472 std::optional<Qualifiers> Quals = std::nullopt) {
3473 const char *
const SpanOpen =
"std::span<";
3476 return SpanOpen + EltTyText.str() +
' ' + Quals->getAsString() +
'>';
3477 return SpanOpen + EltTyText.str() +
'>';
3480std::optional<FixItList>
3482 const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
3487 if (
auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
3488 if (ConstVal->isNegative())
3489 return std::nullopt;
3510 const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
3517 std::optional<SourceLocation> LHSLocation =
getPastLoc(LHS, SM, LangOpts);
3519 return std::nullopt;
3524 std::optional<SourceLocation> AddOpLocation =
3526 std::optional<SourceLocation> DerefOpLocation =
3529 if (!AddOpLocation || !DerefOpLocation)
3530 return std::nullopt;
3540 return std::nullopt;
3543std::optional<FixItList>
3544PointerDereferenceGadget::getFixits(
const FixitStrategy &S)
const {
3555 if (
auto LocPastOperand =
3562 case FixitStrategy::Kind::Iterator:
3563 case FixitStrategy::Kind::Array:
3564 return std::nullopt;
3565 case FixitStrategy::Kind::Vector:
3566 llvm_unreachable(
"FixitStrategy not implemented yet!");
3567 case FixitStrategy::Kind::Wontfix:
3568 llvm_unreachable(
"Invalid strategy!");
3571 return std::nullopt;
3578 std::optional<SourceLocation> EndOfOperand =
3584 return std::nullopt;
3589std::optional<FixItList>
3590UPCStandalonePointerGadget::getFixits(
const FixitStrategy &S)
const {
3593 case FixitStrategy::Kind::Array:
3594 case FixitStrategy::Kind::Span: {
3599 case FixitStrategy::Kind::Wontfix:
3600 case FixitStrategy::Kind::Iterator:
3601 return std::nullopt;
3602 case FixitStrategy::Kind::Vector:
3603 llvm_unreachable(
"unsupported strategies for FixableGadgets");
3606 return std::nullopt;
3611static std::optional<FixItList>
3618 const Expr *Idx = ArraySub->getIdx();
3621 std::stringstream SS;
3622 bool IdxIsLitZero =
false;
3625 if ((*ICE).isZero())
3626 IdxIsLitZero =
true;
3627 std::optional<StringRef> DreString =
getExprText(DRE, SM, LangOpts);
3629 return std::nullopt;
3633 SS << (*DreString).str() <<
".data()";
3635 std::optional<StringRef> IndexString =
getExprText(Idx, SM, LangOpts);
3637 return std::nullopt;
3639 SS <<
"&" << (*DreString).str() <<
".data()"
3640 <<
"[" << (*IndexString).str() <<
"]";
3646std::optional<FixItList>
3650 if (DREs.size() != 1)
3651 return std::nullopt;
3653 if (
const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3657 const Stmt *AddAssignNode = Node;
3658 StringRef varName = VD->
getName();
3662 return std::nullopt;
3666 (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc());
3667 std::string SS = varName.str() +
" = " + varName.str() +
".subspan";
3671 std::optional<SourceLocation> AddAssignLocation =
getEndCharLoc(
3673 if (!AddAssignLocation)
3674 return std::nullopt;
3681 Offset->getEndLoc().getLocWithOffset(1),
")"));
3685 return std::nullopt;
3688std::optional<FixItList>
3692 if (DREs.size() != 1)
3693 return std::nullopt;
3695 if (
const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3698 std::stringstream SS;
3699 StringRef varName = VD->
getName();
3703 SS <<
"(" << varName.data() <<
" = " << varName.data()
3704 <<
".subspan(1)).data()";
3705 std::optional<SourceLocation> PreIncLocation =
3707 if (!PreIncLocation)
3708 return std::nullopt;
3711 SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));
3715 return std::nullopt;
3733static std::optional<FixItList>
3735 const StringRef UserFillPlaceHolder) {
3743 if (
Init->isNullPointerConstant(
3748 NPC_ValueDependentIsNotNull)) {
3749 std::optional<SourceLocation> InitLocation =
3752 return std::nullopt;
3760 std::string ExtentText = UserFillPlaceHolder.data();
3761 StringRef One =
"1";
3766 if (
auto CxxNew = dyn_cast<CXXNewExpr>(
Init->IgnoreImpCasts())) {
3771 if (
const Expr *Ext = CxxNew->getArraySize().value_or(
nullptr)) {
3772 if (!Ext->HasSideEffects(Ctx)) {
3773 std::optional<StringRef> ExtentString =
getExprText(Ext, SM, LangOpts);
3775 return std::nullopt;
3776 ExtentText = *ExtentString;
3778 }
else if (!CxxNew->isArray())
3790 if (
auto AddrOfExpr = dyn_cast<UnaryOperator>(
Init->IgnoreImpCasts()))
3791 if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
3792 isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
3799 std::optional<SourceLocation> LocPassInit =
getPastLoc(
Init, SM, LangOpts);
3802 return std::nullopt;
3804 StrBuffer.append(
", ");
3805 StrBuffer.append(ExtentText);
3806 StrBuffer.append(
"}");
3812#define DEBUG_NOTE_DECL_FAIL(D, Msg) \
3813 Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \
3814 "failed to produce fixit for declaration '" + \
3815 (D)->getNameAsString() + "'" + (Msg))
3817#define DEBUG_NOTE_DECL_FAIL(D, Msg)
3823static std::optional<std::string>
3827 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
3832 return std::nullopt;
3834 std::string SpanTyText =
"std::span<";
3836 SpanTyText.append(*PteTyText);
3838 if (PteTyQualifiers) {
3839 SpanTyText.append(
" ");
3840 SpanTyText.append(PteTyQualifiers->getAsString());
3842 SpanTyText.append(
">");
3861 const StringRef UserFillPlaceHolder,
3875 std::stringstream SS;
3880 std::optional<FixItList> InitFixIts =
3884 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
3885 std::make_move_iterator(InitFixIts->end()));
3892 if (!EndLocForReplacement.
isValid()) {
3942static std::optional<FixItList>
3948 return std::nullopt;
3953 std::vector<std::string> NewTysTexts(NumParms);
3954 std::vector<bool> ParmsMask(NumParms,
false);
3955 bool AtLeastOneParmToFix =
false;
3957 for (
unsigned i = 0; i < NumParms; i++) {
3964 return std::nullopt;
3966 std::optional<Qualifiers> PteTyQuals = std::nullopt;
3967 std::optional<std::string> PteTyText =
3972 return std::nullopt;
3976 ParmsMask[i] =
true;
3977 AtLeastOneParmToFix =
true;
3979 if (!AtLeastOneParmToFix)
3986 const auto NewOverloadSignatureCreator =
3987 [&SM, &LangOpts, &NewTysTexts,
3988 &ParmsMask](
const FunctionDecl *FD) -> std::optional<std::string> {
3989 std::stringstream SS;
3997 SS << Prefix->str();
3999 return std::nullopt;
4003 for (
unsigned i = 0; i < NumParms; i++) {
4011 SS << NewTysTexts[i];
4014 SS <<
' ' << II->getName().str();
4015 }
else if (
auto ParmTypeText =
4019 SS << ParmTypeText->str();
4021 return std::nullopt;
4022 if (i != NumParms - 1)
4031 const auto OldOverloadDefCreator =
4032 [&Handler, &SM, &LangOpts, &NewTysTexts,
4033 &ParmsMask](
const FunctionDecl *FD) -> std::optional<std::string> {
4034 std::stringstream SS;
4042 << FDPrefix->str() <<
"{";
4044 return std::nullopt;
4047 SS <<
"return " << FunQualName->str() <<
"(";
4049 return std::nullopt;
4053 for (
unsigned i = 0; i < NumParms; i++) {
4062 return std::nullopt;
4069 if (i != NumParms - 1)
4080 std::optional<SourceLocation> Loc =
getPastLoc(FReDecl, SM, LangOpts);
4084 if (FReDecl->isThisDeclarationADefinition()) {
4085 assert(FReDecl == FD &&
"inconsistent function definition");
4088 if (
auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
4094 if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
4097 FReDecl->getBeginLoc(),
" ")));
4100 if (
auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
4122 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
4138 std::stringstream SS;
4141 if (PteTyQualifiers)
4150 SS <<
' ' << PVDNameText->str();
4156 const DeclUseTracker &Tracker,
4159 const DeclStmt *DS = Tracker.lookupDecl(VD);
4162 " : variables declared this way not implemented yet");
4186 const QualType &ArrayEltT = CAT->getElementType();
4187 assert(!ArrayEltT.
isNull() &&
"Trying to fix a non-array type variable!");
4196 auto MaybeElemTypeTxt =
4199 if (!MaybeElemTypeTxt)
4201 const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
4206 while (NextTok && !NextTok->is(tok::l_square) &&
4219 if (!MaybeArraySizeTxt)
4221 const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
4222 if (ArraySizeTxt.empty()) {
4233 std::optional<StringRef> IdentText =
4242 llvm::raw_svector_ostream OS(Replacement);
4243 OS <<
"std::array<" << ElemTypeTxt <<
", " << ArraySizeTxt <<
"> "
4244 << IdentText->str();
4254 const DeclUseTracker &Tracker,
4257 const DeclStmt *DS = Tracker.lookupDecl(VD);
4258 assert(DS &&
"Fixing non-local variables not implemented yet!");
4277 const DeclUseTracker &Tracker,
ASTContext &Ctx,
4279 if (
const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
4280 auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
4281 if (!FD || FD != D) {
4298 isa_and_nonnull<CXXTryStmt>(FD->
getBody()) ||
4308 if (
const auto *PVD = dyn_cast<ParmVarDecl>(VD))
4326 llvm_unreachable(
"FixitStrategy not implemented yet!");
4328 llvm_unreachable(
"Invalid strategy!");
4330 llvm_unreachable(
"Unknown strategy!");
4338 return llvm::any_of(FixIts, [](
const FixItHint &Hint) {
4340 if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
4357 std::map<const VarDecl *, FixItList> &FixItsForVariable,
4362 for (
const auto &[VD, Ignore] : FixItsForVariable) {
4364 if (llvm::any_of(Grp,
4365 [&FixItsForVariable](
const VarDecl *GrpMember) ->
bool {
4366 return !FixItsForVariable.count(GrpMember);
4371 ToErase.push_back(
Member);
4374 for (
auto *VarToErase : ToErase)
4375 FixItsForVariable.erase(VarToErase);
4386 std::map<const VarDecl *, FixItList> &FixItsForVariable ,
4390 FixItList FixItsSharedByParms{};
4392 std::optional<FixItList> OverloadFixes =
4395 if (OverloadFixes) {
4396 FixItsSharedByParms.append(*OverloadFixes);
4402 FixItsForVariable.erase(
Member);
4404 return FixItsSharedByParms;
4408static std::map<const VarDecl *, FixItList>
4417 std::map<const VarDecl *, FixItList> FixItsForVariable;
4422 for (
const auto &[VD, Fixables] : FixablesForAllVars.
byVar) {
4423 FixItsForVariable[VD] =
4427 if (FixItsForVariable[VD].empty()) {
4428 FixItsForVariable.erase(VD);
4431 for (
const auto &F : Fixables) {
4432 std::optional<FixItList> Fixits = F->getFixits(S);
4435 FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
4436 Fixits->begin(), Fixits->end());
4441 VD, F->getSourceLoc(),
4442 (
"gadget '" + F->getDebugName() +
"' refused to produce a fix")
4445 FixItsForVariable.erase(VD);
4464 FixItList FixItsSharedByParms{};
4466 if (
auto *FD = dyn_cast<FunctionDecl>(D))
4468 FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
4472 std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
4475 for (
auto &[Var, Ignore] : FixItsForVariable) {
4476 bool AnyParm =
false;
4477 const auto VarGroupForVD = VarGrpMgr.
getGroupOfVar(Var, &AnyParm);
4479 for (
const VarDecl *GrpMate : VarGroupForVD) {
4482 if (FixItsForVariable.count(GrpMate))
4483 FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
4487 assert(!FixItsSharedByParms.empty() &&
4488 "Should not try to fix a parameter that does not belong to a "
4490 FinalFixItsForVariable[Var].append(FixItsSharedByParms);
4497 for (
auto Iter = FinalFixItsForVariable.begin();
4498 Iter != FinalFixItsForVariable.end();)
4501 Iter = FinalFixItsForVariable.erase(Iter);
4504 return FinalFixItsForVariable;
4507template <
typename VarDeclIterTy>
4511 for (
const VarDecl *VD : UnsafeVars) {
4522 const std::vector<VarGrpTy> &Groups;
4523 const std::map<const VarDecl *, unsigned> &VarGrpMap;
4524 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
4528 const std::vector<VarGrpTy> &Groups,
4529 const std::map<const VarDecl *, unsigned> &VarGrpMap,
4530 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
4531 : Groups(Groups), VarGrpMap(VarGrpMap),
4532 GrpsUnionForParms(GrpsUnionForParms) {}
4535 if (GrpsUnionForParms.contains(Var)) {
4538 return GrpsUnionForParms.getArrayRef();
4543 auto It = VarGrpMap.find(Var);
4545 if (It == VarGrpMap.end())
4547 return Groups[It->second];
4551 return GrpsUnionForParms.getArrayRef();
4556 WarningGadgetList WarningGadgets,
4557 DeclUseTracker Tracker,
4559 bool EmitSuggestions) {
4560 if (!EmitSuggestions) {
4564 for (
const auto &G : WarningGadgets) {
4565 G->handleUnsafeOperation(Handler,
false,
4571 assert(FixableGadgets.empty() &&
4572 "Fixable gadgets found but suggestions not requested!");
4578 if (!WarningGadgets.empty()) {
4582 for (
const auto &G : FixableGadgets) {
4583 for (
const auto *DRE : G->getClaimedVarUseSites()) {
4584 Tracker.claimUse(DRE);
4600 if (WarningGadgets.empty())
4608 std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
4611 for (
auto it = FixablesForAllVars.
byVar.cbegin();
4612 it != FixablesForAllVars.
byVar.cend();) {
4617 (
"failed to produce fixit for '" +
4618 it->first->getNameAsString() +
4619 "' : neither local nor a parameter"));
4621 it = FixablesForAllVars.
byVar.erase(it);
4622 }
else if (it->first->getType().getCanonicalType()->isReferenceType()) {
4625 (
"failed to produce fixit for '" +
4626 it->first->getNameAsString() +
4627 "' : has a reference type"));
4629 it = FixablesForAllVars.
byVar.erase(it);
4630 }
else if (Tracker.hasUnclaimedUses(it->first)) {
4631 it = FixablesForAllVars.
byVar.erase(it);
4632 }
else if (it->first->isInitCapture()) {
4635 (
"failed to produce fixit for '" +
4636 it->first->getNameAsString() +
4637 "' : init capture"));
4639 it = FixablesForAllVars.
byVar.erase(it);
4646 for (
const auto &it : UnsafeOps.
byVar) {
4647 const VarDecl *
const UnsafeVD = it.first;
4648 auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
4649 if (UnclaimedDREs.empty())
4653 std::string UnclaimedUseTrace =
4658 (
"failed to produce fixit for '" + UnfixedVDName +
4659 "' : has an unclaimed use\nThe unclaimed DRE trace: " +
4660 UnclaimedUseTrace));
4667 llvm::DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
4668 DepMapTy DependenciesMap{};
4669 DepMapTy PtrAssignmentGraph{};
4671 for (
const auto &it : FixablesForAllVars.
byVar) {
4672 for (
const FixableGadget *fixable : it.second) {
4673 std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
4674 fixable->getStrategyImplications();
4676 std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
4677 PtrAssignmentGraph[Impl.first].insert(Impl.second);
4699 std::set<const VarDecl *> VisitedVarsDirected{};
4700 for (
const auto &[Var, ignore] : UnsafeOps.
byVar) {
4701 if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
4703 std::queue<const VarDecl *> QueueDirected{};
4704 QueueDirected.push(Var);
4705 while (!QueueDirected.empty()) {
4706 const VarDecl *CurrentVar = QueueDirected.front();
4707 QueueDirected.pop();
4708 VisitedVarsDirected.insert(CurrentVar);
4709 auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
4710 for (
const VarDecl *Adj : AdjacentNodes) {
4711 if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
4712 QueueDirected.push(Adj);
4714 DependenciesMap[Var].insert(Adj);
4715 DependenciesMap[Adj].insert(Var);
4722 std::vector<VarGrpTy> Groups;
4726 std::map<const VarDecl *, unsigned> VarGrpMap;
4728 llvm::SetVector<const VarDecl *>
4733 std::set<const VarDecl *> VisitedVars{};
4734 for (
const auto &[Var, ignore] : UnsafeOps.
byVar) {
4735 if (VisitedVars.find(Var) == VisitedVars.end()) {
4736 VarGrpTy &VarGroup = Groups.emplace_back();
4737 std::queue<const VarDecl *> Queue{};
4740 while (!Queue.empty()) {
4741 const VarDecl *CurrentVar = Queue.front();
4743 VisitedVars.insert(CurrentVar);
4744 VarGroup.push_back(CurrentVar);
4745 auto AdjacentNodes = DependenciesMap[CurrentVar];
4746 for (
const VarDecl *Adj : AdjacentNodes) {
4747 if (VisitedVars.find(Adj) == VisitedVars.end()) {
4753 bool HasParm =
false;
4754 unsigned GrpIdx = Groups.size() - 1;
4756 for (
const VarDecl *
V : VarGroup) {
4757 VarGrpMap[
V] = GrpIdx;
4762 GrpsUnionForParms.insert_range(VarGroup);
4784 for (
auto I = FixablesForAllVars.
byVar.begin();
4785 I != FixablesForAllVars.
byVar.end();) {
4787 if (!VisitedVars.count((*I).first)) {
4789 I = FixablesForAllVars.
byVar.erase(I);
4797 VisitedVars, [&FixablesForAllVars](
const VarDecl *
V) {
4799 return FixablesForAllVars.
byVar.count(
V);
4806 FixItsForVariableGroup =
4808 Tracker, Handler, VarGrpMgr);
4810 for (
const auto &G : UnsafeOps.
noVar) {
4811 G->handleUnsafeOperation(Handler,
false,
4815 for (
const auto &[VD, WarningGadgets] : UnsafeOps.
byVar) {
4816 auto FixItsIt = FixItsForVariableGroup.find(VD);
4818 FixItsIt != FixItsForVariableGroup.end()
4819 ? std::move(FixItsIt->second)
4822 for (
const auto &G : WarningGadgets) {
4823 G->handleUnsafeOperation(Handler,
true,
4831 bool EmitSuggestions) {
4837 if (
const auto *FD = dyn_cast<FunctionDecl>(D)) {
4845 if (
const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
4846 if (MD->getParent()->isLambda() && MD->getParent()->isLocalClass())
4851 if (FReDecl->isExternC()) {
4854 EmitSuggestions =
false;
4864 assert(!Stmts.empty());
4866 FixableGadgetList FixableGadgets;
4867 WarningGadgetList WarningGadgets;
4868 DeclUseTracker Tracker;
4869 for (
const Stmt *S : Stmts) {
4871 WarningGadgets, Tracker);
4873 applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
4874 std::move(Tracker), Handler, EmitSuggestions);
4878 std::set<const Expr *> &UnsafePointers) {
4882 void handleUnsafeOperation(
const Stmt *,
bool,
ASTContext &)
override {}
4884 const Expr *UnsafeArg =
nullptr)
override {}
4885 void handleUnsafeOperationInContainer(
const Stmt *,
bool,
4887 void handleUnsafeOperationInStringView(
const Stmt *,
bool,
4889 void handleUnsafeVariableGroup(
const VarDecl *,
4893 void handleUnsafeUniquePtrArrayAccess(
const DynTypedNode &Node,
4894 bool IsRelatedToDecl,
4902 bool ignoreUnsafeBufferInLibcCall(
const SourceLocation &)
const override {
4905 bool ignoreUnsafeBufferInStaticSizedArray(
4909 std::string getUnsafeBufferUsageAttributeTextAt(
4920 WarningGadgetList WarningGadgets;
4921 bool Matched =
false;
4929#define WARNING_GADGET(name) \
4930 if (name##Gadget::matches(S, Ctx, Result)) \
4931 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));
4932#define WARNING_OPTIONAL_GADGET(name) \
4933 if (name##Gadget::matches(S, Ctx, &Handler, Result)) \
4934 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));
4935#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
4937 for (
auto &WG : WarningGadgets)
4938 for (
auto *E : WG->getUnsafePtrs()) {
4939 UnsafePointers.insert(E);
Defines the clang::ASTContext interface.
static bool ignoreUnsafeLibcCall(const ASTContext &Ctx, const Stmt &Node, const UnsafeBufferUsageHandler *Handler)
static void findStmtsInUnspecifiedLvalueContext(const Stmt *S, const llvm::function_ref< void(const Expr *)> OnResult)
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 bool ignoreUnsafeBufferInContainer(const Stmt &Node, const UnsafeBufferUsageHandler *Handler)
static WarningGadgetSets groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations)
static bool hasArrayType(const Expr &E)
static StringRef getEndOfLine()
static bool notInSafeBufferOptOut(const Stmt &Node, const UnsafeBufferUsageHandler *Handler)
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 bool areEqualIntegralBinaryOperators(const BinaryOperator *E1, const Expr *E2_LHS, BinaryOperatorKind BOP, const Expr *E2_RHS, ASTContext &Ctx)
static bool hasPointerType(const Expr &E)
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 bool isSafeArraySubscript(const ArraySubscriptExpr &Node, const ASTContext &Ctx, const bool IgnoreStaticSizedArrays)
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)
static bool isNullTermPointer(const Expr *Ptr, ASTContext &Ctx)
static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node, ASTContext &Ctx)
static bool isSafeStringViewTwoParamConstruct(const CXXConstructExpr &Node, ASTContext &Ctx)
static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
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 FixitStrategy getNaiveStrategy(llvm::iterator_range< VarDeclIterTy > UnsafeVars)
static std::optional< std::string > createSpanTypeForVarDecl(const VarDecl *VD, const ASTContext &Ctx)
static bool hasConflictingOverload(const FunctionDecl *FD)
static void findStmtsInUnspecifiedPointerContext(const Stmt *S, llvm::function_ref< void(const Stmt *)> InnerMatcher)
static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD, const ASTContext &Ctx)
static bool overlapWithMacro(const FixItList &FixIts)
static void forEachDescendantStmt(const Stmt *S, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler, FastMatcher &Matcher)
static bool hasUnsupportedSpecifiers(const VarDecl *VD, const SourceManager &SM)
static const Expr * tryConstantFoldConditionalExpr(const Expr *E, const ASTContext &Ctx)
#define DEBUG_NOTE_DECL_FAIL(D, Msg)
static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets, WarningGadgetList WarningGadgets, DeclUseTracker Tracker, UnsafeBufferUsageHandler &Handler, bool EmitSuggestions)
static bool isSafePointerArithmetic(const Expr *Ptr, const Expr *OffsetExpr, BinaryOperatorKind Opcode, const ASTContext &Ctx)
static bool areEqualIntegers(const Expr *E1, const Expr *E2, ASTContext &Ctx)
static void findGadgets(const Stmt *S, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler, bool EmitSuggestions, FixableGadgetList &FixableGadgets, WarningGadgetList &WarningGadgets, DeclUseTracker &Tracker)
static const Expr * getSubExprInSizeOfExpr(const Expr &E)
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 bool isPtrBufferSafe(const Expr *Ptr, const Expr *Size, ASTContext &Ctx)
static const Expr * getSubExprInAddressOfExpr(const Expr &E)
static void forEachDescendantEvaluatedStmt(const Stmt *S, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler, FastMatcher &Matcher)
static void findStmtsInUnspecifiedUntypedContext(const Stmt *S, llvm::function_ref< void(const Stmt *)> InnerMatcher)
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)
#define SIZED_CONTAINER_OR_VIEW_LIST
static void populateStmtsForFindingGadgets(SmallVector< const Stmt * > &Stmts, const Decl *D)
static std::optional< StringRef > getFunNameText(const FunctionDecl *FD, const SourceManager &SM, const LangOptions &LangOpts)
static Decl::Kind getKind(const Decl *D)
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the C++ template declaration subclasses.
Defines the clang::Preprocessor interface.
MatchFinder::MatchResult MatchResult
Defines the clang::SourceLocation class and associated facilities.
static QualType getPointeeType(const MemRegion *R)
C Language Family Type Representation.
virtual std::optional< FixItList > getFixits(const FixitStrategy &s) const final
static bool matches(const Stmt *S, llvm::SmallVectorImpl< MatchResult > &Results)
DerefSimplePtrArithFixableGadget(const MatchResult &Result)
SourceLocation getSourceLoc() const override
virtual DeclUseList getClaimedVarUseSites() const final
FixableGadgetMatcher(FixableGadgetList &FixableGadgets, DeclUseTracker &Tracker)
bool matches(const DynTypedNode &DynNode, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler) override
Represents the length modifier in a format string in scanf/printf.
bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) override
bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node, bool TraverseQualifier) override
bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node, bool TraverseQualifier) override
bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) override
MatchDescendantVisitor(ASTContext &Context, FastMatcher &Matcher, bool FindAll, bool IgnoreUnevaluatedContext, const UnsafeBufferUsageHandler &NewHandler)
bool TraverseDecl(Decl *Node) override
bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) override
bool findMatch(const DynTypedNode &DynNode)
bool TraverseCXXDefaultInitExpr(CXXDefaultInitExpr *Node) override
bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) override
bool TraverseStmt(Stmt *Node) override
virtual std::optional< FixItList > getFixits(const FixitStrategy &S) const override
static bool matches(const Stmt *S, llvm::SmallVectorImpl< MatchResult > &Results)
SourceLocation getSourceLoc() const override
virtual DeclUseList getClaimedVarUseSites() const override
UPCPreIncrementGadget(const MatchResult &Result)
static bool classof(const Gadget *G)
static bool classof(const Gadget *G)
UUCAddAssignGadget(const MatchResult &Result)
virtual std::optional< FixItList > getFixits(const FixitStrategy &S) const override
static bool matches(const Stmt *S, llvm::SmallVectorImpl< MatchResult > &Results)
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,...
bool matches(const DynTypedNode &DynNode, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler) override
WarningGadgetMatcher(WarningGadgetList &WarningGadgets)
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
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
QualType getSizeType() const
Return the unique type for "size_t" (C99 7.17), defined in <stddef.h>.
const TargetInfo & getTargetInfo() const
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
Attr - This represents one attribute.
A builtin binary operation expression such as "x + y" or "x <= y".
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.
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
unsigned getNumArgs() const
Return the number of arguments to the constructor call.
Expr * getInit() const
Get the initializer.
A use of a default initializer in a constructor or in aggregate initialization.
Expr * getExpr()
Get the initialization expression that will be used.
Represents a static or instance method of a struct/union/class.
const CXXRecordDecl * getParent() const
Return the parent of this method declaration, which is the class in which this method is defined.
Represents a C++11 noexcept expression (C++ [expr.unary.noexcept]).
OverloadedOperatorKind getOperator() const
Returns the kind of overloaded operator that this expression refers to.
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]).
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
static const char * getCastKindName(CastKind CK)
Represents a byte-granular source range.
static CharSourceRange getCharRange(SourceRange R)
SourceLocation getEnd() const
bool isOne() const
Test whether the quantity equals one.
Represents a class template specialization, which refers to a class template with a given set of temp...
const TemplateArgumentList & getTemplateArgs() const
Retrieve the template arguments of the class template specialization.
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
bool isSingleResult() const
DeclContext * getParent()
getParent - Returns the containing DeclContext.
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.
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.
const Decl * getSingleDecl() const
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...
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
DeclContext * getDeclContext()
SourceLocation getBeginLoc() const LLVM_READONLY
virtual Decl * getCanonicalDecl()
Retrieves the "canonical" declaration of the given declaration.
SourceLocation getTypeSpecEndLoc() const
SourceLocation getBeginLoc() const LLVM_READONLY
NestedNameSpecifierLoc getQualifierLoc() const
Retrieve the nested-name-specifier (with source-location information) that qualifies the name of this...
NestedNameSpecifier getQualifier() const
Retrieve the nested-name-specifier that qualifies the name of this declaration, if it was present in ...
Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.
const DynTypedNode * begin() const
A dynamically typed AST node container.
const T * get() const
Retrieve the stored node as type T.
static DynTypedNode create(const T &Node)
Creates a DynTypedNode from Node.
virtual bool TraverseDecl(MaybeConst< Decl > *D)
bool ShouldVisitTemplateInstantiations
bool ShouldVisitImplicitCode
virtual bool TraverseStmt(MaybeConst< Stmt > *S)
This represents one expression.
bool EvaluateAsInt(EvalResult &Result, const ASTContext &Ctx, SideEffectsKind AllowSideEffects=SE_NoSideEffects, bool InConstantContext=false) const
EvaluateAsInt - Return true if this is a constant which we can fold and convert to an integer,...
bool isValueDependent() const
Determines whether the value of this expression depends on.
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Expr * 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, bool AllowRelaxedEval=false) const
isIntegerConstantExpr - Return the value if this expression is a valid integer constant expression.
Expr * IgnoreImpCasts() LLVM_READONLY
Skip past any implicit casts which might surround this expression until reaching a fixed point.
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.
Kind lookup(const VarDecl *VD) const
void set(const VarDecl *VD, Kind K)
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.
unsigned getBuiltinID(bool ConsiderWrapperFunctions=false) const
Returns a value indicating whether this function corresponds to a builtin function.
ArrayRef< ParmVarDecl * > parameters() const
param_iterator param_begin()
bool isVariadic() const
Whether this function is variadic.
TemplatedKind getTemplatedKind() const
What kind of templated function this is.
bool isConstexpr() const
Whether this is a (C++11) constexpr function or constexpr constructor.
redecl_range redecls() const
Returns an iterator range for all the redeclarations of the same decl.
bool isMain() const
Determines whether this function is "main", which is the entry point into an executable program.
bool isOverloadedOperator() const
Whether this function declaration represents an C++ overloaded operator, e.g., "operator+".
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
DeclarationNameInfo getNameInfo() const
Represents a C11 generic selection.
Expr * getResultExpr()
Return the result expression of this controlling expression.
One of these records is kept for each identifier that is lexed.
StringRef getName() const
Return the actual identifier string.
A simple pair of identifier info and location.
static IntegerLiteral * Create(const ASTContext &C, const llvm::APInt &V, QualType type, SourceLocation l)
Returns a new integer literal with value 'V' and type 'type'.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
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.
A single parameter index whose accessors require each use to make explicit the parameter index encodi...
bool isValid() const
Is this parameter index valid?
unsigned getASTIndex() const
Get the parameter index as it would normally be encoded at the AST level of representation: zero-orig...
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.
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
Represents a struct/union/class.
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.
bool isBeforeInTranslationUnit(SourceLocation LHS, SourceLocation RHS) const
Determines the order of 2 source locations in the translation unit.
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
Stmt - This represents one statement.
StmtClass getStmtClass() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
const char * getStmtClassName() const
SourceLocation getBeginLoc() const LLVM_READONLY
Exposes information about the current target.
A template argument list.
unsigned size() const
Retrieve the number of template arguments in this template argument list.
Represents a template argument.
QualType getAsType() const
Retrieve the type for a type template argument.
@ Type
The template argument is a type.
ArgKind getKind() const
Return the kind of stored template argument.
The base class of the type hierarchy.
bool isConstantSizeType() const
Return true if this is not a variable sized type, according to the rules of C99 6....
bool isPointerType() const
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
const T * castAs() const
Member-template castAs<specific type>.
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
bool isAnyCharacterType() const
Determine whether this type is any of the built-in character types.
bool isUnsignedIntegerType() const
Return true if this is an integer type that is unsigned, according to C99 6.2.5p6 [which returns true...
bool isAnyPointerType() const
const Type * getUnqualifiedDesugaredType() const
Return the specified type with any "sugar" removed from the type, removing any typedefs,...
UnaryExprOrTypeTraitExpr - expression with either a type or (unevaluated) expression operand.
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Expr * getSubExpr() const
static bool isIncrementOp(Opcode Op)
SourceLocation getBeginLoc() const LLVM_READONLY
static bool isDecrementOp(Opcode Op)
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...
virtual void handleUnsafeUniquePtrArrayAccess(const DynTypedNode &Node, bool IsRelatedToDecl, ASTContext &Ctx)=0
void addDebugNoteForVar(const VarDecl *VD, SourceLocation Loc, std::string Text)
virtual std::string getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc, StringRef WSSuffix="") const =0
virtual bool isSafeBufferOptOut(const SourceLocation &Loc) const =0
virtual bool ignoreUnsafeBufferInContainer(const SourceLocation &Loc) 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 handleUnsafeOperationInStringView(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx)=0
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 bool ignoreUnsafeBufferInStaticSizedArray(const SourceLocation &Loc) const =0
virtual bool ignoreUnsafeBufferInLibcCall(const SourceLocation &Loc) const =0
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.
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
VarDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
bool isInlineSpecified() const
bool hasConstantInitialization() const
Determine whether this variable has constant initialization.
const Expr * getInit() const
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.
const Expr * getAnyInitializer() const
Get the initializer for this variable, no matter which declaration it is attached to.
VariableGroupsManager()=default
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,...
unsigned getArgIndex() const
bool hasDataArgument() const
HowSpecified getHowSpecified() const
unsigned getConstantAmount() const
const OptionalAmount & getPrecision() const
const PrintfConversionSpecifier & getConversionSpecifier() const
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
void matchEachArgumentWithParamType(const CallExpr &Node, llvm::function_ref< void(QualType, const Expr *)> OnParamAndArg)
bool anyConflict(const llvm::SmallVectorImpl< FixItHint > &FixIts, const SourceManager &SM)
bool matches(const til::SExpr *E1, const til::SExpr *E2)
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
bool matchUnsafePointers(const DynTypedNode &N, ASTContext &Ctx, std::set< const Expr * > &UnsafePointers)
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
void checkUnsafeBufferUsage(const Decl *D, UnsafeBufferUsageHandler &Handler, bool EmitSuggestions)
SourceLocation getVarDeclIdentifierLoc(const DeclaratorDecl *VD)
static bool classof(const OMPClause *T)
std::vector< const VarDecl * > VarGrpTy
std::optional< StringRef > getExprText(const Expr *E, const SourceManager &SM, const LangOptions &LangOpts)
@ Result
The result type of a method or function.
const FunctionProtoType * T
std::optional< std::string > getPointeeTypeText(const DeclaratorDecl *VD, const SourceManager &SM, const LangOptions &LangOpts, std::optional< Qualifiers > *QualifiersToAppend)
std::optional< StringRef > getRangeText(SourceRange SR, const SourceManager &SM, const LangOptions &LangOpts)
std::optional< StringRef > getVarDeclIdentifierText(const DeclaratorDecl *VD, const SourceManager &SM, const LangOptions &LangOpts)
std::optional< SourceLocation > getPastLoc(const NodeTy *Node, const SourceManager &SM, const LangOptions &LangOpts)
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
U cast(CodeGen::Address addr)
ArrayRef< const VarDecl * > VarGrpRef
static StringRef matchLibcNameOrBuiltinChk(StringRef Name)
static bool hasUnsafePrintfStringArg(const CallExpr &Node, ASTContext &Ctx, MatchResult &Result, llvm::StringRef Tag)
static bool isPredefinedUnsafeLibcFunc(const FunctionDecl &Node)
static bool hasUnsafeSnprintfBuffer(const CallExpr &Node, ASTContext &Ctx)
static bool isUnsafeVaListPrintfFunc(const FunctionDecl &Node)
static bool isUnsafeSprintfFunc(const FunctionDecl &Node)
static bool hasUnsafeFormatOrSArg(ASTContext &Ctx, const CallExpr *Call, const Expr *&UnsafeArg, const unsigned FmtIdx, std::optional< const unsigned > FmtArgIdx=std::nullopt, bool isKprintf=false)
static StringRef matchLibcName(StringRef Name)
static bool isUnsafeMemset(const CallExpr &Node, ASTContext &Ctx)
static StringRef matchName(StringRef FunName, bool isBuiltin)
static bool isNormalPrintfFunc(const FunctionDecl &Node)
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.
const BoundNodes Nodes
Contains the nodes bound on the current match.