<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/94227>94227</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            clang::SoureRange::fullyContains behaves curious with clang::ForStmt & clang::ForStmt::getCond
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            clang
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          T-Gruber
      </td>
    </tr>
</table>

<pre>
    While working with the LibTooling library (llvm-project release 17.x), I noticed a strange behaviour during the comparison of SourceRanges. I have written a small clang-based tool that essentially has the task of detecting all DeclRefExprs in a given C file and and checks if it has a parent of type ForStmt. If this is the case, it is checked whether the DeclRefExpr is part of the condition expression or not.

The implementation of the tool looks something like this:
```cpp
inline bool isInForStmtCond(const clang::ForStmt *FS,
                            const clang::DeclRefExpr *DRE,
                            const clang::SourceManager &SrcMgr) {
  const clang::Expr *CondExpr = FS->getCond();
  if (!CondExpr)
 return false;

  bool IsInside =
 CondExpr->getSourceRange().fullyContains(DRE->getSourceRange());

 llvm::outs() << "CondExpr:\n";
  CondExpr->dump();
  llvm::outs() << "SourceLocation:\n";
  CondExpr->getBeginLoc().dump(SrcMgr);
 CondExpr->getEndLoc().dump(SrcMgr);

  llvm::outs() << "DeclRefExpr:\n";
  DRE->dump();
  llvm::outs() << "SourceLocation:\n";
  DRE->getBeginLoc().dump(SrcMgr);
 DRE->getEndLoc().dump(SrcMgr);
  llvm::outs() << "==> isInForStmtCond: return = " << IsInside << "\n\n";

  return IsInside;
}

template <typename NodeT>
inline const clang::ForStmt *getForStmtParent(const NodeT &Node,
 clang::ASTContext *Context) {
 clang::DynTypedNodeList Parents = Context->getParents(Node);
  for (const clang::DynTypedNode &Parent : Parents) {
    if (const clang::ForStmt *FS = Parent.get<clang::ForStmt>())
      return FS;
    if (const clang::ForStmt *FS = getForStmtParent(Parent, Context))
      return FS;
  }
  return nullptr;
}

class DeclRefExprCallback
 : public clang::ast_matchers::MatchFinder::MatchCallback {
public:
 explicit DeclRefExprCallback() {}

  void
  run(const clang::ast_matchers::MatchFinder::MatchResult &Result) override {
    if (const clang::DeclRefExpr *DRE =
 Result.Nodes.getNodeAs<clang::DeclRefExpr>("decl_ref_expr")) {
      if (const clang::ForStmt *FS = getForStmtParent(*DRE, Result.Context)) {
 isInForStmtCond(FS, DRE, *Result.SourceManager);
      }
    }
 }
};
```

I tested the code snippet using the follwing code snippet:
```cpp
int Counter = 0;

int main(int argc, char **argv) {
  for (unsigned int Index = 0; Index < 2; ++Index) {
 ++Counter;
  }
  return 0;
}
```

The result is in line with my expectations. Only the first DeclRefExpr found is part of the CondExpr.
```
CondExpr:
BinaryOperator 0x55f8f753c0f8 'int' '<'
|-ImplicitCastExpr 0x55f8f753c0c8 'unsigned int' <LValueToRValue>
| `-DeclRefExpr 0x55f8f753c088 'unsigned int' lvalue Var 0x55f8f753bfd0 'Index' 'unsigned int'
`-ImplicitCastExpr 0x55f8f753c0e0 'unsigned int' <IntegralCast>
  `-IntegerLiteral 0x55f8f753c0a8 'int' 2
SourceLocation:
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:32
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:40
DeclRefExpr:
DeclRefExpr 0x55f8f753c088 'unsigned int' lvalue Var 0x55f8f753bfd0 'Index' 'unsigned int'
SourceLocation:
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:32
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:32
==> isInForStmtCond: return = 1

CondExpr:
BinaryOperator 0x55f8f753c0f8 'int' '<'
|-ImplicitCastExpr 0x55f8f753c0c8 'unsigned int' <LValueToRValue>
| `-DeclRefExpr 0x55f8f753c088 'unsigned int' lvalue Var 0x55f8f753bfd0 'Index' 'unsigned int'
`-ImplicitCastExpr 0x55f8f753c0e0 'unsigned int' <IntegralCast>
  `-IntegerLiteral 0x55f8f753c0a8 'int' 2
SourceLocation:
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:32
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:40
DeclRefExpr:
DeclRefExpr 0x55f8f753c118 'unsigned int' lvalue Var 0x55f8f753bfd0 'Index' 'unsigned int'
SourceLocation:
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:45
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:45
==> isInForStmtCond: return = 0

CondExpr:
BinaryOperator 0x55f8f753c0f8 'int' '<'
|-ImplicitCastExpr 0x55f8f753c0c8 'unsigned int' <LValueToRValue>
| `-DeclRefExpr 0x55f8f753c088 'unsigned int' lvalue Var 0x55f8f753bfd0 'Index' 'unsigned int'
`-ImplicitCastExpr 0x55f8f753c0e0 'unsigned int' <IntegralCast>
  `-IntegerLiteral 0x55f8f753c0a8 'int' 2
