Using Libtooling to find for loop break statements

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Using Libtooling to find for loop break statements

Hubert Tong via cfe-dev
Hi,

I am trying to find all of the break statements that can cause termination of for loops. The following gives me all the break statements under a “for” node, but I cannot see how to filter out those that cannot terminate the current loop (e.g., they may terminate a case statement, be enclosed within a nested loop, etc.):

forStmt( forEachDescentant( breakStmt().bind( “myBreaks” ) ) ).bind( “myFors” )

How should I go about filtering out the unwanted breaks?

Sent from my iPhone
_______________________________________________
cfe-dev mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: Using Libtooling to find for loop break statements

Hubert Tong via cfe-dev
Here is how a RecursiveASTVisitor could do it (not tested):

```
#include "clang/AST/RecursiveASTVisitor.h"

/// Usage: 
///   std::map<ForStmt *, std::vector<BreakStmt *>> for2breaks;
///   ForBreakMapper(for2breaks).TraverseDecl(
///       /*Context.getTranslationUnitDecl(), some function decl, etc…);
///
class ForBreakMapper : public RecursiveASTVisitor<ForBreakMapper> {
  using BaseType = RecursiveASTVisitor<ForBreakMapper>;

  ForStmt *ActiveFor = nullptr;
  std::map<ForStmt *, std::vector<BreakStmt *>> &forbreaks_mapref;

  

public:
  ForBreakMapper(decltype(for2breaks_mapref) forbreaks_mapref)
      : for2breaks_mapref(for2breaks_mapref) {}

  // Need this override to disable "data recursion" and thus
  // guarantee VisitBreakStmt happens within its parent's 
  // Traverse*Stmt call:
  bool TraverseStmt(Stmt *S) { return BaseType::TraverseStmt(S); }

  

  bool TraverseForStmt(ForStmt *S) {
    SaveAndRestore<ForStmt *> RAII(ActiveFor, S);
    return BaseType::TraverseForStmt(S);
  }

  

  // Nullify ActiveFor for SwitchStmts (any others?)
  bool TraverseSwitchStmt(SwitchStmt *S) {
    SaveAndRestore<ForStmt *> RAII(ActiveFor, nullptr);
    return BaseType::TraverseSwitchStmt(S);
  }

  

  bool VisitBreakStmt(BreakStmt *S) {
    if (ActiveFor && S)
      forbreaks_mapref[ActiveFor].push_back(S);
    return true;
  }
};
```

I’m a big fan of this general technique, i.e. using llvm::SaveAndRestore in Traverse* overrides to keep track of traversal state data, which you then use in your Visit* overrides.  

You do need that seemingly-trivial TraverseStmt override whenever you do this sort of thing with Stmts though, otherwise the Visit* statements will not be called within the ancestor’s Traverse* statements (due to the "data recursion" queueing performed by default in the base RecursiveASTVisitor.)

Good luck,

- Dave

On Aug 12, 2020, at 11:16 AM, Phil King via cfe-dev <[hidden email]> wrote:

Hi,

I am trying to find all of the break statements that can cause termination of for loops. The following gives me all the break statements under a “for” node, but I cannot see how to filter out those that cannot terminate the current loop (e.g., they may terminate a case statement, be enclosed within a nested loop, etc.):

forStmt( forEachDescentant( breakStmt().bind( “myBreaks” ) ) ).bind( “myFors” )

How should I go about filtering out the unwanted breaks?

Sent from my iPhone
_______________________________________________
cfe-dev mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev


_______________________________________________
cfe-dev mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: Using Libtooling to find for loop break statements

Hubert Tong via cfe-dev
Thanks, I’ll have a look at that. I was trying to use the marchers and hadn’t thought of other approaches!

Sent from my iPhone

On 12 Aug 2020, at 20:36, David Rector <[hidden email]> wrote:


Here is how a RecursiveASTVisitor could do it (not tested):

```
#include "clang/AST/RecursiveASTVisitor.h"

/// Usage: 
///   std::map<ForStmt *, std::vector<BreakStmt *>> for2breaks;
///   ForBreakMapper(for2breaks).TraverseDecl(
///       /*Context.getTranslationUnitDecl(), some function decl, etc…);
///
class ForBreakMapper : public RecursiveASTVisitor<ForBreakMapper> {
  using BaseType = RecursiveASTVisitor<ForBreakMapper>;

  ForStmt *ActiveFor = nullptr;
  std::map<ForStmt *, std::vector<BreakStmt *>> &forbreaks_mapref;

  

public:
  ForBreakMapper(decltype(for2breaks_mapref) forbreaks_mapref)
      : for2breaks_mapref(for2breaks_mapref) {}

  // Need this override to disable "data recursion" and thus
  // guarantee VisitBreakStmt happens within its parent's 
  // Traverse*Stmt call:
  bool TraverseStmt(Stmt *S) { return BaseType::TraverseStmt(S); }

  

  bool TraverseForStmt(ForStmt *S) {
    SaveAndRestore<ForStmt *> RAII(ActiveFor, S);
    return BaseType::TraverseForStmt(S);
  }

  

  // Nullify ActiveFor for SwitchStmts (any others?)
  bool TraverseSwitchStmt(SwitchStmt *S) {
    SaveAndRestore<ForStmt *> RAII(ActiveFor, nullptr);
    return BaseType::TraverseSwitchStmt(S);
  }

  

  bool VisitBreakStmt(BreakStmt *S) {
    if (ActiveFor && S)
      forbreaks_mapref[ActiveFor].push_back(S);
    return true;
  }
};
```

I’m a big fan of this general technique, i.e. using llvm::SaveAndRestore in Traverse* overrides to keep track of traversal state data, which you then use in your Visit* overrides.  

You do need that seemingly-trivial TraverseStmt override whenever you do this sort of thing with Stmts though, otherwise the Visit* statements will not be called within the ancestor’s Traverse* statements (due to the "data recursion" queueing performed by default in the base RecursiveASTVisitor.)

Good luck,

- Dave

On Aug 12, 2020, at 11:16 AM, Phil King via cfe-dev <[hidden email]> wrote:

Hi,

I am trying to find all of the break statements that can cause termination of for loops. The following gives me all the break statements under a “for” node, but I cannot see how to filter out those that cannot terminate the current loop (e.g., they may terminate a case statement, be enclosed within a nested loop, etc.):

forStmt( forEachDescentant( breakStmt().bind( “myBreaks” ) ) ).bind( “myFors” )

How should I go about filtering out the unwanted breaks?

Sent from my iPhone
_______________________________________________
cfe-dev mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev


_______________________________________________
cfe-dev mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev