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();
518 bool AllowDuckTypedContainers =
false) {
520 if (
auto *MCEPtr = dyn_cast<CXXMemberCallExpr>(Ptr->IgnoreParenImpCasts()))
522 dyn_cast<CXXMemberCallExpr>(Size->IgnoreParenImpCasts())) {
523 auto *DREOfPtr = dyn_cast<DeclRefExpr>(
524 MCEPtr->getImplicitObjectArgument()->IgnoreParenImpCasts());
525 auto *DREOfSize = dyn_cast<DeclRefExpr>(
526 MCESize->getImplicitObjectArgument()->IgnoreParenImpCasts());
528 if (!DREOfPtr || !DREOfSize)
532 if (DREOfPtr->getDecl() != DREOfSize->getDecl())
534 const auto *MDData = MCEPtr->getMethodDecl();
535 const auto *MDSize = MCESize->getMethodDecl();
536 if (!MDData || !MDSize)
538 if (MDData->getName() !=
"data")
541 bool AcceptSizeBytes = Ptr->getType()->getPointeeType()->isCharType();
543 if (!((AcceptSizeBytes && MDSize->getName() ==
"size_bytes") ||
548 MDSize->getName() ==
"size"))
551 if (AllowDuckTypedContainers)
555 if (!MCEPtr->getRecordDecl()->isInStdNamespace())
558 auto *ObjII = MCEPtr->getRecordDecl()->getIdentifier();
570 if (Size->EvaluateAsInt(ER, Ctx)) {
572 if (
auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
576 return llvm::APSInt::compareValues(
577 SizeInt, llvm::APSInt(CAT->getSize(),
true)) == 0;
609template <
typename CallOrConstructExpr>
613 bool AllowDuckTypedContainers =
false) {
614 assert(Node.getNumArgs() == 2 &&
615 "expecting a two-parameter container constructor or factory call");
618 auto HaveEqualConstantValues = [&Ctx](
const Expr *E0,
const Expr *E1) {
620 if (
auto E1CV = E1->getIntegerConstantExpr(Ctx)) {
621 return llvm::APSInt::compareValues(*E0CV, *E1CV) == 0;
625 auto AreSameDRE = [](
const Expr *E0,
const Expr *E1) {
626 if (
auto *DRE0 = dyn_cast<DeclRefExpr>(E0))
627 if (
auto *DRE1 = dyn_cast<DeclRefExpr>(E1)) {
628 return DRE0->getDecl() == DRE1->getDecl();
634 if (Arg1CV && Arg1CV->isZero())
640 case Stmt::CXXNewExprClass:
643 return AreSameDRE((*Size)->IgnoreImplicit(), Arg1) ||
644 HaveEqualConstantValues(*Size, Arg1);
649 return Arg1CV && Arg1CV->isOne();
657 if (
auto CCast = dyn_cast<CStyleCastExpr>(Arg0)) {
658 if (!CCast->getType()->isPointerType())
666 if (
const auto *
Call = dyn_cast<CallExpr>(CCast->getSubExpr())) {
668 if (
auto *AllocAttr = FD->getAttr<AllocSizeAttr>()) {
669 const Expr *EleSizeExpr =
670 Call->getArg(AllocAttr->getElemSizeParam().getASTIndex());
672 ParamIdx NumElemIdx = AllocAttr->getNumElemsParam();
679 if (
auto BO = dyn_cast<BinaryOperator>(Arg1))
686 auto IsMethodCallToSizedObject =
687 [AllowDuckTypedContainers](
const Stmt *Node, StringRef MethodName) {
688 if (
const auto *MC = dyn_cast<CXXMemberCallExpr>(Node)) {
689 const auto *MD = MC->getMethodDecl();
690 if (!MD || MD->getName() != MethodName)
692 if (AllowDuckTypedContainers)
694 if (
const auto *RD = MC->getRecordDecl())
695 if (
auto *II = RD->getDeclName().getAsIdentifierInfo();
696 II && RD->isInStdNamespace())
703 if (IsMethodCallToSizedObject(Arg0,
"begin") &&
704 IsMethodCallToSizedObject(Arg1,
"end"))
708 ->getImplicitObjectArgument()
709 ->IgnoreParenImpCasts(),
711 ->getImplicitObjectArgument()
712 ->IgnoreParenImpCasts());
724 if (
const auto *SL = dyn_cast<StringLiteral>(Arg0)) {
726 if (llvm::APSInt::compareValues(
727 llvm::APSInt::getUnsigned(SL->getLength()), *ArgSize) >= 0)
736 if (llvm::APSInt::compareValues(llvm::APSInt(CAT->getSize(),
true),
751 auto GetContainerObj = [](
const Expr *E) ->
const Expr * {
752 E = E->IgnoreParenImpCasts();
753 if (
const auto *MCE = dyn_cast<CXXMemberCallExpr>(E)) {
754 const auto *MD = MCE->getMethodDecl();
755 if (MD && MD->getIdentifier())
756 if (MD->getName() ==
"begin" || MD->getName() ==
"end")
757 return MCE->getImplicitObjectArgument()->IgnoreParenImpCasts();
762 const Expr *Obj0 = GetContainerObj(Arg0);
763 const Expr *Obj1 = GetContainerObj(Arg1);
766 const auto *DRE0 = dyn_cast<DeclRefExpr>(Obj0);
767 const auto *DRE1 = dyn_cast<DeclRefExpr>(Obj1);
772 if (DRE0->getDecl()->getCanonicalDecl() ==
773 DRE1->getDecl()->getCanonicalDecl())
786 const bool IgnoreStaticSizedArrays) {
795 if (
const auto *CATy =
796 dyn_cast<ConstantArrayType>(Node.
getBase()
800 limit = CATy->getLimitedSize();
801 }
else if (
const auto *SLiteral = dyn_cast<clang::StringLiteral>(
803 limit = SLiteral->getLength() + 1;
808 if (IgnoreStaticSizedArrays) {
818 llvm::APSInt ArrIdx = EVResult.
Val.
getInt();
821 if (ArrIdx.isNonNegative() && ArrIdx.getLimitedValue() < limit)
823 }
else if (
const auto *BE = dyn_cast<BinaryOperator>(IndexExpr)) {
826 if (BE->getOpcode() != BO_And && BE->getOpcode() != BO_Rem)
829 const Expr *LHS = BE->getLHS();
830 const Expr *RHS = BE->getRHS();
832 if (BE->getOpcode() == BO_Rem) {
839 llvm::APSInt result = EVResult.
Val.
getInt();
840 if (result.isNonNegative() && result.getLimitedValue() <= limit)
851 llvm::APSInt result = EVResult.
Val.
getInt();
852 if (result.isNonNegative() && result.getLimitedValue() < limit)
872 const Expr *
Base = Ptr->IgnoreParenImpCasts();
874 if (
const auto *CATy = dyn_cast<ConstantArrayType>(
875 Base->getType()->getUnqualifiedDesugaredType())) {
876 limit = CATy->getLimitedSize();
877 }
else if (
const auto *SLiteral = dyn_cast<clang::StringLiteral>(
Base)) {
878 limit = SLiteral->getLength() + 1;
883 llvm::APSInt OffsetVal = EVResult.
Val.
getInt();
884 if (Opcode == BO_Sub)
885 OffsetVal = -OffsetVal;
889 return OffsetVal.isNonNegative() && OffsetVal.getLimitedValue() < limit;
898 if (
const auto *CE = dyn_cast<ConditionalOperator>(E)) {
900 const auto *Cond = CE->getCond();
902 if (!Cond->isValueDependent() &&
903 Cond->EvaluateAsBooleanCondition(CondEval, Ctx))
904 return CondEval ? CE->getLHS() : CE->getRHS();
913 Ptr = Ptr->IgnoreParenImpCasts();
914 if (
const auto *DefaultArgE = dyn_cast<CXXDefaultArgExpr>(Ptr))
915 Ptr = DefaultArgE->getExpr()->IgnoreParenImpCasts();
922 if (
const auto *CondE = dyn_cast<ConditionalOperator>(Ptr)) {
931 if (
auto *MCE = dyn_cast<CXXMemberCallExpr>(Ptr)) {
941 static const llvm::StringSet<> NullTermFunctions = {
"strerror"};
942 if (
auto *CE = dyn_cast<CallExpr>(Ptr)) {
965 if (Name.ends_with(
"_s"))
966 return Name.drop_back(2 );
973 if (Name.starts_with(
"__") && Name.ends_with(
"_chk"))
975 Name.drop_front(2).drop_back(4) );
979static StringRef
matchName(StringRef FunName,
bool isBuiltin) {
981 if (isBuiltin && FunName.starts_with(
"__builtin_"))
985 FunName.drop_front(10 ));
987 if (FunName.starts_with(
"__asan_"))
1004 const Expr *&UnsafeArg,
const unsigned FmtIdx,
1005 std::optional<const unsigned> FmtArgIdx = std::nullopt,
1006 bool isKprintf =
false) {
1007 class StringFormatStringHandler
1011 const Expr *&UnsafeArg;
1027 unsigned PArgIdx = Precision.
getArgIndex() + FmtArgIdx;
1029 if (PArgIdx < Call->getNumArgs()) {
1030 const Expr *PArg =
Call->getArg(PArgIdx);
1033 if (
auto *CE = dyn_cast<CastExpr>(PArg);
1034 CE && CE->getType()->isSignedIntegerType())
1035 PArg = CE->getSubExpr();
1040 analyze_printf::OptionalAmount::HowSpecified::Constant) {
1042 llvm::APSInt PArgVal = llvm::APSInt(
1052 StringFormatStringHandler(
const CallExpr *
Call,
unsigned FmtArgIdx,
1054 :
Call(
Call), FmtArgIdx(FmtArgIdx), UnsafeArg(UnsafeArg), Ctx(Ctx),
1055 UnsafeArgSet(
false) {}
1058 const char *startSpecifier,
1059 unsigned specifierLen,
1067 if (ArgIdx >=
Call->getNumArgs())
1071 const Expr *Arg =
Call->getArg(ArgIdx);
1081 bool IsArgTypeValid =
1084 ?
ArgType->getPointeeType()->isWideCharType()
1085 :
ArgType->getPointeeType()->isCharType());
1088 Precision && IsArgTypeValid)
1092 UnsafeArg =
Call->getArg(ArgIdx);
1093 UnsafeArgSet =
true;
1097 bool isUnsafeArgSet() {
return UnsafeArgSet; }
1100 const Expr *Fmt =
Call->getArg(FmtIdx);
1101 unsigned FmtArgStartingIdx =
1102 FmtArgIdx.has_value() ?
static_cast<unsigned>(*FmtArgIdx) : FmtIdx + 1;
1105 if (SL->getCharByteWidth() == 1) {
1106 StringRef FmtStr = SL->getString();
1107 StringFormatStringHandler Handler(
Call, FmtArgStartingIdx, UnsafeArg,
1111 Handler, FmtStr.begin(), FmtStr.end(), Ctx.
getLangOpts(),
1113 Handler.isUnsafeArgSet();
1116 if (
auto FmtStr = SL->tryEvaluateString(Ctx)) {
1117 StringFormatStringHandler Handler(
Call, FmtArgStartingIdx, UnsafeArg,
1120 Handler, FmtStr->data(), FmtStr->data() + FmtStr->size(),
1122 Handler.isUnsafeArgSet();
1128 return llvm::any_of(
1129 llvm::make_range(
Call->arg_begin() + FmtIdx,
Call->arg_end()),
1130 [&UnsafeArg, &Ctx](
const Expr *Arg) ->
bool {
1131 if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg, Ctx)) {
1149 static const std::set<StringRef> PredefinedNames = {
1228 if (PredefinedNames.count(Name))
1231 std::string NameWCS = Name.str();
1232 size_t WcsPos = NameWCS.find(
"wcs");
1234 while (WcsPos != std::string::npos) {
1235 NameWCS[WcsPos++] =
's';
1236 NameWCS[WcsPos++] =
't';
1237 NameWCS[WcsPos++] =
'r';
1238 WcsPos = NameWCS.find(
"wcs", WcsPos);
1240 if (PredefinedNames.count(NameWCS))
1244 return Name.ends_with(
"scanf");
1252 assert(FD &&
"It should have been checked that FD is non-null.");
1259 if (Name !=
"memset")
1271 const auto *AddressOfVar = dyn_cast_if_present<DeclRefExpr>(
1276 const auto *SizeOfVar =
1281 return AddressOfVar->getDecl() != SizeOfVar->getDecl();
1295 return Name.starts_with(
"v") && Name.ends_with(
"printf");
1308 return Name ==
"sprintf" || Name ==
"swprintf";
1322 if (!Name.ends_with(
"printf"))
1325 StringRef Prefix = Name.drop_back(6);
1327 if (Prefix.ends_with(
"w"))
1328 Prefix = Prefix.drop_back(1);
1330 return Prefix.empty() || Prefix ==
"k" || Prefix ==
"f" || Prefix ==
"sn";
1338 MatchResult &Result, llvm::StringRef Tag) {
1342 assert(FD &&
"It should have been checked that FD is non-null.");
1360 const Expr *UnsafeArg;
1371 bool isKprintf =
false;
1372 const Expr *UnsafeArg;
1375 isKprintf = II->getName() ==
"kprintf";
1377 std::nullopt, isKprintf)) {
1390 const Expr *UnsafeArg;
1401 for (
const auto *Arg : Node.
arguments())
1416 assert(FD &&
"It should have been checked that FD is non-null.");
1431 !Size->getType()->isUnsignedIntegerType())
1460#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1463 Gadget(Kind K) : K(K) {}
1465 Kind
getKind()
const {
return K; }
1468 StringRef getDebugName()
const {
1473#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1475 llvm_unreachable(
"Unhandled Gadget::Kind enum");
1479 virtual bool isWarningGadget()
const = 0;
1482 virtual SourceLocation getSourceLoc()
const = 0;
1487 virtual DeclUseList getClaimedVarUseSites()
const = 0;
1489 virtual ~Gadget() =
default;
1497class WarningGadget :
public Gadget {
1499 WarningGadget(Kind K) : Gadget(K) {}
1501 static bool classof(
const Gadget *G) {
return G->isWarningGadget(); }
1502 bool isWarningGadget() const final {
return true; }
1504 virtual void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1505 bool IsRelatedToDecl,
1506 ASTContext &Ctx)
const = 0;
1508 virtual SmallVector<const Expr *, 1> getUnsafePtrs()
const = 0;
1515class FixableGadget :
public Gadget {
1517 FixableGadget(Kind K) : Gadget(K) {}
1519 static bool classof(
const Gadget *G) {
return !G->isWarningGadget(); }
1520 bool isWarningGadget() const final {
return false; }
1525 virtual std::optional<FixItList> getFixits(
const FixitStrategy &)
const {
1526 return std::nullopt;
1535 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1536 getStrategyImplications()
const {
1537 return std::nullopt;
1541static bool isSupportedVariable(
const DeclRefExpr &Node) {
1553 dyn_cast<ClassTemplateSpecializationDecl>(
RecordDecl);
1554 if (!class_template_specialization_decl)
1559 if (template_args.
size() == 0)
1570class UniquePtrArrayAccessGadget :
public WarningGadget {
1572 static constexpr const char *
const AccessorTag =
"unique_ptr_array_access";
1573 const CXXOperatorCallExpr *AccessorExpr;
1577 : WarningGadget(
Kind::UniquePtrArrayAccess),
1578 AccessorExpr(
Result.getNodeAs<CXXOperatorCallExpr>(AccessorTag)) {
1579 assert(AccessorExpr &&
1580 "UniquePtrArrayAccessGadget requires a matched CXXOperatorCallExpr");
1583 static bool classof(
const Gadget *G) {
1584 return G->getKind() == Kind::UniquePtrArrayAccess;
1587 static bool matches(
const Stmt *S,
const ASTContext &Ctx,
1590 const CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(S);
1591 if (!OpCall || OpCall->
getOperator() != OO_Subscript)
1598 const CXXMethodDecl *
Method =
1603 if (
Method->getOverloadedOperator() != OO_Subscript)
1607 if (!isUniquePtrArray(RecordDecl))
1610 const Expr *IndexExpr = OpCall->
getArg(1);
1611 clang::Expr::EvalResult Eval;
1617 Result.addNode(AccessorTag, DynTypedNode::create(*OpCall));
1620 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1621 bool IsRelatedToDecl,
1622 ASTContext &Ctx)
const override {
1624 DynTypedNode::create(*AccessorExpr), IsRelatedToDecl, Ctx);
1627 SourceLocation getSourceLoc()
const override {
1629 return AccessorExpr->getOperatorLoc();
1630 return SourceLocation();
1633 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
1634 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
1637using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>;
1638using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>;
1642class IncrementGadget :
public WarningGadget {
1643 static constexpr const char *
const OpTag =
"op";
1644 const UnaryOperator *Op;
1648 : WarningGadget(
Kind::Increment),
1649 Op(
Result.getNodeAs<UnaryOperator>(OpTag)) {}
1651 static bool classof(
const Gadget *G) {
1652 return G->getKind() == Kind::Increment;
1655 static bool matches(
const Stmt *S,
const ASTContext &Ctx,
1657 const auto *UO = dyn_cast<UnaryOperator>(S);
1662 Result.addNode(OpTag, DynTypedNode::create(*UO));
1666 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1667 bool IsRelatedToDecl,
1668 ASTContext &Ctx)
const override {
1671 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
1673 DeclUseList getClaimedVarUseSites()
const override {
1674 SmallVector<const DeclRefExpr *, 2> Uses;
1675 if (
const auto *DRE =
1676 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
1677 Uses.push_back(DRE);
1680 return std::move(Uses);
1683 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
1684 return {Op->getSubExpr()->IgnoreParenImpCasts()};
1690class DecrementGadget :
public WarningGadget {
1691 static constexpr const char *
const OpTag =
"op";
1692 const UnaryOperator *Op;
1696 : WarningGadget(
Kind::Decrement),
1697 Op(
Result.getNodeAs<UnaryOperator>(OpTag)) {}
1699 static bool classof(
const Gadget *G) {
1700 return G->getKind() == Kind::Decrement;
1703 static bool matches(
const Stmt *S,
const ASTContext &Ctx,
1705 const auto *UO = dyn_cast<UnaryOperator>(S);
1710 Result.addNode(OpTag, DynTypedNode::create(*UO));
1714 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1715 bool IsRelatedToDecl,
1716 ASTContext &Ctx)
const override {
1719 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
1721 DeclUseList getClaimedVarUseSites()
const override {
1722 if (
const auto *DRE =
1723 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
1730 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
1731 return {Op->getSubExpr()->IgnoreParenImpCasts()};
1737class ArraySubscriptGadget :
public WarningGadget {
1738 static constexpr const char *
const ArraySubscrTag =
"ArraySubscript";
1739 const ArraySubscriptExpr *ASE;
1744 ASE(
Result.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {}
1746 static bool classof(
const Gadget *G) {
1747 return G->getKind() == Kind::ArraySubscript;
1750 static bool matches(
const Stmt *S,
const ASTContext &Ctx,
1751 const UnsafeBufferUsageHandler *Handler,
1753 const auto *ASE = dyn_cast<ArraySubscriptExpr>(S);
1756 const auto *
const Base = ASE->getBase()->IgnoreParenImpCasts();
1759 const auto *Idx = dyn_cast<IntegerLiteral>(ASE->getIdx());
1760 bool IsSafeIndex = (Idx && Idx->getValue().isZero()) ||
1767 Result.addNode(ArraySubscrTag, DynTypedNode::create(*ASE));
1771 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1772 bool IsRelatedToDecl,
1773 ASTContext &Ctx)
const override {
1776 SourceLocation getSourceLoc()
const override {
return ASE->getBeginLoc(); }
1778 DeclUseList getClaimedVarUseSites()
const override {
1779 if (
const auto *DRE =
1780 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) {
1787 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
1788 return {ASE->getBase()->IgnoreParenImpCasts()};
1796class PointerArithmeticGadget :
public WarningGadget {
1797 static constexpr const char *
const PointerArithmeticTag =
"ptrAdd";
1798 static constexpr const char *
const PointerArithmeticPointerTag =
"ptrAddPtr";
1799 const BinaryOperator *PA;
1804 : WarningGadget(
Kind::PointerArithmetic),
1805 PA(
Result.getNodeAs<BinaryOperator>(PointerArithmeticTag)),
1806 Ptr(
Result.getNodeAs<Expr>(PointerArithmeticPointerTag)) {}
1808 static bool classof(
const Gadget *G) {
1809 return G->getKind() == Kind::PointerArithmetic;
1812 static bool matches(
const Stmt *S,
const ASTContext &Ctx,
1813 const UnsafeBufferUsageHandler *Handler,
1815 const auto *BO = dyn_cast<BinaryOperator>(S);
1818 const auto *LHS = BO->
getLHS();
1819 const auto *RHS = BO->
getRHS();
1821 const Expr *Ptr =
nullptr;
1822 const Expr *OffsetExpr =
nullptr;
1828 RHS->getType()->isEnumeralType())) {
1835 (LHS->getType()->isIntegerType() || LHS->getType()->isEnumeralType())) {
1840 if (!Ptr || !OffsetExpr)
1851 Result.addNode(PointerArithmeticPointerTag, DynTypedNode::create(*Ptr));
1852 Result.addNode(PointerArithmeticTag, DynTypedNode::create(*BO));
1856 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1857 bool IsRelatedToDecl,
1858 ASTContext &Ctx)
const override {
1861 SourceLocation getSourceLoc()
const override {
return PA->getBeginLoc(); }
1863 DeclUseList getClaimedVarUseSites()
const override {
1864 if (
const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
1871 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
1872 return {Ptr->IgnoreParenImpCasts()};
1879class SpanTwoParamConstructorGadget :
public WarningGadget {
1880 static constexpr const char *
const SpanTwoParamConstructorTag =
1881 "spanTwoParamConstructor";
1882 const CXXConstructExpr *Ctor;
1886 : WarningGadget(
Kind::SpanTwoParamConstructor),
1887 Ctor(
Result.getNodeAs<CXXConstructExpr>(SpanTwoParamConstructorTag)) {}
1889 static bool classof(
const Gadget *G) {
1890 return G->getKind() == Kind::SpanTwoParamConstructor;
1893 static bool matches(
const CXXConstructExpr *CE, ASTContext &Ctx,
1896 const auto *CRecordDecl = CDecl->
getParent();
1897 auto HasTwoParamSpanCtorDecl =
1899 CDecl->getDeclName().getAsString() ==
"span" && CE->
getNumArgs() == 2;
1902 Result.addNode(SpanTwoParamConstructorTag, DynTypedNode::create(*CE));
1906 static bool matches(
const Stmt *S, ASTContext &Ctx,
1907 const UnsafeBufferUsageHandler *Handler,
1909 const auto *CE = dyn_cast<CXXConstructExpr>(S);
1917 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1918 bool IsRelatedToDecl,
1919 ASTContext &Ctx)
const override {
1922 SourceLocation getSourceLoc()
const override {
return Ctor->getBeginLoc(); }
1924 DeclUseList getClaimedVarUseSites()
const override {
1927 if (
auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
1934 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
1937class StringViewTwoParamConstructorGadget :
public WarningGadget {
1938 static constexpr const char *
const StringViewTwoParamConstructorTag =
1939 "stringViewTwoParamConstructor";
1940 const CXXConstructExpr *Ctor;
1944 : WarningGadget(
Kind::StringViewTwoParamConstructor),
1945 Ctor(
Result.getNodeAs<CXXConstructExpr>(
1946 StringViewTwoParamConstructorTag)) {}
1948 static bool classof(
const Gadget *G) {
1949 return G->getKind() == Kind::StringViewTwoParamConstructor;
1952 static bool matches(
const CXXConstructExpr *CE, ASTContext &Ctx,
1955 const auto *CRecordDecl = CDecl->
getParent();
1960 CDecl->getDeclName().getAsString() ==
"basic_string_view" &&
1966 Result.addNode(StringViewTwoParamConstructorTag, DynTypedNode::create(*CE));
1970 static bool matches(
const Stmt *S, ASTContext &Ctx,
1971 const UnsafeBufferUsageHandler *Handler,
1973 const auto *CE = dyn_cast<CXXConstructExpr>(S);
1981 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1982 bool IsRelatedToDecl,
1983 ASTContext &Ctx)
const override {
1987 SourceLocation getSourceLoc()
const override {
return Ctor->getBeginLoc(); }
1989 DeclUseList getClaimedVarUseSites()
const override {
1992 if (
auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
1999 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
2007class UnsafeBufferUsageContainerAttrGadget :
public WarningGadget {
2008 constexpr static const char *
const OpTag =
"container_attr_expr";
2013 : WarningGadget(
Kind::UnsafeBufferUsageContainerAttr),
2014 Op(
Result.getNodeAs<Expr>(OpTag)) {}
2016 static bool classof(
const Gadget *G) {
2017 return G->getKind() == Kind::UnsafeBufferUsageContainerAttr;
2023 template <
typename CallOrConstructExpr>
2024 static bool isUnsafeContainerConstruction(
const Decl *Callee,
2025 const CallOrConstructExpr &Node,
2029 const auto *Attr =
Callee->getAttr<UnsafeBufferUsageAttr>();
2030 if (!Attr || Attr->getCategory() !=
"container")
2032 return Node.getNumArgs() == 2 &&
2037 static bool matches(
const Stmt *S, ASTContext &Ctx,
2038 const UnsafeBufferUsageHandler *Handler,
2044 if (
const auto *CE = dyn_cast<CXXConstructExpr>(S)) {
2047 if (SpanTwoParamConstructorGadget::matches(CE, Ctx, Tmp))
2050 if (!isUnsafeContainerConstruction(CE->
getConstructor(), *CE, Ctx))
2053 Result.addNode(OpTag, DynTypedNode::create(*CE));
2057 if (
const auto *
Call = dyn_cast<CallExpr>(S)) {
2058 if (!isUnsafeContainerConstruction(
Call->getDirectCallee(), *
Call, Ctx))
2061 Result.addNode(OpTag, DynTypedNode::create(*
Call));
2067 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2068 bool IsRelatedToDecl,
2069 ASTContext &Ctx)
const override {
2073 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
2075 DeclUseList getClaimedVarUseSites()
const override {
2076 const Expr *Arg0 =
nullptr;
2078 if (
const auto *CE = dyn_cast<CXXConstructExpr>(Op);
2081 else if (
const auto *
Call = dyn_cast<CallExpr>(Op);
2083 Arg0 =
Call->getArg(0);
2093 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
2100class PointerInitGadget :
public FixableGadget {
2102 static constexpr const char *
const PointerInitLHSTag =
"ptrInitLHS";
2103 static constexpr const char *
const PointerInitRHSTag =
"ptrInitRHS";
2104 const VarDecl *PtrInitLHS;
2105 const DeclRefExpr *PtrInitRHS;
2109 : FixableGadget(
Kind::PointerInit),
2110 PtrInitLHS(
Result.getNodeAs<VarDecl>(PointerInitLHSTag)),
2111 PtrInitRHS(
Result.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}
2113 static bool classof(
const Gadget *G) {
2114 return G->getKind() == Kind::PointerInit;
2117 static bool matches(
const Stmt *S,
2118 llvm::SmallVectorImpl<MatchResult> &Results) {
2119 const DeclStmt *DS = dyn_cast<DeclStmt>(S);
2128 const auto *DRE = dyn_cast<DeclRefExpr>(
Init->IgnoreImpCasts());
2129 if (!DRE || !
hasPointerType(*DRE) || !isSupportedVariable(*DRE)) {
2133 R.addNode(PointerInitLHSTag, DynTypedNode::create(*VD));
2134 R.addNode(PointerInitRHSTag, DynTypedNode::create(*DRE));
2135 Results.emplace_back(std::move(R));
2139 virtual std::optional<FixItList>
2140 getFixits(
const FixitStrategy &S)
const override;
2141 SourceLocation getSourceLoc()
const override {
2142 return PtrInitRHS->getBeginLoc();
2145 virtual DeclUseList getClaimedVarUseSites()
const override {
2146 return DeclUseList{PtrInitRHS};
2149 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
2150 getStrategyImplications()
const override {
2151 return std::make_pair(PtrInitLHS,
cast<VarDecl>(PtrInitRHS->getDecl()));
2160class PtrToPtrAssignmentGadget :
public FixableGadget {
2162 static constexpr const char *
const PointerAssignLHSTag =
"ptrLHS";
2163 static constexpr const char *
const PointerAssignRHSTag =
"ptrRHS";
2164 const DeclRefExpr *PtrLHS;
2165 const DeclRefExpr *PtrRHS;
2169 : FixableGadget(
Kind::PtrToPtrAssignment),
2170 PtrLHS(
Result.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
2171 PtrRHS(
Result.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
2173 static bool classof(
const Gadget *G) {
2174 return G->getKind() == Kind::PtrToPtrAssignment;
2177 static bool matches(
const Stmt *S,
2178 llvm::SmallVectorImpl<MatchResult> &Results) {
2179 size_t SizeBefore = Results.size();
2181 const auto *BO = dyn_cast<BinaryOperator>(S);
2182 if (!BO || BO->
getOpcode() != BO_Assign)
2185 if (
const auto *RHSRef = dyn_cast<DeclRefExpr>(RHS);
2187 !isSupportedVariable(*RHSRef)) {
2190 const auto *LHS = BO->
getLHS();
2191 if (
const auto *LHSRef = dyn_cast<DeclRefExpr>(LHS);
2193 !isSupportedVariable(*LHSRef)) {
2197 R.addNode(PointerAssignLHSTag, DynTypedNode::create(*LHS));
2198 R.addNode(PointerAssignRHSTag, DynTypedNode::create(*RHS));
2199 Results.emplace_back(std::move(R));
2201 return SizeBefore != Results.size();
2204 virtual std::optional<FixItList>
2205 getFixits(
const FixitStrategy &S)
const override;
2206 SourceLocation getSourceLoc()
const override {
return PtrLHS->getBeginLoc(); }
2208 virtual DeclUseList getClaimedVarUseSites()
const override {
2209 return DeclUseList{PtrLHS, PtrRHS};
2212 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
2213 getStrategyImplications()
const override {
2224class CArrayToPtrAssignmentGadget :
public FixableGadget {
2226 static constexpr const char *
const PointerAssignLHSTag =
"ptrLHS";
2227 static constexpr const char *
const PointerAssignRHSTag =
"ptrRHS";
2228 const DeclRefExpr *PtrLHS;
2229 const DeclRefExpr *PtrRHS;
2233 : FixableGadget(
Kind::CArrayToPtrAssignment),
2234 PtrLHS(
Result.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
2235 PtrRHS(
Result.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
2237 static bool classof(
const Gadget *G) {
2238 return G->getKind() == Kind::CArrayToPtrAssignment;
2241 static bool matches(
const Stmt *S,
2242 llvm::SmallVectorImpl<MatchResult> &Results) {
2243 size_t SizeBefore = Results.size();
2245 const auto *BO = dyn_cast<BinaryOperator>(S);
2246 if (!BO || BO->
getOpcode() != BO_Assign)
2249 if (
const auto *RHSRef = dyn_cast<DeclRefExpr>(RHS);
2252 !isSupportedVariable(*RHSRef)) {
2255 const auto *LHS = BO->
getLHS();
2256 if (
const auto *LHSRef = dyn_cast<DeclRefExpr>(LHS);
2258 !isSupportedVariable(*LHSRef)) {
2262 R.addNode(PointerAssignLHSTag, DynTypedNode::create(*LHS));
2263 R.addNode(PointerAssignRHSTag, DynTypedNode::create(*RHS));
2264 Results.emplace_back(std::move(R));
2266 return SizeBefore != Results.size();
2269 virtual std::optional<FixItList>
2270 getFixits(
const FixitStrategy &S)
const override;
2271 SourceLocation getSourceLoc()
const override {
return PtrLHS->getBeginLoc(); }
2273 virtual DeclUseList getClaimedVarUseSites()
const override {
2274 return DeclUseList{PtrLHS, PtrRHS};
2277 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
2278 getStrategyImplications()
const override {
2285class UnsafeBufferUsageAttrGadget :
public WarningGadget {
2286 constexpr static const char *
const OpTag =
"attr_expr";
2291 : WarningGadget(
Kind::UnsafeBufferUsageAttr),
2292 Op(
Result.getNodeAs<Expr>(OpTag)) {}
2294 static bool classof(
const Gadget *G) {
2295 return G->getKind() == Kind::UnsafeBufferUsageAttr;
2298 static bool matches(
const Stmt *S,
const ASTContext &Ctx,
2300 if (
auto *CE = dyn_cast<CallExpr>(S)) {
2301 if (
const auto *Callee = CE->getDirectCallee()) {
2302 if (
const auto *Attr =
Callee->getAttr<UnsafeBufferUsageAttr>()) {
2306 if (!Attr->getCategory().empty())
2308 Result.addNode(OpTag, DynTypedNode::create(*CE));
2313 if (
auto *ME = dyn_cast<MemberExpr>(S)) {
2316 if (
const auto *Attr =
2317 ME->getMemberDecl()->getAttr<UnsafeBufferUsageAttr>()) {
2318 if (!Attr->getCategory().empty())
2320 Result.addNode(OpTag, DynTypedNode::create(*ME));
2327 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2328 bool IsRelatedToDecl,
2329 ASTContext &Ctx)
const override {
2332 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
2334 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
2336 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
2342class UnsafeBufferUsageCtorAttrGadget :
public WarningGadget {
2343 constexpr static const char *
const OpTag =
"cxx_construct_expr";
2344 const CXXConstructExpr *Op;
2348 : WarningGadget(
Kind::UnsafeBufferUsageCtorAttr),
2349 Op(
Result.getNodeAs<CXXConstructExpr>(OpTag)) {}
2351 static bool classof(
const Gadget *G) {
2352 return G->getKind() == Kind::UnsafeBufferUsageCtorAttr;
2356 const auto *CE = dyn_cast<CXXConstructExpr>(S);
2363 if (!Attr || !Attr->getCategory().empty())
2367 if (SpanTwoParamConstructorGadget::matches(CE, Ctx, Tmp))
2369 Result.addNode(OpTag, DynTypedNode::create(*CE));
2373 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2374 bool IsRelatedToDecl,
2375 ASTContext &Ctx)
const override {
2378 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
2380 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
2382 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
2389class DataInvocationGadget :
public WarningGadget {
2390 constexpr static const char *
const OpTag =
"data_invocation_expr";
2391 const ExplicitCastExpr *Op;
2395 : WarningGadget(
Kind::DataInvocation),
2396 Op(
Result.getNodeAs<ExplicitCastExpr>(OpTag)) {}
2398 static bool classof(
const Gadget *G) {
2399 return G->getKind() == Kind::DataInvocation;
2402 static bool matches(
const Stmt *S,
const ASTContext &Ctx,
2404 auto *CE = dyn_cast<ExplicitCastExpr>(S);
2407 for (
auto *Child : CE->
children()) {
2408 if (
auto *MCE = dyn_cast<CXXMemberCallExpr>(Child);
2409 MCE && isDataFunction(MCE)) {
2410 Result.addNode(OpTag, DynTypedNode::create(*CE));
2413 if (
auto *
Paren = dyn_cast<ParenExpr>(Child)) {
2414 if (
auto *MCE = dyn_cast<CXXMemberCallExpr>(
Paren->getSubExpr());
2415 MCE && isDataFunction(MCE)) {
2416 Result.addNode(OpTag, DynTypedNode::create(*CE));
2424 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2425 bool IsRelatedToDecl,
2426 ASTContext &Ctx)
const override {
2429 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
2431 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
2434 static bool isDataFunction(
const CXXMemberCallExpr *call) {
2441 if (method->getNameAsString() ==
"data" &&
2442 method->getParent()->isInStdNamespace() &&
2443 llvm::is_contained({SIZED_CONTAINER_OR_VIEW_LIST},
2444 method->getParent()->getName()))
2449 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
2452class UnsafeLibcFunctionCallGadget :
public WarningGadget {
2453 const CallExpr *
const Call;
2454 const Expr *UnsafeArg =
nullptr;
2455 constexpr static const char *
const Tag =
"UnsafeLibcFunctionCall";
2457 constexpr static const char *
const UnsafeSprintfTag =
2458 "UnsafeLibcFunctionCall_sprintf";
2459 constexpr static const char *
const UnsafeSizedByTag =
2460 "UnsafeLibcFunctionCall_sized_by";
2461 constexpr static const char *
const UnsafeStringTag =
2462 "UnsafeLibcFunctionCall_string";
2463 constexpr static const char *
const UnsafeVaListTag =
2464 "UnsafeLibcFunctionCall_va_list";
2478 } WarnedFunKind = OTHERS;
2481 : WarningGadget(
Kind::UnsafeLibcFunctionCall),
2482 Call(
Result.getNodeAs<CallExpr>(Tag)) {
2483 if (
Result.getNodeAs<Decl>(UnsafeSprintfTag))
2484 WarnedFunKind = SPRINTF;
2485 else if (
auto *E =
Result.getNodeAs<Expr>(UnsafeStringTag)) {
2486 WarnedFunKind = STRING;
2488 }
else if (
Result.getNodeAs<CallExpr>(UnsafeSizedByTag)) {
2489 WarnedFunKind = SIZED_BY;
2490 UnsafeArg = Call->getArg(0);
2491 }
else if (
Result.getNodeAs<Decl>(UnsafeVaListTag))
2492 WarnedFunKind = VA_LIST;
2495 static bool matches(
const Stmt *S, ASTContext &Ctx,
2496 const UnsafeBufferUsageHandler *Handler,
2498 const auto *CE = dyn_cast<CallExpr>(S);
2501 const auto *FD = CE->getDirectCallee();
2507 const bool IsGlobalAndNotInAnyNamespace =
2508 FD->isGlobal() && !FD->getEnclosingNamespaceContext()->isNamespace();
2512 if (!FD->isInStdNamespace() && !IsGlobalAndNotInAnyNamespace)
2519 const bool isSingleStringLiteralArg =
2522 if (!isSingleStringLiteralArg) {
2525 Result.addNode(Tag, DynTypedNode::create(*CE));
2529 Result.addNode(Tag, DynTypedNode::create(*CE));
2533 Result.addNode(Tag, DynTypedNode::create(*CE));
2534 Result.addNode(UnsafeVaListTag, DynTypedNode::create(*FD));
2538 Result.addNode(Tag, DynTypedNode::create(*CE));
2539 Result.addNode(UnsafeSprintfTag, DynTypedNode::create(*FD));
2545 Result.addNode(Tag, DynTypedNode::create(*CE));
2546 Result.addNode(UnsafeSizedByTag, DynTypedNode::create(*CE));
2551 Result.addNode(Tag, DynTypedNode::create(*CE));
2558 const Stmt *getBaseStmt()
const {
return Call; }
2560 SourceLocation getSourceLoc()
const override {
return Call->getBeginLoc(); }
2562 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2563 bool IsRelatedToDecl,
2564 ASTContext &Ctx)
const override {
2568 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
2570 SmallVector<const Expr *, 1> getUnsafePtrs()
const override {
return {}; }
2573class UnsafeFormatAttributedFunctionCallGadget :
public WarningGadget {
2574 const CallExpr *
const Call;
2575 const Expr *UnsafeArg =
nullptr;
2576 constexpr static const char *
const Tag =
"UnsafeFormatAttributedFunctionCall";
2577 constexpr static const char *
const UnsafeStringTag =
2578 "UnsafeFormatAttributedFunctionCall_string";
2582 : WarningGadget(
Kind::UnsafeLibcFunctionCall),
2583 Call(
Result.getNodeAs<CallExpr>(Tag)),
2584 UnsafeArg(
Result.getNodeAs<Expr>(UnsafeStringTag)) {}
2586 static bool matches(
const Stmt *S, ASTContext &Ctx,
2587 const UnsafeBufferUsageHandler *Handler,
2589 auto *CE = dyn_cast<CallExpr>(S);
2590 if (!CE || !CE->getDirectCallee())
2592 const FunctionDecl *FD = CE->getDirectCallee();
2598 const FormatAttr *Attr =
nullptr;
2599 bool IsPrintf =
false;
2600 bool AnyAttr = llvm::any_of(
2602 [&Attr, &IsPrintf](
const FormatAttr *FA) ->
bool {
2603 if (const auto *II = FA->getType()) {
2604 if (II->getName() ==
"printf" || II->getName() ==
"scanf") {
2606 IsPrintf = II->getName() ==
"printf";
2612 const Expr *UnsafeArg;
2618 unsigned FmtIdx = Attr->getFormatIdx() - 1;
2619 std::optional<unsigned> FmtArgIdx = Attr->getFirstArg() - 1;
2629 }
else if (CE->
getStmtClass() != Stmt::CallExprClass &&
2639 FmtArgIdx = std::nullopt;
2641 if (AnyAttr && !IsPrintf && FmtArgIdx) {
2643 Result.addNode(Tag, DynTypedNode::create(*CE));
2648 Ctx, CE, UnsafeArg, FmtIdx, FmtArgIdx)) {
2649 Result.addNode(Tag, DynTypedNode::create(*CE));
2650 Result.addNode(UnsafeStringTag, DynTypedNode::create(*UnsafeArg));
2656 const Stmt *getBaseStmt()
const {
return Call; }
2661 bool IsRelatedToDecl,
2666 UnsafeLibcFunctionCallGadget::UnsafeKind::STRING |
2667 UnsafeLibcFunctionCallGadget::UnsafeKind::FORMAT_ATTR,
2672 UnsafeLibcFunctionCallGadget::UnsafeKind::OTHERS |
2673 UnsafeLibcFunctionCallGadget::UnsafeKind::FORMAT_ATTR,
2677 DeclUseList getClaimedVarUseSites()
const override {
return {}; }
2685class ULCArraySubscriptGadget :
public FixableGadget {
2687 static constexpr const char *
const ULCArraySubscriptTag =
2688 "ArraySubscriptUnderULC";
2689 const ArraySubscriptExpr *Node;
2693 : FixableGadget(
Kind::ULCArraySubscript),
2694 Node(
Result.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) {
2695 assert(Node !=
nullptr &&
"Expecting a non-null matching result");
2698 static bool classof(
const Gadget *G) {
2699 return G->getKind() == Kind::ULCArraySubscript;
2702 static bool matches(
const Stmt *S,
2703 llvm::SmallVectorImpl<MatchResult> &Results) {
2704 size_t SizeBefore = Results.size();
2706 const auto *ASE = dyn_cast<ArraySubscriptExpr>(E);
2712 !isSupportedVariable(*DRE))
2715 R.addNode(ULCArraySubscriptTag, DynTypedNode::create(*ASE));
2716 Results.emplace_back(std::move(R));
2718 return SizeBefore != Results.size();
2721 virtual std::optional<FixItList>
2722 getFixits(
const FixitStrategy &S)
const override;
2723 SourceLocation getSourceLoc()
const override {
return Node->getBeginLoc(); }
2725 virtual DeclUseList getClaimedVarUseSites()
const override {
2726 if (
const auto *DRE =
2727 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) {
2737class UPCStandalonePointerGadget :
public FixableGadget {
2739 static constexpr const char *
const DeclRefExprTag =
"StandalonePointer";
2740 const DeclRefExpr *Node;
2744 : FixableGadget(
Kind::UPCStandalonePointer),
2745 Node(
Result.getNodeAs<DeclRefExpr>(DeclRefExprTag)) {
2746 assert(Node !=
nullptr &&
"Expecting a non-null matching result");
2749 static bool classof(
const Gadget *G) {
2750 return G->getKind() == Kind::UPCStandalonePointer;
2753 static bool matches(
const Stmt *S,
2754 llvm::SmallVectorImpl<MatchResult> &Results) {
2755 size_t SizeBefore = Results.size();
2757 auto *E = dyn_cast<Expr>(S);
2762 !isSupportedVariable(*DRE))
2765 R.addNode(DeclRefExprTag, DynTypedNode::create(*DRE));
2766 Results.emplace_back(std::move(R));
2768 return SizeBefore != Results.size();
2771 virtual std::optional<FixItList>
2772 getFixits(
const FixitStrategy &S)
const override;
2773 SourceLocation getSourceLoc()
const override {
return Node->getBeginLoc(); }
2775 virtual DeclUseList getClaimedVarUseSites()
const override {
return {Node}; }
2778class PointerDereferenceGadget :
public FixableGadget {
2779 static constexpr const char *
const BaseDeclRefExprTag =
"BaseDRE";
2780 static constexpr const char *
const OperatorTag =
"op";
2782 const DeclRefExpr *BaseDeclRefExpr =
nullptr;
2783 const UnaryOperator *Op =
nullptr;
2787 : FixableGadget(
Kind::PointerDereference),
2788 BaseDeclRefExpr(
Result.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
2789 Op(
Result.getNodeAs<UnaryOperator>(OperatorTag)) {}
2791 static bool classof(
const Gadget *G) {
2792 return G->getKind() == Kind::PointerDereference;
2795 static bool matches(
const Stmt *S,
2796 llvm::SmallVectorImpl<MatchResult> &Results) {
2797 size_t SizeBefore = Results.size();
2799 const auto *UO = dyn_cast<UnaryOperator>(S);
2806 const auto *DRE = dyn_cast<DeclRefExpr>(CE);
2807 if (!DRE || !isSupportedVariable(*DRE))
2810 R.addNode(BaseDeclRefExprTag, DynTypedNode::create(*DRE));
2811 R.addNode(OperatorTag, DynTypedNode::create(*UO));
2812 Results.emplace_back(std::move(R));
2814 return SizeBefore != Results.size();
2817 DeclUseList getClaimedVarUseSites()
const override {
2818 return {BaseDeclRefExpr};
2821 virtual std::optional<FixItList>
2822 getFixits(
const FixitStrategy &S)
const override;
2823 SourceLocation getSourceLoc()
const override {
return Op->getBeginLoc(); }
2829class UPCAddressofArraySubscriptGadget :
public FixableGadget {
2831 static constexpr const char *
const UPCAddressofArraySubscriptTag =
2832 "AddressofArraySubscriptUnderUPC";
2833 const UnaryOperator *Node;
2837 : FixableGadget(
Kind::ULCArraySubscript),
2838 Node(
Result.getNodeAs<UnaryOperator>(UPCAddressofArraySubscriptTag)) {
2839 assert(Node !=
nullptr &&
"Expecting a non-null matching result");
2842 static bool classof(
const Gadget *G) {
2843 return G->getKind() == Kind::UPCAddressofArraySubscript;
2846 static bool matches(
const Stmt *S,
2847 llvm::SmallVectorImpl<MatchResult> &Results) {
2848 size_t SizeBefore = Results.size();
2850 auto *E = dyn_cast<Expr>(S);
2854 if (!UO || UO->
getOpcode() != UO_AddrOf)
2856 const auto *ASE = dyn_cast<ArraySubscriptExpr>(UO->
getSubExpr());
2861 if (!DRE || !isSupportedVariable(*DRE))
2864 R.addNode(UPCAddressofArraySubscriptTag, DynTypedNode::create(*UO));
2865 Results.emplace_back(std::move(R));
2867 return SizeBefore != Results.size();
2870 virtual std::optional<FixItList>
2871 getFixits(
const FixitStrategy &)
const override;
2872 SourceLocation getSourceLoc()
const override {
return Node->getBeginLoc(); }
2874 virtual DeclUseList getClaimedVarUseSites()
const override {
2887class DeclUseTracker {
2888 using UseSetTy = llvm::SmallPtrSet<const DeclRefExpr *, 16>;
2889 using DefMapTy = llvm::DenseMap<const VarDecl *, const DeclStmt *>;
2892 std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
2896 DeclUseTracker() =
default;
2897 DeclUseTracker(
const DeclUseTracker &) =
delete;
2898 DeclUseTracker &operator=(
const DeclUseTracker &) =
delete;
2899 DeclUseTracker(DeclUseTracker &&) =
default;
2900 DeclUseTracker &operator=(DeclUseTracker &&) =
default;
2903 void discoverUse(
const DeclRefExpr *DRE) { Uses->insert(DRE); }
2906 void claimUse(
const DeclRefExpr *DRE) {
2907 assert(Uses->count(DRE) &&
2908 "DRE not found or claimed by multiple matchers!");
2913 bool hasUnclaimedUses(
const VarDecl *VD)
const {
2915 return any_of(*Uses, [VD](
const DeclRefExpr *DRE) {
2920 UseSetTy getUnclaimedUses(
const VarDecl *VD)
const {
2922 for (
auto use : *Uses) {
2924 ReturnSet.insert(use);
2930 void discoverDecl(
const DeclStmt *DS) {
2931 for (
const Decl *D : DS->
decls()) {
2932 if (
const auto *VD = dyn_cast<VarDecl>(D)) {
2943 const DeclStmt *lookupDecl(
const VarDecl *VD)
const {
2944 return Defs.lookup(VD);
2953 static constexpr const char *
const UPCPreIncrementTag =
2954 "PointerPreIncrementUnderUPC";
2959 : FixableGadget(Kind::UPCPreIncrement),
2961 assert(Node !=
nullptr &&
"Expecting a non-null matching result");
2965 return G->getKind() == Kind::UPCPreIncrement;
2974 size_t SizeBefore = Results.size();
2976 auto *E = dyn_cast<Expr>(S);
2980 if (!UO || UO->
getOpcode() != UO_PreInc)
2982 const auto *DRE = dyn_cast<DeclRefExpr>(UO->
getSubExpr());
2983 if (!DRE || !isSupportedVariable(*DRE))
2987 Results.emplace_back(std::move(R));
2989 return SizeBefore != Results.size();
2992 virtual std::optional<FixItList>
2997 return {dyn_cast<DeclRefExpr>(Node->getSubExpr())};
3005 static constexpr const char *
const UUCAddAssignTag =
3006 "PointerAddAssignUnderUUC";
3007 static constexpr const char *
const OffsetTag =
"Offset";
3010 const Expr *Offset =
nullptr;
3014 : FixableGadget(Kind::UUCAddAssign),
3017 assert(Node !=
nullptr &&
"Expecting a non-null matching result");
3021 return G->getKind() == Kind::UUCAddAssign;
3026 size_t SizeBefore = Results.size();
3028 const auto *E = dyn_cast<Expr>(S);
3032 if (!BO || BO->
getOpcode() != BO_AddAssign)
3034 const auto *DRE = dyn_cast<DeclRefExpr>(BO->
getLHS());
3040 Results.emplace_back(std::move(R));
3042 return SizeBefore != Results.size();
3045 virtual std::optional<FixItList>
3050 return {dyn_cast<DeclRefExpr>(Node->getLHS())};
3057 static constexpr const char *
const BaseDeclRefExprTag =
"BaseDRE";
3058 static constexpr const char *
const DerefOpTag =
"DerefOp";
3059 static constexpr const char *
const AddOpTag =
"AddOp";
3060 static constexpr const char *
const OffsetTag =
"Offset";
3069 : FixableGadget(Kind::DerefSimplePtrArithFixable),
3077 auto IsPtr = [](
const Expr *E, MatchResult &R) {
3081 if (!DRE || !isSupportedVariable(*DRE))
3086 const auto IsPlusOverPtrAndInteger = [&IsPtr](
const Expr *E,
3088 const auto *BO = dyn_cast<BinaryOperator>(E);
3092 const auto *LHS = BO->
getLHS();
3093 const auto *RHS = BO->
getRHS();
3106 size_t SizeBefore = Results.size();
3107 const auto InnerMatcher = [&IsPlusOverPtrAndInteger,
3108 &Results](
const Expr *E) {
3109 const auto *UO = dyn_cast<UnaryOperator>(E);
3115 if (IsPlusOverPtrAndInteger(Operand, R)) {
3117 Results.emplace_back(std::move(R));
3121 return SizeBefore != Results.size();
3124 virtual std::optional<FixItList>
3127 return DerefOp->getBeginLoc();
3131 return {BaseDeclRefExpr};
3139 : WarningGadgets(WarningGadgets) {}
3148#define WARNING_GADGET(name) \
3149 if (name##Gadget::matches(S, Ctx, Result) && \
3150 notInSafeBufferOptOut(*S, &Handler)) { \
3151 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
3154#define WARNING_OPTIONAL_GADGET(name) \
3155 if (name##Gadget::matches(S, Ctx, &Handler, Result) && \
3156 notInSafeBufferOptOut(*S, &Handler)) { \
3157 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
3160#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
3165 WarningGadgetList &WarningGadgets;
3172 DeclUseTracker &Tracker)
3173 : FixableGadgets(FixableGadgets), Tracker(Tracker) {}
3177 bool matchFound =
false;
3184#define FIXABLE_GADGET(name) \
3185 if (name##Gadget::matches(S, Results)) { \
3186 for (const auto &R : Results) { \
3187 FixableGadgets.push_back(std::make_unique<name##Gadget>(R)); \
3188 matchFound = true; \
3192#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
3195 if (
auto *DRE = findDeclRefExpr(S); DRE) {
3196 Tracker.discoverUse(DRE);
3202 if (
auto *DS = findDeclStmt(S); DS) {
3203 Tracker.discoverDecl(DS);
3211 const auto *DRE = dyn_cast<DeclRefExpr>(S);
3219 const DeclStmt *findDeclStmt(
const Stmt *S) {
3220 const auto *DS = dyn_cast<DeclStmt>(S);
3225 FixableGadgetList &FixableGadgets;
3226 DeclUseTracker &Tracker;
3232 bool EmitSuggestions, FixableGadgetList &FixableGadgets,
3233 WarningGadgetList &WarningGadgets,
3234 DeclUseTracker &Tracker) {
3237 if (EmitSuggestions) {
3246 return N1->getBeginLoc().getRawEncoding() <
3247 N2->getBeginLoc().getRawEncoding();
3260 auto AddStmt = [&Stmts](
const Stmt *S) {
3264 if (
const auto *FD = dyn_cast<FunctionDecl>(D)) {
3267 if (PD->hasDefaultArg() && !PD->hasUninstantiatedDefaultArg())
3268 AddStmt(PD->getDefaultArg());
3269 if (
const auto *CtorD = dyn_cast<CXXConstructorDecl>(FD))
3271 Stmts, llvm::map_range(CtorD->inits(),
3275 }
else if (
const auto *VD = dyn_cast<VarDecl>(D)) {
3277 }
else if (
const auto *FD = dyn_cast<FieldDecl>(D)) {
3278 AddStmt(FD->getInClassInitializer());
3283 std::map<const VarDecl *, std::set<const WarningGadget *>,
3297 for (
auto &G : AllUnsafeOperations) {
3298 DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
3300 bool AssociatedWithVarDecl =
false;
3301 for (
const DeclRefExpr *DRE : ClaimedVarUseSites) {
3302 if (
const auto *VD = dyn_cast<VarDecl>(DRE->
getDecl())) {
3303 result.
byVar[VD].insert(G.get());
3304 AssociatedWithVarDecl =
true;
3308 if (!AssociatedWithVarDecl) {
3309 result.
noVar.push_back(G.get());
3317 std::map<const VarDecl *, std::set<const FixableGadget *>,
3327 for (
auto &F : AllFixableOperations) {
3328 DeclUseList DREs = F->getClaimedVarUseSites();
3331 if (
const auto *VD = dyn_cast<VarDecl>(DRE->
getDecl())) {
3332 FixablesForUnsafeVars.
byVar[VD].insert(F.get());
3336 return FixablesForUnsafeVars;
3343 std::vector<const FixItHint *>
All;
3347 std::sort(
All.begin(),
All.end(),
3349 return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
3350 H2->RemoveRange.getBegin());
3358 Hint->RemoveRange.getBegin())) {
3370std::optional<FixItList>
3371PtrToPtrAssignmentGadget::getFixits(
const FixitStrategy &S)
const {
3374 switch (S.
lookup(LeftVD)) {
3378 return std::nullopt;
3380 return std::nullopt;
3383 return std::nullopt;
3385 llvm_unreachable(
"unsupported strategies for FixableGadgets");
3387 return std::nullopt;
3391static inline std::optional<FixItList>
createDataFixit(
const ASTContext &Ctx,
3392 const DeclRefExpr *DRE);
3394std::optional<FixItList>
3395CArrayToPtrAssignmentGadget::getFixits(
const FixitStrategy &S)
const {
3414 if (S.
lookup(LeftVD) == FixitStrategy::Kind::Span) {
3415 if (S.
lookup(RightVD) == FixitStrategy::Kind::Wontfix) {
3418 }
else if (S.
lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {
3419 if (S.
lookup(RightVD) == FixitStrategy::Kind::Array) {
3423 return std::nullopt;
3426std::optional<FixItList>
3427PointerInitGadget::getFixits(
const FixitStrategy &S)
const {
3428 const auto *LeftVD = PtrInitLHS;
3430 switch (S.
lookup(LeftVD)) {
3431 case FixitStrategy::Kind::Span:
3432 if (S.
lookup(RightVD) == FixitStrategy::Kind::Span)
3434 return std::nullopt;
3435 case FixitStrategy::Kind::Wontfix:
3436 return std::nullopt;
3437 case FixitStrategy::Kind::Iterator:
3438 case FixitStrategy::Kind::Array:
3439 return std::nullopt;
3440 case FixitStrategy::Kind::Vector:
3441 llvm_unreachable(
"unsupported strategies for FixableGadgets");
3443 return std::nullopt;
3449 if (ConstVal->isNegative())
3456std::optional<FixItList>
3457ULCArraySubscriptGadget::getFixits(
const FixitStrategy &S)
const {
3458 if (
const auto *DRE =
3460 if (
const auto *VD = dyn_cast<VarDecl>(DRE->
getDecl())) {
3462 case FixitStrategy::Kind::Span: {
3466 const ASTContext &Ctx =
3469 return std::nullopt;
3473 case FixitStrategy::Kind::Array:
3475 case FixitStrategy::Kind::Wontfix:
3476 case FixitStrategy::Kind::Iterator:
3477 case FixitStrategy::Kind::Vector:
3478 llvm_unreachable(
"unsupported strategies for FixableGadgets");
3481 return std::nullopt;
3484static std::optional<FixItList>
3487std::optional<FixItList>
3488UPCAddressofArraySubscriptGadget::getFixits(
const FixitStrategy &S)
const {
3489 auto DREs = getClaimedVarUseSites();
3493 case FixitStrategy::Kind::Span:
3495 case FixitStrategy::Kind::Wontfix:
3496 case FixitStrategy::Kind::Iterator:
3497 case FixitStrategy::Kind::Array:
3498 return std::nullopt;
3499 case FixitStrategy::Kind::Vector:
3500 llvm_unreachable(
"unsupported strategies for FixableGadgets");
3502 return std::nullopt;
3507 static const char *
const EOL =
"\n";
3514 std::string s = std::string(
"<# ");
3515 s += HintTextToUser;
3521template <
typename NodeTy>
3522static std::optional<SourceLocation>
3525 if (
unsigned TkLen =
3532 return std::nullopt;
3545 bool AttrRangeOverlapping = llvm::any_of(VD->
attrs(), [&](
Attr *At) ->
bool {
3546 return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
3547 VD->getBeginLoc())) &&
3548 !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
3549 At->getRange().getBegin()));
3553 AttrRangeOverlapping;
3595 std::optional<Qualifiers> Quals = std::nullopt) {
3596 const char *
const SpanOpen =
"std::span<";
3599 return SpanOpen + EltTyText.str() +
' ' + Quals->getAsString() +
'>';
3600 return SpanOpen + EltTyText.str() +
'>';
3603std::optional<FixItList>
3605 const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
3610 if (
auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
3611 if (ConstVal->isNegative())
3612 return std::nullopt;
3633 const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
3640 std::optional<SourceLocation> LHSLocation =
getPastLoc(LHS, SM, LangOpts);
3642 return std::nullopt;
3647 std::optional<SourceLocation> AddOpLocation =
3649 std::optional<SourceLocation> DerefOpLocation =
3652 if (!AddOpLocation || !DerefOpLocation)
3653 return std::nullopt;
3663 return std::nullopt;
3666std::optional<FixItList>
3667PointerDereferenceGadget::getFixits(
const FixitStrategy &S)
const {
3678 if (
auto LocPastOperand =
3685 case FixitStrategy::Kind::Iterator:
3686 case FixitStrategy::Kind::Array:
3687 return std::nullopt;
3688 case FixitStrategy::Kind::Vector:
3689 llvm_unreachable(
"FixitStrategy not implemented yet!");
3690 case FixitStrategy::Kind::Wontfix:
3691 llvm_unreachable(
"Invalid strategy!");
3694 return std::nullopt;
3701 std::optional<SourceLocation> EndOfOperand =
3707 return std::nullopt;
3712std::optional<FixItList>
3713UPCStandalonePointerGadget::getFixits(
const FixitStrategy &S)
const {
3716 case FixitStrategy::Kind::Array:
3717 case FixitStrategy::Kind::Span: {
3722 case FixitStrategy::Kind::Wontfix:
3723 case FixitStrategy::Kind::Iterator:
3724 return std::nullopt;
3725 case FixitStrategy::Kind::Vector:
3726 llvm_unreachable(
"unsupported strategies for FixableGadgets");
3729 return std::nullopt;
3734static std::optional<FixItList>
3741 const Expr *Idx = ArraySub->getIdx();
3744 std::stringstream SS;
3745 bool IdxIsLitZero =
false;
3748 if ((*ICE).isZero())
3749 IdxIsLitZero =
true;
3750 std::optional<StringRef> DreString =
getExprText(DRE, SM, LangOpts);
3752 return std::nullopt;
3756 SS << (*DreString).str() <<
".data()";
3758 std::optional<StringRef> IndexString =
getExprText(Idx, SM, LangOpts);
3760 return std::nullopt;
3762 SS <<
"&" << (*DreString).str() <<
".data()"
3763 <<
"[" << (*IndexString).str() <<
"]";
3769std::optional<FixItList>
3773 if (DREs.size() != 1)
3774 return std::nullopt;
3776 if (
const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3780 const Stmt *AddAssignNode = Node;
3781 StringRef varName = VD->
getName();
3785 return std::nullopt;
3789 (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc());
3790 std::string SS = varName.str() +
" = " + varName.str() +
".subspan";
3794 std::optional<SourceLocation> AddAssignLocation =
getEndCharLoc(
3796 if (!AddAssignLocation)
3797 return std::nullopt;
3804 Offset->getEndLoc().getLocWithOffset(1),
")"));
3808 return std::nullopt;
3811std::optional<FixItList>
3815 if (DREs.size() != 1)
3816 return std::nullopt;
3818 if (
const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3821 std::stringstream SS;
3822 StringRef varName = VD->
getName();
3826 SS <<
"(" << varName.data() <<
" = " << varName.data()
3827 <<
".subspan(1)).data()";
3828 std::optional<SourceLocation> PreIncLocation =
3830 if (!PreIncLocation)
3831 return std::nullopt;
3834 SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));
3838 return std::nullopt;
3856static std::optional<FixItList>
3858 const StringRef UserFillPlaceHolder) {
3866 if (
Init->isNullPointerConstant(
3871 NPC_ValueDependentIsNotNull)) {
3872 std::optional<SourceLocation> InitLocation =
3875 return std::nullopt;
3883 std::string ExtentText = UserFillPlaceHolder.data();
3884 StringRef One =
"1";
3889 if (
auto CxxNew = dyn_cast<CXXNewExpr>(
Init->IgnoreImpCasts())) {
3894 if (
const Expr *Ext = CxxNew->getArraySize().value_or(
nullptr)) {
3895 if (!Ext->HasSideEffects(Ctx)) {
3896 std::optional<StringRef> ExtentString =
getExprText(Ext, SM, LangOpts);
3898 return std::nullopt;
3899 ExtentText = *ExtentString;
3901 }
else if (!CxxNew->isArray())
3913 if (
auto AddrOfExpr = dyn_cast<UnaryOperator>(
Init->IgnoreImpCasts()))
3914 if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
3915 isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
3922 std::optional<SourceLocation> LocPassInit =
getPastLoc(
Init, SM, LangOpts);
3925 return std::nullopt;
3927 StrBuffer.append(
", ");
3928 StrBuffer.append(ExtentText);
3929 StrBuffer.append(
"}");
3935#define DEBUG_NOTE_DECL_FAIL(D, Msg) \
3936 Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \
3937 "failed to produce fixit for declaration '" + \
3938 (D)->getNameAsString() + "'" + (Msg))
3940#define DEBUG_NOTE_DECL_FAIL(D, Msg)
3946static std::optional<std::string>
3950 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
3955 return std::nullopt;
3957 std::string SpanTyText =
"std::span<";
3959 SpanTyText.append(*PteTyText);
3961 if (PteTyQualifiers) {
3962 SpanTyText.append(
" ");
3963 SpanTyText.append(PteTyQualifiers->getAsString());
3965 SpanTyText.append(
">");
3984 const StringRef UserFillPlaceHolder,
3998 std::stringstream SS;
4003 std::optional<FixItList> InitFixIts =
4007 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
4008 std::make_move_iterator(InitFixIts->end()));
4015 if (!EndLocForReplacement.
isValid()) {
4065static std::optional<FixItList>
4071 return std::nullopt;
4076 std::vector<std::string> NewTysTexts(NumParms);
4077 std::vector<bool> ParmsMask(NumParms,
false);
4078 bool AtLeastOneParmToFix =
false;
4080 for (
unsigned i = 0; i < NumParms; i++) {
4087 return std::nullopt;
4089 std::optional<Qualifiers> PteTyQuals = std::nullopt;
4090 std::optional<std::string> PteTyText =
4095 return std::nullopt;
4099 ParmsMask[i] =
true;
4100 AtLeastOneParmToFix =
true;
4102 if (!AtLeastOneParmToFix)
4109 const auto NewOverloadSignatureCreator =
4110 [&SM, &LangOpts, &NewTysTexts,
4111 &ParmsMask](
const FunctionDecl *FD) -> std::optional<std::string> {
4112 std::stringstream SS;
4120 SS << Prefix->str();
4122 return std::nullopt;
4126 for (
unsigned i = 0; i < NumParms; i++) {
4134 SS << NewTysTexts[i];
4137 SS <<
' ' << II->getName().str();
4138 }
else if (
auto ParmTypeText =
4142 SS << ParmTypeText->str();
4144 return std::nullopt;
4145 if (i != NumParms - 1)
4154 const auto OldOverloadDefCreator =
4155 [&Handler, &SM, &LangOpts, &NewTysTexts,
4156 &ParmsMask](
const FunctionDecl *FD) -> std::optional<std::string> {
4157 std::stringstream SS;
4165 << FDPrefix->str() <<
"{";
4167 return std::nullopt;
4170 SS <<
"return " << FunQualName->str() <<
"(";
4172 return std::nullopt;
4176 for (
unsigned i = 0; i < NumParms; i++) {
4185 return std::nullopt;
4192 if (i != NumParms - 1)
4203 std::optional<SourceLocation> Loc =
getPastLoc(FReDecl, SM, LangOpts);
4207 if (FReDecl->isThisDeclarationADefinition()) {
4208 assert(FReDecl == FD &&
"inconsistent function definition");
4211 if (
auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
4217 if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
4220 FReDecl->getBeginLoc(),
" ")));
4223 if (
auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
4245 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
4261 std::stringstream SS;
4264 if (PteTyQualifiers)
4273 SS <<
' ' << PVDNameText->str();
4279 const DeclUseTracker &Tracker,
4282 const DeclStmt *DS = Tracker.lookupDecl(VD);
4285 " : variables declared this way not implemented yet");
4309 const QualType &ArrayEltT = CAT->getElementType();
4310 assert(!ArrayEltT.
isNull() &&
"Trying to fix a non-array type variable!");
4319 auto MaybeElemTypeTxt =
4322 if (!MaybeElemTypeTxt)
4324 const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
4329 while (NextTok && !NextTok->is(tok::l_square) &&
4342 if (!MaybeArraySizeTxt)
4344 const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
4345 if (ArraySizeTxt.empty()) {
4356 std::optional<StringRef> IdentText =
4365 llvm::raw_svector_ostream OS(Replacement);
4366 OS <<
"std::array<" << ElemTypeTxt <<
", " << ArraySizeTxt <<
"> "
4367 << IdentText->str();
4377 const DeclUseTracker &Tracker,
4380 const DeclStmt *DS = Tracker.lookupDecl(VD);
4381 assert(DS &&
"Fixing non-local variables not implemented yet!");
4400 const DeclUseTracker &Tracker,
ASTContext &Ctx,
4402 if (
const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
4403 auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
4404 if (!FD || FD != D) {
4421 isa_and_nonnull<CXXTryStmt>(FD->
getBody()) ||
4431 if (
const auto *PVD = dyn_cast<ParmVarDecl>(VD))
4449 llvm_unreachable(
"FixitStrategy not implemented yet!");
4451 llvm_unreachable(
"Invalid strategy!");
4453 llvm_unreachable(
"Unknown strategy!");
4461 return llvm::any_of(FixIts, [](
const FixItHint &Hint) {
4463 if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
4480 std::map<const VarDecl *, FixItList> &FixItsForVariable,
4485 for (
const auto &[VD, Ignore] : FixItsForVariable) {
4487 if (llvm::any_of(Grp,
4488 [&FixItsForVariable](
const VarDecl *GrpMember) ->
bool {
4489 return !FixItsForVariable.count(GrpMember);
4494 ToErase.push_back(
Member);
4497 for (
auto *VarToErase : ToErase)
4498 FixItsForVariable.erase(VarToErase);
4509 std::map<const VarDecl *, FixItList> &FixItsForVariable ,
4513 FixItList FixItsSharedByParms{};
4515 std::optional<FixItList> OverloadFixes =
4518 if (OverloadFixes) {
4519 FixItsSharedByParms.append(*OverloadFixes);
4525 FixItsForVariable.erase(
Member);
4527 return FixItsSharedByParms;
4531static std::map<const VarDecl *, FixItList>
4540 std::map<const VarDecl *, FixItList> FixItsForVariable;
4545 for (
const auto &[VD, Fixables] : FixablesForAllVars.
byVar) {
4546 FixItsForVariable[VD] =
4550 if (FixItsForVariable[VD].empty()) {
4551 FixItsForVariable.erase(VD);
4554 for (
const auto &F : Fixables) {
4555 std::optional<FixItList> Fixits = F->getFixits(S);
4558 FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
4559 Fixits->begin(), Fixits->end());
4564 VD, F->getSourceLoc(),
4565 (
"gadget '" + F->getDebugName() +
"' refused to produce a fix")
4568 FixItsForVariable.erase(VD);
4587 FixItList FixItsSharedByParms{};
4589 if (
auto *FD = dyn_cast<FunctionDecl>(D))
4591 FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
4595 std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
4598 for (
auto &[Var, Ignore] : FixItsForVariable) {
4599 bool AnyParm =
false;
4600 const auto VarGroupForVD = VarGrpMgr.
getGroupOfVar(Var, &AnyParm);
4602 for (
const VarDecl *GrpMate : VarGroupForVD) {
4605 if (FixItsForVariable.count(GrpMate))
4606 FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
4610 assert(!FixItsSharedByParms.empty() &&
4611 "Should not try to fix a parameter that does not belong to a "
4613 FinalFixItsForVariable[Var].append(FixItsSharedByParms);
4620 for (
auto Iter = FinalFixItsForVariable.begin();
4621 Iter != FinalFixItsForVariable.end();)
4624 Iter = FinalFixItsForVariable.erase(Iter);
4627 return FinalFixItsForVariable;
4630template <
typename VarDeclIterTy>
4634 for (
const VarDecl *VD : UnsafeVars) {
4645 const std::vector<VarGrpTy> &Groups;
4646 const std::map<const VarDecl *, unsigned> &VarGrpMap;
4647 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
4651 const std::vector<VarGrpTy> &Groups,
4652 const std::map<const VarDecl *, unsigned> &VarGrpMap,
4653 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
4654 : Groups(Groups), VarGrpMap(VarGrpMap),
4655 GrpsUnionForParms(GrpsUnionForParms) {}
4658 if (GrpsUnionForParms.contains(Var)) {
4661 return GrpsUnionForParms.getArrayRef();
4666 auto It = VarGrpMap.find(Var);
4668 if (It == VarGrpMap.end())
4670 return Groups[It->second];
4674 return GrpsUnionForParms.getArrayRef();
4679 WarningGadgetList WarningGadgets,
4680 DeclUseTracker Tracker,
4682 bool EmitSuggestions) {
4683 if (!EmitSuggestions) {
4687 for (
const auto &G : WarningGadgets) {
4688 G->handleUnsafeOperation(Handler,
false,
4694 assert(FixableGadgets.empty() &&
4695 "Fixable gadgets found but suggestions not requested!");
4701 if (!WarningGadgets.empty()) {
4705 for (
const auto &G : FixableGadgets) {
4706 for (
const auto *DRE : G->getClaimedVarUseSites()) {
4707 Tracker.claimUse(DRE);
4723 if (WarningGadgets.empty())
4731 std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
4734 for (
auto it = FixablesForAllVars.
byVar.cbegin();
4735 it != FixablesForAllVars.
byVar.cend();) {
4740 (
"failed to produce fixit for '" +
4741 it->first->getNameAsString() +
4742 "' : neither local nor a parameter"));
4744 it = FixablesForAllVars.
byVar.erase(it);
4745 }
else if (it->first->getType().getCanonicalType()->isReferenceType()) {
4748 (
"failed to produce fixit for '" +
4749 it->first->getNameAsString() +
4750 "' : has a reference type"));
4752 it = FixablesForAllVars.
byVar.erase(it);
4753 }
else if (Tracker.hasUnclaimedUses(it->first)) {
4754 it = FixablesForAllVars.
byVar.erase(it);
4755 }
else if (it->first->isInitCapture()) {
4758 (
"failed to produce fixit for '" +
4759 it->first->getNameAsString() +
4760 "' : init capture"));
4762 it = FixablesForAllVars.
byVar.erase(it);
4769 for (
const auto &it : UnsafeOps.
byVar) {
4770 const VarDecl *
const UnsafeVD = it.first;
4771 auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
4772 if (UnclaimedDREs.empty())
4776 std::string UnclaimedUseTrace =
4781 (
"failed to produce fixit for '" + UnfixedVDName +
4782 "' : has an unclaimed use\nThe unclaimed DRE trace: " +
4783 UnclaimedUseTrace));
4790 llvm::DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
4791 DepMapTy DependenciesMap{};
4792 DepMapTy PtrAssignmentGraph{};
4794 for (
const auto &it : FixablesForAllVars.
byVar) {
4795 for (
const FixableGadget *fixable : it.second) {
4796 std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
4797 fixable->getStrategyImplications();
4799 std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
4800 PtrAssignmentGraph[Impl.first].insert(Impl.second);
4822 std::set<const VarDecl *> VisitedVarsDirected{};
4823 for (
const auto &[Var, ignore] : UnsafeOps.
byVar) {
4824 if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
4826 std::queue<const VarDecl *> QueueDirected{};
4827 QueueDirected.push(Var);
4828 while (!QueueDirected.empty()) {
4829 const VarDecl *CurrentVar = QueueDirected.front();
4830 QueueDirected.pop();
4831 VisitedVarsDirected.insert(CurrentVar);
4832 auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
4833 for (
const VarDecl *Adj : AdjacentNodes) {
4834 if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
4835 QueueDirected.push(Adj);
4837 DependenciesMap[Var].insert(Adj);
4838 DependenciesMap[Adj].insert(Var);
4845 std::vector<VarGrpTy> Groups;
4849 std::map<const VarDecl *, unsigned> VarGrpMap;
4851 llvm::SetVector<const VarDecl *>
4856 std::set<const VarDecl *> VisitedVars{};
4857 for (
const auto &[Var, ignore] : UnsafeOps.
byVar) {
4858 if (VisitedVars.find(Var) == VisitedVars.end()) {
4859 VarGrpTy &VarGroup = Groups.emplace_back();
4860 std::queue<const VarDecl *> Queue{};
4863 while (!Queue.empty()) {
4864 const VarDecl *CurrentVar = Queue.front();
4866 VisitedVars.insert(CurrentVar);
4867 VarGroup.push_back(CurrentVar);
4868 auto AdjacentNodes = DependenciesMap[CurrentVar];
4869 for (
const VarDecl *Adj : AdjacentNodes) {
4870 if (VisitedVars.find(Adj) == VisitedVars.end()) {
4876 bool HasParm =
false;
4877 unsigned GrpIdx = Groups.size() - 1;
4879 for (
const VarDecl *
V : VarGroup) {
4880 VarGrpMap[
V] = GrpIdx;
4885 GrpsUnionForParms.insert_range(VarGroup);
4907 for (
auto I = FixablesForAllVars.
byVar.begin();
4908 I != FixablesForAllVars.
byVar.end();) {
4910 if (!VisitedVars.count((*I).first)) {
4912 I = FixablesForAllVars.
byVar.erase(I);
4920 VisitedVars, [&FixablesForAllVars](
const VarDecl *
V) {
4922 return FixablesForAllVars.
byVar.count(
V);
4929 FixItsForVariableGroup =
4931 Tracker, Handler, VarGrpMgr);
4933 for (
const auto &G : UnsafeOps.
noVar) {
4934 G->handleUnsafeOperation(Handler,
false,
4938 for (
const auto &[VD, WarningGadgets] : UnsafeOps.
byVar) {
4939 auto FixItsIt = FixItsForVariableGroup.find(VD);
4941 FixItsIt != FixItsForVariableGroup.end()
4942 ? std::move(FixItsIt->second)
4945 for (
const auto &G : WarningGadgets) {
4946 G->handleUnsafeOperation(Handler,
true,
4954 bool EmitSuggestions) {
4960 if (
const auto *FD = dyn_cast<FunctionDecl>(D)) {
4968 if (
const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
4969 if (MD->getParent()->isLambda() && MD->getParent()->isLocalClass())
4974 if (FReDecl->isExternC()) {
4977 EmitSuggestions =
false;
4987 assert(!Stmts.empty());
4989 FixableGadgetList FixableGadgets;
4990 WarningGadgetList WarningGadgets;
4991 DeclUseTracker Tracker;
4992 for (
const Stmt *S : Stmts) {
4994 WarningGadgets, Tracker);
4996 applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
4997 std::move(Tracker), Handler, EmitSuggestions);
5001 std::set<const Expr *> &UnsafePointers) {
5005 void handleUnsafeOperation(
const Stmt *,
bool,
ASTContext &)
override {}
5007 const Expr *UnsafeArg =
nullptr)
override {}
5008 void handleUnsafeOperationInContainer(
const Stmt *,
bool,
5010 void handleUnsafeOperationInStringView(
const Stmt *,
bool,
5012 void handleUnsafeVariableGroup(
const VarDecl *,
5016 void handleUnsafeUniquePtrArrayAccess(
const DynTypedNode &Node,
5017 bool IsRelatedToDecl,
5025 bool ignoreUnsafeBufferInLibcCall(
const SourceLocation &)
const override {
5028 bool ignoreUnsafeBufferInStaticSizedArray(
5032 std::string getUnsafeBufferUsageAttributeTextAt(
5043 WarningGadgetList WarningGadgets;
5044 bool Matched =
false;
5052#define WARNING_GADGET(name) \
5053 if (name##Gadget::matches(S, Ctx, Result)) \
5054 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));
5055#define WARNING_OPTIONAL_GADGET(name) \
5056 if (name##Gadget::matches(S, Ctx, &Handler, Result)) \
5057 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));
5058#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
5060 for (
auto &WG : WarningGadgets)
5061 for (
auto *E : WG->getUnsafePtrs()) {
5062 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 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 bool isSafeTwoParamContainerConstruct(const CallOrConstructExpr &Node, ASTContext &Ctx, bool AllowDuckTypedContainers=false)
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 isPtrBufferSafe(const Expr *Ptr, const Expr *Size, ASTContext &Ctx, bool AllowDuckTypedContainers=false)
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 const Expr * getSubExprInAddressOfExpr(const Expr &E)
static void forEachDescendantEvaluatedStmt(const Stmt *S, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler, FastMatcher &Matcher)
static void findStmtsInUnspecifiedUntypedContext(const Stmt *S, llvm::function_ref< void(const Stmt *)> InnerMatcher)
static std::optional< FixItList > createOverloadsForFixedParams(const FixitStrategy &S, const FunctionDecl *FD, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static void eraseVarsForUnfixableGroupMates(std::map< const VarDecl *, FixItList > &FixItsForVariable, const VariableGroupsManager &VarGrpMgr)
static FixableGadgetSets groupFixablesByVar(FixableGadgetList &&AllFixableOperations)
static bool isParameterOf(const VarDecl *VD, const Decl *D)
#define SIZED_CONTAINER_OR_VIEW_LIST
static void populateStmtsForFindingGadgets(SmallVector< const Stmt * > &Stmts, const Decl *D)
static std::optional< StringRef > getFunNameText(const FunctionDecl *FD, const SourceManager &SM, const LangOptions &LangOpts)
static Decl::Kind getKind(const Decl *D)
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the C++ template declaration subclasses.
Defines the clang::Preprocessor interface.
MatchFinder::MatchResult MatchResult
Defines the clang::SourceLocation class and associated facilities.
static QualType getPointeeType(const MemRegion *R)
C Language Family Type Representation.
virtual std::optional< FixItList > getFixits(const FixitStrategy &s) const final
static bool matches(const Stmt *S, llvm::SmallVectorImpl< MatchResult > &Results)
DerefSimplePtrArithFixableGadget(const MatchResult &Result)
SourceLocation getSourceLoc() const override
virtual DeclUseList getClaimedVarUseSites() const final
FixableGadgetMatcher(FixableGadgetList &FixableGadgets, DeclUseTracker &Tracker)
bool matches(const DynTypedNode &DynNode, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler) override
Represents the length modifier in a format string in scanf/printf.
bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) override
bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node, bool TraverseQualifier) override
bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node, bool TraverseQualifier) override
bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) override
MatchDescendantVisitor(ASTContext &Context, FastMatcher &Matcher, bool FindAll, bool IgnoreUnevaluatedContext, const UnsafeBufferUsageHandler &NewHandler)
bool TraverseDecl(Decl *Node) override
bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) override
bool findMatch(const DynTypedNode &DynNode)
bool TraverseCXXDefaultInitExpr(CXXDefaultInitExpr *Node) override
bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) override
bool TraverseStmt(Stmt *Node) override
virtual std::optional< FixItList > getFixits(const FixitStrategy &S) const override
static bool matches(const Stmt *S, llvm::SmallVectorImpl< MatchResult > &Results)
SourceLocation getSourceLoc() const override
virtual DeclUseList getClaimedVarUseSites() const override
UPCPreIncrementGadget(const MatchResult &Result)
static bool classof(const Gadget *G)
static bool classof(const Gadget *G)
UUCAddAssignGadget(const MatchResult &Result)
virtual std::optional< FixItList > getFixits(const FixitStrategy &S) const override
static bool matches(const Stmt *S, llvm::SmallVectorImpl< MatchResult > &Results)
virtual DeclUseList getClaimedVarUseSites() const override
SourceLocation getSourceLoc() const override
VariableGroupsManagerImpl(const std::vector< VarGrpTy > &Groups, const std::map< const VarDecl *, unsigned > &VarGrpMap, const llvm::SetVector< const VarDecl * > &GrpsUnionForParms)
VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override
Returns the set of variables (including Var) that need to be fixed together in one step.
VarGrpRef getGroupOfParms() const override
Returns the non-empty group of variables that include parameters of the analyzing function,...
bool matches(const DynTypedNode &DynNode, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler) override
WarningGadgetMatcher(WarningGadgetList &WarningGadgets)
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
SourceManager & getSourceManager()
const ConstantArrayType * getAsConstantArrayType(QualType T) const
DynTypedNodeList getParents(const NodeT &Node)
Forwards to get node parents from the ParentMapContext.
QualType getFILEType() const
Retrieve the C FILE type.
const LangOptions & getLangOpts() const
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
QualType getSizeType() const
Return the unique type for "size_t" (C99 7.17), defined in <stddef.h>.
const TargetInfo & getTargetInfo() const
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
Attr - This represents one attribute.
A builtin binary operation expression such as "x + y" or "x <= y".
static StringRef getOpcodeStr(Opcode Op)
getOpcodeStr - Turn an Opcode enum value into the punctuation char it corresponds to,...
Represents a call to a C++ constructor.
Expr * getArg(unsigned Arg)
Return the specified argument.
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
unsigned getNumArgs() const
Return the number of arguments to the constructor call.
Expr * getInit() const
Get the initializer.
A use of a default initializer in a constructor or in aggregate initialization.
Expr * getExpr()
Get the initialization expression that will be used.
Represents a static or instance method of a struct/union/class.
const CXXRecordDecl * getParent() const
Return the parent of this method declaration, which is the class in which this method is defined.
Represents a C++11 noexcept expression (C++ [expr.unary.noexcept]).
OverloadedOperatorKind getOperator() const
Returns the kind of overloaded operator that this expression refers to.
Represents a C++ struct/union/class.
CXXRecordDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
A C++ typeid expression (C++ [expr.typeid]), which gets the type_info that corresponds to the supplie...
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
static const char * getCastKindName(CastKind CK)
Represents a byte-granular source range.
static CharSourceRange getCharRange(SourceRange R)
SourceLocation getEnd() const
bool isOne() const
Test whether the quantity equals one.
Represents a class template specialization, which refers to a class template with a given set of temp...
const TemplateArgumentList & getTemplateArgs() const
Retrieve the template arguments of the class template specialization.
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
bool isSingleResult() const
DeclContext * getParent()
getParent - Returns the containing DeclContext.
lookup_result lookup(DeclarationName Name) const
lookup - Find the declarations (if any) with the given Name in this context.
A reference to a declared variable, function, enum, etc.
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
bool isSingleDecl() const
isSingleDecl - This method returns true if this DeclStmt refers to a single Decl.
const Decl * getSingleDecl() const
Decl - This represents one declaration (or definition), e.g.
bool isInStdNamespace() const
SourceLocation getEndLoc() const LLVM_READONLY
ASTContext & getASTContext() const LLVM_READONLY
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
DeclContext * getDeclContext()
SourceLocation getBeginLoc() const LLVM_READONLY
virtual Decl * getCanonicalDecl()
Retrieves the "canonical" declaration of the given declaration.
SourceLocation getTypeSpecEndLoc() const
SourceLocation getBeginLoc() const LLVM_READONLY
NestedNameSpecifierLoc getQualifierLoc() const
Retrieve the nested-name-specifier (with source-location information) that qualifies the name of this...
NestedNameSpecifier getQualifier() const
Retrieve the nested-name-specifier that qualifies the name of this declaration, if it was present in ...
Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.
const DynTypedNode * begin() const
A dynamically typed AST node container.
const T * get() const
Retrieve the stored node as type T.
static DynTypedNode create(const T &Node)
Creates a DynTypedNode from Node.
virtual bool TraverseDecl(MaybeConst< Decl > *D)
bool ShouldVisitTemplateInstantiations
bool ShouldVisitImplicitCode
virtual bool TraverseStmt(MaybeConst< Stmt > *S)
This represents one expression.
bool EvaluateAsInt(EvalResult &Result, const ASTContext &Ctx, SideEffectsKind AllowSideEffects=SE_NoSideEffects, bool InConstantContext=false) const
EvaluateAsInt - Return true if this is a constant which we can fold and convert to an integer,...
bool isValueDependent() const
Determines whether the value of this expression depends on.
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
NullPointerConstantValueDependence
Enumeration used to describe how isNullPointerConstant() should cope with value-dependent expressions...
std::optional< llvm::APSInt > getIntegerConstantExpr(const ASTContext &Ctx, bool AllowRelaxedEval=false) const
isIntegerConstantExpr - Return the value if this expression is a valid integer constant expression.
Expr * IgnoreImpCasts() LLVM_READONLY
Skip past any implicit casts which might surround this expression until reaching a fixed point.
Annotates a diagnostic with some code that should be inserted, removed, or replaced to fix the proble...
CharSourceRange RemoveRange
Code that should be replaced to correct the error.
static FixItHint CreateReplacement(CharSourceRange RemoveRange, StringRef Code)
Create a code modification hint that replaces the given source range with the given code string.
static FixItHint CreateRemoval(CharSourceRange RemoveRange)
Create a code modification hint that removes the given source range.
static FixItHint CreateInsertion(SourceLocation InsertionLoc, StringRef Code, bool BeforePreviousInsertions=false)
Create a code modification hint that inserts the given code string at a specific location.
Kind lookup(const VarDecl *VD) const
void set(const VarDecl *VD, Kind K)
Represents a function declaration or definition.
const ParmVarDecl * getParamDecl(unsigned i) const
Stmt * getBody(const FunctionDecl *&Definition) const
Retrieve the body (definition) of the function.
unsigned getBuiltinID(bool ConsiderWrapperFunctions=false) const
Returns a value indicating whether this function corresponds to a builtin function.
ArrayRef< ParmVarDecl * > parameters() const
param_iterator param_begin()
bool isVariadic() const
Whether this function is variadic.
TemplatedKind getTemplatedKind() const
What kind of templated function this is.
bool isConstexpr() const
Whether this is a (C++11) constexpr function or constexpr constructor.
redecl_range redecls() const
Returns an iterator range for all the redeclarations of the same decl.
bool isMain() const
Determines whether this function is "main", which is the entry point into an executable program.
bool isOverloadedOperator() const
Whether this function declaration represents an C++ overloaded operator, e.g., "operator+".
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
DeclarationNameInfo getNameInfo() const
Represents a C11 generic selection.
Expr * getResultExpr()
Return the result expression of this controlling expression.
One of these records is kept for each identifier that is lexed.
StringRef getName() const
Return the actual identifier string.
A simple pair of identifier info and location.
static IntegerLiteral * Create(const ASTContext &C, const llvm::APInt &V, QualType type, SourceLocation l)
Returns a new integer literal with value 'V' and type 'type'.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
static std::optional< Token > findNextToken(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts, bool IncludeComments=false)
Finds the token that comes right after the given location.
static unsigned MeasureTokenLength(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
MeasureTokenLength - Relex the token at the specified location and return its length in bytes in the ...
static SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &LangOpts)
Computes the source location just past the end of the token at this source location.
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
SourceLocation getBeginLoc() const
Retrieve the location of the beginning of this nested-name-specifier.
A single parameter index whose accessors require each use to make explicit the parameter index encodi...
bool isValid() const
Is this parameter index valid?
unsigned getASTIndex() const
Get the parameter index as it would normally be encoded at the AST level of representation: zero-orig...
Represents a parameter to a function.
bool hasDefaultArg() const
Determines whether this parameter has a default argument, either parsed or not.
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
PointerType - C99 6.7.5.1 - Pointer Declarators.
A (possibly-)qualified type.
bool hasQualifiers() const
Determine whether this type has any qualifiers.
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Qualifiers getQualifiers() const
Retrieve the set of qualifiers applied to this type.
QualType getCanonicalType() const
bool isConstQualified() const
Determine whether this type is const-qualified.
std::string getAsString() const
Represents a struct/union/class.
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
bool isBeforeInTranslationUnit(SourceLocation LHS, SourceLocation RHS) const
Determines the order of 2 source locations in the translation unit.
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
Stmt - This represents one statement.
StmtClass getStmtClass() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
const char * getStmtClassName() const
SourceLocation getBeginLoc() const LLVM_READONLY
Exposes information about the current target.
A template argument list.
unsigned size() const
Retrieve the number of template arguments in this template argument list.
Represents a template argument.
QualType getAsType() const
Retrieve the type for a type template argument.
@ Type
The template argument is a type.
ArgKind getKind() const
Return the kind of stored template argument.
The base class of the type hierarchy.
bool isConstantSizeType() const
Return true if this is not a variable sized type, according to the rules of C99 6....
bool isPointerType() const
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
const T * castAs() const
Member-template castAs<specific type>.
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
bool isAnyCharacterType() const
Determine whether this type is any of the built-in character types.
bool isUnsignedIntegerType() const
Return true if this is an integer type that is unsigned, according to C99 6.2.5p6 [which returns true...
bool isAnyPointerType() const
const Type * getUnqualifiedDesugaredType() const
Return the specified type with any "sugar" removed from the type, removing any typedefs,...
UnaryExprOrTypeTraitExpr - expression with either a type or (unevaluated) expression operand.
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Expr * getSubExpr() const
static bool isIncrementOp(Opcode Op)
SourceLocation getBeginLoc() const LLVM_READONLY
static bool isDecrementOp(Opcode Op)
static StringRef getOpcodeStr(Opcode Op)
getOpcodeStr - Turn an Opcode enum value into the punctuation char it corresponds to,...
The interface that lets the caller handle unsafe buffer usage analysis results by overriding this cla...
virtual void handleUnsafeUniquePtrArrayAccess(const DynTypedNode &Node, bool IsRelatedToDecl, ASTContext &Ctx)=0
void addDebugNoteForVar(const VarDecl *VD, SourceLocation Loc, std::string Text)
virtual std::string getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc, StringRef WSSuffix="") const =0
virtual bool isSafeBufferOptOut(const SourceLocation &Loc) const =0
virtual bool ignoreUnsafeBufferInContainer(const SourceLocation &Loc) const =0
virtual void handleUnsafeOperation(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx)=0
Invoked when an unsafe operation over raw pointers is found.
virtual void handleUnsafeOperationInStringView(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx)=0
virtual void handleUnsafeVariableGroup(const VarDecl *Variable, const VariableGroupsManager &VarGrpMgr, FixItList &&Fixes, const Decl *D, const FixitStrategy &VarTargetTypes)=0
Invoked when a fix is suggested against a variable.
virtual void handleUnsafeOperationInContainer(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx)=0
Invoked when an unsafe operation with a std container is found.
virtual bool ignoreUnsafeBufferInStaticSizedArray(const SourceLocation &Loc) const =0
virtual bool ignoreUnsafeBufferInLibcCall(const SourceLocation &Loc) const =0
virtual void handleUnsafeLibcCall(const CallExpr *Call, unsigned PrintfInfo, ASTContext &Ctx, const Expr *UnsafeArg=nullptr)=0
Invoked when a call to an unsafe libc function is found.
Represents a variable declaration or definition.
bool isConstexpr() const
Whether this variable is (C++11) constexpr.
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
VarDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
bool isInlineSpecified() const
bool hasConstantInitialization() const
Determine whether this variable has constant initialization.
const Expr * getInit() const
bool hasLocalStorage() const
Returns true if a variable with function scope is a non-static local variable.
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
const Expr * getAnyInitializer() const
Get the initializer for this variable, no matter which declaration it is attached to.
VariableGroupsManager()=default
virtual VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm=nullptr) const =0
Returns the set of variables (including Var) that need to be fixed together in one step.
virtual VarGrpRef getGroupOfParms() const =0
Returns the non-empty group of variables that include parameters of the analyzing function,...
unsigned getArgIndex() const
bool hasDataArgument() const
HowSpecified getHowSpecified() const
unsigned getConstantAmount() const
const OptionalAmount & getPrecision() const
const PrintfConversionSpecifier & getConversionSpecifier() const
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
void matchEachArgumentWithParamType(const CallExpr &Node, llvm::function_ref< void(QualType, const Expr *)> OnParamAndArg)
bool anyConflict(const llvm::SmallVectorImpl< FixItHint > &FixIts, const SourceManager &SM)
bool matches(const til::SExpr *E1, const til::SExpr *E2)
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
bool matchUnsafePointers(const DynTypedNode &N, ASTContext &Ctx, std::set< const Expr * > &UnsafePointers)
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
void checkUnsafeBufferUsage(const Decl *D, UnsafeBufferUsageHandler &Handler, bool EmitSuggestions)
SourceLocation getVarDeclIdentifierLoc(const DeclaratorDecl *VD)
static bool classof(const OMPClause *T)
std::vector< const VarDecl * > VarGrpTy
std::optional< StringRef > getExprText(const Expr *E, const SourceManager &SM, const LangOptions &LangOpts)
@ Result
The result type of a method or function.
const FunctionProtoType * T
std::optional< std::string > getPointeeTypeText(const DeclaratorDecl *VD, const SourceManager &SM, const LangOptions &LangOpts, std::optional< Qualifiers > *QualifiersToAppend)
std::optional< StringRef > getRangeText(SourceRange SR, const SourceManager &SM, const LangOptions &LangOpts)
std::optional< StringRef > getVarDeclIdentifierText(const DeclaratorDecl *VD, const SourceManager &SM, const LangOptions &LangOpts)
std::optional< SourceLocation > getPastLoc(const NodeTy *Node, const SourceManager &SM, const LangOptions &LangOpts)
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
U cast(CodeGen::Address addr)
ArrayRef< const VarDecl * > VarGrpRef
static StringRef matchLibcNameOrBuiltinChk(StringRef Name)
static bool hasUnsafePrintfStringArg(const CallExpr &Node, ASTContext &Ctx, MatchResult &Result, llvm::StringRef Tag)
static bool isPredefinedUnsafeLibcFunc(const FunctionDecl &Node)
static bool hasUnsafeSnprintfBuffer(const CallExpr &Node, ASTContext &Ctx)
static bool isUnsafeVaListPrintfFunc(const FunctionDecl &Node)
static bool isUnsafeSprintfFunc(const FunctionDecl &Node)
static bool hasUnsafeFormatOrSArg(ASTContext &Ctx, const CallExpr *Call, const Expr *&UnsafeArg, const unsigned FmtIdx, std::optional< const unsigned > FmtArgIdx=std::nullopt, bool isKprintf=false)
static StringRef matchLibcName(StringRef Name)
static bool isUnsafeMemset(const CallExpr &Node, ASTContext &Ctx)
static StringRef matchName(StringRef FunName, bool isBuiltin)
static bool isNormalPrintfFunc(const FunctionDecl &Node)
bool operator()(const NodeTy *N1, const NodeTy *N2) const
std::map< const VarDecl *, std::set< const FixableGadget * >, CompareNode< VarDecl > > byVar
std::map< const VarDecl *, std::set< const WarningGadget * >, CompareNode< VarDecl > > byVar
llvm::SmallVector< const WarningGadget *, 16 > noVar
SourceLocation getBeginLoc() const
getBeginLoc - Retrieve the location of the first token.
SourceLocation getEndLoc() const LLVM_READONLY
EvalResult is a struct with detailed info about an evaluated expression.
APValue Val
Val - This is the value the expression can be folded to.
const BoundNodes Nodes
Contains the nodes bound on the current match.