Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -5,6 +5,7 @@ UnconventionalAssignOperatorCheck.cpp DefinitionsInHeadersCheck.cpp MiscTidyModule.cpp + TerminatingContinueCheck.cpp NewDeleteOverloadsCheck.cpp NonCopyableObjects.cpp RedundantExpressionCheck.cpp Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -16,6 +16,7 @@ #include "NonCopyableObjects.h" #include "RedundantExpressionCheck.h" #include "StaticAssertCheck.h" +#include "TerminatingContinueCheck.h" #include "ThrowByValueCatchByReferenceCheck.h" #include "UnconventionalAssignOperatorCheck.h" #include "UniqueptrResetReleaseCheck.h" @@ -31,6 +32,8 @@ public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck("misc-misplaced-const"); + CheckFactories.registerCheck( + "misc-terminating-continue"); CheckFactories.registerCheck( "misc-unconventional-assign-operator"); CheckFactories.registerCheck( Index: clang-tidy/misc/TerminatingContinueCheck.h =================================================================== --- /dev/null +++ clang-tidy/misc/TerminatingContinueCheck.h @@ -0,0 +1,36 @@ +//===--- TerminatingContinueCheck.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_MISC_TERMINATING_CONTINUE_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_TERMINATING_CONTINUE_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace misc { + +/// Checks if a 'continue' statement terminates the loop (i.e. the loop has +/// a condition which always evaluates to false). +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc-terminating-continue.html +class TerminatingContinueCheck : public ClangTidyCheck { +public: + TerminatingContinueCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_TERMINATING_CONTINUE_H Index: clang-tidy/misc/TerminatingContinueCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/TerminatingContinueCheck.cpp @@ -0,0 +1,50 @@ +//===--- TerminatingContinueCheck.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 "TerminatingContinueCheck.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 misc { + +void TerminatingContinueCheck::registerMatchers(MatchFinder *Finder) { + const auto doWithFalse = + doStmt(hasCondition(ignoringImpCasts( + anyOf(cxxBoolLiteral(equals(false)), integerLiteral(equals(0)), + cxxNullPtrLiteralExpr(), gnuNullExpr()))), + equalsBoundNode("closestLoop")) + .bind("doWithFalseCond"); + + Finder->addMatcher( + continueStmt(anyOf(hasAncestor(forStmt().bind("closestLoop")), + hasAncestor(cxxForRangeStmt().bind("closestLoop")), + hasAncestor(whileStmt().bind("closestLoop")), + hasAncestor(doStmt().bind("closestLoop"))), + hasAncestor(doWithFalse)) + .bind("continue"), + this); +} + +void TerminatingContinueCheck::check(const MatchFinder::MatchResult &Result) { + const auto *ContStmt = Result.Nodes.getNodeAs("continue"); + + auto Diag = + diag(ContStmt->getLocStart(), + "'continue' in loop with false condition is equivalent to 'break'"); + Diag << FixItHint::CreateReplacement(ContStmt->getSourceRange(), "break"); +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -149,6 +149,7 @@ misc-non-copyable-objects misc-redundant-expression misc-static-assert + misc-terminating-continue misc-throw-by-value-catch-by-reference misc-unconventional-assign-operator misc-uniqueptr-reset-release Index: docs/clang-tidy/checks/misc-terminating-continue.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/misc-terminating-continue.rst @@ -0,0 +1,34 @@ +.. title:: clang-tidy - misc-terminating-continue + +misc-terminating-continue +========================= + +Detects `do while` loops with a condition always evaluating to false that +have a `continue` statement, as this `continue` terminates the loop +effectively. + +.. code-block:: c++ + + Parser::TPResult Parser::TryParseProtocolQualifiers() { + assert(Tok.is(tok::less) && "Expected '<' for qualifier list"); + ConsumeToken(); + do { + if (Tok.isNot(tok::identifier)) + return TPResult::Error; + ConsumeToken(); + + if (Tok.is(tok::comma)) { + ConsumeToken(); + + //Terminating continue + continue; + } + + if (Tok.is(tok::greater)) { + ConsumeToken(); + return TPResult::Ambiguous; + } + } while (false); + + return TPResult::Error; + } Index: test/clang-tidy/misc-terminating-continue.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-terminating-continue.cpp @@ -0,0 +1,65 @@ +// RUN: %check_clang_tidy %s misc-terminating-continue %t + +void f() { + do { + continue; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'continue' in loop with false condition is equivalent to 'break' [misc-terminating-continue] + // CHECK-FIXES: break; + } while(false); + + do { + continue; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'continue' in loop with false condition is equivalent to 'break' [misc-terminating-continue] + // CHECK-FIXES: break; + } while(0); + + do { + continue; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'continue' in loop with false condition is equivalent to 'break' [misc-terminating-continue] + // CHECK-FIXES: break; + } while(nullptr); + + do { + continue; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'continue' in loop with false condition is equivalent to 'break' [misc-terminating-continue] + // CHECK-FIXES: break; + } while(__null); + + + do { + int x = 1; + if (x > 0) continue; + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'continue' in loop with false condition is equivalent to 'break' [misc-terminating-continue] + // CHECK-FIXES: if (x > 0) break; + } while (false); +} + +void g() { + do { + do { + continue; + int x = 1; + } while (1 == 1); + } while (false); + + do { + for (int i = 0; i < 1; ++i) { + continue; + int x = 1; + } + } while (false); + + do { + while (true) { + continue; + int x = 1; + } + } while (false); + + int v[] = {1,2,3,34}; + do { + for (int n : v) { + if (n>2) continue; + } + } while (false); +}