Sockke created this revision.
Sockke added a reviewer: aaron.ballman.
Herald added subscribers: carlosgalvezp, xazax.hun, mgorny.
Herald added a project: All.
Sockke requested review of this revision.
Herald added a project: clang-tools-extra.
Herald added a subscriber: cfe-commits.

Calls to **stoi** and **stod** families of functions may throw exceptions so 
they need to be surrounded by try/catch.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D128697

Files:
  clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
  clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
  clang-tools-extra/clang-tidy/bugprone/UnhandledExceptionAtStoCheck.cpp
  clang-tools-extra/clang-tidy/bugprone/UnhandledExceptionAtStoCheck.h
  clang-tools-extra/docs/ReleaseNotes.rst
  
clang-tools-extra/docs/clang-tidy/checks/bugprone/unhandled-exception-at-sto.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  
clang-tools-extra/test/clang-tidy/checkers/bugprone/unhandled-exception-at-sto.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/unhandled-exception-at-sto.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/unhandled-exception-at-sto.cpp
@@ -0,0 +1,128 @@
+// RUN: %check_clang_tidy %s bugprone-unhandled-exception-at-sto %t
+
+namespace std {
+
+template <typename Char>
+struct basic_string {
+  basic_string(const Char *);
+  ~basic_string();
+};
+typedef basic_string<char> string;
+
+typedef unsigned int size_t;
+
+int stoi(const std::string &str, size_t *pos = nullptr, int base = 10) { return 0; }
+long stol(const std::string &str, size_t *pos = nullptr, int base = 10) { return 0; }
+long long stoll(const std::string &str, size_t *pos = nullptr, int base = 10) { return 0; }
+
+float stof(const std::string &str, size_t *pos = nullptr) { return 0; }
+double stod(const std::string &str, size_t *pos = nullptr) { return 0; }
+long double stold(const std::string &str, size_t *pos = nullptr) { return 0; }
+
+struct exception {};
+struct logic_error : public exception {};
+struct invalid_argument : public logic_error {};
+struct out_of_range : public logic_error {};
+
+} // namespace std
+
+void test() {
+  std::string s("123");
+
+  int a = std::stoi(s);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: missing exception handler for invalid argument and out of range at 'std::stoi' [bugprone-unhandled-exception-at-sto]
+  long b = std::stol(s);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: missing exception handler for invalid argument and out of range at 'std::stol' [bugprone-unhandled-exception-at-sto]
+  long long c = std::stoll(s);
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: missing exception handler for invalid argument and out of range at 'std::stoll' [bugprone-unhandled-exception-at-sto]
+  float d = std::stof(s);
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: missing exception handler for invalid argument and out of range at 'std::stof' [bugprone-unhandled-exception-at-sto]
+  double e = std::stod(s);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: missing exception handler for invalid argument and out of range at 'std::stod' [bugprone-unhandled-exception-at-sto]
+  long double f = std::stold(s);
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: missing exception handler for invalid argument and out of range at 'std::stold' [bugprone-unhandled-exception-at-sto]
+
+  try {
+    int v = std::stoi(s);
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: missing exception handler for invalid argument at 'std::stoi' [bugprone-unhandled-exception-at-sto]
+  } catch (std::out_of_range) {
+  }
+  try {
+    int v = std::stoi(s);
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: missing exception handler for invalid argument at 'std::stoi' [bugprone-unhandled-exception-at-sto]
+  } catch (std::out_of_range &) {
+  }
+  try {
+    int v = std::stoi(s);
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: missing exception handler for invalid argument at 'std::stoi' [bugprone-unhandled-exception-at-sto]
+  } catch (const std::out_of_range &) {
+  }
+  try {
+    int v = std::stoi(s);
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: missing exception handler for invalid argument and out of range at 'std::stoi' [bugprone-unhandled-exception-at-sto]
+  } catch (std::out_of_range *) {
+  }
+
+  try {
+    int v = std::stoi(s);
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: missing exception handler for out of range at 'std::stoi' [bugprone-unhandled-exception-at-sto]
+  } catch (std::invalid_argument) {
+  }
+  try {
+    int v = std::stoi(s);
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: missing exception handler for out of range at 'std::stoi' [bugprone-unhandled-exception-at-sto]
+  } catch (std::invalid_argument &) {
+  }
+  try {
+    int v = std::stoi(s);
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: missing exception handler for out of range at 'std::stoi' [bugprone-unhandled-exception-at-sto]
+  } catch (const std::invalid_argument &) {
+  }
+  try {
+    int v = std::stoi(s);
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: missing exception handler for invalid argument and out of range at 'std::stoi' [bugprone-unhandled-exception-at-sto]
+  } catch (std::invalid_argument *) {
+  }
+
+  try {
+    int v = std::stoi(s);
+  } catch (std::exception) {
+  }
+  try {
+    int v = std::stoi(s);
+  } catch (std::exception &) {
+  }
+  try {
+    int v = std::stoi(s);
+  } catch (const std::exception &) {
+  }
+  try {
+    int v = std::stoi(s);
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: missing exception handler for invalid argument and out of range at 'std::stoi' [bugprone-unhandled-exception-at-sto]
+  } catch (std::exception *) {
+  }
+
+  try {
+    int v = std::stoi(s);
+  } catch (std::logic_error) {
+  }
+  try {
+    int v = std::stoi(s);
+  } catch (std::logic_error &) {
+  }
+  try {
+    int v = std::stoi(s);
+  } catch (const std::logic_error &) {
+  }
+  try {
+    int v = std::stoi(s);
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: missing exception handler for invalid argument and out of range at 'std::stoi' [bugprone-unhandled-exception-at-sto]
+  } catch (std::logic_error *) {
+  }
+
+  try {
+    int v = std::stoi(s);
+  } catch (const std::invalid_argument &) {
+  } catch (const std::out_of_range &) {
+  }
+}
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -133,6 +133,7 @@
    `bugprone-undefined-memory-manipulation <bugprone/undefined-memory-manipulation.html>`_,
    `bugprone-undelegated-constructor <bugprone/undelegated-constructor.html>`_,
    `bugprone-unhandled-exception-at-new <bugprone/unhandled-exception-at-new.html>`_,
