firolino updated this revision to Diff 81730.
firolino added a comment.
little cleanup in getUserWrittenType
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,142 @@
+// 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;
+
+ /* *& */ int /* *& */ ** /* *& */ pp,*xx;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: declaration statement can be split up into single line declarations [readability-one-name-per-declaration]
+ // CHECK-FIXES: {{^ }}/* *& */ int /* *& */ ** /* *& */ pp;
+ // CHECK-FIXES: {{^ }}int *xx;
+
+ 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;
+
+ const int * const cpc = &dontTouchC, simple = 0;
+ // 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 * const cpc = &dontTouchC;
+ // CHECK-FIXES: {{^ }}const int simple = 0;
+
+ 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++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: 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" };
+ }
+
+ 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,217 @@
+// 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;
+
+ void /*(*/ ( /*(*/ *f3)(int), (*g3)(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 /*(*/ ( /*(*/ *f3)(int);
+ // CHECK-FIXES: {{^ }}void (*g3)(int, float);
+
+ }
+
+ 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;
+
+ int /* :: */ S::*pp2 = &S::a, var1 = 0;
+ // 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::*pp2 = &S::a;
+ // CHECK-FIXES: {{^ }}int var1 = 0;
+
+ 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;
+
+ 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++
+
+ {
+ 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,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,212 @@
+//===--- 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 {
+
+const internal::VariadicDynCastAllOfMatcher<Decl, TagDecl> tagDecl;
+
+static bool isWhitespaceExceptNL(unsigned char C);
+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 {...}.
+ // 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()), hasDescendant(namedDecl()),
+ unless(hasDescendant(tagDecl())), unless(declCountIs(1))))
+ .bind("declstmt"),
+ this);
+}
+
+void OneNamePerDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *DeclStatement = Result.Nodes.getNodeAs<DeclStmt>("declstmt");
+ if (DeclStatement == nullptr)
+ return;
+
+ // Macros will be ignored
+ if (DeclStatement->getLocStart().isMacroID())
+ return;
+
+ SourceManager &SM = *Result.SourceManager;
+ const LangOptions &LangOpts = getLangOpts();
+ const auto DeclGroup = DeclStatement->getDeclGroup();
+
+ const auto DeclStmtStart = DeclStatement->getLocStart();
+ const std::string CurrentIndent = getCurrentLineIndent(DeclStmtStart, SM);
+ const std::string UserWrittenType = getUserWrittenType(DeclStatement, SM);
+
+ auto Diag = diag(
+ DeclStmtStart,
+ "declaration statement can be split up into single line declarations");
+
+ // 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 (auto It = DeclGroup.begin() + 1; It != DeclGroup.end(); ++It) {
+
+ const auto NameLocation = dyn_cast<const NamedDecl>(*It)->getLocation();
+ const auto CommaLocation = utils::lexer::findTokenLocationBackward(
+ *Result.Context, NameLocation, tok::comma);
+
+ 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 pre-processor directive and add appropriate newline
+ if (AnyTokenBetweenCommaAndVarName.front() == '#')
+ AnyTokenBetweenCommaAndVarName.insert(0, "\n");
+
+ Diag << FixItHint::CreateReplacement(CommaLocation, ";")
+ << FixItHint::CreateReplacement(AfterCommaToVarNameRange,
+ "\n" + CurrentIndent +
+ UserWrittenType + " " +
+ AnyTokenBetweenCommaAndVarName);
+ }
+ }
+}
+
+std::string
+OneNamePerDeclarationCheck::getUserWrittenType(const DeclStmt *DeclStmt,
+ SourceManager &SM) {
+ const auto FirstVarIt = DeclStmt->getDeclGroup().begin();
+
+ SourceLocation Location;
+ QualType Type;
+
+ if (const auto *FirstVar = dyn_cast<const DeclaratorDecl>(*FirstVarIt)) {
+ Location = FirstVar->getLocation();
+ Type = FirstVar->getType();
+ } else if (const auto *FirstVar = dyn_cast<const TypedefDecl>(*FirstVarIt)) {
+ Location = FirstVar->getLocation();
+ Type = FirstVar->getTypeSourceInfo()->getType();
+ if (Type->isLValueReferenceType()) {
+ Type = Type->getPointeeType();
+ }
+ } 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
+
+ if (Type->isPointerType() || Type->isArrayType() || Type->isReferenceType() ||
+ Type->isFunctionPointerType() || Type->isFunctionProtoType()) {
+ const auto Pos = UserWrittenType.find_first_of("&*(");
+ if (Pos != std::string::npos) { // might be hidden behind typedef etc.
+ UserWrittenType.erase(Pos);
+ 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 removeMultiLineComments(std::string Str) {
+ auto Pos1 = Str.find("/*");
+ while (Pos1 != std::string::npos) {
+ const auto 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 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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits