@@ -1028,6 +1028,46 @@ class UPCPreIncrementGadget : public FixableGadget {
10281028 }
10291029};
10301030
1031+ // Representing a pointer type expression of the form `Ptr += n` in an
1032+ // Unspecified Untyped Context (UUC):
1033+ class UUCAddAssignGadget : public FixableGadget {
1034+ private:
1035+ static constexpr const char *const UUCAddAssignTag =
1036+ " PointerAddAssignUnderUUC" ;
1037+ static constexpr const char *const OffsetTag = " Offset" ;
1038+
1039+ const BinaryOperator *Node; // the `Ptr += n` node
1040+ const Expr *Offset = nullptr ;
1041+
1042+ public:
1043+ UUCAddAssignGadget (const MatchFinder::MatchResult &Result)
1044+ : FixableGadget(Kind::UUCAddAssign),
1045+ Node (Result.Nodes.getNodeAs<BinaryOperator>(UUCAddAssignTag)),
1046+ Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) {
1047+ assert (Node != nullptr && " Expecting a non-null matching result" );
1048+ }
1049+
1050+ static bool classof (const Gadget *G) {
1051+ return G->getKind () == Kind::UUCAddAssign;
1052+ }
1053+
1054+ static Matcher matcher () {
1055+ return stmt (isInUnspecifiedUntypedContext (expr (ignoringImpCasts (
1056+ binaryOperator (hasOperatorName (" +=" ),
1057+ hasLHS (declRefExpr (toSupportedVariable ())),
1058+ hasRHS (expr ().bind (OffsetTag)))
1059+ .bind (UUCAddAssignTag)))));
1060+ }
1061+
1062+ virtual std::optional<FixItList> getFixits (const Strategy &S) const override ;
1063+
1064+ virtual const Stmt *getBaseStmt () const override { return Node; }
1065+
1066+ virtual DeclUseList getClaimedVarUseSites () const override {
1067+ return {dyn_cast<DeclRefExpr>(Node->getLHS ())};
1068+ }
1069+ };
1070+
10311071// Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
10321072// ptr)`:
10331073class DerefSimplePtrArithFixableGadget : public FixableGadget {
@@ -1312,21 +1352,29 @@ PointerInitGadget::getFixits(const Strategy &S) const {
13121352 return std::nullopt ;
13131353}
13141354
1355+ static bool isNonNegativeIntegerExpr (const Expr *Expr, const VarDecl *VD,
1356+ const ASTContext &Ctx) {
1357+ if (auto ConstVal = Expr->getIntegerConstantExpr (Ctx)) {
1358+ if (ConstVal->isNegative ())
1359+ return false ;
1360+ } else if (!Expr->getType ()->isUnsignedIntegerType ())
1361+ return false ;
1362+ return true ;
1363+ }
1364+
13151365std::optional<FixItList>
13161366ULCArraySubscriptGadget::getFixits (const Strategy &S) const {
13171367 if (const auto *DRE =
13181368 dyn_cast<DeclRefExpr>(Node->getBase ()->IgnoreImpCasts ()))
13191369 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl ())) {
13201370 switch (S.lookup (VD)) {
13211371 case Strategy::Kind::Span: {
1372+
13221373 // If the index has a negative constant value, we give up as no valid
13231374 // fix-it can be generated:
13241375 const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
13251376 VD->getASTContext ();
1326- if (auto ConstVal = Node->getIdx ()->getIntegerConstantExpr (Ctx)) {
1327- if (ConstVal->isNegative ())
1328- return std::nullopt ;
1329- } else if (!Node->getIdx ()->getType ()->isUnsignedIntegerType ())
1377+ if (!isNonNegativeIntegerExpr (Node->getIdx (), VD, Ctx))
13301378 return std::nullopt ;
13311379 // no-op is a good fix-it, otherwise
13321380 return FixItList{};
@@ -1405,10 +1453,8 @@ static std::optional<SourceLocation> getPastLoc(const NodeTy *Node,
14051453 const LangOptions &LangOpts) {
14061454 SourceLocation Loc =
14071455 Lexer::getLocForEndOfToken (Node->getEndLoc (), 0 , SM, LangOpts);
1408-
14091456 if (Loc.isValid ())
14101457 return Loc;
1411-
14121458 return std::nullopt ;
14131459}
14141460
@@ -1766,6 +1812,47 @@ fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node) {
17661812 FixItHint::CreateReplacement (Node->getSourceRange (), SS.str ())};
17671813}
17681814
1815+ std::optional<FixItList>
1816+ UUCAddAssignGadget::getFixits (const Strategy &S) const {
1817+ DeclUseList DREs = getClaimedVarUseSites ();
1818+
1819+ if (DREs.size () != 1 )
1820+ return std::nullopt ; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
1821+ // give up
1822+ if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front ()->getDecl ())) {
1823+ if (S.lookup (VD) == Strategy::Kind::Span) {
1824+ FixItList Fixes;
1825+
1826+ const Stmt *AddAssignNode = getBaseStmt ();
1827+ StringRef varName = VD->getName ();
1828+ const ASTContext &Ctx = VD->getASTContext ();
1829+
1830+ if (!isNonNegativeIntegerExpr (Offset, VD, Ctx))
1831+ return std::nullopt ;
1832+
1833+ // To transform UUC(p += n) to UUC(p = p.subspan(..)):
1834+ bool NotParenExpr =
1835+ (Offset->IgnoreParens ()->getBeginLoc () == Offset->getBeginLoc ());
1836+ std::string SS = varName.str () + " = " + varName.str () + " .subspan" ;
1837+ if (NotParenExpr)
1838+ SS += " (" ;
1839+
1840+ std::optional<SourceLocation> AddAssignLocation = getEndCharLoc (
1841+ AddAssignNode, Ctx.getSourceManager (), Ctx.getLangOpts ());
1842+ if (!AddAssignLocation)
1843+ return std::nullopt ;
1844+
1845+ Fixes.push_back (FixItHint::CreateReplacement (
1846+ SourceRange (AddAssignNode->getBeginLoc (), Node->getOperatorLoc ()),
1847+ SS));
1848+ if (NotParenExpr)
1849+ Fixes.push_back (FixItHint::CreateInsertion (
1850+ Offset->getEndLoc ().getLocWithOffset (1 ), " )" ));
1851+ return Fixes;
1852+ }
1853+ }
1854+ return std::nullopt ; // Not in the cases that we can handle for now, give up.
1855+ }
17691856
17701857std::optional<FixItList> UPCPreIncrementGadget::getFixits (const Strategy &S) const {
17711858 DeclUseList DREs = getClaimedVarUseSites ();
0 commit comments