+   `bugprone-unhandled-exception-at-sto <bugprone/unhandled-exception-at-sto.html>`_, "Yes"
    `bugprone-unhandled-self-assignment <bugprone/unhandled-self-assignment.html>`_,
    `bugprone-unused-raii <bugprone/unused-raii.html>`_, "Yes"
    `bugprone-unused-return-value <bugprone/unused-return-value.html>`_,
Index: clang-tools-extra/docs/clang-tidy/checks/bugprone/unhandled-exception-at-sto.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/bugprone/unhandled-exception-at-sto.rst
@@ -0,0 +1,15 @@
+.. title:: clang-tidy - bugprone-unhandled-exception-at-sto
+
+bugprone-unhandled-exception-at-sto
+===================================
+
+Finds calls to ``stoi`` and ``stof`` families of functions with missing exception handler for ``std::invalid_argument`` and ``std::out_of_range``.
+
+.. code-block:: c++
+
+  int test(const std::string &s) {
+    int v = std::stoi(s); 
+    return v;
+  }
+
+Calls to ``stoi`` and ``stof`` families of functions can throw exceptions of type ``std::invalid_argument`` and ``std::out_of_range`` that should be handled by the code.
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -132,6 +132,11 @@
 
   Detects confusable Unicode identifiers.
 
+- New :doc:`bugprone-unhandled-exception-at-sto
+  <clang-tidy/checks/bugprone/unhandled-exception-at-sto>` check.
+
+  Finds calls to ``stoi`` and ``stod`` families of functions with missing exception handler for ``std::invalid_argument`` and ``std::out_of_range``.
+
 - New :doc:`modernize-macro-to-enum
   <clang-tidy/checks/modernize/macro-to-enum>` check.
 
