Commit 782392db authored by Mitchell Balan's avatar Mitchell Balan
Browse files

[clang-tidy] modernize-use-using work with multi-argument templates

Summary:
If clang-tidy's modernize-use-using feature finds any commas that are not within parentheses, it won't create a fix. That means it won't change lines like:
  typedef std::pair<int, int> Point;
to
  using Point = std::pair<int, int>;
or even:
  typedef std::map<std::string, Foo> MyMap;
  typedef std::vector<int,MyCustomAllocator<int>> MyVector;

This patch allows the fix to apply to lines with commas if they are within parentheses or angle brackets that were not themselves within parentheses.

Reviewers: alexfh, hokein, aaron.ballman

Patch by: poelmanc

Subscribers: jonathanmeier, cfe-commits

Tags: #clang, #clang-tools-extra

Differential Revision: https://reviews.llvm.org/D67460
parent 9c1baa23
Loading
Loading
Loading
Loading
+31 −11
Original line number Diff line number Diff line
@@ -39,25 +39,46 @@ static bool CheckRemoval(SourceManager &SM, SourceLocation StartLoc,
                  File.begin(), TokenBegin, File.end());

  Token Tok;
  int ParenLevel = 0;
  int NestingLevel = 0; // Parens, braces, and square brackets
  int AngleBracketLevel = 0;
  bool FoundTypedef = false;

  while (!DeclLexer.LexFromRawLexer(Tok) && !Tok.is(tok::semi)) {
    switch (Tok.getKind()) {
    case tok::l_brace:
    case tok::r_brace:
      // This might be the `typedef struct {...} T;` case.
      if (NestingLevel == 0 && AngleBracketLevel == 0) {
        // At top level, this might be the `typedef struct {...} T;` case.
        // Inside parens, square brackets, or angle brackets it's not.
        return false;
      }
      ++NestingLevel;
      break;
    case tok::l_paren:
      ParenLevel++;
    case tok::l_square:
      ++NestingLevel;
      break;
    case tok::r_brace:
    case tok::r_paren:
      ParenLevel--;
    case tok::r_square:
      --NestingLevel;
      break;
    case tok::less:
      // If not nested in paren/brace/square bracket, treat as opening angle bracket.
      if (NestingLevel == 0)
        ++AngleBracketLevel;
      break;
    case tok::greater:
      // Per C++ 17 Draft N4659, Section 17.2/3
      //   https://timsong-cpp.github.io/cppwp/n4659/temp.names#3:
      // "When parsing a template-argument-list, the first non-nested > is
      // taken as the ending delimiter rather than a greater-than operator."
      // If not nested in paren/brace/square bracket, treat as closing angle bracket.
      if (NestingLevel == 0)
        --AngleBracketLevel;
      break;
    case tok::comma:
      if (ParenLevel == 0) {
        // If there is comma and we are not between open parenthesis then it is
        // two or more declarations in this chain.
      if (NestingLevel == 0 && AngleBracketLevel == 0) {
        // If there is a non-nested comma we have two or more declarations in this chain.
        return false;
      }
      break;
@@ -88,8 +109,7 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
  if (StartLoc.isMacroID() && IgnoreMacros)
    return;

  auto Diag =
      diag(StartLoc, "use 'using' instead of 'typedef'");
  auto Diag = diag(StartLoc, "use 'using' instead of 'typedef'");

  // do not fix if there is macro or array
  if (MatchedDecl->getUnderlyingType()->isArrayType() || StartLoc.isMacroID())
+64 −0
Original line number Diff line number Diff line
@@ -183,3 +183,67 @@ class E : public C<E> {
  void f() override { super::f(); }
};
}

template <typename T1, typename T2>
class TwoArgTemplate {
  typedef TwoArgTemplate<T1, T2> self;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'using' instead of 'typedef'
  // CHECK-FIXES: using self = TwoArgTemplate<T1, T2>;
};

template <bool B, typename T>
struct S {};

typedef S<(0 > 0), int> S_t, *S_p;
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: typedef S<(0 > 0), int> S_t, *S_p;

typedef S<(0 < 0), int> S2_t, *S2_p;
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: typedef S<(0 < 0), int> S2_t, *S2_p;

typedef S<(0 > 0 && (3 > 1) && (1 < 1)), int> S3_t;
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: using S3_t = S<(0 > 0 && (3 > 1) && (1 < 1)), int>;

template <bool B>
struct Q {};

constexpr bool b[1] = {true};

typedef Q<b[0 < 0]> Q_t, *Q_p;
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: typedef Q<b[0 < 0]> Q_t, *Q_p;

typedef Q<b[0 < 0]> Q2_t;
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: using Q2_t = Q<b[0 < 0]>;

struct T {
  constexpr T(bool) {}

  static constexpr bool b = true;
};

typedef Q<T{0 < 0}.b> Q3_t, *Q3_p;
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: typedef Q<T{0 < 0}.b> Q3_t, *Q3_p;

typedef Q<T{0 < 0}.b> Q3_t;
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: using Q3_t = Q<T{0 < 0}.b>;

typedef TwoArgTemplate<TwoArgTemplate<int, Q<T{0 < 0}.b> >, S<(0 < 0), Q<b[0 < 0]> > > Nested_t;
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: using Nested_t = TwoArgTemplate<TwoArgTemplate<int, Q<T{0 < 0}.b> >, S<(0 < 0), Q<b[0 < 0]> > >;

template <typename... Args>
class Variadic {};

typedef Variadic<Variadic<int, bool, Q<T{0 < 0}.b> >, S<(0 < 0), Variadic<Q<b[0 < 0]> > > > Variadic_t;
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: using Variadic_t = Variadic<Variadic<int, bool, Q<T{0 < 0}.b> >, S<(0 < 0), Variadic<Q<b[0 < 0]> > > >

typedef Variadic<Variadic<int, bool, Q<T{0 < 0}.b> >, S<(0 < 0), Variadic<Q<b[0 < 0]> > > > Variadic_t, *Variadic_p;
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: typedef Variadic<Variadic<int, bool, Q<T{0 < 0}.b> >, S<(0 < 0), Variadic<Q<b[0 < 0]> > > > Variadic_t, *Variadic_p;