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;
1880 const auto *CE = dyn_cast<CXXConstructExpr>(S);
1883 const auto *CDecl = CE->getConstructor();
1884 const auto *CRecordDecl = CDecl->getParent();
1885 auto HasTwoParamSpanCtorDecl =
1886 CRecordDecl->isInStdNamespace() &&
1887 CDecl->getDeclName().getAsString() ==
"span" && CE->getNumArgs() == 2;
1890 Result.addNode(SpanTwoParamConstructorTag, DynTypedNode::create(*CE));
1894 static bool matches(
const Stmt *S, ASTContext &Ctx,
1895 const UnsafeBufferUsageHandler *Handler,
1902 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1903 bool IsRelatedToDecl,
1904 ASTContext &Ctx)
const override {
1907 SourceLocation getSourceLoc()
const override {
return Ctor->getBeginLoc(); }
1909 DeclUseList getClaimedVarUseSites()
const override {
1912 if (
auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
1919 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
1922class StringViewTwoParamConstructorGadget :
public WarningGadget {
1923 static constexpr const char *
const StringViewTwoParamConstructorTag =
1924 "stringViewTwoParamConstructor";
1925 const CXXConstructExpr *Ctor;
1929 : WarningGadget(
Kind::StringViewTwoParamConstructor),
1930 Ctor(
Result.getNodeAs<CXXConstructExpr>(
1931 StringViewTwoParamConstructorTag)) {}
1933 static bool classof(
const Gadget *G) {
1934 return G->getKind() == Kind::StringViewTwoParamConstructor;
1938 const auto *CE = dyn_cast<CXXConstructExpr>(S);
1941 const auto *CDecl = CE->getConstructor();
1942 const auto *CRecordDecl = CDecl->getParent();
1946 CRecordDecl->isInStdNamespace() &&
1947 CDecl->getDeclName().getAsString() ==
"basic_string_view" &&
1948 CE->getNumArgs() == 2;
1953 Result.addNode(StringViewTwoParamConstructorTag, DynTypedNode::create(*CE));
1957 static bool matches(
const Stmt *S, ASTContext &Ctx,
1958 const UnsafeBufferUsageHandler *Handler,
1965 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1966 bool IsRelatedToDecl,
1967 ASTContext &Ctx)
const override {
1971 SourceLocation getSourceLoc()
const override {
return Ctor->getBeginLoc(); }
1973 DeclUseList getClaimedVarUseSites()
const override {
1976 if (
auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
1983 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
1990class PointerInitGadget :
public FixableGadget {
1992 static constexpr const char *
const PointerInitLHSTag =
"ptrInitLHS";
1993 static constexpr const char *
const PointerInitRHSTag =
"ptrInitRHS";
1994 const VarDecl *PtrInitLHS;
1995 const DeclRefExpr *PtrInitRHS;
1999 : FixableGadget(
Kind::PointerInit),
2000 PtrInitLHS(
Result.getNodeAs<VarDecl>(PointerInitLHSTag)),
2001 PtrInitRHS(
Result.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}
2003 static bool classof(
const Gadget *G) {
2004 return G->getKind() == Kind::PointerInit;
2007 static bool matches(
const Stmt *S,
2008 llvm::SmallVectorImpl<MatchResult> &Results) {
2009 const DeclStmt *DS = dyn_cast<DeclStmt>(S);
2018 const auto *DRE = dyn_cast<DeclRefExpr>(
Init->IgnoreImpCasts());
2019 if (!DRE || !
hasPointerType(*DRE) || !isSupportedVariable(*DRE)) {
2023 R.addNode(PointerInitLHSTag, DynTypedNode::create(*VD));
2024 R.addNode(PointerInitRHSTag, DynTypedNode::create(*DRE));
2025 Results.emplace_back(std::move(R));
2029 virtual std::optional<FixItList>
2030 getFixits(
const FixitStrategy &S)
const override;
2031 SourceLocation getSourceLoc()
const override {
2032 return PtrInitRHS->getBeginLoc();
2035 virtual DeclUseList getClaimedVarUseSites()
const override {
2036 return DeclUseList{PtrInitRHS};
2039 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
2040 getStrategyImplications()
const override {
2041 return std::make_pair(PtrInitLHS,
cast<VarDecl>(PtrInitRHS->getDecl()));
2050class PtrToPtrAssignmentGadget :
public FixableGadget {
2052 static constexpr const char *
const PointerAssignLHSTag =
"ptrLHS";
2053 static constexpr const char *
const PointerAssignRHSTag =
"ptrRHS";
2054 const DeclRefExpr *PtrLHS;
2055 const DeclRefExpr *PtrRHS;
2059 : FixableGadget(
Kind::PtrToPtrAssignment),
2060 PtrLHS(
Result.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
2061 PtrRHS(
Result.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
2063 static bool classof(
const Gadget *G) {
2064 return G->getKind() == Kind::PtrToPtrAssignment;
2067 static bool matches(
const Stmt *S,
2068 llvm::SmallVectorImpl<MatchResult> &Results) {
2069 size_t SizeBefore = Results.size();
2071 const auto *BO = dyn_cast<BinaryOperator>(S);
2072 if (!BO || BO->
getOpcode() != BO_Assign)
2075 if (
const auto *RHSRef = dyn_cast<DeclRefExpr>(RHS);
2077 !isSupportedVariable(*RHSRef)) {
2080 const auto *LHS = BO->
getLHS();
2081 if (
const auto *LHSRef = dyn_cast<DeclRefExpr>(LHS);
2083 !isSupportedVariable(*LHSRef)) {
2087 R.addNode(PointerAssignLHSTag, DynTypedNode::create(*LHS));
2088 R.addNode(PointerAssignRHSTag, DynTypedNode::create(*RHS));
2089 Results.emplace_back(std::move(R));
2091 return SizeBefore != Results.size();
2094 virtual std::optional<FixItList>
2095 getFixits(
const FixitStrategy &S)
const override;
2096 SourceLocation getSourceLoc()
const override {
return PtrLHS->getBeginLoc(); }
2098 virtual DeclUseList getClaimedVarUseSites()
const override {
2099 return DeclUseList{PtrLHS, PtrRHS};
2102 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
2103 getStrategyImplications()
const override {
2114class CArrayToPtrAssignmentGadget :
public FixableGadget {
2116 static constexpr const char *
const PointerAssignLHSTag =
"ptrLHS";
2117 static constexpr const char *
const PointerAssignRHSTag =
"ptrRHS";
2118 const DeclRefExpr *PtrLHS;
2119 const DeclRefExpr *PtrRHS;
2123 : FixableGadget(
Kind::CArrayToPtrAssignment),
2124 PtrLHS(
Result.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
2125 PtrRHS(
Result.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
2127 static bool classof(
const Gadget *G) {
2128 return G->getKind() == Kind::CArrayToPtrAssignment;
2131 static bool matches(
const Stmt *S,
2132 llvm::SmallVectorImpl<MatchResult> &Results) {
2133 size_t SizeBefore = Results.size();
2135 const auto *BO = dyn_cast<BinaryOperator>(S);
2136 if (!BO || BO->
getOpcode() != BO_Assign)
2139 if (
const auto *RHSRef = dyn_cast<DeclRefExpr>(RHS);
2142 !isSupportedVariable(*RHSRef)) {
2145 const auto *LHS = BO->
getLHS();
2146 if (
const auto *LHSRef = dyn_cast<DeclRefExpr>(LHS);
2148 !isSupportedVariable(*LHSRef)) {
2152 R.addNode(PointerAssignLHSTag, DynTypedNode::create(*LHS));
2153 R.addNode(PointerAssignRHSTag, DynTypedNode::create(*RHS));
2154 Results.emplace_back(std::move(R));
2156 return SizeBefore != Results.size();
2159 virtual std::optional<FixItList>
2160 getFixits(
const FixitStrategy &S)
const override;
2161 SourceLocation getSourceLoc()
const override {
return PtrLHS->getBeginLoc(); }
2163 virtual DeclUseList getClaimedVarUseSites()
const override {
2164 return DeclUseList{PtrLHS, PtrRHS};
2167 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
2168 getStrategyImplications()
const override {
2175class UnsafeBufferUsageAttrGadget :
public WarningGadget {
2176 constexpr static const char *
const OpTag =
"attr_expr";
2181 : WarningGadget(
Kind::UnsafeBufferUsageAttr),
2182 Op(
Result.getNodeAs<Expr>(OpTag)) {}
2184 static bool classof(
const Gadget *G) {
2185 return G->getKind() == Kind::UnsafeBufferUsageAttr;
2188 static bool matches(
const Stmt *S,
const ASTContext &Ctx,
2190 if (
auto *CE = dyn_cast<CallExpr>(S)) {
2191 if (CE->getDirectCallee() &&
2192 CE->getDirectCallee()->hasAttr<UnsafeBufferUsageAttr>()) {
2193 Result.addNode(OpTag, DynTypedNode::create(*CE));
2197 if (
auto *ME = dyn_cast<MemberExpr>(S)) {
2200 if (ME->getMemberDecl()->hasAttr<UnsafeBufferUsageAttr>()) {
2201 Result.addNode(OpTag, DynTypedNode::create(*ME));
2208 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2209 bool IsRelatedToDecl,
2210 ASTContext &Ctx)
const override {
2213 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
2215 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
2217 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
2223class UnsafeBufferUsageCtorAttrGadget :
public WarningGadget {
2224 constexpr static const char *
const OpTag =
"cxx_construct_expr";
2225 const CXXConstructExpr *Op;
2229 : WarningGadget(
Kind::UnsafeBufferUsageCtorAttr),
2230 Op(
Result.getNodeAs<CXXConstructExpr>(OpTag)) {}
2232 static bool classof(
const Gadget *G) {
2233 return G->getKind() == Kind::UnsafeBufferUsageCtorAttr;
2237 const auto *CE = dyn_cast<CXXConstructExpr>(S);
2238 if (!CE || !CE->getConstructor()->hasAttr<UnsafeBufferUsageAttr>())
2242 if (SpanTwoParamConstructorGadget::matches(CE, Ctx, Tmp))
2244 Result.addNode(OpTag, DynTypedNode::create(*CE));
2248 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2249 bool IsRelatedToDecl,
2250 ASTContext &Ctx)
const override {
2253 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
2255 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
2257 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
2264class DataInvocationGadget :
public WarningGadget {
2265 constexpr static const char *
const OpTag =
"data_invocation_expr";
2266 const ExplicitCastExpr *Op;
2270 : WarningGadget(
Kind::DataInvocation),
2271 Op(
Result.getNodeAs<ExplicitCastExpr>(OpTag)) {}
2273 static bool classof(
const Gadget *G) {
2274 return G->getKind() == Kind::DataInvocation;
2277 static bool matches(
const Stmt *S,
const ASTContext &Ctx,
2279 auto *CE = dyn_cast<ExplicitCastExpr>(S);
2282 for (
auto *Child : CE->children()) {
2283 if (
auto *MCE = dyn_cast<CXXMemberCallExpr>(Child);
2284 MCE && isDataFunction(MCE)) {
2285 Result.addNode(OpTag, DynTypedNode::create(*CE));
2288 if (
auto *
Paren = dyn_cast<ParenExpr>(Child)) {
2289 if (
auto *MCE = dyn_cast<CXXMemberCallExpr>(
Paren->getSubExpr());
2290 MCE && isDataFunction(MCE)) {
2291 Result.addNode(OpTag, DynTypedNode::create(*CE));
2299 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2300 bool IsRelatedToDecl,
2301 ASTContext &Ctx)
const override {
2304 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
2306 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
2309 static bool isDataFunction(
const CXXMemberCallExpr *call) {
2316 if (method->getNameAsString() ==
"data" &&
2317 method->getParent()->isInStdNamespace() &&
2318 llvm::is_contained({SIZED_CONTAINER_OR_VIEW_LIST},
2319 method->getParent()->getName()))
2324 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
2327class UnsafeLibcFunctionCallGadget :
public WarningGadget {
2328 const CallExpr *
const Call;
2329 const Expr *UnsafeArg =
nullptr;
2330 constexpr static const char *
const Tag =
"UnsafeLibcFunctionCall";
2332 constexpr static const char *
const UnsafeSprintfTag =
2333 "UnsafeLibcFunctionCall_sprintf";
2334 constexpr static const char *
const UnsafeSizedByTag =
2335 "UnsafeLibcFunctionCall_sized_by";
2336 constexpr static const char *
const UnsafeStringTag =
2337 "UnsafeLibcFunctionCall_string";
2338 constexpr static const char *
const UnsafeVaListTag =
2339 "UnsafeLibcFunctionCall_va_list";
2353 } WarnedFunKind = OTHERS;
2356 : WarningGadget(
Kind::UnsafeLibcFunctionCall),
2357 Call(
Result.getNodeAs<CallExpr>(Tag)) {
2358 if (
Result.getNodeAs<Decl>(UnsafeSprintfTag))
2359 WarnedFunKind = SPRINTF;
2360 else if (
auto *E =
Result.getNodeAs<Expr>(UnsafeStringTag)) {
2361 WarnedFunKind = STRING;
2363 }
else if (
Result.getNodeAs<CallExpr>(UnsafeSizedByTag)) {
2364 WarnedFunKind = SIZED_BY;
2365 UnsafeArg = Call->getArg(0);
2366 }
else if (
Result.getNodeAs<Decl>(UnsafeVaListTag))
2367 WarnedFunKind = VA_LIST;
2370 static bool matches(
const Stmt *S, ASTContext &Ctx,
2371 const UnsafeBufferUsageHandler *Handler,
2375 const auto *CE = dyn_cast<CallExpr>(S);
2378 const auto *FD = CE->getDirectCallee();
2382 const bool IsGlobalAndNotInAnyNamespace =
2383 FD->isGlobal() && !FD->getEnclosingNamespaceContext()->isNamespace();
2387 if (!FD->isInStdNamespace() && !IsGlobalAndNotInAnyNamespace)
2394 const bool isSingleStringLiteralArg =
2395 CE->getNumArgs() == 1 &&
2397 if (!isSingleStringLiteralArg) {
2400 Result.addNode(Tag, DynTypedNode::create(*CE));
2404 Result.addNode(Tag, DynTypedNode::create(*CE));
2408 Result.addNode(Tag, DynTypedNode::create(*CE));
2409 Result.addNode(UnsafeVaListTag, DynTypedNode::create(*FD));
2413 Result.addNode(Tag, DynTypedNode::create(*CE));
2414 Result.addNode(UnsafeSprintfTag, DynTypedNode::create(*FD));
2420 Result.addNode(Tag, DynTypedNode::create(*CE));
2421 Result.addNode(UnsafeSizedByTag, DynTypedNode::create(*CE));
2426 Result.addNode(Tag, DynTypedNode::create(*CE));
2433 const Stmt *getBaseStmt()
const {
return Call; }
2435 SourceLocation getSourceLoc()
const override {
return Call->getBeginLoc(); }
2437 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2438 bool IsRelatedToDecl,
2439 ASTContext &Ctx)
const override {
2443 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
2445 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
2448class UnsafeFormatAttributedFunctionCallGadget :
public WarningGadget {
2449 const CallExpr *
const Call;
2450 const Expr *UnsafeArg =
nullptr;
2451 constexpr static const char *
const Tag =
"UnsafeFormatAttributedFunctionCall";
2452 constexpr static const char *
const UnsafeStringTag =
2453 "UnsafeFormatAttributedFunctionCall_string";
2457 : WarningGadget(
Kind::UnsafeLibcFunctionCall),
2458 Call(
Result.getNodeAs<CallExpr>(Tag)),
2459 UnsafeArg(
Result.getNodeAs<Expr>(UnsafeStringTag)) {}
2461 static bool matches(
const Stmt *S, ASTContext &Ctx,
2462 const UnsafeBufferUsageHandler *Handler,
2466 auto *CE = dyn_cast<CallExpr>(S);
2467 if (!CE || !CE->getDirectCallee())
2469 const FunctionDecl *FD = CE->getDirectCallee();
2473 const FormatAttr *Attr =
nullptr;
2474 bool IsPrintf =
false;
2475 bool AnyAttr = llvm::any_of(
2477 [&Attr, &IsPrintf](
const FormatAttr *FA) ->
bool {
2478 if (const auto *II = FA->getType()) {
2479 if (II->getName() ==
"printf" || II->getName() ==
"scanf") {
2481 IsPrintf = II->getName() ==
"printf";
2487 const Expr *UnsafeArg;
2493 unsigned FmtIdx = Attr->getFormatIdx() - 1;
2494 std::optional<unsigned> FmtArgIdx = Attr->getFirstArg() - 1;
2504 }
else if (CE->getStmtClass() != Stmt::CallExprClass &&
2507 if (*FmtArgIdx >= CE->getNumArgs())
2514 FmtArgIdx = std::nullopt;
2516 if (AnyAttr && !IsPrintf && FmtArgIdx) {
2518 Result.addNode(Tag, DynTypedNode::create(*CE));
2523 Ctx, CE, UnsafeArg, FmtIdx, FmtArgIdx)) {
2524 Result.addNode(Tag, DynTypedNode::create(*CE));
2525 Result.addNode(UnsafeStringTag, DynTypedNode::create(*UnsafeArg));
2531 const Stmt *getBaseStmt()
const {
return Call; }
2536 bool IsRelatedToDecl,
2541 UnsafeLibcFunctionCallGadget::UnsafeKind::STRING |
2542 UnsafeLibcFunctionCallGadget::UnsafeKind::FORMAT_ATTR,
2547 UnsafeLibcFunctionCallGadget::UnsafeKind::OTHERS |
2548 UnsafeLibcFunctionCallGadget::UnsafeKind::FORMAT_ATTR,
2552 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
2560class ULCArraySubscriptGadget :
public FixableGadget {
2562 static constexpr const char *
const ULCArraySubscriptTag =
2563 "ArraySubscriptUnderULC";
2564 const ArraySubscriptExpr *Node;
2568 : FixableGadget(
Kind::ULCArraySubscript),
2569 Node(
Result.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) {
2570 assert(Node !=
nullptr &&
"Expecting a non-null matching result");
2573 static bool classof(
const Gadget *G) {
2574 return G->getKind() == Kind::ULCArraySubscript;
2577 static bool matches(
const Stmt *S,
2578 llvm::SmallVectorImpl<MatchResult> &Results) {
2579 size_t SizeBefore = Results.size();
2581 const auto *ASE = dyn_cast<ArraySubscriptExpr>(E);
2587 !isSupportedVariable(*DRE))
2590 R.addNode(ULCArraySubscriptTag, DynTypedNode::create(*ASE));
2591 Results.emplace_back(std::move(R));
2593 return SizeBefore != Results.size();
2596 virtual std::optional<FixItList>
2597 getFixits(
const FixitStrategy &S)
const override;
2598 SourceLocation getSourceLoc()
const override {
return Node->getBeginLoc(); }
2600 virtual DeclUseList getClaimedVarUseSites()
const override {
2601 if (
const auto *DRE =
2602 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) {
2612class UPCStandalonePointerGadget :
public FixableGadget {
2614 static constexpr const char *
const DeclRefExprTag =
"StandalonePointer";
2615 const DeclRefExpr *Node;
2619 : FixableGadget(
Kind::UPCStandalonePointer),
2620 Node(
Result.getNodeAs<DeclRefExpr>(DeclRefExprTag)) {
2621 assert(Node !=
nullptr &&
"Expecting a non-null matching result");
2624 static bool classof(
const Gadget *G) {
2625 return G->getKind() == Kind::UPCStandalonePointer;
2628 static bool matches(
const Stmt *S,
2629 llvm::SmallVectorImpl<MatchResult> &Results) {
2630 size_t SizeBefore = Results.size();
2632 auto *E = dyn_cast<Expr>(S);
2637 !isSupportedVariable(*DRE))
2640 R.addNode(DeclRefExprTag, DynTypedNode::create(*DRE));
2641 Results.emplace_back(std::move(R));
2643 return SizeBefore != Results.size();
2646 virtual std::optional<FixItList>
2647 getFixits(
const FixitStrategy &S)
const override;
2648 SourceLocation getSourceLoc()
const override {
return Node->getBeginLoc(); }
2650 virtual DeclUseList getClaimedVarUseSites()
const override {
return {Node}; }
2653class PointerDereferenceGadget :
public FixableGadget {
2654 static constexpr const char *
const BaseDeclRefExprTag =
"BaseDRE";
2655 static constexpr const char *
const OperatorTag =
"op";
2657 const DeclRefExpr *BaseDeclRefExpr =
nullptr;
2658 const UnaryOperator *Op =
nullptr;
2662 : FixableGadget(
Kind::PointerDereference),
2663 BaseDeclRefExpr(
Result.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
2664 Op(
Result.getNodeAs<UnaryOperator>(OperatorTag)) {}
2666 static bool classof(
const Gadget *G) {
2667 return G->getKind() == Kind::PointerDereference;
2670 static bool matches(
const Stmt *S,
2671 llvm::SmallVectorImpl<MatchResult> &Results) {
2672 size_t SizeBefore = Results.size();
2674 const auto *UO = dyn_cast<UnaryOperator>(S);
2681 const auto *DRE = dyn_cast<DeclRefExpr>(CE);
2682 if (!DRE || !isSupportedVariable(*DRE))
2685 R.addNode(BaseDeclRefExprTag, DynTypedNode::create(*DRE));
2686 R.addNode(OperatorTag, DynTypedNode::create(*UO));
2687 Results.emplace_back(std::move(R));
2689 return SizeBefore != Results.size();
2692 DeclUseList getClaimedVarUseSites()
const override {
2693 return {BaseDeclRefExpr};
2696 virtual std::optional<FixItList>
2697 getFixits(
const FixitStrategy &S)
const override;
2698 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
2704class UPCAddressofArraySubscriptGadget :
public FixableGadget {
2706 static constexpr const char *
const UPCAddressofArraySubscriptTag =
2707 "AddressofArraySubscriptUnderUPC";
2708 const UnaryOperator *Node;
2712 : FixableGadget(
Kind::ULCArraySubscript),
2713 Node(
Result.getNodeAs<UnaryOperator>(UPCAddressofArraySubscriptTag)) {
2714 assert(Node !=
nullptr &&
"Expecting a non-null matching result");
2717 static bool classof(
const Gadget *G) {
2718 return G->getKind() == Kind::UPCAddressofArraySubscript;
2721 static bool matches(
const Stmt *S,
2722 llvm::SmallVectorImpl<MatchResult> &Results) {
2723 size_t SizeBefore = Results.size();
2725 auto *E = dyn_cast<Expr>(S);
2729 if (!UO || UO->
getOpcode() != UO_AddrOf)
2731 const auto *ASE = dyn_cast<ArraySubscriptExpr>(UO->
getSubExpr());
2736 if (!DRE || !isSupportedVariable(*DRE))
2739 R.addNode(UPCAddressofArraySubscriptTag, DynTypedNode::create(*UO));
2740 Results.emplace_back(std::move(R));
2742 return SizeBefore != Results.size();
2745 virtual std::optional<FixItList>
2746 getFixits(
const FixitStrategy &)
const override;
2747 SourceLocation getSourceLoc()
const override {
return Node->getBeginLoc(); }
2749 virtual DeclUseList getClaimedVarUseSites()
const override {
2762class DeclUseTracker {
2763 using UseSetTy = llvm::SmallPtrSet<const DeclRefExpr *, 16>;
2764 using DefMapTy = llvm::DenseMap<const VarDecl *, const DeclStmt *>;
2767 std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
2771 DeclUseTracker() =
default;
2772 DeclUseTracker(
const DeclUseTracker &) =
delete;
2773 DeclUseTracker &operator=(
const DeclUseTracker &) =
delete;
2774 DeclUseTracker(DeclUseTracker &&) =
default;
2775 DeclUseTracker &operator=(DeclUseTracker &&) =
default;
2778 void discoverUse(
const DeclRefExpr *DRE) { Uses->insert(DRE); }
2781 void claimUse(
const DeclRefExpr *DRE) {
2782 assert(Uses->count(DRE) &&
2783 "DRE not found or claimed by multiple matchers!");
2788 bool hasUnclaimedUses(
const VarDecl *VD)
const {
2790 return any_of(*Uses, [VD](
const DeclRefExpr *DRE) {
2795 UseSetTy getUnclaimedUses(
const VarDecl *VD)
const {
2797 for (
auto use : *Uses) {
2799 ReturnSet.insert(use);
2805 void discoverDecl(
const DeclStmt *DS) {
2806 for (
const Decl *D : DS->
decls()) {
2807 if (
const auto *VD = dyn_cast<VarDecl>(D)) {
2818 const DeclStmt *lookupDecl(
const VarDecl *VD)
const {
2819 return Defs.lookup(VD);
2828 static constexpr const char *
const UPCPreIncrementTag =
2829 "PointerPreIncrementUnderUPC";
2834 : FixableGadget(Kind::UPCPreIncrement),
2836 assert(Node !=
nullptr &&
"Expecting a non-null matching result");
2840 return G->getKind() == Kind::UPCPreIncrement;
2849 size_t SizeBefore = Results.size();
2851 auto *E = dyn_cast<Expr>(S);
2855 if (!UO || UO->
getOpcode() != UO_PreInc)
2857 const auto *DRE = dyn_cast<DeclRefExpr>(UO->
getSubExpr());
2858 if (!DRE || !isSupportedVariable(*DRE))
2862 Results.emplace_back(std::move(R));
2864 return SizeBefore != Results.size();
2867 virtual std::optional<FixItList>
2872 return {dyn_cast<DeclRefExpr>(Node->getSubExpr())};
2880 static constexpr const char *
const UUCAddAssignTag =
2881 "PointerAddAssignUnderUUC";
2882 static constexpr const char *
const OffsetTag =
"Offset";
2885 const Expr *Offset =
nullptr;
2889 : FixableGadget(Kind::UUCAddAssign),
2892 assert(Node !=
nullptr &&
"Expecting a non-null matching result");
2896 return G->getKind() == Kind::UUCAddAssign;
2901 size_t SizeBefore = Results.size();
2903 const auto *E = dyn_cast<Expr>(S);
2907 if (!BO || BO->
getOpcode() != BO_AddAssign)
2909 const auto *DRE = dyn_cast<DeclRefExpr>(BO->
getLHS());
2915 Results.emplace_back(std::move(R));
2917 return SizeBefore != Results.size();
2920 virtual std::optional<FixItList>
2925 return {dyn_cast<DeclRefExpr>(Node->getLHS())};
2932 static constexpr const char *
const BaseDeclRefExprTag =
"BaseDRE";
2933 static constexpr const char *
const DerefOpTag =
"DerefOp";
2934 static constexpr const char *
const AddOpTag =
"AddOp";
2935 static constexpr const char *
const OffsetTag =
"Offset";
2944 : FixableGadget(Kind::DerefSimplePtrArithFixable),
2952 auto IsPtr = [](
const Expr *E, MatchResult &R) {
2956 if (!DRE || !isSupportedVariable(*DRE))
2961 const auto IsPlusOverPtrAndInteger = [&IsPtr](
const Expr *E,
2963 const auto *BO = dyn_cast<BinaryOperator>(E);
2967 const auto *LHS = BO->
getLHS();
2968 const auto *RHS = BO->
getRHS();
2981 size_t SizeBefore = Results.size();
2982 const auto InnerMatcher = [&IsPlusOverPtrAndInteger,
2983 &Results](
const Expr *E) {
2984 const auto *UO = dyn_cast<UnaryOperator>(E);
2990 if (IsPlusOverPtrAndInteger(Operand, R)) {
2992 Results.emplace_back(std::move(R));
2996 return SizeBefore != Results.size();
2999 virtual std::optional<FixItList>
3002 return DerefOp->getBeginLoc();
3006 return {BaseDeclRefExpr};
3014 : WarningGadgets(WarningGadgets) {}
3023#define WARNING_GADGET(name) \
3024 if (name##Gadget::matches(S, Ctx, Result) && \
3025 notInSafeBufferOptOut(*S, &Handler)) { \
3026 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
3029#define WARNING_OPTIONAL_GADGET(name) \
3030 if (name##Gadget::matches(S, Ctx, &Handler, Result) && \
3031 notInSafeBufferOptOut(*S, &Handler)) { \
3032 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
3035#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
3040 WarningGadgetList &WarningGadgets;
3047 DeclUseTracker &Tracker)
3048 : FixableGadgets(FixableGadgets), Tracker(Tracker) {}
3052 bool matchFound =
false;
3059#define FIXABLE_GADGET(name) \
3060 if (name##Gadget::matches(S, Results)) { \
3061 for (const auto &R : Results) { \
3062 FixableGadgets.push_back(std::make_unique<name##Gadget>(R)); \
3063 matchFound = true; \
3067#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
3070 if (
auto *DRE = findDeclRefExpr(S); DRE) {
3071 Tracker.discoverUse(DRE);
3077 if (
auto *DS = findDeclStmt(S); DS) {
3078 Tracker.discoverDecl(DS);
3086 const auto *DRE = dyn_cast<DeclRefExpr>(S);
3094 const DeclStmt *findDeclStmt(
const Stmt *S) {
3095 const auto *DS = dyn_cast<DeclStmt>(S);
3100 FixableGadgetList &FixableGadgets;
3101 DeclUseTracker &Tracker;
3107 bool EmitSuggestions, FixableGadgetList &FixableGadgets,
3108 WarningGadgetList &WarningGadgets,
3109 DeclUseTracker &Tracker) {
3112 if (EmitSuggestions) {
3121 return N1->getBeginLoc().getRawEncoding() <
3122 N2->getBeginLoc().getRawEncoding();
3135 auto AddStmt = [&Stmts](
const Stmt *S) {
3139 if (
const auto *FD = dyn_cast<FunctionDecl>(D)) {
3142 if (PD->hasDefaultArg() && !PD->hasUninstantiatedDefaultArg())
3143 AddStmt(PD->getDefaultArg());
3144 if (
const auto *CtorD = dyn_cast<CXXConstructorDecl>(FD))
3146 Stmts, llvm::map_range(CtorD->inits(),
3150 }
else if (
const auto *VD = dyn_cast<VarDecl>(D)) {
3152 }
else if (
const auto *FD = dyn_cast<FieldDecl>(D)) {
3153 AddStmt(FD->getInClassInitializer());
3158 std::map<const VarDecl *, std::set<const WarningGadget *>,
3172 for (
auto &G : AllUnsafeOperations) {
3173 DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
3175 bool AssociatedWithVarDecl =
false;
3176 for (
const DeclRefExpr *DRE : ClaimedVarUseSites) {
3177 if (
const auto *VD = dyn_cast<VarDecl>(DRE->
getDecl())) {
3178 result.
byVar[VD].insert(G.get());
3179 AssociatedWithVarDecl =
true;
3183 if (!AssociatedWithVarDecl) {
3184 result.
noVar.push_back(G.get());
3192 std::map<const VarDecl *, std::set<const FixableGadget *>,
3202 for (
auto &F : AllFixableOperations) {
3203 DeclUseList DREs = F->getClaimedVarUseSites();
3206 if (
const auto *VD = dyn_cast<VarDecl>(DRE->
getDecl())) {
3207 FixablesForUnsafeVars.
byVar[VD].insert(F.get());
3211 return FixablesForUnsafeVars;
3218 std::vector<const FixItHint *>
All;
3222 std::sort(
All.begin(),
All.end(),
3224 return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
3225 H2->RemoveRange.getBegin());
3233 Hint->RemoveRange.getBegin())) {
3245std::optional<FixItList>
3246PtrToPtrAssignmentGadget::getFixits(
const FixitStrategy &S)
const {
3249 switch (S.
lookup(LeftVD)) {
3253 return std::nullopt;
3255 return std::nullopt;
3258 return std::nullopt;
3260 llvm_unreachable(
"unsupported strategies for FixableGadgets");
3262 return std::nullopt;
3266static inline std::optional<FixItList>
createDataFixit(
const ASTContext &Ctx,
3267 const DeclRefExpr *DRE);
3269std::optional<FixItList>
3270CArrayToPtrAssignmentGadget::getFixits(
const FixitStrategy &S)
const {
3289 if (S.
lookup(LeftVD) == FixitStrategy::Kind::Span) {
3290 if (S.
lookup(RightVD) == FixitStrategy::Kind::Wontfix) {
3293 }
else if (S.
lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {
3294 if (S.
lookup(RightVD) == FixitStrategy::Kind::Array) {
3298 return std::nullopt;
3301std::optional<FixItList>
3302PointerInitGadget::getFixits(
const FixitStrategy &S)
const {
3303 const auto *LeftVD = PtrInitLHS;
3305 switch (S.
lookup(LeftVD)) {
3306 case FixitStrategy::Kind::Span:
3307 if (S.
lookup(RightVD) == FixitStrategy::Kind::Span)
3309 return std::nullopt;
3310 case FixitStrategy::Kind::Wontfix:
3311 return std::nullopt;
3312 case FixitStrategy::Kind::Iterator:
3313 case FixitStrategy::Kind::Array:
3314 return std::nullopt;
3315 case FixitStrategy::Kind::Vector:
3316 llvm_unreachable(
"unsupported strategies for FixableGadgets");
3318 return std::nullopt;
3324 if (ConstVal->isNegative())
3331std::optional<FixItList>
3332ULCArraySubscriptGadget::getFixits(
const FixitStrategy &S)
const {
3333 if (
const auto *DRE =
3335 if (
const auto *VD = dyn_cast<VarDecl>(DRE->
getDecl())) {
3337 case FixitStrategy::Kind::Span: {
3341 const ASTContext &Ctx =
3344 return std::nullopt;
3348 case FixitStrategy::Kind::Array:
3350 case FixitStrategy::Kind::Wontfix:
3351 case FixitStrategy::Kind::Iterator:
3352 case FixitStrategy::Kind::Vector:
3353 llvm_unreachable(
"unsupported strategies for FixableGadgets");
3356 return std::nullopt;
3359static std::optional<FixItList>
3362std::optional<FixItList>
3363UPCAddressofArraySubscriptGadget::getFixits(
const FixitStrategy &S)
const {
3364 auto DREs = getClaimedVarUseSites();
3368 case FixitStrategy::Kind::Span:
3370 case FixitStrategy::Kind::Wontfix:
3371 case FixitStrategy::Kind::Iterator:
3372 case FixitStrategy::Kind::Array:
3373 return std::nullopt;
3374 case FixitStrategy::Kind::Vector:
3375 llvm_unreachable(
"unsupported strategies for FixableGadgets");
3377 return std::nullopt;
3382 static const char *
const EOL =
"\n";
3389 std::string s = std::string(
"<# ");
3390 s += HintTextToUser;
3396template <
typename NodeTy>
3397static std::optional<SourceLocation>
3400 if (
unsigned TkLen =
3407 return std::nullopt;
3420 bool AttrRangeOverlapping = llvm::any_of(VD->
attrs(), [&](
Attr *At) ->
bool {
3421 return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
3422 VD->getBeginLoc())) &&
3423 !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
3424 At->getRange().getBegin()));
3428 AttrRangeOverlapping;
3470 std::optional<Qualifiers> Quals = std::nullopt) {
3471 const char *
const SpanOpen =
"std::span<";
3474 return SpanOpen + EltTyText.str() +
' ' + Quals->getAsString() +
'>';
3475 return SpanOpen + EltTyText.str() +
'>';
3478std::optional<FixItList>
3480 const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
3485 if (
auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
3486 if (ConstVal->isNegative())
3487 return std::nullopt;
3508 const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
3515 std::optional<SourceLocation> LHSLocation =
getPastLoc(LHS, SM, LangOpts);
3517 return std::nullopt;
3522 std::optional<SourceLocation> AddOpLocation =
3524 std::optional<SourceLocation> DerefOpLocation =
3527 if (!AddOpLocation || !DerefOpLocation)
3528 return std::nullopt;
3538 return std::nullopt;
3541std::optional<FixItList>
3542PointerDereferenceGadget::getFixits(
const FixitStrategy &S)
const {
3553 if (
auto LocPastOperand =
3560 case FixitStrategy::Kind::Iterator:
3561 case FixitStrategy::Kind::Array:
3562 return std::nullopt;
3563 case FixitStrategy::Kind::Vector:
3564 llvm_unreachable(
"FixitStrategy not implemented yet!");
3565 case FixitStrategy::Kind::Wontfix:
3566 llvm_unreachable(
"Invalid strategy!");
3569 return std::nullopt;
3576 std::optional<SourceLocation> EndOfOperand =
3582 return std::nullopt;
3587std::optional<FixItList>
3588UPCStandalonePointerGadget::getFixits(
const FixitStrategy &S)
const {
3591 case FixitStrategy::Kind::Array:
3592 case FixitStrategy::Kind::Span: {
3597 case FixitStrategy::Kind::Wontfix:
3598 case FixitStrategy::Kind::Iterator:
3599 return std::nullopt;
3600 case FixitStrategy::Kind::Vector:
3601 llvm_unreachable(
"unsupported strategies for FixableGadgets");
3604 return std::nullopt;
3609static std::optional<FixItList>
3616 const Expr *Idx = ArraySub->getIdx();
3619 std::stringstream SS;
3620 bool IdxIsLitZero =
false;
3623 if ((*ICE).isZero())
3624 IdxIsLitZero =
true;
3625 std::optional<StringRef> DreString =
getExprText(DRE, SM, LangOpts);
3627 return std::nullopt;
3631 SS << (*DreString).str() <<
".data()";
3633 std::optional<StringRef> IndexString =
getExprText(Idx, SM, LangOpts);
3635 return std::nullopt;
3637 SS <<
"&" << (*DreString).str() <<
".data()"
3638 <<
"[" << (*IndexString).str() <<
"]";
3644std::optional<FixItList>
3648 if (DREs.size() != 1)
3649 return std::nullopt;
3651 if (
const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3655 const Stmt *AddAssignNode = Node;
3656 StringRef varName = VD->
getName();
3660 return std::nullopt;
3664 (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc());
3665 std::string SS = varName.str() +
" = " + varName.str() +
".subspan";
3669 std::optional<SourceLocation> AddAssignLocation =
getEndCharLoc(
3671 if (!AddAssignLocation)
3672 return std::nullopt;
3679 Offset->getEndLoc().getLocWithOffset(1),
")"));
3683 return std::nullopt;
3686std::optional<FixItList>
3690 if (DREs.size() != 1)
3691 return std::nullopt;
3693 if (
const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3696 std::stringstream SS;
3697 StringRef varName = VD->
getName();
3701 SS <<
"(" << varName.data() <<
" = " << varName.data()
3702 <<
".subspan(1)).data()";
3703 std::optional<SourceLocation> PreIncLocation =
3705 if (!PreIncLocation)
3706 return std::nullopt;
3709 SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));
3713 return std::nullopt;
3731static std::optional<FixItList>
3733 const StringRef UserFillPlaceHolder) {
3741 if (
Init->isNullPointerConstant(
3746 NPC_ValueDependentIsNotNull)) {
3747 std::optional<SourceLocation> InitLocation =
3750 return std::nullopt;
3758 std::string ExtentText = UserFillPlaceHolder.data();
3759 StringRef One =
"1";
3764 if (
auto CxxNew = dyn_cast<CXXNewExpr>(
Init->IgnoreImpCasts())) {
3769 if (
const Expr *Ext = CxxNew->getArraySize().value_or(
nullptr)) {
3770 if (!Ext->HasSideEffects(Ctx)) {
3771 std::optional<StringRef> ExtentString =
getExprText(Ext, SM, LangOpts);
3773 return std::nullopt;
3774 ExtentText = *ExtentString;
3776 }
else if (!CxxNew->isArray())
3788 if (
auto AddrOfExpr = dyn_cast<UnaryOperator>(
Init->IgnoreImpCasts()))
3789 if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
3790 isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
3797 std::optional<SourceLocation> LocPassInit =
getPastLoc(
Init, SM, LangOpts);
3800 return std::nullopt;
3802 StrBuffer.append(
", ");
3803 StrBuffer.append(ExtentText);
3804 StrBuffer.append(
"}");
3810#define DEBUG_NOTE_DECL_FAIL(D, Msg) \
3811 Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \
3812 "failed to produce fixit for declaration '" + \
3813 (D)->getNameAsString() + "'" + (Msg))
3815#define DEBUG_NOTE_DECL_FAIL(D, Msg)
3821static std::optional<std::string>
3825 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
3830 return std::nullopt;
3832 std::string SpanTyText =
"std::span<";
3834 SpanTyText.append(*PteTyText);
3836 if (PteTyQualifiers) {
3837 SpanTyText.append(
" ");
3838 SpanTyText.append(PteTyQualifiers->getAsString());
3840 SpanTyText.append(
">");
3859 const StringRef UserFillPlaceHolder,
3873 std::stringstream SS;
3878 std::optional<FixItList> InitFixIts =
3882 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
3883 std::make_move_iterator(InitFixIts->end()));
3890 if (!EndLocForReplacement.
isValid()) {
3940static std::optional<FixItList>
3946 return std::nullopt;
3951 std::vector<std::string> NewTysTexts(NumParms);
3952 std::vector<bool> ParmsMask(NumParms,
false);
3953 bool AtLeastOneParmToFix =
false;
3955 for (
unsigned i = 0; i < NumParms; i++) {
3962 return std::nullopt;
3964 std::optional<Qualifiers> PteTyQuals = std::nullopt;
3965 std::optional<std::string> PteTyText =
3970 return std::nullopt;
3974 ParmsMask[i] =
true;
3975 AtLeastOneParmToFix =
true;
3977 if (!AtLeastOneParmToFix)
3984 const auto NewOverloadSignatureCreator =
3985 [&SM, &LangOpts, &NewTysTexts,
3986 &ParmsMask](
const FunctionDecl *FD) -> std::optional<std::string> {
3987 std::stringstream SS;
3995 SS << Prefix->str();
3997 return std::nullopt;
4001 for (
unsigned i = 0; i < NumParms; i++) {
4009 SS << NewTysTexts[i];
4012 SS <<
' ' << II->getName().str();
4013 }
else if (
auto ParmTypeText =
4017 SS << ParmTypeText->str();
4019 return std::nullopt;
4020 if (i != NumParms - 1)
4029 const auto OldOverloadDefCreator =
4030 [&Handler, &SM, &LangOpts, &NewTysTexts,
4031 &ParmsMask](
const FunctionDecl *FD) -> std::optional<std::string> {
4032 std::stringstream SS;
4040 << FDPrefix->str() <<
"{";
4042 return std::nullopt;
4045 SS <<
"return " << FunQualName->str() <<
"(";
4047 return std::nullopt;
4051 for (
unsigned i = 0; i < NumParms; i++) {
4060 return std::nullopt;
4067 if (i != NumParms - 1)
4078 std::optional<SourceLocation> Loc =
getPastLoc(FReDecl, SM, LangOpts);
4082 if (FReDecl->isThisDeclarationADefinition()) {
4083 assert(FReDecl == FD &&
"inconsistent function definition");
4086 if (
auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
4092 if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
4095 FReDecl->getBeginLoc(),
" ")));
4098 if (
auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
4120 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
4136 std::stringstream SS;
4139 if (PteTyQualifiers)
4148 SS <<
' ' << PVDNameText->str();
4154 const DeclUseTracker &Tracker,
4157 const DeclStmt *DS = Tracker.lookupDecl(VD);
4160 " : variables declared this way not implemented yet");
4184 const QualType &ArrayEltT = CAT->getElementType();
4185 assert(!ArrayEltT.
isNull() &&
"Trying to fix a non-array type variable!");
4194 auto MaybeElemTypeTxt =
4197 if (!MaybeElemTypeTxt)
4199 const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
4204 while (NextTok && !NextTok->is(tok::l_square) &&
4217 if (!MaybeArraySizeTxt)
4219 const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
4220 if (ArraySizeTxt.empty()) {
4231 std::optional<StringRef> IdentText =
4240 llvm::raw_svector_ostream OS(Replacement);
4241 OS <<
"std::array<" << ElemTypeTxt <<
", " << ArraySizeTxt <<
"> "
4242 << IdentText->str();
4252 const DeclUseTracker &Tracker,
4255 const DeclStmt *DS = Tracker.lookupDecl(VD);
4256 assert(DS &&
"Fixing non-local variables not implemented yet!");
4275 const DeclUseTracker &Tracker,
ASTContext &Ctx,
4277 if (
const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
4278 auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
4279 if (!FD || FD != D) {
4306 if (
const auto *PVD = dyn_cast<ParmVarDecl>(VD))
4324 llvm_unreachable(
"FixitStrategy not implemented yet!");
4326 llvm_unreachable(
"Invalid strategy!");
4328 llvm_unreachable(
"Unknown strategy!");
4336 return llvm::any_of(FixIts, [](
const FixItHint &Hint) {
4338 if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
4355 std::map<const VarDecl *, FixItList> &FixItsForVariable,
4360 for (
const auto &[VD, Ignore] : FixItsForVariable) {
4362 if (llvm::any_of(Grp,
4363 [&FixItsForVariable](
const VarDecl *GrpMember) ->
bool {
4364 return !FixItsForVariable.count(GrpMember);
4369 ToErase.push_back(
Member);
4372 for (
auto *VarToErase : ToErase)
4373 FixItsForVariable.erase(VarToErase);
4384 std::map<const VarDecl *, FixItList> &FixItsForVariable ,
4388 FixItList FixItsSharedByParms{};
4390 std::optional<FixItList> OverloadFixes =
4393 if (OverloadFixes) {
4394 FixItsSharedByParms.append(*OverloadFixes);
4400 FixItsForVariable.erase(
Member);
4402 return FixItsSharedByParms;
4406static std::map<const VarDecl *, FixItList>
4415 std::map<const VarDecl *, FixItList> FixItsForVariable;
4420 for (
const auto &[VD, Fixables] : FixablesForAllVars.
byVar) {
4421 FixItsForVariable[VD] =
4425 if (FixItsForVariable[VD].empty()) {
4426 FixItsForVariable.erase(VD);
4429 for (
const auto &F : Fixables) {
4430 std::optional<FixItList> Fixits = F->getFixits(S);
4433 FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
4434 Fixits->begin(), Fixits->end());
4439 VD, F->getSourceLoc(),
4440 (
"gadget '" + F->getDebugName() +
"' refused to produce a fix")
4443 FixItsForVariable.erase(VD);
4462 FixItList FixItsSharedByParms{};
4464 if (
auto *FD = dyn_cast<FunctionDecl>(D))
4466 FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
4470 std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
4473 for (
auto &[Var, Ignore] : FixItsForVariable) {
4474 bool AnyParm =
false;
4475 const auto VarGroupForVD = VarGrpMgr.
getGroupOfVar(Var, &AnyParm);
4477 for (
const VarDecl *GrpMate : VarGroupForVD) {
4480 if (FixItsForVariable.count(GrpMate))
4481 FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
4485 assert(!FixItsSharedByParms.empty() &&
4486 "Should not try to fix a parameter that does not belong to a "
4488 FinalFixItsForVariable[Var].append(FixItsSharedByParms);
4495 for (
auto Iter = FinalFixItsForVariable.begin();
4496 Iter != FinalFixItsForVariable.end();)
4499 Iter = FinalFixItsForVariable.erase(Iter);
4502 return FinalFixItsForVariable;
4505template <
typename VarDeclIterTy>
4509 for (
const VarDecl *VD : UnsafeVars) {
4520 const std::vector<VarGrpTy> &Groups;
4521 const std::map<const VarDecl *, unsigned> &VarGrpMap;
4522 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
4526 const std::vector<VarGrpTy> &Groups,
4527 const std::map<const VarDecl *, unsigned> &VarGrpMap,
4528 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
4529 : Groups(Groups), VarGrpMap(VarGrpMap),
4530 GrpsUnionForParms(GrpsUnionForParms) {}
4533 if (GrpsUnionForParms.contains(Var)) {
4536 return GrpsUnionForParms.getArrayRef();
4541 auto It = VarGrpMap.find(Var);
4543 if (It == VarGrpMap.end())
4545 return Groups[It->second];
4549 return GrpsUnionForParms.getArrayRef();
4554 WarningGadgetList WarningGadgets,
4555 DeclUseTracker Tracker,
4557 bool EmitSuggestions) {
4558 if (!EmitSuggestions) {
4562 for (
const auto &G : WarningGadgets) {
4563 G->handleUnsafeOperation(Handler,
false,
4569 assert(FixableGadgets.empty() &&
4570 "Fixable gadgets found but suggestions not requested!");
4576 if (!WarningGadgets.empty()) {
4580 for (
const auto &G : FixableGadgets) {
4581 for (
const auto *DRE : G->getClaimedVarUseSites()) {
4582 Tracker.claimUse(DRE);
4598 if (WarningGadgets.empty())
4606 std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
4609 for (
auto it = FixablesForAllVars.
byVar.cbegin();
4610 it != FixablesForAllVars.
byVar.cend();) {
4615 (
"failed to produce fixit for '" +
4616 it->first->getNameAsString() +
4617 "' : neither local nor a parameter"));
4619 it = FixablesForAllVars.
byVar.erase(it);
4620 }
else if (it->first->getType().getCanonicalType()->isReferenceType()) {
4623 (
"failed to produce fixit for '" +
4624 it->first->getNameAsString() +
4625 "' : has a reference type"));
4627 it = FixablesForAllVars.
byVar.erase(it);
4628 }
else if (Tracker.hasUnclaimedUses(it->first)) {
4629 it = FixablesForAllVars.
byVar.erase(it);
4630 }
else if (it->first->isInitCapture()) {
4633 (
"failed to produce fixit for '" +
4634 it->first->getNameAsString() +
4635 "' : init capture"));
4637 it = FixablesForAllVars.
byVar.erase(it);
4644 for (
const auto &it : UnsafeOps.
byVar) {
4645 const VarDecl *
const UnsafeVD = it.first;
4646 auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
4647 if (UnclaimedDREs.empty())
4651 std::string UnclaimedUseTrace =
4656 (
"failed to produce fixit for '" + UnfixedVDName +
4657 "' : has an unclaimed use\nThe unclaimed DRE trace: " +
4658 UnclaimedUseTrace));
4665 llvm::DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
4666 DepMapTy DependenciesMap{};
4667 DepMapTy PtrAssignmentGraph{};
4669 for (
const auto &it : FixablesForAllVars.
byVar) {
4670 for (
const FixableGadget *fixable : it.second) {
4671 std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
4672 fixable->getStrategyImplications();
4674 std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
4675 PtrAssignmentGraph[Impl.first].insert(Impl.second);
4697 std::set<const VarDecl *> VisitedVarsDirected{};
4698 for (
const auto &[Var, ignore] : UnsafeOps.
byVar) {
4699 if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
4701 std::queue<const VarDecl *> QueueDirected{};
4702 QueueDirected.push(Var);
4703 while (!QueueDirected.empty()) {
4704 const VarDecl *CurrentVar = QueueDirected.front();
4705 QueueDirected.pop();
4706 VisitedVarsDirected.insert(CurrentVar);
4707 auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
4708 for (
const VarDecl *Adj : AdjacentNodes) {
4709 if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
4710 QueueDirected.push(Adj);
4712 DependenciesMap[Var].insert(Adj);
4713 DependenciesMap[Adj].insert(Var);
4720 std::vector<VarGrpTy> Groups;
4724 std::map<const VarDecl *, unsigned> VarGrpMap;
4726 llvm::SetVector<const VarDecl *>
4731 std::set<const VarDecl *> VisitedVars{};
4732 for (
const auto &[Var, ignore] : UnsafeOps.
byVar) {
4733 if (VisitedVars.find(Var) == VisitedVars.end()) {
4734 VarGrpTy &VarGroup = Groups.emplace_back();
4735 std::queue<const VarDecl *> Queue{};
4738 while (!Queue.empty()) {
4739 const VarDecl *CurrentVar = Queue.front();
4741 VisitedVars.insert(CurrentVar);
4742 VarGroup.push_back(CurrentVar);
4743 auto AdjacentNodes = DependenciesMap[CurrentVar];
4744 for (
const VarDecl *Adj : AdjacentNodes) {
4745 if (VisitedVars.find(Adj) == VisitedVars.end()) {
4751 bool HasParm =
false;
4752 unsigned GrpIdx = Groups.size() - 1;
4754 for (
const VarDecl *
V : VarGroup) {
4755 VarGrpMap[
V] = GrpIdx;
4760 GrpsUnionForParms.insert_range(VarGroup);
4782 for (
auto I = FixablesForAllVars.
byVar.begin();
4783 I != FixablesForAllVars.
byVar.end();) {
4785 if (!VisitedVars.count((*I).first)) {
4787 I = FixablesForAllVars.
byVar.erase(I);
4795 VisitedVars, [&FixablesForAllVars](
const VarDecl *
V) {
4797 return FixablesForAllVars.
byVar.count(
V);
4804 FixItsForVariableGroup =
4806 Tracker, Handler, VarGrpMgr);
4808 for (
const auto &G : UnsafeOps.
noVar) {
4809 G->handleUnsafeOperation(Handler,
false,
4813 for (
const auto &[VD, WarningGadgets] : UnsafeOps.
byVar) {
4814 auto FixItsIt = FixItsForVariableGroup.find(VD);
4816 FixItsIt != FixItsForVariableGroup.end()
4817 ? std::move(FixItsIt->second)
4820 for (
const auto &G : WarningGadgets) {
4821 G->handleUnsafeOperation(Handler,
true,
4829 bool EmitSuggestions) {
4835 if (
const auto *FD = dyn_cast<FunctionDecl>(D)) {
4843 if (
const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
4844 if (MD->getParent()->isLambda() && MD->getParent()->isLocalClass())
4849 if (FReDecl->isExternC()) {
4852 EmitSuggestions =
false;
4862 assert(!Stmts.empty());
4864 FixableGadgetList FixableGadgets;
4865 WarningGadgetList WarningGadgets;
4866 DeclUseTracker Tracker;
4867 for (
const Stmt *S : Stmts) {
4869 WarningGadgets, Tracker);
4871 applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
4872 std::move(Tracker), Handler, EmitSuggestions);
4876 std::set<const Expr *> &UnsafePointers) {
4880 void handleUnsafeOperation(
const Stmt *,
bool,
ASTContext &)
override {}
4882 const Expr *UnsafeArg =
nullptr)
override {}
4883 void handleUnsafeOperationInContainer(
const Stmt *,
bool,
4885 void handleUnsafeOperationInStringView(
const Stmt *,
bool,
4887 void handleUnsafeVariableGroup(
const VarDecl *,
4891 void handleUnsafeUniquePtrArrayAccess(
const DynTypedNode &Node,
4892 bool IsRelatedToDecl,
4900 bool ignoreUnsafeBufferInLibcCall(
const SourceLocation &)
const override {
4903 bool ignoreUnsafeBufferInStaticSizedArray(
4907 std::string getUnsafeBufferUsageAttributeTextAt(
4918 WarningGadgetList WarningGadgets;
4919 bool Matched =
false;
4927#define WARNING_GADGET(name) \
4928 if (name##Gadget::matches(S, Ctx, Result)) \
4929 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));
4930#define WARNING_OPTIONAL_GADGET(name) \
4931 if (name##Gadget::matches(S, Ctx, &Handler, Result)) \
4932 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));
4933#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
4935 for (
auto &WG : WarningGadgets)
4936 for (
auto *E : WG->getUnsafePtrs()) {
4937 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.
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.
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
isOne - 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
bool hasBody(const FunctionDecl *&Definition) const
Returns true if the function has a body.
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.