JonasToth updated this revision to Diff 129433.
JonasToth added a comment.

I enhanced the check to do more:

- check that jumps will only be forward. AFAIK that is required in all 
sensefull usecases of goto, is it?
- additionally check for gotos in nested loops. These are not diagnosed if the 
jump is forward implementing the exception in the core guidelines.

With these modifications the check can be used to enforce the rule in the 
CoreGuidelines and the goto part of `6.3.1 Ensure that the label(s) for a jump 
statement or a switch condition appear later, in the same or an enclosing block`
for the HICPP module.

Some test cases for all combinations are missing, i can add those once you 
agree that the functionality change is indeed ok.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D41815

Files:
  clang-tidy/cppcoreguidelines/AvoidGotoCheck.cpp
  clang-tidy/cppcoreguidelines/AvoidGotoCheck.h
  clang-tidy/cppcoreguidelines/CMakeLists.txt
  clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/cppcoreguidelines-avoid-goto.rst
  docs/clang-tidy/checks/list.rst
  test/clang-tidy/cppcoreguidelines-avoid-goto.cpp

Index: test/clang-tidy/cppcoreguidelines-avoid-goto.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/cppcoreguidelines-avoid-goto.cpp
@@ -0,0 +1,91 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-avoid-goto %t
+
+void noop() {}
+
+int main() {
+  noop();
+  goto jump_to_me;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: avoid using 'goto' for flow control
+  noop();
+
+jump_to_me:;
+
+jump_backwards:;
+  noop();
+  goto jump_backwards;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: avoid using 'goto' for flow control
+  // CHECK-MESSAGES: [[@LINE-2]]:3: warning: do not jump backwards with 'goto'
+  // CHECK-MESSAGES: [[@LINE-5]]:1: note: label defined here
+
+  // Test the GNU extension https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
+some_label:;
+  void *dynamic_label = &&some_label;
+
+  // FIXME: Not detected
+  goto *dynamic_label;
+  // CHECK MESSAGES: [[@LINE-1]]:3: warning: avoid using 'goto' for flow control
+}
+
+void forward_jump_out_nested_loop() {
+  for (int i = 0; i < 10; ++i) {
+    noop();
+    for (int j = 0; j < 10; ++j) {
+      noop();
+      if (i + j > 10)
+        goto early_exit1;
+    }
+    noop();
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    noop();
+    while (true) {
+      noop();
+      if (i > 5)
+        goto early_exit1;
+    }
+    noop();
+  }
+  // Jumping further results in error, because the variable declaration would
+  // be skipped.
+early_exit1:;
+
+  int i = 0;
+  while (true) {
+    noop();
+    while (true) {
+      noop();
+      if (i > 5)
+        goto early_exit2;
+      i++;
+    }
+    noop();
+  }
+
+  while (true) {
+    noop();
+    for (int j = 0; j < 10; ++j) {
+      noop();
+      if (j > 5)
+        goto early_exit2;
+    }
+    noop();
+  }
+
+early_exit2:;
+}
+
+void jump_out_backwards() {
+
+before_the_loop:
+  noop();
+
+  for (int i = 0; i < 10; ++i) {
+    for (int j = 0; j < 10; ++j) {
+      if (i * j > 80)
+        goto before_the_loop;
+      // CHECK-MESSAGES: [[@LINE-1]]:9: warning: do not jump backwards with 'goto'
+      // CHECK-MESSAGES: [[@LINE-8]]:1: note: label defined here
+    }
+  }
+}
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -52,6 +52,7 @@
    cert-msc30-c (redirects to cert-msc50-cpp) <cert-msc30-c>
    cert-msc50-cpp
    cert-oop11-cpp (redirects to performance-move-constructor-init) <cert-oop11-cpp>
+   cppcoreguidelines-avoid-goto
    cppcoreguidelines-c-copy-assignment-signature (redirects to misc-unconventional-assign-operator) <cppcoreguidelines-c-copy-assignment-signature>
    cppcoreguidelines-interfaces-global-init
    cppcoreguidelines-no-malloc
Index: docs/clang-tidy/checks/cppcoreguidelines-avoid-goto.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/cppcoreguidelines-avoid-goto.rst
@@ -0,0 +1,11 @@
+.. title:: clang-tidy - cppcoreguidelines-avoid-goto
+
+cppcoreguidelines-avoid-goto
+============================
+
+The usage of ``goto`` has been discouraged for a long time and is diagnosed
+with this check.
+
+This check implements `ES.76 <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es76-avoid-goto>`_ 
+from the CppCoreGuidelines. For more information on why to avoid programming 
+with ``goto`` you can read the famous paper `A Case against the GO TO Statement. <https://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF>`_.
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -57,6 +57,12 @@
 Improvements to clang-tidy
 --------------------------
 
