firolino updated the summary for this revision.
firolino updated this revision to Diff 82855.
firolino marked an inline comment as done.
firolino added a comment.

- applied suggestions from Aaron
- added support and test cases for TagDecl e.g.

  struct S {} S1, S2;


https://reviews.llvm.org/D27621

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/OneNamePerDeclarationCheck.cpp
  clang-tidy/readability/OneNamePerDeclarationCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-one-name-per-declaration.rst
  test/clang-tidy/readability-one-name-per-declaration-complex.cpp
  test/clang-tidy/readability-one-name-per-declaration-modern.cpp
  test/clang-tidy/readability-one-name-per-declaration-simple.cpp

Index: test/clang-tidy/readability-one-name-per-declaration-simple.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/readability-one-name-per-declaration-simple.cpp
@@ -0,0 +1,176 @@
+// RUN: %check_clang_tidy %s readability-one-name-per-declaration %t
+
+int cantTouchA, cantTouchB;
+
+void simple() 
+{
+    int dontTouchC;
+    
+    long empty;
+    long long1 = 11, *long2 = &empty, * long3 = ∅
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}long long1 = 11;
+    // CHECK-FIXES: {{^    }}long *long2 = ∅
+    // CHECK-FIXES: {{^    }}long * long3 = ∅
+    
+    long ** lint1, lint2 = 0, * lint3, **linn;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}long ** lint1;
+    // CHECK-FIXES: {{^    }}long lint2 = 0;
+    // CHECK-FIXES: {{^    }}long * lint3;
+    // CHECK-FIXES: {{^    }}long **linn;
+    
+    	long int* lint4, *lint5,  lint6;
+    	// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: multiple declarations should be split
+    	// CHECK-FIXES: {{^    	}}long int* lint4;
+    	// CHECK-FIXES: {{^    	}}long int *lint5;
+    	// CHECK-FIXES: {{^    	}}long int lint6;
+    
+    /* *& */ int /* *& */ ** /* *& */ pp,*xx;
+    // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}/* *& */ int /* *& */ ** /* *& */ pp;
+    // CHECK-FIXES: {{^    }}int *xx;
+    
+    unsigned int uint1 = 0,uint2 = 44u, uint3, uint4=4;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}unsigned int uint1 = 0;
+    // CHECK-FIXES: {{^    }}unsigned int uint2 = 44u;
+    // CHECK-FIXES: {{^    }}unsigned int uint3;
+    // CHECK-FIXES: {{^    }}unsigned int uint4=4;
+    
+    const int * const cpc = &dontTouchC, simple = 0;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}const int * const cpc = &dontTouchC;
+    // CHECK-FIXES: {{^    }}const int simple = 0;
+    
+    double darray1[] = {}, darray2[] = {1,	2},dv1 = 3,dv2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}double darray1[] = {};
+    // CHECK-FIXES: {{^    }}double darray2[] = {1,	2};
+    // CHECK-FIXES: {{^    }}double dv1 = 3;
+    // CHECK-FIXES: {{^    }}double dv2;
+    
+    int notransform[] =   {
+                              1,
+                              2
+                          };
+    
+    const int cx = 1, cy = 2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}const int cx = 1;
+    // CHECK-FIXES: {{^    }}const int cy = 2;
+    
+    volatile int vx, vy;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}volatile int vx;
+    // CHECK-FIXES: {{^    }}volatile int vy;
+    
+    signed char sc1 = 'h', sc2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}signed char sc1 = 'h';
+    // CHECK-FIXES: {{^    }}signed char sc2;
+    
+    long long ll1, ll2, ***ft;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}long long ll1;
+    // CHECK-FIXES: {{^    }}long long ll2;
+    // CHECK-FIXES: {{^    }}long long ***ft;
+    
+    const char *cstr1 = "str1", *cstr2="str2";
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}const char *cstr1 = "str1";
+    // CHECK-FIXES: {{^    }}const char *cstr2="str2";
+    
+    const char *literal1 = "clang"		"test" \
+                           "one",
+               *literal2 = "empty", literal3[] = "three";
+    // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}const char *literal1 = "clang"		"test" \
+    // CHECK-FIXES: {{^                           }}"one";
+    // CHECK-FIXES: {{^    }}const char *literal2 = "empty";
+    // CHECK-FIXES: {{^    }}const char literal3[] = "three";
+    
+    int intarray[] =
+          {
+           			1,
+                    2,
+                    3,
+                    4
+          }, bb = 5;
+    // CHECK-MESSAGES: :[[@LINE-7]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}int intarray[] =
+    // CHECK-FIXES: {{^    }}      {
+    // CHECK-FIXES: {{^    }}       			1,
+    // CHECK-FIXES: {{^    }}                2,
+    // CHECK-FIXES: {{^    }}                3,
+    // CHECK-FIXES: {{^    }}                4
+    // CHECK-FIXES: {{^    }}      };
+    // CHECK-FIXES: {{^    }}int bb = 5;
+    
+    const int cint3 = 4, cintarray[] = { 1, 2, 3, 4 };
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}const int cint3 = 4;
+    // CHECK-FIXES: {{^    }}const int cintarray[] = { 1, 2, 3, 4 };
+
+    union Ss{
+        int m1;
+        float m2;
+    } in, out;
+    // CHECK-MESSAGES: :[[@LINE-4]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}Ss in;
+    // CHECK-FIXES: {{^    }}Ss out;
+    
+    enum E {} E1, E2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}E E1;
+    // CHECK-FIXES: {{^    }}E E2;
+
+    struct S {int t;} S1 = {1}, S2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}S S1 = {1};
+    // CHECK-FIXES: {{^    }}S S2;
+
+    class C {}C1, C2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}C C1;
+    // CHECK-FIXES: {{^    }}C C2; 
+
+    struct B {} ignoreMe1;
+    enum {} ignoreMe2, ignoreMe3;
+    struct {} ignoreMe4, ignoreMe5;
+
+    typedef struct X { int t; } X, Y;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}typedef X X;
+    // CHECK-FIXES: {{^    }}typedef X Y;
+
+    
+    int refme1;
+    int refme2 = 0;
+    const int &r1 = refme1, &r2 = refme2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}const int &r1 = refme1;
+    // CHECK-FIXES: {{^    }}const int &r2 = refme2;
+    
+    typedef int ta, tb;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}typedef int ta;
+    // CHECK-FIXES: {{^    }}typedef int tb;
+    
+    typedef const int tca, tcb;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}typedef const int tca;
+    // CHECK-FIXES: {{^    }}typedef const int tcb;
+    
+    typedef int const tac, tbc;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}typedef int const tac;
+    // CHECK-FIXES: {{^    }}typedef int const tbc;
+    
+    int *(i), (*j), (((k)));
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}int *(i);
+    // CHECK-FIXES: {{^    }}int (*j);
+    // CHECK-FIXES: {{^    }}int (((k)));
+}
+
Index: test/clang-tidy/readability-one-name-per-declaration-modern.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/readability-one-name-per-declaration-modern.cpp
@@ -0,0 +1,83 @@
+// RUN: %check_clang_tidy %s readability-one-name-per-declaration %t -- -- \
+// RUN:   -std=c++1y
+
+namespace std {    
+    template <typename T>
+    class initializer_list {};
+    
+    template <typename T>
+    class vector 
+    {
+      public:
+        vector() = default;
+        vector(initializer_list<T> init) {}
+    };
+    
+    class string 
+    {
+      public:
+        string() = default;
+        string(const char*) {}
+    };
+    
+    namespace string_literals {    
+        string operator""s(const char*, decltype(sizeof(int))) 
+        {   
+            return string(); 
+        }
+    }
+}
+
+namespace Types {    
+    typedef int MyType;    
+    int dontTouch1, dontTouch2;
+}
+
+void modern() 
+{
+    auto autoInt1 = 3, autoInt2 = 4;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^....}}auto autoInt1 = 3;
+    // CHECK-FIXES: {{^....}}auto autoInt2 = 4;
+    
+    decltype(int()) declnottouch= 4;
+    
+    decltype(int()) declint1 = 5, declint2 = 3;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split    
+    // CHECK-FIXES: {{^....}}decltype(int()) declint1 = 5;
+    // CHECK-FIXES: {{^....}}decltype(int()) declint2 = 3;
+    
+    std::vector<int> vectorA = {1,2}, vectorB = {1,2,3}, vectorC({1,1,1});
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^....}}std::vector<int> vectorA = {1,2};
+    // CHECK-FIXES: {{^....}}std::vector<int> vectorB = {1,2,3};
+    // CHECK-FIXES: {{^....}}std::vector<int> vectorC({1,1,1});
+    
+    using uType = int;
+    uType utype1, utype2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^....}}uType utype1;
+    // CHECK-FIXES: {{^....}}uType utype2;
+    
+    Types::MyType mytype1, mytype2, mytype3 = 3;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^....}}Types::MyType mytype1;
+    // CHECK-FIXES: {{^....}}Types::MyType mytype2;
+    // CHECK-FIXES: {{^....}}Types::MyType mytype3 = 3;
+    
+    {
+        using namespace std::string_literals;
+        
+        std::vector<std::string> s{"foo"s, "bar"s}, t{"foo"s}, u, a({"hey", "you"}), bb = {"h", "a" };
+        // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: multiple declarations should be split
+        // CHECK-FIXES: {{^        }}std::vector<std::string> s{"foo"s, "bar"s};
+        // CHECK-FIXES: {{^        }}std::vector<std::string> t{"foo"s};
+        // CHECK-FIXES: {{^        }}std::vector<std::string> u;
+        // CHECK-FIXES: {{^        }}std::vector<std::string> a({"hey", "you"});
+        // CHECK-FIXES: {{^        }}std::vector<std::string> bb = {"h", "a" };
+    }
+    
+    struct X { int a, b, c; };
+    auto [a, b, c] = X();
+}
+
Index: test/clang-tidy/readability-one-name-per-declaration-complex.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/readability-one-name-per-declaration-complex.cpp
@@ -0,0 +1,247 @@
+// RUN: %check_clang_tidy %s readability-one-name-per-declaration %t -- -- \
+// RUN:    -std=c++11
+
+void dontTouchParameter(int param1, int param2)
+{}
+
+int returner(void) 
+{
+    int f0 = 0, f1 = 1;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}int f0 = 0;
+    // CHECK-FIXES: {{^    }}int f1 = 1;
+    
+    return 3;
+}
+
+struct StructOne 
+{
+    StructOne(){}
+    StructOne(int b){}
+    
+    int cantTouch1, cantTouch2;
+};
+
+using PointerType = int;
+
+struct TemT
+{
+    template<typename T>
+    T* getAs()
+    {
+        return nullptr;
+    }
+} TT1, TT2;
+
+void complex() 
+{
+    typedef int* IntPtr;
+    typedef int ArrayType[2];
+    typedef int FunType(void);
+    
+    IntPtr intptr1, intptr2 = nullptr, intptr3;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^....}}IntPtr intptr1;
+    // CHECK-FIXES: {{^....}}IntPtr intptr2 = nullptr;
+    // CHECK-FIXES: {{^....}}IntPtr intptr3;
+    
+    ArrayType arraytype1, arraytype2 = {1}, arraytype3;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^....}}ArrayType arraytype1;
+    // CHECK-FIXES: {{^....}}ArrayType arraytype2 = {1};
+    // CHECK-FIXES: {{^....}}ArrayType arraytype3;
+    
+    FunType funtype1, funtype2, functype3;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^....}}FunType funtype1;
+    // CHECK-FIXES: {{^....}}FunType funtype2;
+    // CHECK-FIXES: {{^....}}FunType functype3;
+    
+    for(int index1 = 0, index2 = 0;;)
+    {
+        int localFor1 = 1, localFor2 = 2;
+        // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: multiple declarations should be split
+        // CHECK-FIXES: {{^        }}int localFor1 = 1;
+        // CHECK-FIXES: {{^        }}int localFor2 = 2;
+    }
+    
+    int v1, v2(3), v3, v4(4 ), v5{2}, v6 = {3};
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}int v1;
+    // CHECK-FIXES: {{^    }}int v2(3);
+    // CHECK-FIXES: {{^    }}int v3;
+    // CHECK-FIXES: {{^    }}int v4(4 );
+    // CHECK-FIXES: {{^    }}int v5{2};
+    // CHECK-FIXES: {{^    }}int v6 = {3};
+    
+    StructOne s1, s2(23), s3, s4(3), *sptr = new StructOne(2);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}StructOne s1;
+    // CHECK-FIXES: {{^    }}StructOne s2(23);
+    // CHECK-FIXES: {{^    }}StructOne s3;
+    // CHECK-FIXES: {{^    }}StructOne s4(3);
+    // CHECK-FIXES: {{^    }}StructOne *sptr = new StructOne(2);
+    
+    struct StructOne cs1, cs2( 42 );
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}struct StructOne cs1;
+    // CHECK-FIXES: {{^    }}struct StructOne cs2( 42 );
+    
+    int *ptrArray[3], dummy, **ptrArray2[5], twoDim[2][3], *twoDimPtr[2][3];
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}int *ptrArray[3];
+    // CHECK-FIXES: {{^    }}int dummy;
+    // CHECK-FIXES: {{^    }}int **ptrArray2[5];
+    // CHECK-FIXES: {{^    }}int twoDim[2][3];
+    // CHECK-FIXES: {{^    }}int *twoDimPtr[2][3];
+    
+        {
+            void f1(int), g1(int, float);
+            // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: multiple declarations should be split
+            // CHECK-FIXES: {{^            }}void f1(int);
+            // CHECK-FIXES: {{^            }}void g1(int, float);
+        }
+
+        {
+            void gg(int, float);
+            
+            void ( *f2)(int), (*g2)(int, float) = gg;
+            // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: multiple declarations should be split
+            // CHECK-FIXES: {{^            }}void ( *f2)(int);
+            // CHECK-FIXES: {{^            }}void (*g2)(int, float) = gg;
+            
+            void /*(*/ ( /*(*/ *f3)(int), (*g3)(int, float);
+            // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: multiple declarations should be split
+            // CHECK-FIXES: {{^            }}void /*(*/ ( /*(*/ *f3)(int);
+            // CHECK-FIXES: {{^            }}void (*g3)(int, float);
+            
+        }
+    
+    struct S { int a; const int b; void f() {}};
+    
+    int S::*p = &S::a, S::* const q = &S::a;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}int S::*p = &S::a;
+    // CHECK-FIXES: {{^    }}int S::* const q = &S::a;
+    
+    int /* :: */ S::*pp2 = &S::a, var1 = 0;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}int /* :: */ S::*pp2 = &S::a;
+    // CHECK-FIXES: {{^    }}int var1 = 0;
+    
+    const int S::*r = &S::b, S::*t;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}const int S::*r = &S::b;
+    // CHECK-FIXES: {{^    }}const int S::*t;
+    
+    {
+        int S::*mdpa1[2] = {&S::a, &S::a}, var1 = 0;
+        // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: multiple declarations should be split
+        // CHECK-FIXES: {{^        }}int S::*mdpa1[2] = {&S::a, &S::a};
+        // CHECK-FIXES: {{^        }}int var1 = 0;
+        
+        int S :: * * mdpa2[2] = {&p, &pp2}, var2 = 0;
+        // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: multiple declarations should be split
+        // CHECK-FIXES: {{^        }}int S :: * * mdpa2[2] = {&p, &pp2};
+        // CHECK-FIXES: {{^        }}int var2 = 0;
+        
+        void (S::*mdfp1)() = &S::f, (S::*mdfp2)() = &S::f;
+        // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: multiple declarations should be split
+        // CHECK-FIXES: {{^        }}void (S::*mdfp1)() = &S::f;
+        // CHECK-FIXES: {{^        }}void (S::*mdfp2)() = &S::f;
+        
+        void (S::*mdfpa1[2])() = {&S::f, &S::f}, (S::*mdfpa2)() = &S::f;
+        // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: multiple declarations should be split
+        // CHECK-FIXES: {{^        }}void (S::*mdfpa1[2])() = {&S::f, &S::f};
+        // CHECK-FIXES: {{^        }}void (S::*mdfpa2)() = &S::f;
+        
+        void (S::**mdfpa3[2])() = {&mdfpa1[0], &mdfpa1[1]}, (S::*mdfpa4)() = &S::f;
+        // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: multiple declarations should be split
+        // CHECK-FIXES: {{^        }}void (S::**mdfpa3[2])() = {&mdfpa1[0], &mdfpa1[1]};
+        // CHECK-FIXES: {{^        }}void (S::*mdfpa4)() = &S::f;
+    }
+    
+    typedef const int S::*MemPtr;
+    MemPtr aaa =  &S::a, bbb = &S::b;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}MemPtr aaa =  &S::a;
+    // CHECK-FIXES: {{^    }}MemPtr bbb = &S::b;
+    
+    class CS { public: int a; const int b; };
+    int const CS :: * pp = &CS::a, CS::* const qq = &CS::a;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}int const CS :: * pp = &CS::a;
+    // CHECK-FIXES: {{^    }}int const CS::* const qq = &CS::a;
+    
+    int intfunction = returner(), intarray[] =
+          {
+                  1,
+                  2,
+                  3,
+                  4
+          }, bb = 4;
+    // CHECK-MESSAGES: :[[@LINE-7]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}int intfunction = returner();
+    // CHECK-FIXES: {{^    }}int intarray[] =
+    // CHECK-FIXES: {{^          }}{
+    // CHECK-FIXES: {{^                  }}1,
+    // CHECK-FIXES: {{^                  }}2,
+    // CHECK-FIXES: {{^                  }}3,
+    // CHECK-FIXES: {{^                  }}4
+    // CHECK-FIXES: {{^          }}};
+    // CHECK-FIXES: {{^    }}int bb = 4;
+    
+    TemT *T1 = &TT1, *T2 = &TT2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}TemT *T1 = &TT1;
+    // CHECK-FIXES: {{^    }}TemT *T2 = &TT2;
+
+    const PointerType *PT1 = T1->getAs<PointerType>(),
+                      *PT2 = T2->getAs<PointerType>();
+    // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}const PointerType *PT1 = T1->getAs<PointerType>();
+    // CHECK-FIXES: {{^    }}const PointerType *PT2 = T2->getAs<PointerType>();
+    
+    bool defPre = false,
+#ifdef IS_ENABLED
+       defTest = false;
+#else
+       defTest = true;
+#endif
+    // CHECK-MESSAGES: :[[@LINE-6]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}bool defPre = false;
+    // CHECK-FIXES: {{^    }}bool 
+    // CHECK-FIXES: {{^#}}ifdef IS_ENABLED
+    // CHECK-FIXES: {{^       }}defTest = false;
+    // CHECK-FIXES: {{^#}}else
+    // CHECK-FIXES: {{^       }}defTest = true;
+    // CHECK-FIXES: {{^#}}endif
+    
+    const int *p1 = nullptr;
+    const int *p2 = nullptr;
+    
+    const int *&pref1 = p1, *&pref2 = p2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}const int *&pref1 = p1;
+    // CHECK-FIXES: {{^    }}const int *&pref2 = p2;
+    
+    typedef int *tptr, tbt;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}typedef int *tptr;
+    // CHECK-FIXES: {{^    }}typedef int tbt;
+    
+    typedef int (&tfp)(int, long), tarr[10];
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}typedef int (&tfp)(int, long);
+    // CHECK-FIXES: {{^    }}typedef int tarr[10];
+    
+    typedef int tarr2[10], tct;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple declarations should be split
+    // CHECK-FIXES: {{^    }}typedef int tarr2[10];
+    // CHECK-FIXES: {{^    }}typedef int tct;
+
+}
+
+template <typename A, typename B>
+void should_not_be_touched(A, B);
+
Index: docs/clang-tidy/checks/readability-one-name-per-declaration.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/readability-one-name-per-declaration.rst
@@ -0,0 +1,57 @@
+.. title:: clang-tidy - readability-one-name-per-declaration
+
+readability-one-name-per-declaration
+====================================
+
+This check can be used to find declarations that declare more than one name.
+It helps improve readability and prevents potential bugs caused by inattention
+and C/C++ syntax specifics.
+
+In addition, appropriate fix-it hints are provided and all user-intended 
+indentation will be preserved. For example:
+
+.. code-block:: c++
+
+  {
+    long ** lint1, lint2 = 0, * lint3, **linn;
+  
+    const int cx = 1, cy = 2;
+  
+    int const CS :: * pp = &CS::a, CS::* const qq = &CS::a;
+  
+    decltype(int()) declint1 = 5, declint2 = 3;
+    
+    typedef int ta, tb;
+  }
+
+will be transformed to:
+
+.. code-block:: c++
+
+  {
+    long ** lint1;
+    long lint2 = 0;
+    long * lint3;
+    long **linn;
+    
+    const int cx = 1;
+    const int cy = 2;
+    
+    int const CS :: * pp = &CS::a;
+    int const CS::* const qq = &CS::a;
+    
+    decltype(int()) declint1 = 5;
+    decltype(int()) declint2 = 3;
+    
+    typedef int ta;
+    typedef int tb;
+  }
+
+Only declarations within a compound statement are matched. Meaning, global declarations
+and function parameters are not matched. Moreover, it does not match on the following:
+
+.. code-block:: c++
+
+  {
+    for(int i = 0, j = 0;;);
+  }
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -136,6 +136,7 @@
    readability-misplaced-array-index
    readability-named-parameter
    readability-non-const-parameter
