[clang-tools-extra] r356802 - [clang-tidy] openmp-exception-escape - a new check

Roman Lebedev via cfe-commits cfe-commits at lists.llvm.org
Fri Mar 22 12:46:25 PDT 2019


Author: lebedevri
Date: Fri Mar 22 12:46:25 2019
New Revision: 356802

URL: http://llvm.org/viewvc/llvm-project?rev=356802&view=rev
Log:
[clang-tidy] openmp-exception-escape - a new check

Summary:
Finally, we are here!

Analyzes OpenMP Structured Blocks and checks that no exception escapes
out of the Structured Block it was thrown in.

As per the OpenMP specification, structured block is an executable statement,
possibly compound, with a single entry at the top and a single exit at the
bottom. Which means, ``throw`` may not be used to to 'exit' out of the
structured block. If an exception is not caught in the same structured block
it was thrown in, the behaviour is undefined / implementation defined,
the program will likely terminate.

Reviewers: JonasToth, aaron.ballman, baloghadamsoftware, gribozavr

Reviewed By: aaron.ballman, gribozavr

Subscribers: mgorny, xazax.hun, rnkovacs, guansong, jdoerfert, cfe-commits, ABataev

Tags: #clang-tools-extra, #openmp, #clang

Differential Revision: https://reviews.llvm.org/D59466

Added:
    clang-tools-extra/trunk/clang-tidy/openmp/ExceptionEscapeCheck.cpp
    clang-tools-extra/trunk/clang-tidy/openmp/ExceptionEscapeCheck.h
    clang-tools-extra/trunk/docs/clang-tidy/checks/openmp-exception-escape.rst
    clang-tools-extra/trunk/test/clang-tidy/bugprone-exception-escape-openmp.cpp
    clang-tools-extra/trunk/test/clang-tidy/openmp-exception-escape.cpp
Modified:
    clang-tools-extra/trunk/clang-tidy/openmp/CMakeLists.txt
    clang-tools-extra/trunk/clang-tidy/openmp/OpenMPTidyModule.cpp
    clang-tools-extra/trunk/clang-tidy/utils/ExceptionAnalyzer.cpp
    clang-tools-extra/trunk/clang-tidy/utils/ExceptionAnalyzer.h
    clang-tools-extra/trunk/docs/ReleaseNotes.rst
    clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst

Modified: clang-tools-extra/trunk/clang-tidy/openmp/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/openmp/CMakeLists.txt?rev=356802&r1=356801&r2=356802&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/openmp/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clang-tidy/openmp/CMakeLists.txt Fri Mar 22 12:46:25 2019
@@ -1,6 +1,7 @@
 set(LLVM_LINK_COMPONENTS support)
 
 add_clang_library(clangTidyOpenMPModule
+  ExceptionEscapeCheck.cpp
   OpenMPTidyModule.cpp
   UseDefaultNoneCheck.cpp
 
@@ -9,4 +10,5 @@ add_clang_library(clangTidyOpenMPModule
   clangASTMatchers
   clangBasic
   clangTidy
+  clangTidyUtils
   )

Added: clang-tools-extra/trunk/clang-tidy/openmp/ExceptionEscapeCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/openmp/ExceptionEscapeCheck.cpp?rev=356802&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/openmp/ExceptionEscapeCheck.cpp (added)
+++ clang-tools-extra/trunk/clang-tidy/openmp/ExceptionEscapeCheck.cpp Fri Mar 22 12:46:25 2019
@@ -0,0 +1,84 @@
+//===--- ExceptionEscapeCheck.cpp - clang-tidy ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExceptionEscapeCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/OpenMPClause.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/StmtOpenMP.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersMacros.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace openmp {
+
+ExceptionEscapeCheck::ExceptionEscapeCheck(StringRef Name,
+                                           ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      RawIgnoredExceptions(Options.get("IgnoredExceptions", "")) {
+  llvm::SmallVector<StringRef, 8> FunctionsThatShouldNotThrowVec,
+      IgnoredExceptionsVec;
+
+  llvm::StringSet<> IgnoredExceptions;
+  StringRef(RawIgnoredExceptions).split(IgnoredExceptionsVec, ",", -1, false);
+  llvm::transform(IgnoredExceptionsVec, IgnoredExceptionsVec.begin(),
+                  [](StringRef S) { return S.trim(); });
+  IgnoredExceptions.insert(IgnoredExceptionsVec.begin(),
+                           IgnoredExceptionsVec.end());
+  Tracer.ignoreExceptions(std::move(IgnoredExceptions));
+  Tracer.ignoreBadAlloc(true);
+}
+
+void ExceptionEscapeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IgnoredExceptions", RawIgnoredExceptions);
+}
+
+void ExceptionEscapeCheck::registerMatchers(MatchFinder *Finder) {
+  // Don't register the check if OpenMP is not enabled; the OpenMP pragmas are
+  // completely ignored then, so no OpenMP entires will be present in the AST.
+  if (!getLangOpts().OpenMP)
+    return;
+  // Similarly, if C++ Exceptions are not enabled, nothing to do.
+  if (!getLangOpts().CPlusPlus || !getLangOpts().CXXExceptions)
+    return;
+
+  Finder->addMatcher(ompExecutableDirective(
+                         unless(isStandaloneDirective()),
+                         hasStructuredBlock(stmt().bind("structured-block")))
+                         .bind("directive"),
+                     this);
+}
+
+void ExceptionEscapeCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Directive =
+      Result.Nodes.getNodeAs<OMPExecutableDirective>("directive");
+  assert(Directive && "Expected to match some OpenMP Executable directive.");
+  const auto *StructuredBlock =
+      Result.Nodes.getNodeAs<Stmt>("structured-block");
+  assert(StructuredBlock && "Expected to get some OpenMP Structured Block.");
+
+  if (Tracer.analyze(StructuredBlock).getBehaviour() !=
+      utils::ExceptionAnalyzer::State::Throwing)
+    return; // No exceptions have been proven to escape out of the struc. block.
+
+  // FIXME: We should provide more information about the exact location where
+  // the exception is thrown, maybe the full path the exception escapes.
+
+  diag(Directive->getBeginLoc(),
+       "an exception thrown inside of the OpenMP '%0' region is not caught in "
+       "that same region")
+      << getOpenMPDirectiveName(Directive->getDirectiveKind());
+}
+
+} // namespace openmp
+} // namespace tidy
+} // namespace clang

