[PATCH] D98738: [clang-tidy] performance-* checks: Match AllowedTypes against qualified type names when they contain "::".

Haojian Wu via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 17 13:31:53 PDT 2021


hokein added inline comments.


================
Comment at: clang-tools-extra/clang-tidy/utils/Matchers.h:52
 
 AST_MATCHER_P(NamedDecl, matchesAnyListedName, std::vector<std::string>,
               NameList) {
----------------
worth some comments on this.


================
Comment at: clang-tools-extra/clang-tidy/utils/Matchers.h:55
   return llvm::any_of(NameList, [&Node](const std::string &Name) {
-      return llvm::Regex(Name).match(Node.getName());
-    });
+    if (Name.find("::") != std::string::npos) {
+      return llvm::Regex(Name).match(Node.getQualifiedNameAsString());
----------------
If we pass the name as a `llvm::StringRef`, we can use `if (Name.contains("::"))` which seems a bit clearer to me.


================
Comment at: clang-tools-extra/clang-tidy/utils/Matchers.h:55
   return llvm::any_of(NameList, [&Node](const std::string &Name) {
-      return llvm::Regex(Name).match(Node.getName());
-    });
+    if (Name.find("::") != std::string::npos) {
+      return llvm::Regex(Name).match(Node.getQualifiedNameAsString());
----------------
hokein wrote:
> If we pass the name as a `llvm::StringRef`, we can use `if (Name.contains("::"))` which seems a bit clearer to me.
there is a tricky case where the `::` is a prefix of the Name, e.g.  a global symbol x, the Name is `::x`, and `getQualifiedNameAsString` of symbol x is `x` (without the `::` prefix), then the matcher will not find a match, but we expect this is a match right?



================
Comment at: clang-tools-extra/clang-tidy/utils/Matchers.h:56
+    if (Name.find("::") != std::string::npos) {
+      return llvm::Regex(Name).match(Node.getQualifiedNameAsString());
+    }
----------------
ymandel wrote:
> flx wrote:
> > ymandel wrote:
> > > flx wrote:
> > > > ymandel wrote:
> > > > > nit: while this change is not much worse than the existing code, the matcher really should perform this test for "::", and create the Regex, for each string just once. Regex construction is relatively expensive, and matchers of this sort (very generic) are often called quite frequently.
> > > > > 
> > > > > for that matter, this matcher is now really close to `matchesName` and would probably be best as a first-class matcher, rather than limited to clang-tidy/utils. But, I'll admit that such is beyond the scope of this patch.
> > > > It wouldn't be hard to change it something like this:
> > > > 
> > > > 
> > > > ```
> > > > struct MatcherPair {                                                                                                       
> > > >     llvm::Regex regex;                                                                                                       
> > > >     bool matchQualifiedName = false;                                                                                         
> > > >   };                                                                                                                         
> > > >   std::vector<MatcherPair> Matchers;                                                                                         
> > > >   std::transform(                                                                                                            
> > > >       NameList.begin(), NameList.end(), std::back_inserter(Matchers),                                                        
> > > >       [](const std::string& Name) {                                                                                          
> > > >         return MatcherPair{                                                                                                  
> > > >             .regex = llvm::Regex(Name),                                                                                      
> > > >             .matchQualifiedName = Name.find("::") != std::string::npos,                                                      
> > > >         };                                                                                                                   
> > > >       });                                                                                                                    
> > > >   return llvm::any_of(Matchers, [&Node](const MatcherPair& Matcher) {                                                        
> > > >     if (Matcher.matchQualifiedName) {                                                                                        
> > > >       return Matcher.regex.match(Node.getQualifiedNameAsString());                                                           
> > > >     }                                                                                                                        
> > > >     return Matcher.regex.match(Node.getName());                                                                              
> > > >   });                       
> > > > ```
> > > > 
> > > > Is this what you had in mind?
> > > Thanks, that's almost it. The key point is that the code before the `return` be executed once, during matcher construction, and not each time `match` is called. You could define your own class (instead of using the macro) or just your own factory function:
> > > 
> > > ```
> > > struct MatcherPair {                                                                                                       
> > >     llvm::Regex regex;                                                                                                       
> > >     bool matchQualifiedName = false;                                                                                         
> > > };        
> > > 
> > > AST_MATCHER_P(NamedDecl, matchesAnyListedNameImpl, std::vector<MatcherPair>, Matchers) {
> > >   return llvm::any_of(Matchers, [&Node](const MatcherPair& Matcher) {                                                        
> > >     if (Matcher.matchQualifiedName) {                                                                                        
> > >       return Matcher.regex.match(Node.getQualifiedNameAsString());                                                           
> > >     }                                                                                                                        
> > >     return Matcher.regex.match(Node.getName());                                                                              
> > >   });
> > > }
> > > 
> > > Matcher<NamedDecl> matchesAnyListedName(std::vector<std::string> NameList) {
> > >   std::vector<MatcherPair> Matchers;                                                                                         
> > >   std::transform(                                                                                                            
> > >       NameList.begin(), NameList.end(), std::back_inserter(Matchers),                                                        
> > >       [](const std::string& Name) {                                                                                          
> > >         return MatcherPair{                                                                                                  
> > >             .regex = llvm::Regex(Name),                                                                                      
> > >             .matchQualifiedName = Name.find("::") != std::string::npos,                                                      
> > >         };                                                                                                                   
> > >       });
> > >   return matchesAnyListNameImpl(std::move(Matchers));
> > > }
> > > ```
> > Thanks for the snippet! I tried, but ran into the issue that llvm::Regex can only be moved, but AST_MATCHER_P uses copy construction of its param somewhere. I was able to work around it by using shared ptrs:
> > 
> > ```
> >                                                                                                                         
> > struct RegexAndScope {                                                                                                       
> >   llvm::Regex regex;                                                                                                         
> >   bool matchQualifiedName = false;                                                                                           
> > };                                                                                                                           
> >                                                                                                                              
> > AST_MATCHER_P(NamedDecl, matchesAnyListedNameImpl,                                                                           
> >               std::vector<std::shared_ptr<RegexAndScope>>, Matchers) {                                                       
> >   return llvm::any_of(                                                                                                       
> >       Matchers, [&Node](const std::shared_ptr<RegexAndScope>& Matcher) {                                                     
> >         if (Matcher->matchQualifiedName) {                                                                                   
> >           return Matcher->regex.match(Node.getQualifiedNameAsString());                                                      
> >         }                                                                                                                    
> >         return Matcher->regex.match(Node.getName());                                                                         
> >       });                                                                                                                    
> > }                                                                                                                            
> >                                                                                                                              
> > ast_matchers::internal::Matcher<NamedDecl> matchesAnyListedName(                                                             
> >     const std::vector<std::string>& NameList) {                                                                              
> >   std::vector<std::shared_ptr<RegexAndScope>> Matchers;                                                                      
> >   std::transform(NameList.begin(), NameList.end(), std::back_inserter(Matchers),                                             
> >                  [](const std::string& Name) {                                                                               
> >                    auto RAS = std::make_shared<RegexAndScope>();                                                             
> >                    RAS->regex = llvm::Regex(Name);                                                                           
> >                    RAS->matchQualifiedName =                                                                                 
> >                        Name.find("::") != std::string::npos;                                                                 
> >                    return RAS;                                                                                               
> >                  });                                                                                                         
> >   return matchesAnyListedNameImpl(std::move(Matchers));                                                                      
> > }                 
> > ```
> > 
> > What do you think?
> That works for me, but let's see what other reviewers think. Personally, I'd just define the class directly at this point:
> ```
> class MatchesAnyListedNameMatcher
>     : public ast_matchers::internal::MatcherInterface<NamedDecl> {
>  public:
>   explicit MatchesAnyListedNameMatcher(llvm::ArrayRef<std::string> NameList) {
>     std::transform(
>         NameList.begin(), NameList.end(), std::back_inserter(Matchers),
>         [](const std::string& Name) {
>           return MatcherPair{
>               .regex = llvm::Regex(Name),
>               .matchQualifiedName = Name.find("::") != std::string::npos,
>           };
>         });
>   }
>   bool matches(
>       const NamedDecl& Node, ast_matchers::internal::ASTMatchFinder* Finder,
>       ast_matchers::internal::BoundNodesTreeBuilder* Builder) const override {
>     return llvm::any_of(Matchers, [&Node](const MatcherPair& Matcher) {
>       if (Matcher.matchQualifiedName) {
>         return Matcher.regex.match(Node.getQualifiedNameAsString());
>       }
>       return Matcher.regex.match(Node.getName());
>     });
>   }
> 
>  private:
>   std::vector<MatcherPair> Matchers;
> };
> 
> inline ::clang::ast_matchers::internal::Matcher<Type> matchesAnyListedName(
>     llvm::ArrayRef<std::string> NameList) {
>   return ::clang::ast_matchers::internal::makeMatcher(
>       new MatchesAnyListedNameMatcher(NameList))
> }
> ```
+1 the idea looks good to me. 


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D98738/new/

https://reviews.llvm.org/D98738



More information about the cfe-commits mailing list