+   readability-one-name-per-declaration
    readability-redundant-control-flow
    readability-redundant-declaration
    readability-redundant-member-init
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -136,6 +136,11 @@
   Flags function parameters of a pointer type that could be changed to point to
   a constant type instead.
 
+- New `readability-one-name-per-declaration
+  <http://clang.llvm.org/extra/clang-tidy/checks/readability-one-name-per-declaration.html>`_ check
+
+  Finds declarations declaring more than one name.
+
 - New `readability-redundant-declaration
   <http://clang.llvm.org/extra/clang-tidy/checks/readability-redundant-declaration.html>`_ check
 
Index: clang-tidy/utils/LexerUtils.h
===================================================================
--- clang-tidy/utils/LexerUtils.h
+++ clang-tidy/utils/LexerUtils.h
@@ -23,6 +23,13 @@
 Token getPreviousNonCommentToken(const ASTContext &Context,
                                  SourceLocation Location);
 
+/// \brief This function searches backward from the given location until
+/// TokenToFind is found. If the tokens is not found, the returned source
+/// location will be invalid.
+SourceLocation findTokenLocationBackward(const ASTContext &Context,
+                                         SourceLocation Location,
+                                         tok::TokenKind TokenToFind);
+
 } // namespace lexer
 } // namespace utils
 } // namespace tidy
