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 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 + +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 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"); + 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(*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(*FirstVarIt)) { + Location = FirstVar->getLocation(); + Type = FirstVar->getType(); + } else if (const auto *FirstVar = dyn_cast(*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()) { + 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/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( "readability-misplaced-array-index"); + CheckFactories.registerCheck( + "readability-one-name-per-declaration"); CheckFactories.registerCheck( "readability-redundant-member-init"); CheckFactories.registerCheck( 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 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: 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 + `_ check + + Finds declarations declaring more that one name. + - New `readability-redundant-declaration `_ check 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/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: 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 + 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(), + *PT2 = T2->getAs(); + // 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(); + // CHECK-FIXES: {{^ }}const PointerType *PT2 = T2->getAs(); + + 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: 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 + class initializer_list {}; + + template + class vector + { + public: + vector() = default; + vector(initializer_list 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 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 vectorA = {1,2}; + // CHECK-FIXES: {{^....}}std::vector vectorB = {1,2,3}; + // CHECK-FIXES: {{^....}}std::vector 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 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 s{"foo"s, "bar"s}; + // CHECK-FIXES: {{^ }}std::vector t{"foo"s}; + // CHECK-FIXES: {{^ }}std::vector u; + // CHECK-FIXES: {{^ }}std::vector a({"hey", "you"}); + // CHECK-FIXES: {{^ }}std::vector bb = {"h", "a" }; + } + + struct X { int a, b, c; }; + auto [a, b, c] = X(); +} + 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; +} +