+- New `cppcoreguidelines-avoid-goto
+  <http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-avoid-goto.html>`_ check
+
+  The usage of ``goto`` has been discouraged for a long time and is diagnosed
+  with this check.
+
 - ...
 
 Improvements to include-fixer
Index: clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
===================================================================
--- clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
+++ clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
@@ -11,6 +11,7 @@
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
 #include "../misc/UnconventionalAssignOperatorCheck.h"
+#include "AvoidGotoCheck.h"
 #include "InterfacesGlobalInitCheck.h"
 #include "NoMallocCheck.h"
 #include "OwningMemoryCheck.h"
@@ -35,6 +36,8 @@
 class CppCoreGuidelinesModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<AvoidGotoCheck>(
+        "cppcoreguidelines-avoid-goto");
     CheckFactories.registerCheck<InterfacesGlobalInitCheck>(
         "cppcoreguidelines-interfaces-global-init");
     CheckFactories.registerCheck<NoMallocCheck>("cppcoreguidelines-no-malloc");
Index: clang-tidy/cppcoreguidelines/CMakeLists.txt
===================================================================
--- clang-tidy/cppcoreguidelines/CMakeLists.txt
+++ clang-tidy/cppcoreguidelines/CMakeLists.txt
@@ -1,6 +1,7 @@
 set(LLVM_LINK_COMPONENTS support)
 
 add_clang_library(clangTidyCppCoreGuidelinesModule
+  AvoidGotoCheck.cpp
   CppCoreGuidelinesTidyModule.cpp
   InterfacesGlobalInitCheck.cpp
   NoMallocCheck.cpp
Index: clang-tidy/cppcoreguidelines/AvoidGotoCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/cppcoreguidelines/AvoidGotoCheck.h
@@ -0,0 +1,36 @@
+//===--- AvoidGotoCheck.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_CPPCOREGUIDELINES_AVOIDGOTOCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_AVOIDGOTOCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// The usage of `goto` has been discouraged for a long time and is diagnosed
+/// with this check.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-avoid-goto.html
+class AvoidGotoCheck : public ClangTidyCheck {
+public:
+  AvoidGotoCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_AVOIDGOTOCHECK_H
Index: clang-tidy/cppcoreguidelines/AvoidGotoCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/cppcoreguidelines/AvoidGotoCheck.cpp
@@ -0,0 +1,58 @@
+//===--- AvoidGotoCheck.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 "AvoidGotoCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+AST_MATCHER(GotoStmt, isForwardJumping) {
+
+  return Node.getLocStart() < Node.getLabel()->getLocStart();
+}
+
+void AvoidGotoCheck::registerMatchers(MatchFinder *Finder) {
+  // Check if the 'goto' is used for control flow other then jumping
+  // out of a nested loop.
+  auto Loop = stmt(anyOf(forStmt(), cxxForRangeStmt(), whileStmt(), doStmt()));
+  auto NestedLoop =
+      stmt(anyOf(forStmt(hasAncestor(Loop)), cxxForRangeStmt(hasAncestor(Loop)),
+                 whileStmt(hasAncestor(Loop)), doStmt(hasAncestor(Loop))))
+          .bind("nested-loop");
+
+  Finder->addMatcher(gotoStmt(unless(isForwardJumping())).bind("backward-goto"),
+                     this);
+  Finder->addMatcher(
+      gotoStmt(unless(hasAncestor(NestedLoop))).bind("flow-goto"), this);
+}
+
+void AvoidGotoCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *BackGoto =
+          Result.Nodes.getNodeAs<GotoStmt>("backward-goto")) {
+    diag(BackGoto->getGotoLoc(), "do not jump backwards with 'goto'")
+        << BackGoto->getSourceRange();
+    diag(BackGoto->getLabel()->getLocStart(), "label defined here",
+         DiagnosticIDs::Note);
+  }
+
+  // The guidelines allow 'goto' only for jumps out of a nested loop.
+  if (const auto *Flow = Result.Nodes.getNodeAs<GotoStmt>("flow-goto")) {
+    diag(Flow->getGotoLoc(), "avoid using 'goto' for flow control")
+        << Flow->getSourceRange();
+  }
+} // namespace cppcoreguidelines
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to