firolino updated this revision to Diff 81022.
firolino marked 7 inline comments as done.
firolino added a comment.

Applied suggestions from malcolm.


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,132 @@
+// 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    	// CHECK-FIXES: {{^    	}}long int* lint4;
+    	// CHECK-FIXES: {{^    	}}long int *lint5;
+    	// CHECK-FIXES: {{^    	}}long int lint6;
+    
+    unsigned int uint1 = 0, uint2 = 44u, uint3, uint4=4;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^    }}unsigned int uint1 = 0;
+    // CHECK-FIXES: {{^    }}unsigned int uint2 = 44u;
+    // CHECK-FIXES: {{^    }}unsigned int uint3;
+    // CHECK-FIXES: {{^    }}unsigned int uint4=4;
+    
+    double darray1[] = {}, darray2[] = {1,	2}, dv1 = 3, dv2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^    }}const int cx = 1;
+    // CHECK-FIXES: {{^    }}const int cy = 2;
+    
+    volatile int vx, vy;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^    }}volatile int vx;
+    // CHECK-FIXES: {{^    }}volatile int vy;
+    
+    signed char sc1 = 'h', sc2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^    }}signed char sc1 = 'h';
+    // CHECK-FIXES: {{^    }}signed char sc2;
+    
+    long long ll1, ll2, ***ft;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^    }}const int cint3 = 4;
+    // CHECK-FIXES: {{^    }}const int cintarray[] = { 1, 2, 3, 4 };
+
+    union {
+        int m1;
+        float m2;
+    } in, out;
+    
+    int refme1;
+    int refme2 = 0;
+    const int &r1 = refme1, &r2 = refme2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^    }}const int &r1 = refme1;
+    // CHECK-FIXES: {{^    }}const int &r2 = refme2;
+    
+    typedef int ta, tb;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^    }}typedef int ta;
+    // CHECK-FIXES: {{^    }}typedef int tb;
+    
+    typedef const int tca, tcb;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^    }}typedef const int tca;
+    // CHECK-FIXES: {{^    }}typedef const int tcb;
+    
+    typedef int const tac, tbc;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^    }}typedef int const tac;
+    // CHECK-FIXES: {{^    }}typedef int const tbc;
+}
+
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++14
+
+namespace std {
+    
+    template <typename T>
+    class initializer_list {};
+    
+    template <typename T>
+    class vector 
+    {
+      public:
+        vector() {}
+        vector(initializer_list<T> init) {}
+    };
+    
+    class string 
+    {
+      public:
+        string() {}
+        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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]    
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^....}}uType utype1;
+    // CHECK-FIXES: {{^....}}uType utype2;
+    
+    Types::MyType mytype1, mytype2, mytype3 = 3;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+        // 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" };
+    }
+}
+
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,207 @@
+// 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^....}}IntPtr intptr1;
+    // CHECK-FIXES: {{^....}}IntPtr intptr2 = nullptr;
+    // CHECK-FIXES: {{^....}}IntPtr intptr3;
+    
+    ArrayType arraytype1, arraytype2 = {1}, arraytype3;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^....}}ArrayType arraytype1;
+    // CHECK-FIXES: {{^....}}ArrayType arraytype2 = {1};
+    // CHECK-FIXES: {{^....}}ArrayType arraytype3;
+    
+    FunType funtype1, funtype2, functype3;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+        // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+            // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+            // CHECK-FIXES: {{^            }}void ( *f2)(int);
+            // CHECK-FIXES: {{^            }}void (*g2)(int, float) = gg;
+            
+        }
+    
+    struct S { int a; const int b; };
+    
+    int S::*p = &S::a, S::* const q = &S::a;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^    }}int S::*p = &S::a;
+    // CHECK-FIXES: {{^    }}int S::* const q = &S::a;
+    
+    const int S::*r = &S::b, S::*t;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^    }}const int S::*r = &S::b;
+    // CHECK-FIXES: {{^    }}const int S::*t;
+    
+    typedef const int S::*MemPtr;
+    MemPtr aaa =  &S::a, bbb = &S::b;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // 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: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^    }}const int *&pref1 = p1;
+    // CHECK-FIXES: {{^    }}const int *&pref2 = p2;
+    
+    typedef int *tptr, tbt;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^    }}typedef int *tptr;
+    // CHECK-FIXES: {{^    }}typedef int tbt;
+    
+    typedef int (&tfp)(int, long), tarr[10];
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^    }}typedef int (&tfp)(int, long);
+    // CHECK-FIXES: {{^    }}typedef int tarr[10];
+    
+    typedef int tarr2[10], tct;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+    // CHECK-FIXES: {{^    }}typedef int tarr2[10];
+    // CHECK-FIXES: {{^    }}typedef int tct;
+
+}
+
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,59 @@
+.. title:: clang-tidy - readability-one-name-per-declaration
+
+readability-one-name-per-declaration
+====================================
+
+This check can be used to find declarations, which declare more than one name. 
+It helps improving 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;
+    
+    typdef int ta;
+    typdef 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++
+
+  {
+    class A { } Object1, Object2;
+    
+    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 that 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
@@ -12,6 +12,7 @@
 
 #include "clang/AST/ASTContext.h"
 #include "clang/Lex/Lexer.h"
+#include <vector>
 
 namespace clang {
 namespace tidy {
@@ -23,6 +24,14 @@
 Token getPreviousNonCommentToken(const ASTContext &Context,
                                  SourceLocation Location);
 
+/// \brief \arg Loc is the end of a statement range. This returns the location
+/// immediately after one of the token given in tokens is found after the
+/// statement. If non of the tokens are found, the returned source location
+/// will be invalid.
+SourceLocation findLocationAfterToken(SourceLocation Loc,
+                                      const std::vector<tok::TokenKind> &Tokens,
+                                      ASTContext &Context);
+
 } // 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,23 @@
   return Token;
 }
 
+SourceLocation findLocationAfterToken(SourceLocation Loc,
+                                      const std::vector<tok::TokenKind> &Tokens,
+                                      ASTContext &Context) {
+  const auto &SM = Context.getSourceManager();
+  const auto &LO = Context.getLangOpts();
+
+  for (auto Token : Tokens) {
+    auto FLoc = Lexer::findLocationAfterToken(
+        Loc, Token, SM, LO, /*SkipTrailingWhitespaceAndNewLine*/ true);
+
+    if (FLoc.isValid())
+      return FLoc;
+  }
+
+  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,40 @@
+//===--- 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 clang::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,275 @@
+//===--- 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;
+using namespace clang::ast_matchers;
+using namespace llvm;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+const internal::VariadicDynCastAllOfMatcher<Decl, TagDecl> tagDecl;
+
+static bool isWhitespaceExceptNL(unsigned char c);
+static std::string getCurrentLineIndent(SourceLocation Loc,
+                                        const SourceManager &SM);
+
+void OneNamePerDeclarationCheck::registerMatchers(MatchFinder *Finder) {
+
+  // Matches all non-single declaration within a compound statement {...}.
+  // Unless, the variable declaration is a object definition directly after
+  // a tag declaration (e.g. struct, class etc.):
+  // class A { } Object1, Object2;  <-- won't be matched
+  Finder->addMatcher(
+      declStmt(allOf(hasParent(compoundStmt()),
+                     unless(hasDescendant(tagDecl())), unless(declCountIs(1))))
+          .bind("declstmt"),
+      this);
+}
+
+void OneNamePerDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *DeclStmt = Result.Nodes.getNodeAs<clang::DeclStmt>("declstmt");
+  if (DeclStmt == nullptr)
+    return;
+
+  // Macros will be ignored
+  if (DeclStmt->getLocStart().isMacroID())
+    return;
+
+  SourceManager &SM = *Result.SourceManager;
+  const LangOptions &LangOpts = getLangOpts();
+  const auto DeclGroup = DeclStmt->getDeclGroup();
+
+  const std::string CurrentIndent =
+      getCurrentLineIndent(DeclStmt->getLocStart(), SM);
+  const std::string UserWrittenType = getUserWrittenType(DeclStmt, SM);
+
+  std::string AllSingleDeclarations;
+  SourceRange VariableLocation;
+
+  // We will iterate through the declaration group and split it into
+  // single declarations. For example:
+  // int *p, q = 2, v;
+  // - UserWrittenType will be int
+  // - Fist iteration will cut-off 'int *p' and set locations accordingly
+  // - Next iteration will start after the comma and so cut-off 'q = 2'
+  //     - 'UserWrittenType q = 2' will be saved
+  // - Next iteration will cut-off v
+  //     - 'UserWrittenType v' will be saved
+  // - and so on...
+  for (auto It = DeclGroup.begin(); It != DeclGroup.end(); ++It) {
+
+    std::string SingleDeclaration = UserWrittenType + " ";
+
+    if (const auto *DecDecl =
+            llvm::dyn_cast<const clang::DeclaratorDecl>(*It)) {
+      VariableLocation.setEnd(DecDecl->getLocEnd());
+    } else if (const auto *TypeDecl =
+                   llvm::dyn_cast<const clang::TypedefDecl>(*It)) {
+      VariableLocation.setEnd(TypeDecl->getLocEnd());
+    } else {
+      llvm_unreachable(
+          "Declaration is neither a DeclaratorDecl nor a TypedefDecl");
+    }
+
+    if (It == DeclGroup.begin()) {
+      VariableLocation.setBegin(DeclStmt->getLocStart());
+    }
+
+    std::string VariableLocationStr =
+        Lexer::getSourceText(CharSourceRange::getTokenRange(VariableLocation),
+                             SM, LangOpts)
+            .trim();
+
+    // Check for pre-processor directive and add appropriate newline
+    if (VariableLocationStr[0] == '#') {
+      VariableLocationStr.insert(0, "\n");
+    }
+
+    if (It == DeclGroup.begin()) {
+      SingleDeclaration = VariableLocationStr;
+    } else {
+      SingleDeclaration += VariableLocationStr;
+    }
+
+    // Workaround for
+    // http://lists.llvm.org/pipermail/cfe-dev/2016-November/051425.html
+    if (const auto *VarDecl = llvm::dyn_cast<const clang::VarDecl>(*It)) {
+
+      if (VarDecl->getType().getCanonicalType()->isScalarType() &&
+          VarDecl->hasInit() &&
+          VarDecl->getInitStyle() == clang::VarDecl::CallInit) {
+
+        const auto PP =
+            Lexer::findLocationAfterToken(VariableLocation.getEnd(),
+                                          tok::r_paren, SM, LangOpts, true)
+                .getLocWithOffset(-1);
+
+        const std::string Appendee = Lexer::getSourceText(
+            CharSourceRange::getTokenRange(
+                VariableLocation.getEnd().getLocWithOffset(1), PP),
+            SM, LangOpts);
+
+        SingleDeclaration += Appendee;
+        VariableLocation.setEnd(
+            VariableLocation.getEnd().getLocWithOffset(Appendee.size()));
+      }
+    }
+
+    AllSingleDeclarations += SingleDeclaration + ";\n" + CurrentIndent;
+
+    // Lookout for next location start
+    const std::vector<tok::TokenKind> Tokens = {tok::semi, tok::comma};
+    VariableLocation.setBegin(tidy::utils::lexer::findLocationAfterToken(
+        VariableLocation.getEnd(), Tokens, *Result.Context));
+  }
+
+  if (!AllSingleDeclarations.empty()) {
+
+    // Remove last indent and '\n'
+    AllSingleDeclarations = StringRef(AllSingleDeclarations).rtrim();
+
+    auto Diag = diag(DeclStmt->getSourceRange().getBegin(),
+                     "declaration statement can be split up into single "
+                     "line declarations");
+    Diag << FixItHint::CreateReplacement(DeclStmt->getSourceRange(),
+                                         AllSingleDeclarations);
+  }
+}
+
+std::string
+OneNamePerDeclarationCheck::getUserWrittenType(const clang::DeclStmt *DeclStmt,
+                                               SourceManager &SM) {
+  const auto FirstVarIt = DeclStmt->getDeclGroup().begin();
+
+  SourceLocation Location;
+  size_t NameSize = 0;
+  QualType Type;
+
+  if (const auto *FirstVar =
+          llvm::dyn_cast<const clang::DeclaratorDecl>(*FirstVarIt)) {
+    Location = FirstVar->getLocation();
+    NameSize = FirstVar->getName().size();
+    Type = FirstVar->getType();
+  } else if (const auto *FirstVar =
+                 llvm::dyn_cast<const clang::TypedefDecl>(*FirstVarIt)) {
+    Location = FirstVar->getLocation();
+    NameSize = FirstVar->getName().size();
+
+    Type = FirstVar->getTypeSourceInfo()->getType();
+    if (Type->isLValueReferenceType()) {
+      Type = Type->getPointeeType();
+    }
+  } else {
+    llvm_unreachable(
+        "Declaration is neither a DeclaratorDecl nor a TypedefDecl");
+  }
+
+  SourceRange FVLoc(DeclStmt->getLocStart(), Location);
+
+  std::string FVStr = Lexer::getSourceText(
+      CharSourceRange::getTokenRange(FVLoc), SM, getLangOpts());
+
+  FVStr.erase(FVStr.size() - NameSize); // remove var name
+  std::string UserWrittenType = StringRef(FVStr).trim();
+
+  // UserWrittenType might be and we want ->
+  // const int S::* -> const int
+  // const int *&   -> const int
+  // long **        -> long int
+
+  if (Type->isFunctionPointerType() || Type->isFunctionProtoType()) {
+    auto Pos = UserWrittenType.find('(');
+    if (Pos != std::string::npos) { // might be hidden behind typedef etc.
+      UserWrittenType.erase(Pos);
+      UserWrittenType = StringRef(UserWrittenType).trim();
+    }
+
+    return UserWrittenType;
+  }
+
+  if (Type->isPointerType() || Type->isArrayType() || Type->isReferenceType()) {
+    auto Pos = UserWrittenType.find_last_not_of("&*");
+    if (Pos != std::string::npos) { // might be hidden behind typedef etc.
+      UserWrittenType.erase(Pos + 1);
+      UserWrittenType = StringRef(UserWrittenType).trim();
+    }
+
+    return UserWrittenType;
+  }
+
+  if (const auto *MemberPointerT = Type->getAs<MemberPointerType>()) {
+    auto Pos = UserWrittenType.find("::");
+    if (Pos != std::string::npos) { // might be hidden behind typedef etc.
+
+      StringRef CN =
+          MemberPointerT->getClass()->getCanonicalTypeInternal().getAsString();
+
+      // CN will be 'struct/class Typename'. we are only interested in the
+      // second part
+      CN = CN.split(' ').second;
+      Pos = UserWrittenType.rfind(CN, Pos);
+
+      UserWrittenType.erase(Pos);
+      UserWrittenType = StringRef(UserWrittenType).trim();
+    }
+  }
+
+  return UserWrittenType;
+}
+
+static bool isWhitespaceExceptNL(unsigned char c) {
+  switch (c) {
+  case ' ':
+  case '\t':
+  case '\f':
+  case '\v':
+  case '\r':
+    return true;
+  default:
+    return false;
+  }
+}
+
+static std::string getCurrentLineIndent(SourceLocation Loc,
+                                        const SourceManager &SM) {
+  const auto 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 (isWhitespaceExceptNL(MB[i])) {
+      ++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