@@ -1021,6 +1021,46 @@ class UPCPreIncrementGadget : public FixableGadget {
10211021 }
10221022};
10231023
1024+ // Representing a pointer type expression of the form `Ptr += n` in an
1025+ // Unspecified Untyped Context (UUC):
1026+ class UUCAddAssignGadget : public FixableGadget {
1027+ private:
1028+ static constexpr const char *const UUCAddAssignTag =
1029+ " PointerAddAssignUnderUUC" ;
1030+ static constexpr const char *const OffsetTag = " Offset" ;
1031+
1032+ const BinaryOperator *Node; // the `Ptr += n` node
1033+ const Expr *Offset = nullptr ;
1034+
1035+ public:
1036+ UUCAddAssignGadget (const MatchFinder::MatchResult &Result)
1037+ : FixableGadget(Kind::UUCAddAssign),
1038+ Node (Result.Nodes.getNodeAs<BinaryOperator>(UUCAddAssignTag)),
1039+ Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) {
1040+ assert (Node != nullptr && " Expecting a non-null matching result" );
1041+ }
1042+
1043+ static bool classof (const Gadget *G) {
1044+ return G->getKind () == Kind::UUCAddAssign;
1045+ }
1046+
1047+ static Matcher matcher () {
1048+ return stmt (isInUnspecifiedUntypedContext (expr (ignoringImpCasts (
1049+ binaryOperator (hasOperatorName (" +=" ),
1050+ hasLHS (declRefExpr (toSupportedVariable ())),
1051+ hasRHS (expr ().bind (OffsetTag)))
1052+ .bind (UUCAddAssignTag)))));
1053+ }
1054+
1055+ virtual std::optional<FixItList> getFixits (const Strategy &S) const override ;
1056+
1057+ virtual const Stmt *getBaseStmt () const override { return Node; }
1058+
1059+ virtual DeclUseList getClaimedVarUseSites () const override {
1060+ return {dyn_cast<DeclRefExpr>(Node->getLHS ())};
1061+ }
1062+ };
1063+
10241064// Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
10251065// ptr)`:
10261066class DerefSimplePtrArithFixableGadget : public FixableGadget {
@@ -1304,21 +1344,29 @@ std::optional<FixItList> PointerInitGadget::getFixits(const Strategy &S) const {
13041344 return std::nullopt ;
13051345}
13061346
1347+ static bool isNonNegativeIntegerExpr (const Expr *Expr, const VarDecl *VD,
1348+ const ASTContext &Ctx) {
1349+ if (auto ConstVal = Expr->getIntegerConstantExpr (Ctx)) {
1350+ if (ConstVal->isNegative ())
1351+ return false ;
1352+ } else if (!Expr->getType ()->isUnsignedIntegerType ())
1353+ return false ;
1354+ return true ;
1355+ }
1356+
13071357std::optional<FixItList>
13081358ULCArraySubscriptGadget::getFixits (const Strategy &S) const {
13091359 if (const auto *DRE =
13101360 dyn_cast<DeclRefExpr>(Node->getBase ()->IgnoreImpCasts ()))
13111361 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl ())) {
13121362 switch (S.lookup (VD)) {
13131363 case Strategy::Kind::Span: {
1364+
13141365 // If the index has a negative constant value, we give up as no valid
13151366 // fix-it can be generated:
13161367 const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
13171368 VD->getASTContext ();
1318- if (auto ConstVal = Node->getIdx ()->getIntegerConstantExpr (Ctx)) {
1319- if (ConstVal->isNegative ())
1320- return std::nullopt ;
1321- } else if (!Node->getIdx ()->getType ()->isUnsignedIntegerType ())
1369+ if (!isNonNegativeIntegerExpr (Node->getIdx (), VD, Ctx))
13221370 return std::nullopt ;
13231371 // no-op is a good fix-it, otherwise
13241372 return FixItList{};
@@ -1397,10 +1445,8 @@ static std::optional<SourceLocation> getPastLoc(const NodeTy *Node,
13971445 const LangOptions &LangOpts) {
13981446 SourceLocation Loc =
13991447 Lexer::getLocForEndOfToken (Node->getEndLoc (), 0 , SM, LangOpts);
1400-
14011448 if (Loc.isValid ())
14021449 return Loc;
1403-
14041450 return std::nullopt ;
14051451}
14061452
@@ -1788,6 +1834,49 @@ UPCPreIncrementGadget::getFixits(const Strategy &S) const {
17881834 return std::nullopt ; // Not in the cases that we can handle for now, give up.
17891835}
17901836
1837+ std::optional<FixItList>
1838+ UUCAddAssignGadget::getFixits (const Strategy &S) const {
1839+ DeclUseList DREs = getClaimedVarUseSites ();
1840+
1841+ if (DREs.size () != 1 )
1842+ return std::nullopt ; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
1843+ // give up
1844+ if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front ()->getDecl ())) {
1845+ if (S.lookup (VD) == Strategy::Kind::Span) {
1846+ FixItList Fixes;
1847+
1848+ const Stmt *AddAssignNode = getBaseStmt ();
1849+ StringRef varName = VD->getName ();
1850+ const ASTContext &Ctx = VD->getASTContext ();
1851+
1852+ if (!isNonNegativeIntegerExpr (Offset, VD, Ctx))
1853+ return std::nullopt ;
1854+
1855+ // To transform UUC(p += n) to UUC(p = p.subspan(..)):
1856+ bool NotParenExpr =
1857+ (Offset->IgnoreParens ()->getBeginLoc () == Offset->getBeginLoc ());
1858+ std::string SS = varName.str () + " = " + varName.str () + " .subspan" ;
1859+ if (NotParenExpr)
1860+ SS += " (" ;
1861+
1862+ std::optional<SourceLocation> AddAssignLocation = getEndCharLoc (
1863+ AddAssignNode, Ctx.getSourceManager (), Ctx.getLangOpts ());
1864+ if (!AddAssignLocation)
1865+ return std::nullopt ;
1866+
1867+ Fixes.push_back (FixItHint::CreateReplacement (
1868+ SourceRange (AddAssignNode->getBeginLoc (), Node->getOperatorLoc ()),
1869+ SS));
1870+ if (NotParenExpr)
1871+ Fixes.push_back (FixItHint::CreateInsertion (
1872+ Offset->getEndLoc ().getLocWithOffset (1 ), " )" ));
1873+ return Fixes;
1874+ }
1875+ }
1876+ return std::nullopt ; // Not in the cases that we can handle for now, give up.
1877+ }
1878+
1879+
17911880// For a non-null initializer `Init` of `T *` type, this function returns
17921881// `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it
17931882// to output stream.
0 commit comments