Index: clang-tidy/utils/LexerUtils.cpp
===================================================================
--- clang-tidy/utils/LexerUtils.cpp
+++ clang-tidy/utils/LexerUtils.cpp
@@ -35,6 +35,27 @@
   return Token;
 }
 
+SourceLocation findTokenLocationBackward(const ASTContext &Context,
+                                         SourceLocation Location,
+                                         tok::TokenKind TokenToFind) {
+  const auto &SM = Context.getSourceManager();
+  const auto &LO = Context.getLangOpts();
+  auto StartOfFile = SM.getLocForStartOfFile(SM.getFileID(Location));
+
+  Token CurrentToken;
+  CurrentToken.setKind(tok::unknown);
+
+  while (Location != StartOfFile) {
+    if (!Lexer::getRawToken(Location, CurrentToken, SM, LO) &&
+        CurrentToken.is(TokenToFind)) {
+      return Location;
+    }
+    Location = Location.getLocWithOffset(-1);
+  }
+
+  return SourceLocation();
+}
+
 } // namespace lexer
 } // namespace utils
 } // namespace tidy
Index: clang-tidy/readability/ReadabilityTidyModule.cpp
===================================================================
--- clang-tidy/readability/ReadabilityTidyModule.cpp
+++ clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -22,6 +22,7 @@
 #include "MisplacedArrayIndexCheck.h"
 #include "NamedParameterCheck.h"
 #include "NonConstParameterCheck.h"