SourceLocation:
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:32
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:40
DeclRefExpr:
DeclRefExpr 0x55f8f753c150 'int' lvalue Var 0x55f8f753bc90 'Counter' 'int'
SourceLocation:
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:10:7
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:10:7
==> isInForStmtCond: return = 0
```

However, if I set the limit of the loop using a macro, the result changes. 
```cpp
#define LOOP_COUNT 2

int Counter = 0;

int main(int argc, char **argv) {
  for (unsigned int Index = 0; Index < LOOP_COUNT; ++Index) {
    ++Counter;
 }
  return 0;
}
```

Although the SourceRanges and begin/end are still the same, the result of fullyContains is true for all cases.
```
CondExpr:
BinaryOperator 0x558b9052c228 'int' '<'
|-ImplicitCastExpr 0x558b9052c1f8 'unsigned int' <LValueToRValue>
| `-DeclRefExpr 0x558b9052c1b8 'unsigned int' lvalue Var 0x558b9052c100 'Index' 'unsigned int'
`-ImplicitCastExpr 0x558b9052c210 'unsigned int' <IntegralCast>
  `-IntegerLiteral 0x558b9052c1d8 'int' 2
SourceLocation:
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:32
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:40 <Spelling=/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:3:20>
DeclRefExpr:
DeclRefExpr 0x558b9052c1b8 'unsigned int' lvalue Var 0x558b9052c100 'Index' 'unsigned int'
SourceLocation:
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:32
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:32
==> isInForStmtCond: return = 1

CondExpr:
BinaryOperator 0x558b9052c228 'int' '<'
|-ImplicitCastExpr 0x558b9052c1f8 'unsigned int' <LValueToRValue>
| `-DeclRefExpr 0x558b9052c1b8 'unsigned int' lvalue Var 0x558b9052c100 'Index' 'unsigned int'
`-ImplicitCastExpr 0x558b9052c210 'unsigned int' <IntegralCast>
  `-IntegerLiteral 0x558b9052c1d8 'int' 2
SourceLocation:
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:32
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:40 <Spelling=/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:3:20>
DeclRefExpr:
DeclRefExpr 0x558b9052c248 'unsigned int' lvalue Var 0x558b9052c100 'Index' 'unsigned int'
SourceLocation:
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:54
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:54
==> isInForStmtCond: return = 1

CondExpr:
BinaryOperator 0x558b9052c228 'int' '<'
|-ImplicitCastExpr 0x558b9052c1f8 'unsigned int' <LValueToRValue>
| `-DeclRefExpr 0x558b9052c1b8 'unsigned int' lvalue Var 0x558b9052c100 'Index' 'unsigned int'
`-ImplicitCastExpr 0x558b9052c210 'unsigned int' <IntegralCast>
  `-IntegerLiteral 0x558b9052c1d8 'int' 2
SourceLocation:
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:32
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:9:40 <Spelling=/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:3:20>
DeclRefExpr:
DeclRefExpr 0x558b9052c280 'int' lvalue Var 0x558b9052bdc0 'Counter' 'int'
SourceLocation:
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:10:7
/home/gruber/llvm-project/clang-tools-extra/loop_check/demo.c:10:7
==> isInForStmtCond: return = 1
```

