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