+#include "OneNamePerDeclarationCheck.h"
 #include "RedundantControlFlowCheck.h"
 #include "RedundantDeclarationCheck.h"
 #include "RedundantMemberInitCheck.h"
@@ -59,6 +60,8 @@
         "readability-inconsistent-declaration-parameter-name");
     CheckFactories.registerCheck<MisplacedArrayIndexCheck>(
         "readability-misplaced-array-index");
+    CheckFactories.registerCheck<OneNamePerDeclarationCheck>(
+        "readability-one-name-per-declaration");
     CheckFactories.registerCheck<RedundantMemberInitCheck>(
         "readability-redundant-member-init");
     CheckFactories.registerCheck<StaticDefinitionInAnonymousNamespaceCheck>(
Index: clang-tidy/readability/OneNamePerDeclarationCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/readability/OneNamePerDeclarationCheck.h
@@ -0,0 +1,39 @@
+//===--- OneNamePerDeclarationCheck.h - clang-tidy---------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ONE_NAME_PER_DECLARATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ONE_NAME_PER_DECLARATION_H
+
+#include "../ClangTidy.h"
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Checks for declarations, declaring more than one name.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-one-name-per-declaration.html
+class OneNamePerDeclarationCheck : public ClangTidyCheck {
+private:
+  std::string getUserWrittenType(const DeclStmt *DeclStmt, SourceManager &SM);
+
+public:
+  OneNamePerDeclarationCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ONE_NAME_PER_DECLARATION_H
Index: clang-tidy/readability/OneNamePerDeclarationCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/readability/OneNamePerDeclarationCheck.cpp
@@ -0,0 +1,234 @@
+//===--- OneNamePerDeclarationCheck.cpp - clang-tidy-----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "OneNamePerDeclarationCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+static std::string removeMultiLineComments(std::string Str);
+static std::string getCurrentLineIndent(SourceLocation Loc,
+                                        const SourceManager &SM);
+
+void OneNamePerDeclarationCheck::registerMatchers(MatchFinder *Finder) {
+  // Matches all non-single declaration within a compound statement {...}.
+  Finder->addMatcher(declStmt(hasParent(compoundStmt()),
+                              hasDescendant(namedDecl()),
+                              unless(declCountIs(1)))
+                         .bind("declstmt"),
+                     this);
+}
+
+void OneNamePerDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *DeclStatement = Result.Nodes.getNodeAs<DeclStmt>("declstmt");
+  if (!DeclStatement)
+    return;
+
+  // Macros will be ignored
+  if (DeclStatement->getLocStart().isMacroID())
+    return;
+
+  bool IsTagDef = false;
+  const DeclGroupRef DeclGroup = DeclStatement->getDeclGroup();
+
+  if (const auto *TagDef = dyn_cast<TagDecl>(*DeclGroup.begin())) {
+    // Ignore:
+    //  - Anonymous struct, class, enum or union
+    //  - Single variable definitons (e.g. struct S {} a;)
+    //     - First element is the TagDecl and second the variable, thus 2 below
+    if (TagDef->getName().empty() || DeclGroup.getDeclGroup().size() == 2)
+      return;
+
+    IsTagDef = true;
+  }
+
+  SourceManager &SM = *Result.SourceManager;
+  const LangOptions &LangOpts = getLangOpts();
+
+  const SourceLocation DeclStmtStart = DeclStatement->getLocStart();
+  const std::string CurrentIndent = getCurrentLineIndent(DeclStmtStart, SM);
+  const std::string UserWrittenType = getUserWrittenType(DeclStatement, SM);
+
+  auto Diag =
+      diag(DeclStmtStart, "multiple declarations should be split into "
+                          "individual declarations to improve readability");
+
+  // We will iterate through the declaration group starting with the second
+  // declaration. Then, the previous comma will be searched and replaced by a
+  // ';' and the UserWrittenType inserted after it.
+  // For a statement that is a tag + var definition (e.g. struct S {} a,b;),
+  // we will start looking for a closing }-brace instead a comma.
+
+  auto SecondElement = DeclGroup.begin() + 1;
+
+  for (auto It = SecondElement; It != DeclGroup.end(); ++It) {
+    const auto NameLocation = dyn_cast<NamedDecl>(*It)->getLocation();
+    SourceLocation CommaLocation = utils::lexer::findTokenLocationBackward(
+        *Result.Context, NameLocation, tok::comma);
+
+    // If we are starting with a TagDecl, find the closing }-brace
+    // instead a comma
+    if (IsTagDef && (It == SecondElement))
+      CommaLocation = utils::lexer::findTokenLocationBackward(
+          *Result.Context, NameLocation, tok::r_brace);
+
+    if (CommaLocation.isValid()) {
+      const SourceRange AfterCommaToVarNameRange(
+          CommaLocation.getLocWithOffset(1), NameLocation);
+      std::string AnyTokenBetweenCommaAndVarName =
+          Lexer::getSourceText(
+              CharSourceRange::getTokenRange(AfterCommaToVarNameRange), SM,
+              LangOpts)
+              .ltrim(); // may be &, *, etc.
+
+      // Check for preprocessor directive and add appropriate newline.
+      if (AnyTokenBetweenCommaAndVarName.front() == '#')
+        AnyTokenBetweenCommaAndVarName.insert(0, "\n");
+
+      if (IsTagDef && (It == SecondElement))
+        Diag << FixItHint::CreateInsertion(
+            CommaLocation.getLocWithOffset(1),
+            ";"); // Insert ; after closing }-brace
+      else
+        Diag << FixItHint::CreateReplacement(CommaLocation, ";");
+
+      Diag << FixItHint::CreateReplacement(AfterCommaToVarNameRange,
+                                           (llvm::Twine("\n") + CurrentIndent +
+                                            UserWrittenType + " " +
+                                            AnyTokenBetweenCommaAndVarName)
+                                               .str());
+    }
+  }
+}
+
+std::string
+OneNamePerDeclarationCheck::getUserWrittenType(const DeclStmt *DeclStmt,
+                                               SourceManager &SM) {
+  const auto FirstVarIt = DeclStmt->getDeclGroup().begin();
+
+  if (const auto *TagDef = dyn_cast<TagDecl>(*FirstVarIt)) {
+    auto TypeString = TagDef->getNameAsString();
+
+    if (!getLangOpts().CPlusPlus)
+      TypeString = (TagDef->getKindName() + " " + TypeString)
+                       .str(); // Add struct, enum or union before the typename
+
+    if (isa<TypedefDecl>(*(FirstVarIt + 1)))
+      return "typedef " + TypeString;
+
+    return TypeString;
+  }
+
+  SourceLocation Location;
+  const Type *StmtType = nullptr;
+  if (const auto *FirstVar = dyn_cast<DeclaratorDecl>(*FirstVarIt)) {
+    Location = FirstVar->getLocation();
+    StmtType = FirstVar->getType().getTypePtr();
+  } else if (const auto *FirstVar = dyn_cast<TypedefDecl>(*FirstVarIt)) {
+    Location = FirstVar->getLocation();
+    StmtType = FirstVar->getTypeSourceInfo()->getType().getTypePtr();
+    // Typedefs on function pointers are covered into LValueReferenceType's
+    while (StmtType->isLValueReferenceType()) {
+      StmtType = StmtType->getPointeeType().getTypePtr();
+    }
+  } else {
+    llvm_unreachable(
+        "Declaration is neither a DeclaratorDecl nor a TypedefDecl");
+  }
+
+  const SourceRange FVLoc(DeclStmt->getLocStart(), Location);
+  std::string UserWrittenType =
+      Lexer::getSourceText(CharSourceRange::getCharRange(FVLoc), SM,
+                           getLangOpts())
+          .trim();
+
+  UserWrittenType = removeMultiLineComments(UserWrittenType);
+
+  // UserWrittenType might be and we want ->
+  // const int S::* -> const int
+  // const int *&   -> const int
+  // long **        -> long int
+  size_t Pos = std::string::npos;
+
+  if (StmtType->isPointerType() || StmtType->isArrayType() ||
+      StmtType->isReferenceType() || StmtType->isFunctionPointerType() ||
+      StmtType->isFunctionProtoType()) {
+    Pos = UserWrittenType.find_first_of("&*(");
+    if (Pos != std::string::npos) // Might be hidden behind typedef etc.
+      UserWrittenType.erase(Pos);
+
+    while (StmtType->isAnyPointerType() || StmtType->isArrayType())
+      StmtType = StmtType->getPointeeOrArrayElementType();
+  }
+
+  if (StmtType->isMemberFunctionPointerType() &&
+      (Pos = UserWrittenType.find("(")) && Pos != std::string::npos) {
+    UserWrittenType.erase(Pos);
+    return StringRef(UserWrittenType).trim();
+  }
+
+  if (StmtType->isMemberDataPointerType() &&
+      (Pos = UserWrittenType.rfind("::")) && Pos != std::string::npos) {
+    // User might have inserted additional whitespaces:
+    // int   S  ::
+    //      ^-  ^- needed positions
+    UserWrittenType.erase(Pos);
+    UserWrittenType = StringRef(UserWrittenType).trim();
+    Pos = UserWrittenType.rfind(" ");
+    UserWrittenType.erase(Pos);
+  }
+
+  return StringRef(UserWrittenType).trim();
+}
+
+static std::string removeMultiLineComments(std::string Str) {
+  size_t Pos1 = Str.find("/*");
+  while (Pos1 != std::string::npos) {
+    const size_t Pos2 = Str.find("*/", Pos1 + 1);
+    Str.erase(Pos1, Pos2 - Pos1 + 2);
+    Pos1 = Str.find("/*");
+  }
+
+  Str = StringRef(Str).trim();
+  return Str;
+}
+
+static std::string getCurrentLineIndent(SourceLocation Loc,
+                                        const SourceManager &SM) {
+  const std::pair<FileID, unsigned> V = SM.getDecomposedLoc(Loc);
+  const FileID FID = V.first;
+  const unsigned StartOffs = V.second;
+
+  const StringRef MB = SM.getBufferData(FID);
+
+  const unsigned LineNo = SM.getLineNumber(FID, StartOffs) - 1;
+  const SrcMgr::ContentCache *Content =
+      SM.getSLocEntry(FID).getFile().getContentCache();
+  const unsigned LineOffs = Content->SourceLineCache[LineNo];
+
+  // Find the whitespace at the start of the line.
+  StringRef IndentSpace;
+  size_t I = LineOffs;
+  while (isWhitespace(MB[I]) && MB[I] != '\n')
+    ++I;
+  IndentSpace = MB.substr(LineOffs, I - LineOffs);
+
+  return IndentSpace;
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/readability/CMakeLists.txt
===================================================================
--- clang-tidy/readability/CMakeLists.txt
+++ clang-tidy/readability/CMakeLists.txt
@@ -14,6 +14,7 @@
   NamedParameterCheck.cpp
   NamespaceCommentCheck.cpp
   NonConstParameterCheck.cpp
+  OneNamePerDeclarationCheck.cpp
   ReadabilityTidyModule.cpp
   RedundantControlFlowCheck.cpp
   RedundantDeclarationCheck.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to