I can't explain this behaviour. Is this to be expected? And if so, why?
I would be grateful for any advice!
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzsWl1z4yjW_jX4hopLRnZkX_jCceL3dVV6MpVkZy9TSDqS2CBQAXLif78FSLL80WlPT2d3suVUulsCzsP55pxGVGuWC4A5mtygye2A1qaQav589X-qjkENYplu5_8sGAf8JtUrEzl-Y6bApgB8z-JnKbkd4yxWVG0xIlPON-VVpeS_IDFYAQeqAY-i4TsiM0SWeI2FNCyBFFOsjaIiBxxDQTdM1gqntbJ4Fj6RZUUV01JgmeEnWasEHu1yPcRrXNAN4DfFjAFhkUrKOU44FflVTDWk2EjJsSmowaA1CMMo51tcUO3ADdWvFjYFA4mxW1r6W0j4I2R375XSmFncnG1A4CXOrAaoSN2fpIDkVWOWYWYcIsUVVSCMRTTbCvBKqidTmiFeZ9gUTGPmt02oBqsEZuyIw4EUvxVgClBuRY8Fu6SiyqM6hYiUGSYFhvdKgdb2USqrzyEKblGw8H8_F4BZWXEoQRjqCBoEpxMu5avGWpZgCm-7V3BMorABQNeB_02qyo8wwZkAHFt6pteikW8pRYrINJFCG698ixEummmMyGL1hMjSg-APfo4g-npAZHH7ePdzON5vvlFBc7BI108q-ZYrRGYYRTct4BFZu6-V0D-Ht3j1dIXCuxxaua1Hhx0Gy7AbG7U0dtrPKTC1EjijXENH0dI5pa71WmiWgt2nmWhhmj17AeC3HmY159ulFIYyoRGZ3j7efW9tn9MG3sapl1XWRvtlGIVLFC4xIqQTIlygyVIgQnqi9llL67I60sUPwD1_9zJx3vnDLXIwN5AzcS-TRvRm086WO8IDujuRnkF1Jts9nzzJc2OAT9LIzrxnK2NHcp4efsim9U77e3eUBcLOy22kIEJaqp5ndyhWtgP5WgYakJZqNx_d9hcaKCtOjUO1GVfQEvBvMoVnFN7t5ayPklMOpnn73eXvLpc5JJst7MMu8fRQFk_PNvTg3TRpwj7uZ5V-NtuK520FqYW7Z9pgv592ymqIG0s1M4hM_d5962TSJqXjfNuHt1x7DGyN0sHt5bs2W32cuR13HmCYg0Hh8nilVXeXYnrpubHj6qnH_p_Y9YRl2ocl3mn7jE07x-mmRc15ZdT3XCvhVOv-ObyknMc0eW1grFarOuYs6YtAtXkpqUkKUNqPfLNvKyZSUL2BFmxnDY_VHb72cOcsYeYkC01ARjcHTGO8kSztBK3FKTWfy-Mj6Jpba1z7J7ul3IBSLozP8KLjw7t3rnnMofVVbd3KPiz0vnPtZVrvYCSFhL8oyF7Ana3Em3-fn7_mYV2d0fK452i9nY5LIFfm4IYakUUDsFd97Mey_en5Zv9lZ9voduelbVHWN_saG9DGVruuPkwBa8GqCgyudVtHZ5LzN_vSn_-w1jN4KWthwFc9wUGOtvMlZdbD7CNVeWKFTgrqjI3Igqp8c2CaJnPVwrUbKbaUa5HCe7dF97rE9lTAiNwgcuMG97H8RMPhh4EenAjxU1q0BbPyPs9c3e8ODtfnlFsbj5D4OloP8YPgW69WpvRejOJM1iI9LNrbemR4cv9-leUGbpigavtQgaJGKhy8TybZNIsmYRJkU4xIxKynRvbJnqckauVbXq1LnzeWVBvHT584ccR99TuUcHn_B-U1PMtH9293eKJoidF1cNWXr483PYnHNxYE_0H7i-MsDezixpTRCcJONx8LAcF3hFgLA7mi3FJ1ImAngJsCdc8MKMr34GhfocQTHddhnjWyKmQJiKxy1xcjsur3uYisfOtpGyx9Be9GUbtEyurFNXmIrFIo5dCm-RkKFyH5HNxx41cHlerh2Keb8ourscM9s9wd9bPJJaYvMf1fjunR6OvG9HjyybhnxnRwielLTP-dYnoS9NRw2oLJzK1pa2Nvw_9wDI8CFC6iT8b9kzF8quj_f_kGG8vS0vaMa6zBuIKds5J11btlpWmlKC5poqRdb3b9QlI0NxLf66YQCVPIbD9x__Dw-8vy4R-_PbdO_Hfpt3acfdh42R71dO_1863XgptC1rm_UOrf8biLlhhyK_cKRIqpAqwN49wt1bSEA1PIDO_9j7i7c1E1OF242yGqQf98HzaNZ8GEJIT8TH5viEfZL8rvLV58Tn5vFwd_Mb-3Ghj9mvzespX-L-Z3q46nCjhnIre56hduEKJwQYJOv-ecIZ_pLl_cVJ_e613yxiVvfPG8QcZfN29Mxp-Me8kbl7xxyRun88b0g57Vr4nT5NKznsoTp6_7EioQiYy7JKZM-I_Lus_nhnit_ZCROIbm6gpSFK7wQqS20dWug30rtihctaBvsua22cK5ogaymvuWSWwxTTcsAURGg3QeprNwRgcwH0WjaEzGwWQ6KObZJMumwSSJyGQ6jadRFiQky9IoCeNZFl6PB2xOAjIOroNwNB6NR6NhSsmE0vGMwiQIZ9MIjQMoKeNDq-ahVPmAaV3DfDYmJBpwGgPX7vNEQvyFLiFocjtQc2eWuM41GgecaaN3CIYZDvODz8CaD6Lc-36n6BQIGie1YrLW_urv5O3x9Ylh_9J8FjaoFZ8XxlTuhp2srB8xU9TxMJFl40vHLuUk1oisvNCbOfl3AAAA__-K0KgC">