Added: clang-tools-extra/trunk/clang-tidy/openmp/ExceptionEscapeCheck.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/openmp/ExceptionEscapeCheck.h?rev=356802&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/openmp/ExceptionEscapeCheck.h (added)
+++ clang-tools-extra/trunk/clang-tidy/openmp/ExceptionEscapeCheck.h Fri Mar 22 12:46:25 2019
@@ -0,0 +1,41 @@
+//===--- ExceptionEscapeCheck.h - clang-tidy --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OPENMP_EXCEPTIONESCAPECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OPENMP_EXCEPTIONESCAPECHECK_H
+
+#include "../ClangTidy.h"
+#include "../utils/ExceptionAnalyzer.h"
+
+namespace clang {
+namespace tidy {
+namespace openmp {
+
+/// Analyzes OpenMP Structured Blocks and checks that no exception escapes
+/// out of the Structured Block it was thrown in.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/openmp-exception-escape.html
+class ExceptionEscapeCheck : public ClangTidyCheck {
+public:
+  ExceptionEscapeCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  std::string RawIgnoredExceptions;
+
+  utils::ExceptionAnalyzer Tracer;
+};
+
+} // namespace openmp
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OPENMP_EXCEPTIONESCAPECHECK_H

Modified: clang-tools-extra/trunk/clang-tidy/openmp/OpenMPTidyModule.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/openmp/OpenMPTidyModule.cpp?rev=356802&r1=356801&r2=356802&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/openmp/OpenMPTidyModule.cpp (original)
+++ clang-tools-extra/trunk/clang-tidy/openmp/OpenMPTidyModule.cpp Fri Mar 22 12:46:25 2019
@@ -9,6 +9,7 @@
 #include "../ClangTidy.h"
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
+#include "ExceptionEscapeCheck.h"
 #include "UseDefaultNoneCheck.h"
 
 namespace clang {
@@ -19,6 +20,8 @@ namespace openmp {
 class OpenMPModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<ExceptionEscapeCheck>(
+        "openmp-exception-escape");
     CheckFactories.registerCheck<UseDefaultNoneCheck>(
         "openmp-use-default-none");
   }

Modified: clang-tools-extra/trunk/clang-tidy/utils/ExceptionAnalyzer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/utils/ExceptionAnalyzer.cpp?rev=356802&r1=356801&r2=356802&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/utils/ExceptionAnalyzer.cpp (original)
+++ clang-tools-extra/trunk/clang-tidy/utils/ExceptionAnalyzer.cpp Fri Mar 22 12:46:25 2019
@@ -224,6 +224,12 @@ ExceptionAnalyzer::analyzeImpl(const Fun
   return ExceptionList;
 }
 
+ExceptionAnalyzer::ExceptionInfo
+ExceptionAnalyzer::analyzeImpl(const Stmt *Stmt) {
+  llvm::SmallSet<const FunctionDecl *, 32> CallStack;
+  return throwsException(Stmt, ExceptionInfo::Throwables(), CallStack);
+}
+
 template <typename T>
 ExceptionAnalyzer::ExceptionInfo
 ExceptionAnalyzer::analyzeDispatch(const T *Node) {
@@ -245,6 +251,11 @@ ExceptionAnalyzer::analyze(const Functio
   return analyzeDispatch(Func);
 }
 
+ExceptionAnalyzer::ExceptionInfo
+ExceptionAnalyzer::analyze(const Stmt *Stmt) {
+  return analyzeDispatch(Stmt);
+}
+
 } // namespace utils
 } // namespace tidy
 

Modified: clang-tools-extra/trunk/clang-tidy/utils/ExceptionAnalyzer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/utils/ExceptionAnalyzer.h?rev=356802&r1=356801&r2=356802&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/utils/ExceptionAnalyzer.h (original)
+++ clang-tools-extra/trunk/clang-tidy/utils/ExceptionAnalyzer.h Fri Mar 22 12:46:25 2019
@@ -129,6 +129,7 @@ public:
   }
 
   ExceptionInfo analyze(const FunctionDecl *Func);
+  ExceptionInfo analyze(const Stmt *Stmt);
 
 private:
   ExceptionInfo
@@ -139,6 +140,7 @@ private:
                   llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
 
   ExceptionInfo analyzeImpl(const FunctionDecl *Func);
+  ExceptionInfo analyzeImpl(const Stmt *Stmt);
 
   template <typename T> ExceptionInfo analyzeDispatch(const T *Node);
 

Modified: clang-tools-extra/trunk/docs/ReleaseNotes.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/ReleaseNotes.rst?rev=356802&r1=356801&r2=356802&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/ReleaseNotes.rst (original)
+++ clang-tools-extra/trunk/docs/ReleaseNotes.rst Fri Mar 22 12:46:25 2019
@@ -130,6 +130,12 @@ Improvements to clang-tidy
   <clang-tidy/checks/modernize-use-override>` now supports `OverrideSpelling`
   and `FinalSpelling` options.
 
+- New :doc:`openmp-exception-escape
+  <clang-tidy/checks/openmp-exception-escape>` check.
+
+  Analyzes OpenMP Structured Blocks and checks that no exception escapes
+  out of the Structured Block it was thrown in.
+
 - New :doc:`openmp-use-default-none
   <clang-tidy/checks/openmp-use-default-none>` check.
 

Modified: clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst?rev=356802&r1=356801&r2=356802&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst (original)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst Fri Mar 22 12:46:25 2019
@@ -227,6 +227,7 @@ Clang-Tidy Checks
    objc-avoid-spinlock
    objc-forbidden-subclassing
    objc-property-declaration
+   openmp-exception-escape
    openmp-use-default-none
    performance-faster-string-find
    performance-for-range-copy

Added: clang-tools-extra/trunk/docs/clang-tidy/checks/openmp-exception-escape.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/openmp-exception-escape.rst?rev=356802&view=auto
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/openmp-exception-escape.rst (added)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/openmp-exception-escape.rst Fri Mar 22 12:46:25 2019
@@ -0,0 +1,25 @@
+.. title:: clang-tidy - openmp-exception-escape
+
+openmp-exception-escape
+=======================
+
+Analyzes OpenMP Structured Blocks and checks that no exception escapes
+out of the Structured Block it was thrown in.
+
+As per the OpenMP specification, a structured block is an executable statement,
+possibly compound, with a single entry at the top and a single exit at the
+bottom. Which means, ``throw`` may not be used to to 'exit' out of the
+structured block. If an exception is not caught in the same structured block
+it was thrown in, the behaviour is undefined.
+
+FIXME: this check does not model SEH, ``setjmp``/``longjmp``.
+
+WARNING! This check may be expensive on large source files.
+
+Options
+-------
+
+.. option:: IgnoredExceptions
+
+   Comma-separated list containing type names which are not counted as thrown
+   exceptions in the check. Default value is an empty string.

Added: clang-tools-extra/trunk/test/clang-tidy/bugprone-exception-escape-openmp.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/bugprone-exception-escape-openmp.cpp?rev=356802&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/bugprone-exception-escape-openmp.cpp (added)
+++ clang-tools-extra/trunk/test/clang-tidy/bugprone-exception-escape-openmp.cpp Fri Mar 22 12:46:25 2019
@@ -0,0 +1,29 @@
+// RUN: %check_clang_tidy %s bugprone-exception-escape %t -- -extra-arg=-fopenmp=libomp -extra-arg=-fexceptions --
+
+int thrower() {
+  throw 42;
+}
+
+void ok_parallel() {
+#pragma omp parallel
+  thrower();
+}
+
+void bad_for_header_XFAIL(const int a) noexcept {
+#pragma omp for
+  for (int i = 0; i < thrower(); i++)
+    ;
+  // FIXME: this really should be caught by bugprone-exception-escape.
+  // https://bugs.llvm.org/show_bug.cgi?id=41102
+}
+
+void ok_forloop(const int a) {
+#pragma omp for
+  for (int i = 0; i < a; i++)
+    thrower();
+}
+
+void some_exception_just_so_that_check_clang_tidy_shuts_up() noexcept {
+  thrower();
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: an exception may be thrown in function 'some_exception_just_so_that_check_clang_tidy_shuts_up' which should not throw exceptions

Added: clang-tools-extra/trunk/test/clang-tidy/openmp-exception-escape.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/openmp-exception-escape.cpp?rev=356802&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/openmp-exception-escape.cpp (added)
+++ clang-tools-extra/trunk/test/clang-tidy/openmp-exception-escape.cpp Fri Mar 22 12:46:25 2019
@@ -0,0 +1,132 @@
+// RUN: %check_clang_tidy %s openmp-exception-escape %t -- -extra-arg=-fopenmp=libomp -extra-arg=-fexceptions -config="{CheckOptions: [{key: openmp-exception-escape.IgnoredExceptions, value: 'ignored, ignored2'}]}" --
+
+int thrower() {
+  throw 42;
+}
+
+class ignored {};
+class ignored2 {};
+namespace std {
+class bad_alloc {};
+} // namespace std
+
+void parallel() {
+#pragma omp parallel
+  thrower();
+  // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: an exception thrown inside of the OpenMP 'parallel' region is not caught in that same region
+}
+
+void ignore() {
+#pragma omp parallel
+  throw ignored();
+}
+
+void ignore2() {
+#pragma omp parallel
+  throw ignored2();
+}
+
+void standalone_directive() {
+#pragma omp taskwait
+  throw ignored(); // not structured block
+}
+
+void ignore_alloc() {
+#pragma omp parallel
+  throw std::bad_alloc();
+}
+
+void parallel_caught() {
+#pragma omp parallel
+  {
+    try {
+      thrower();
+    } catch (...) {
+    }
+  }
+}
+
+void for_header(const int a) {
+  // Only the body of the loop counts.
+#pragma omp for
+  for (int i = 0; i < thrower(); i++)
+    ;
+}
+
+void forloop(const int a) {
+#pragma omp for
+  for (int i = 0; i < a; i++)
+    thrower();
+  // CHECK-MESSAGES: :[[@LINE-3]]:9: warning: an exception thrown inside of the OpenMP 'for' region is not caught in that same region
+}
+
+void parallel_forloop(const int a) {
+#pragma omp parallel
+  {
+#pragma omp for
+    for (int i = 0; i < a; i++)
+      thrower();
+    thrower();
+    // CHECK-MESSAGES: :[[@LINE-6]]:9: warning: an exception thrown inside of the OpenMP 'parallel' region is not caught in that same region
+    // CHECK-MESSAGES: :[[@LINE-5]]:9: warning: an exception thrown inside of the OpenMP 'for' region is not caught in that same region
+  }
+}
+
+void parallel_forloop_caught(const int a) {
+#pragma omp parallel
+  {
+#pragma omp for
+    for (int i = 0; i < a; i++) {
+      try {
+        thrower();
+      } catch (...) {
+      }
+    }
+    thrower();
+    // CHECK-MESSAGES: :[[@LINE-10]]:9: warning: an exception thrown inside of the OpenMP 'parallel' region is not caught in that same region
+  }
+}
+
+void parallel_caught_forloop(const int a) {
+#pragma omp parallel
+  {
+#pragma omp for
+    for (int i = 0; i < a; i++)
+      thrower();
+    try {
+      thrower();
+    } catch (...) {
+    }
+    // CHECK-MESSAGES: :[[@LINE-7]]:9: warning: an exception thrown inside of the OpenMP 'for' region is not caught in that same region
+  }
+}
+
+void parallel_outercaught_forloop(const int a) {
+#pragma omp parallel
+  {
+    try {
+#pragma omp for
+      for (int i = 0; i < a; i++)
+        thrower();
+      thrower();
+    } catch (...) {
+    }
+    // CHECK-MESSAGES: :[[@LINE-6]]:9: warning: an exception thrown inside of the OpenMP 'for' region is not caught in that same region
+  }
+}
+
+void parallel_outercaught_forloop_caught(const int a) {
+#pragma omp parallel
+  {
+    try {
+#pragma omp for
+      for (int i = 0; i < a; i++) {
+        try {
+          thrower();
+        } catch (...) {
+        }
+      }
+    } catch (...) {
+    }
+  }
+}




More information about the cfe-commits mailing list