Index: clang-tools-extra/clang-tidy/bugprone/UnhandledExceptionAtStoCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/bugprone/UnhandledExceptionAtStoCheck.h
@@ -0,0 +1,38 @@
+//===--- UnhandledExceptionAtStoCheck.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_BUGPRONE_UNHANDLEDEXCEPTIONATSTOCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNHANDLEDEXCEPTIONATSTOCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Finds calls to 'stoi' and 'stof' families of functions that may throw
+/// unhandled exception at invalid argument and out of range.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/unhandled-exception-at-sto.html
+class UnhandledExceptionAtStoCheck : public ClangTidyCheck {
+public:
+  UnhandledExceptionAtStoCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus && LangOpts.CXXExceptions;
+  }
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNHANDLEDEXCEPTIONATSTOCHECK_H
Index: clang-tools-extra/clang-tidy/bugprone/UnhandledExceptionAtStoCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/bugprone/UnhandledExceptionAtStoCheck.cpp
@@ -0,0 +1,131 @@
+//===--- UnhandledExceptionAtStoCheck.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 "UnhandledExceptionAtStoCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+AST_MATCHER_P(CXXTryStmt, hasHandlerFor,
+              ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
+  for (unsigned NH = Node.getNumHandlers(), I = 0; I < NH; ++I) {
+    const CXXCatchStmt *CatchS = Node.getHandler(I);
+    // Check for generic catch handler (match anything).
+    if (CatchS->getCaughtType().isNull())
+      return true;
+    ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);
+    if (InnerMatcher.matches(CatchS->getCaughtType(), Finder, &Result)) {
+      *Builder = std::move(Result);
+      return true;
+    }
+  }
+  return false;
+}
+
+void UnhandledExceptionAtStoCheck::registerMatchers(MatchFinder *Finder) {
+  auto ExceptionType =
+      recordType(hasDeclaration(cxxRecordDecl(hasName("::std::exception"))));
+  auto LogicErrorType =
+      recordType(hasDeclaration(cxxRecordDecl(hasName("::std::logic_error"))));
+  auto InvalidArgumentType = recordType(
+      hasDeclaration(cxxRecordDecl(hasName("::std::invalid_argument"))));
+  auto OutOfRangeType =
+      recordType(hasDeclaration(cxxRecordDecl(hasName("::std::out_of_range"))));
+
+  auto ExceptionReferenceType = referenceType(pointee(ExceptionType));
+  auto LogicErrorReferenceType = referenceType(pointee(LogicErrorType));
+  auto InvalidArgumentReferenceType =
+      referenceType(pointee(InvalidArgumentType));
+  auto OutOfRangeReferenceType = referenceType(pointee(OutOfRangeType));
+
+  auto CatchStoExceptionType = qualType(
+      hasCanonicalType(anyOf(ExceptionType, ExceptionReferenceType,
+                             LogicErrorType, LogicErrorReferenceType)));
+  auto CatchInvalidArgumentType = qualType(hasCanonicalType(
+      anyOf(InvalidArgumentType, InvalidArgumentReferenceType)));
+  auto CatchOutOfRangeType = qualType(
+      hasCanonicalType(anyOf(OutOfRangeType, OutOfRangeReferenceType)));
+
+  auto StoExceptionCatchingTryBlock =
+      cxxTryStmt(hasHandlerFor(CatchStoExceptionType)).bind("all");
+  auto StoInvalidArgumentCatchingTryBlock =
+      cxxTryStmt(hasHandlerFor(CatchInvalidArgumentType)).bind("i");
+  auto StoOutOfRangeCatchingTryBlock =
+      cxxTryStmt(hasHandlerFor(CatchOutOfRangeType)).bind("o");
+
+  const auto StoCallee = callee(functionDecl(anyOf(
+      hasName("std::stoi"), hasName("std::stol"), hasName("std::stoll"),
+      hasName("std::stof"), hasName("std::stod"), hasName("std::stold"))));
+
+  Finder->addMatcher(
+      callExpr(StoCallee,
+               allOf(unless(hasAncestor(StoExceptionCatchingTryBlock)),
+                     unless(hasAncestor(StoInvalidArgumentCatchingTryBlock)),
+                     unless(hasAncestor(StoOutOfRangeCatchingTryBlock))))
+          .bind("catched-no-exception-call"),
+      this);
+  Finder->addMatcher(
+      callExpr(StoCallee,
+               allOf(unless(hasAncestor(StoExceptionCatchingTryBlock)),
+                     hasAncestor(StoInvalidArgumentCatchingTryBlock),
+                     unless(hasAncestor(StoOutOfRangeCatchingTryBlock))))
+          .bind("catched-invalid-argument-call"),
+      this);
+  Finder->addMatcher(
+      callExpr(StoCallee,
+               allOf(unless(hasAncestor(StoExceptionCatchingTryBlock)),
+                     hasAncestor(StoOutOfRangeCatchingTryBlock),
+                     unless(hasAncestor(StoInvalidArgumentCatchingTryBlock))))
+          .bind("catched-out-of-range-call"),
+      this);
+}
+
+void UnhandledExceptionAtStoCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *CatchedNoExceptionCall =
+      Result.Nodes.getNodeAs<CallExpr>("catched-no-exception-call");
+  const auto *CatchedInvalidArgumentCall =
+      Result.Nodes.getNodeAs<CallExpr>("catched-invalid-argument-call");
+  const auto *CatchedOutOfRangeCall =
+      Result.Nodes.getNodeAs<CallExpr>("catched-out-of-range-call");
+
+  const CallExpr *CatedStoCall = nullptr;
+  const FunctionDecl *StoCallDecl = nullptr;
+  std::string Message = "missing exception handler for ";
+
+  if (CatchedNoExceptionCall) {
+    CatedStoCall = CatchedNoExceptionCall;
+    StoCallDecl = CatchedNoExceptionCall->getDirectCallee();
+    if (!StoCallDecl)
+      return;
+    Message += "invalid argument and out of range";
+  } else if (CatchedInvalidArgumentCall) {
+    CatedStoCall = CatchedInvalidArgumentCall;
+    StoCallDecl = CatchedInvalidArgumentCall->getDirectCallee();
+    if (!StoCallDecl)
+      return;
+    Message += "out of range";
+  } else {
+    CatedStoCall = CatchedOutOfRangeCall;
+    StoCallDecl = CatchedOutOfRangeCall->getDirectCallee();
+    if (!StoCallDecl)
+      return;
+    Message += "invalid argument";
+  }
+  diag(CatedStoCall->getBeginLoc(), Message + " at 'std::%0'")
+      << StoCallDecl->getName();
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -63,6 +63,7 @@
   UndefinedMemoryManipulationCheck.cpp
   UndelegatedConstructorCheck.cpp
   UnhandledExceptionAtNewCheck.cpp
+  UnhandledExceptionAtStoCheck.cpp
   UnhandledSelfAssignmentCheck.cpp
   UnusedRaiiCheck.cpp
   UnusedReturnValueCheck.cpp
Index: clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -67,6 +67,7 @@
 #include "UndefinedMemoryManipulationCheck.h"
 #include "UndelegatedConstructorCheck.h"
 #include "UnhandledExceptionAtNewCheck.h"
+#include "UnhandledExceptionAtStoCheck.h"
 #include "UnhandledSelfAssignmentCheck.h"
 #include "UnusedRaiiCheck.h"
 #include "UnusedReturnValueCheck.h"
@@ -192,6 +193,8 @@
         "bugprone-undefined-memory-manipulation");
     CheckFactories.registerCheck<UndelegatedConstructorCheck>(
         "bugprone-undelegated-constructor");
+    CheckFactories.registerCheck<UnhandledExceptionAtStoCheck>(
+        "bugprone-unhandled-exception-at-sto");
     CheckFactories.registerCheck<UnhandledSelfAssignmentCheck>(
         "bugprone-unhandled-self-assignment");
     CheckFactories.registerCheck<UnhandledExceptionAtNewCheck>(
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to