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,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 + +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,268 @@ +//===--- 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 tagDecl; + +static bool isWhitespaceExceptNL(unsigned char c); +static std::string getCurrentLineIndent(SourceLocation Loc, + const SourceManager &SM); + +void OneNamePerDeclarationCheck::registerMatchers(MatchFinder *Finder) { + + // Matches all 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())))) + .bind("declstmt"), + this); +} + +void OneNamePerDeclarationCheck::check(const MatchFinder::MatchResult &Result) { + if (const auto *DeclStmt = + Result.Nodes.getNodeAs("declstmt")) { + + // Single declarations and macros will be ignored + if (DeclStmt->isSingleDecl() == false && + DeclStmt->getLocStart().isMacroID() == false) { + + SourceManager &SM = *Result.SourceManager; + const LangOptions &LangOpts = getLangOpts(); + + const std::string CurrentIndent = + getCurrentLineIndent(DeclStmt->getLocStart(), SM); + 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 = DeclStmt->getDeclGroup().begin(); + it != DeclStmt->getDeclGroup().end(); ++it) { + + std::string SingleDeclaration = UserWrittenType + " "; + + if (const clang::DeclaratorDecl *DecDecl = + llvm::dyn_cast(*it)) + VariableLocation.setEnd(DecDecl->getLocEnd()); + else if (const clang::TypedefDecl *TypeDecl = + llvm::dyn_cast(*it)) + VariableLocation.setEnd(TypeDecl->getLocEnd()); + else + llvm_unreachable( + "Declaration is neither a DeclaratorDecl nor a TypedefDecl"); + + if (it == DeclStmt->getDeclGroup().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 == DeclStmt->getDeclGroup().begin()) + SingleDeclaration = VariableLocationStr; + else + SingleDeclaration += VariableLocationStr; + + // Workaround for + // http://lists.llvm.org/pipermail/cfe-dev/2016-November/051425.html + if (const clang::VarDecl *VarDecl = + llvm::dyn_cast(*it)) { + + if (VarDecl->getType().getCanonicalType()->isScalarType() && + VarDecl->hasInit() && + VarDecl->getInitStyle() == clang::VarDecl::CallInit) { + + auto PP = + Lexer::findLocationAfterToken(VariableLocation.getEnd(), + tok::r_paren, SM, LangOpts, true) + .getLocWithOffset(-1); + + 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 tokens = {tok::semi, tok::comma}; + VariableLocation.setBegin(tidy::utils::lexer::findLocationAfterToken( + VariableLocation.getEnd(), tokens, *Result.Context)); + } + + if (AllSingleDeclarations.empty() == false) { + + // 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) { + auto FirstVarIt = DeclStmt->getDeclGroup().begin(); + + SourceLocation Location; + size_t NameSize = 0; + QualType Type; + + if (auto FirstVar = + llvm::dyn_cast(*FirstVarIt)) { + Location = FirstVar->getLocation(); + NameSize = FirstVar->getName().size(); + Type = FirstVar->getType(); + } else if (auto FirstVar = + llvm::dyn_cast(*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 MemberPointerType *T = Type->getAs()) { + auto Pos = UserWrittenType.find("::"); + if (Pos != std::string::npos) { // might be hidden behind typedef etc. + + StringRef CN = T->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) { + auto V = SM.getDecomposedLoc(Loc); + FileID FID = V.first; + unsigned StartOffs = V.second; + + StringRef MB = SM.getBufferData(FID); + + unsigned LineNo = SM.getLineNumber(FID, StartOffs) - 1; + const SrcMgr::ContentCache *Content = + SM.getSLocEntry(FID).getFile().getContentCache(); + 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,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 &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 &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: 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; + + typdef int ta; + typdef int tb; + } + +Only declaration 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,202 @@ +// 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); + + 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(), + *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++14 + +namespace std { + + template + class initializer_list {}; + + template + class vector + { + public: + vector() {} + vector(initializer_list 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 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" }; + } +} + 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; +} +