1

I'm trying to write a clang-tidy check for std::optional<bool> r = 5 to catch implicit conversions to bool.

|-DeclStmt <line:4:5, col:30>
| `-VarDecl <col:5, col:29> col:25 r 'std::optional<bool>':'std::optional<bool>' cinit
|   `-ExprWithCleanups <col:29> 'std::optional<bool>':'std::optional<bool>'
|     `-ImplicitCastExpr <col:29> 'std::optional<bool>':'std::optional<bool>' <ConstructorConversion>
|       `-CXXConstructExpr <col:29> 'std::optional<bool>':'std::optional<bool>' 'void (int &&) noexcept(is_nothrow_constructible_v<bool, int>)'
|         `-MaterializeTemporaryExpr <col:29> 'int' xvalue
|           `-IntegerLiteral <col:29> 'int' 5

So far, I have match implicitCastExpr(hasDescendant(cxxConstructExpr())) where I'm matching for an implicitCastExpr with a cxxConstructoExpr. The problem is I want to narrow the match on cxxConstructExpr to find only cases where bool is the template argument. Does anyone know how to do this?

1 Answers1

0

Inside cxxConstructExpr(...), you also need to use hasType, classTemplateSpecializationDecl, hasTemplateArgument, refersToType, and booleanType.

Here is a shell script invoking clang-query that finds implicit conversions to std::optional<bool> from a type other than bool:

#!/bin/sh

query='m
  implicitCastExpr(                              # Implicit conversion
    hasSourceExpression(                         # from a
      cxxConstructExpr(                          # constructor expr
        hasType(                                 # whose type is
          classTemplateSpecializationDecl(       # a template specialization
            hasName("::std::optional"),          # of std::optional
            hasTemplateArgument(                 # with template argument
              0, refersToType(booleanType())     # bool,
            )
          )
        ),
        unless(                                  # unless
          hasArgument(                           # the constructor argument
            0, expr(                             # is an expr with
              hasType(                           # type
                booleanType()                    # bool.
              )
            )
          )
        )
      )
    )
  )'

clang-query -c="$query" "$@"

(I use a shell script so I can format the query expression and add comments.)

Test input test.cc:

// test.cc
// Test clang-query finding implicit conversions to optional<bool>.

#include <optional>                    // std::optional

void f()
{
  std::optional<bool> r = 5;           // reported

  std::optional<int>  s = 6;           // not reported

  std::optional<bool> t = false;       // not reported
}

// EOF

Invocation of the script (saved as cmd.sh):

$ ./cmd.sh test.cc -- --std=c++17 

Match #1:

[...path...]/test.cc:8:27: note: "root" binds here
  std::optional<bool> r = 5;           // reported
                          ^
1 match.

I used Clang+LLVM-14.0.0, although I don't think I've used anything particularly recent here.

Figuring out these match expressions can be quite difficult. The main reference is the AST Matcher Reference, but even with that, it often requires a lot of trial and error.

Scott McPeak
  • 8,803
  • 2
  • 40
  • 79