barcisz updated this revision to Diff 460376.
barcisz added a comment.
Rebase
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D133804/new/
https://reviews.llvm.org/D133804
Files:
clang-tools-extra/-
clang-tools-extra/clang-tidy/cuda/CMakeLists.txt
clang-tools-extra/clang-tidy/cuda/CudaTidyModule.cpp
clang-tools-extra/clang-tidy/cuda/UnsafeApiCallCheck.cpp
clang-tools-extra/clang-tidy/cuda/UnsafeApiCallCheck.h
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/cuda/unsafe-api-call.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/cuda/cuda.h
clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/cuda/cuda_runtime.h
clang-tools-extra/test/clang-tidy/checkers/cuda/unsafe-api-call-function-handler.cu
clang-tools-extra/test/clang-tidy/checkers/cuda/unsafe-api-call-macro-handler.cu
Index: clang-tools-extra/test/clang-tidy/checkers/cuda/unsafe-api-call-macro-handler.cu
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/cuda/unsafe-api-call-macro-handler.cu
@@ -0,0 +1,96 @@
+// RUN: %check_clang_tidy %s cuda-unsafe-api-call %t -- \
+// RUN: -config="{CheckOptions: \
+// RUN: [{key: cuda-unsafe-api-call.HandlerName, \
+// RUN: value: 'CUDA_HANDLER'}] \
+// RUN: }" \
+// RUN: -- -isystem %clang_tidy_headers -std=c++14
+#include <cuda/cuda_runtime.h>
+
+class DummyContainer {
+ public:
+ int* begin();
+ int* end();
+};
+
+#define DUMMY_CUDA_HANDLER(stmt) stmt
+#define CUDA_HANDLER(stmt) do {auto err = stmt;} while(0)
+#define API_CALL() do {cudaDeviceReset();} while(0)
+
+void errorCheck();
+void errorCheck(cudaError_t error);
+
+void bad() {
+ API_CALL();
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: Unchecked CUDA API call.
+ // There isn't supposed to be a fix here since it's a macro call
+
+ cudaDeviceReset();
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: Unchecked CUDA API call.
+ // CHECK-FIXES: {{^}} CUDA_HANDLER(cudaDeviceReset());{{$}}
+ errorCheck();
+
+ if (true)
+ cudaDeviceReset();
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: Unchecked CUDA API call.
+ // CHECK-FIXES: {{^}} CUDA_HANDLER(cudaDeviceReset());{{$}}
+
+ while (true)
+ cudaDeviceReset();
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: Unchecked CUDA API call.
+ // CHECK-FIXES: {{^}} CUDA_HANDLER(cudaDeviceReset());{{$}}
+
+ do
+ cudaDeviceReset();
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: Unchecked CUDA API call.
+ // CHECK-FIXES: {{^}} CUDA_HANDLER(cudaDeviceReset());{{$}}
+ while(false);
+
+ switch (0) {
+ case 0:
+ cudaDeviceReset();
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: Unchecked CUDA API call.
+ // CHECK-FIXES: {{^}} CUDA_HANDLER(cudaDeviceReset());{{$}}
+ }
+
+ for(
+ cudaDeviceReset();
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: Unchecked CUDA API call.
+ // CHECK-FIXES: {{^}} CUDA_HANDLER(cudaDeviceReset());{{$}}
+ ;
+ cudaDeviceReset()
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: Unchecked CUDA API call.
+ // CHECK-FIXES: {{^}} CUDA_HANDLER(cudaDeviceReset()){{$}}
+ ) cudaDeviceReset();
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: Unchecked CUDA API call.
+ // CHECK-FIXES: {{^}} ) CUDA_HANDLER(cudaDeviceReset());{{$}}
+
+ for(int i : DummyContainer())
+ cudaDeviceReset();
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: Unchecked CUDA API call.
+ // CHECK-FIXES: {{^}} CUDA_HANDLER(cudaDeviceReset());{{$}}
+
+ auto x = ({
+ cudaDeviceReset();
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: Unchecked CUDA API call.
+ // CHECK-FIXES: {{^}} CUDA_HANDLER(cudaDeviceReset());{{$}}
+ true;
+ });
+}
+
+int good() {
+ DUMMY_CUDA_HANDLER(cudaDeviceReset());
+
+ if (cudaDeviceReset()) {
+ return 0;
+ }
+
+ switch (cudaDeviceReset()) {
+ case cudaErrorInvalidValue: return 1;
+ case cudaErrorMemoryAllocation: return 2;
+ default: return 3;
+ }
+
+ auto err = ({cudaDeviceReset();});
+ // NOTE: We don't check that `errorCheck()` actually handles the error; we just assume it does.
+ errorCheck(cudaDeviceReset());
+}
Index: clang-tools-extra/test/clang-tidy/checkers/cuda/unsafe-api-call-function-handler.cu
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/cuda/unsafe-api-call-function-handler.cu
@@ -0,0 +1,71 @@
+// RUN: %check_clang_tidy %s cuda-unsafe-api-call %t -- \
+// RUN: -config="{CheckOptions: \
+// RUN: [{key: cuda-unsafe-api-call.HandlerName, \
+// RUN: value: 'cudaHandler'}, \
+// RUN: {key: cuda-unsafe-api-call.AcceptedHandlers, \
+// RUN: value: 'CUDA_HANDLER, DUMMY_CUDA_HANDLER, \
+// RUN: alternative::cudaAlternativeHandler, \
+// RUN: cudaOtherAlternativeHandler, bad::cudaBadHandler'}] \
+// RUN: }" \
+// RUN: -- -isystem %clang_tidy_headers -std=c++14
+#include <cuda/cuda_runtime.h>
+
+#define DUMMY_CUDA_HANDLER(stmt) stmt
+#define CUDA_HANDLER(stmt) do {auto err = stmt;} while(0)
+#define API_CALL() do {cudaDeviceReset();} while(0)
+#define HANDLED_API_CALL() do {int err2 = cudaDeviceReset();} while(0)
+
+void cudaHandler();
+void cudaHandler(cudaError_t error);
+void badCudaHandler(cudaError_t error);
+
+namespace alternative {
+
+void cudaAlternativeHandler(cudaError_t error);
+
+void cudaOtherAlternativeHandler(cudaError_t error);
+
+} // namespace alternative
+
+void bad() {
+ API_CALL();
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: CUDA API call not checked properly.
+ // There isn't supposed to be a fix here since it's a macro call
+
+ HANDLED_API_CALL();
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: CUDA API call not checked properly.
+ // There isn't supposed to be a fix here since it's a macro call
+
+ cudaDeviceReset();
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: CUDA API call not checked properly.
+ // CHECK-FIXES: {{^}} cudaHandler(cudaDeviceReset());{{$}}
+ cudaHandler();
+
+ if (true)
+ cudaDeviceReset();
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: CUDA API call not checked properly.
+ // CHECK-FIXES: {{^}} cudaHandler(cudaDeviceReset());{{$}}
+
+ badCudaHandler(cudaDeviceReset());
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: CUDA API call not checked properly.
+ // There isn't supposed to be a fix here since the result value is not unused
+
+ int err = cudaDeviceReset();
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: CUDA API call not checked properly.
+ // There isn't supposed to be a fix here since the result value is not unused
+
+ if (cudaDeviceReset()) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: CUDA API call not checked properly.
+ // There isn't supposed to be a fix here since the result value is not unused
+ return;
+ }
+
+}
+
+void good() {
+ cudaHandler(cudaDeviceReset());
+ alternative::cudaAlternativeHandler(cudaDeviceReset());
+ alternative::cudaOtherAlternativeHandler(cudaDeviceReset());
+ CUDA_HANDLER(cudaDeviceReset() + 1);
+ DUMMY_CUDA_HANDLER(cudaDeviceReset());
+}
Index: clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/cuda/cuda_runtime.h
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/cuda/cuda_runtime.h
@@ -0,0 +1,3 @@
+#include "cuda.h"
+
+cudaError_t cudaDeviceReset();
Index: clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/cuda/cuda.h
===================================================================
--- clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/cuda/cuda.h
+++ clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/cuda/cuda.h
@@ -12,7 +12,10 @@
};
typedef struct cudaStream *cudaStream_t;
-typedef enum cudaError {} cudaError_t;
+typedef enum cudaError {
+ cudaErrorInvalidValue,
+ cudaErrorMemoryAllocation
+} cudaError_t;
extern "C" int cudaConfigureCall(dim3 gridSize, dim3 blockSize,
size_t sharedSize = 0,
cudaStream_t stream = 0);
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
@@ -199,6 +199,7 @@
`cppcoreguidelines-pro-type-vararg <cppcoreguidelines/pro-type-vararg.html>`_,
`cppcoreguidelines-slicing <cppcoreguidelines/slicing.html>`_,
`cppcoreguidelines-special-member-functions <cppcoreguidelines/special-member-functions.html>`_,
+ `cuda-unsafe-api-call <cuda/unsafe-api-call.html>`_, "Yes"
`cppcoreguidelines-virtual-class-destructor <cppcoreguidelines/virtual-class-destructor.html>`_, "Yes"
`darwin-avoid-spinlock <darwin/avoid-spinlock.html>`_,
`darwin-dispatch-once-nonstatic <darwin/dispatch-once-nonstatic.html>`_, "Yes"
Index: clang-tools-extra/docs/clang-tidy/checks/cuda/unsafe-api-call.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/cuda/unsafe-api-call.rst
@@ -0,0 +1,61 @@
+.. title:: clang-tidy - cuda-unsafe-api-call
+
+cuda-unsafe-api-call
+====================
+
+Finds usages of CUDA API where the error returned by the API function is not
+handled in any way. It has narrower specification of what it allows and what it
+doesn't than the
+:doc:`bugprone-unused-return-value <../bugprone/unused-return-value>`
+check, which makes it useful for more applications.
+
+Specification
+-------------
+
+A function is considered to be a part of CUDA API if:
+
+- it is included in a file that ends with either ``cuda_runtime.h`` or
+ ``cuda_runtime_wrapper.h`` (those headers are automatically included from the
+ during CUDA code compilation)
+
+- its return type is ``cudaError_t``
+
+If a call to a function like that is made, it has to be used in another
+statement, for example passed as a function argument, assigned to a variable or
+used in an if statement. The only exception is passing the value to a macro,
+which is considered a valid way to handle the error even if the macro does not
+use the return value of the call (this is to allow dummy marker macros that
+just pass the value through). It is recommended that a project-wide handler(s)
+is set to handle such errors, but this is not a default requirement of the check
+(though it can be set with the check's options).
+
+Example:
+
+.. code-block:: c++
+
+ void foo() {
+ cudaDeviceReset();
+ }
+
+results in the following warnings::
+
+ 1 warning generated when compiling for host.
+ test.cu:2:3: warning: Unchecked CUDA API call. Consider adding logic to check if an error has been returned or specify the error handler for this project. [cuda-unsafe-api-call]
+ cudaDeviceReset();
+ ^
+
+Options
+-------
+
+.. option:: HandlerName
+
+ The name of the function or macro that should be used in fix it hints to
+ handle the return value of an API call.
+
+.. option:: AcceptedHandlers
+
+ The list of handler functions or macros that are allowed for the specific
+ project. If specified, the only valid way to handle the error returned
+ by a CUDA API call is to pass it as one of the arguments to said handlers.
+ If the HandlerName option is also specified then it will be implicitly
+ added as one of the accepted handlers.
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -106,6 +106,11 @@
Warns when a struct or class uses const or reference (lvalue or rvalue) data members.
+- New :doc:`cuda-unsafe-api-call <clang-tidy/checks/cuda/unsafe-api-call>` check.
+
+ Warns whenever the error from CUDA API call is ignored/not handled with a set handler
+ and provides fixes for it.
+
New check aliases
^^^^^^^^^^^^^^^^^
Index: clang-tools-extra/clang-tidy/cuda/UnsafeApiCallCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/cuda/UnsafeApiCallCheck.h
@@ -0,0 +1,109 @@
+//===--- UnsafeApiCallCheck.h - 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
+//
+//===----------------------------------------------------------------------===//
+
+#pragma once
+
+#include "../ClangTidyCheck.h"
+#include "llvm/ADT/StringSet.h"
+#include <memory>
+#include <unordered_set>
+
+namespace clang {
+namespace tidy {
+namespace cuda {
+
+/// Checks for whether the possible errors with the CUDA API invocations have
+/// been handled.
+///
+/// Calls to CUDA API can sometimes fail to perform the action. This may happen
+/// due to a driver malfunction, lack of permissions, lack of a GPU, or a
+/// multitude of other reasons. Such errors are returned by those API calls and
+/// should be handled in some way.
+/// The check provides the following options:
+/// - "HandlerName" (optional):
+/// specifies the name of the function or the macro to which the return
+/// value of the API call should be passed. This effectively automates the
+/// process of adding the error checks in question for projects that have
+/// such a mechanism implemented in them.
+/// - "AcceptedHandlers" (optional):
+/// a comma-separated list specifying the only accepted handling
+/// functions/macros into which the error from the api call can be passed.
+/// If not specified all ways to handle the error that do not just ignore
+/// the output value are accepted. The handlers may have scope specifiers
+/// included in them, but if so then the full qualified name (with all
+/// namespaces explicitly stated) has to be provided (for the performance
+/// sake). If the handler set in the "HandlerName" is not in the list of
+/// accepted handlers then it gets added to it automatially.
+///
+/// Since the behavior of the check is significantly different when the
+/// "AcceptedHandlers" option is set, the implementation is essentially split
+/// into 2 paths, as highlighted by the comments near declarations.
+class UnsafeApiCallCheck : public ClangTidyCheck {
+ class PPCallbacks;
+
+ // For gathering api calls with an unused value - only those nodes
+ // can have a FixItHint when we limit the accepted handlers.
+ //
+ // Only used when "AcceptedHandlers" is set
+ class UnusedValueCallback
+ : public clang::ast_matchers::MatchFinder::MatchCallback {
+ public:
+ UnusedValueCallback(UnsafeApiCallCheck *check) : Check(check) {}
+ void
+ run(const clang::ast_matchers::MatchFinder::MatchResult &Result) override;
+ void onStartOfTranslationUnit() override;
+
+ private:
+ UnsafeApiCallCheck *Check;
+ };
+
+public:
+ UnsafeApiCallCheck(llvm::StringRef Name,
+ clang::tidy::ClangTidyContext *Context);
+
+ void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+ Preprocessor *ModuleExpanderPP) override;
+ void registerMatchers(clang::ast_matchers::MatchFinder *Finder) override;
+ void
+ check(const clang::ast_matchers::MatchFinder::MatchResult &Result) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+ const std::string HandlerName;
+
+ // Only used when "AcceptedHandlers" is not set
+ void registerUnusedValueMatchers(clang::ast_matchers::MatchFinder *Finder);
+ // Only used when "AcceptedHandlers" is set
+ void registerBadlyHandledMatchers(clang::ast_matchers::MatchFinder *Finder);
+
+ // Only used when "AcceptedHandlers" is set
+ void
+ checkUnusedValue(const clang::ast_matchers::MatchFinder::MatchResult &Result);
+ // Only used when "AcceptedHandlers" is not set
+ void
+ checkBadHandler(const clang::ast_matchers::MatchFinder::MatchResult &Result);
+
+ const std::string AcceptedHandlersList; // Data store for AcceptedHandlersSet
+ const llvm::StringSet<llvm::MallocAllocator> AcceptedHandlersSet;
+ // Generates AcceptedHandlersSet from AcceptedHandlersList
+ static llvm::StringSet<llvm::MallocAllocator>
+ splitAcceptedHandlers(const llvm::StringRef &AcceptedHandlers,
+ const llvm::StringRef &HandlerName);
+ bool limitAcceptedHandlers();
+
+ // Only used when "AcceptedHandlers" is set
+ std::unordered_set<SourceLocation,
+ std::function<unsigned(const SourceLocation &)>>
+ AcceptedHandlerMacroLocations;
+ std::unordered_set<const Stmt *> UnusedValueNodes;
+ std::unique_ptr<UnusedValueCallback> UnusedValueCallbackInstance;
+};
+
+} // namespace cuda
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/cuda/UnsafeApiCallCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/cuda/UnsafeApiCallCheck.cpp
@@ -0,0 +1,300 @@
+//===--- UnsafeApiCallCheck.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 "UnsafeApiCallCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/FixIt.h"
+
+#include <functional>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cuda {
+
+namespace {
+
+constexpr auto HandlerNameOptionName = "HandlerName";
+constexpr auto AcceptedHandlersOptionName = "AcceptedHandlers";
+
+} // namespace
+
+UnsafeApiCallCheck::UnsafeApiCallCheck(llvm::StringRef Name,
+ clang::tidy::ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ HandlerName(Options.get(HandlerNameOptionName, "")),
+ AcceptedHandlersList(Options.get(AcceptedHandlersOptionName, "")),
+ AcceptedHandlersSet(
+ splitAcceptedHandlers(AcceptedHandlersList, HandlerName)),
+ AcceptedHandlerMacroLocations(
+ 8, [](const SourceLocation &sLoc) { return sLoc.getHashValue(); }) {
+ // If an empty string was inserted then that means that there was an empty
+ // accepted handler in the list
+ if (AcceptedHandlersSet.find("") != AcceptedHandlersSet.end()) {
+ configurationDiag(
+ "Empty handler name found in the list of accepted handlers",
+ DiagnosticIDs::Error);
+ }
+}
+
+llvm::StringSet<llvm::MallocAllocator>
+UnsafeApiCallCheck::splitAcceptedHandlers(
+ const llvm::StringRef &AcceptedHandlers,
+ const llvm::StringRef &HandlerName) {
+ // Check the case for when the accepted handlers are empty since otherwise
+ // split(...) will still fill the vector with an empty element
+ if (AcceptedHandlers.trim().empty()) {
+ return llvm::StringSet<llvm::MallocAllocator>();
+ }
+ llvm::SmallVector<llvm::StringRef> AcceptedHandlersVector;
+ AcceptedHandlers.split(AcceptedHandlersVector, ',');
+
+ llvm::StringSet<llvm::MallocAllocator> AcceptedHandlersSet;
+ for (auto AcceptedHandler : AcceptedHandlersVector) {
+ AcceptedHandlersSet.insert(AcceptedHandler.trim());
+ }
+
+ // If the handler for FixItHints is set then add it to
+ if (!AcceptedHandlersSet.empty() && !HandlerName.empty()) {
+ AcceptedHandlersSet.insert(HandlerName);
+ }
+
+ return AcceptedHandlersSet;
+}
+
+void UnsafeApiCallCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, HandlerNameOptionName, HandlerName);
+ Options.store(Opts, AcceptedHandlersOptionName, AcceptedHandlersList);
+}
+
+inline bool UnsafeApiCallCheck::limitAcceptedHandlers() {
+ return !AcceptedHandlersSet.empty();
+}
+
+// Used for finding the occurences of accepted handler macros in the source
+// code.
+class UnsafeApiCallCheck::PPCallbacks : public clang::PPCallbacks {
+public:
+ PPCallbacks(UnsafeApiCallCheck *Check) : Check(Check) {}
+
+ void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
+ SourceRange Range, const MacroArgs *Args) override {
+ if (Check->AcceptedHandlersSet.find(
+ MacroNameTok.getIdentifierInfo()->getName()) !=
+ Check->AcceptedHandlersSet.end()) {
+ Check->AcceptedHandlerMacroLocations.insert(MacroNameTok.getLocation());
+ }
+ }
+
+private:
+ UnsafeApiCallCheck *Check;
+};
+
+void UnsafeApiCallCheck::registerPPCallbacks(const SourceManager &SM,
+ Preprocessor *PP,
+ Preprocessor *ModuleExpanderPP) {
+ if (limitAcceptedHandlers()) {
+ ModuleExpanderPP->addPPCallbacks(std::make_unique<PPCallbacks>(this));
+ }
+}
+
+namespace {
+
+// Check if the declaration is in a specific header based on a condition
+AST_MATCHER_P(Decl, isInSourceFile, std::function<bool(const StringRef &)>,
+ SourceFileNameCond) {
+ auto Loc = Node.getLocation();
+ const auto &SM = Finder->getASTContext().getSourceManager();
+ while (Loc.isValid()) {
+ if (SourceFileNameCond(SM.getFilename(Loc))) {
+ return true;
+ }
+ Loc = SM.getIncludeLoc(SM.getFileID(Loc));
+ }
+ return false;
+}
+
+// Check if the name of the declaration matches a specific condition
+AST_MATCHER_P(NamedDecl, hasName, std::function<bool(const StringRef &)>,
+ DeclNameCond) {
+ return DeclNameCond(Node.getName());
+}
+
+// Check if the fully qualified name of the declaration matches a specific
+// condition
+AST_MATCHER_P(NamedDecl, hasQualName, std::function<bool(const StringRef &)>,
+ DeclNameCond) {
+ return DeclNameCond(Node.getQualifiedNameAsString());
+}
+
+constexpr auto UnusedValueBinding = "UnusedValueCall";
+constexpr auto badlyHandledBinding = "badlyHandledCall";
+
+// Common matchers for both unlimited and limited accepted handlers.
+const auto HostFunction = functionDecl(unless(anyOf(
+ hasAttr(attr::CUDADevice),
+ hasAttr(attr::CUDAGlobal)))); // CUDA API cannot be called from device code
+const auto ApiCallExpression = callExpr(
+ callee(functionDecl(isInSourceFile([](StringRef FileName) {
+ return FileName.endswith("cuda_runtime.h") ||
+ FileName.endswith("cuda_runtime_wrapper.h");
+ }), // All CUDA API is included from the cuda_runtime.h
+ // header or __cuda_runtime_wrapper.h
+ returns(asString("cudaError_t")))));
+
+} // namespace
+
+void UnsafeApiCallCheck::UnusedValueCallback::run(
+ const MatchFinder::MatchResult &Result) {
+ auto Node = Result.Nodes.getNodeAs<Stmt>(UnusedValueBinding);
+ assert(Node);
+ Check->UnusedValueNodes.insert(Node);
+}
+
+void UnsafeApiCallCheck::UnusedValueCallback::onStartOfTranslationUnit() {
+ Check->UnusedValueNodes.clear();
+}
+
+void UnsafeApiCallCheck::registerMatchers(MatchFinder *Finder) {
+ if (limitAcceptedHandlers()) {
+ registerBadlyHandledMatchers(Finder);
+ } else {
+ registerUnusedValueMatchers(Finder);
+ }
+}
+
+void UnsafeApiCallCheck::registerUnusedValueMatchers(MatchFinder *Finder) {
+ const auto UnusedValue =
+ matchers::isValueUnused(stmt(ApiCallExpression.bind(UnusedValueBinding)));
+ Finder->addMatcher(functionDecl(HostFunction, hasBody(UnusedValue)), this);
+}
+
+void UnsafeApiCallCheck::registerBadlyHandledMatchers(MatchFinder *Finder) {
+ const auto UnusedValue =
+ matchers::isValueUnused(stmt(ApiCallExpression.bind(UnusedValueBinding)));
+ UnusedValueCallbackInstance = std::make_unique<UnusedValueCallback>(this);
+ Finder->addMatcher(functionDecl(HostFunction, hasBody(UnusedValue)),
+ UnusedValueCallbackInstance.get());
+
+ const auto AcceptedHandlerPred = [this](const StringRef &Name) {
+ return AcceptedHandlersSet.contains(Name);
+ };
+
+ const auto AcceptedHandlerDecl = functionDecl(
+ anyOf(hasName(AcceptedHandlerPred), hasQualName(AcceptedHandlerPred)));
+ const auto AcceptedHandlerParent = callExpr(callee(AcceptedHandlerDecl));
+
+ Finder->addMatcher(
+ functionDecl(
+ HostFunction,
+ forEachDescendant(stmt(ApiCallExpression.bind(badlyHandledBinding),
+ unless(hasParent(AcceptedHandlerParent))))),
+ this);
+}
+
+namespace {
+
+constexpr auto HandlerMsg = "Consider wrapping it with a call to "
+ "an error handler:";
+constexpr auto NoHandlerMsg =
+ "Consider adding logic to check if an error has "
+ "been returned or specify the error handler for this project.";
+
+inline bool isStmtInMacro(const Stmt *const Stmt) {
+ return Stmt->getBeginLoc().isInvalid() || Stmt->getBeginLoc().isMacroID() ||
+ Stmt->getEndLoc().isInvalid() || Stmt->getEndLoc().isMacroID();
+}
+
+} // namespace
+
+void UnsafeApiCallCheck::check(const MatchFinder::MatchResult &Result) {
+ if (limitAcceptedHandlers()) {
+ checkBadHandler(Result);
+ } else {
+ checkUnusedValue(Result);
+ }
+}
+
+void UnsafeApiCallCheck::checkUnusedValue(
+ const MatchFinder::MatchResult &Result) {
+ const std::string MessagePrefix = "Unchecked CUDA API call. ";
+
+ const auto ApiCallNode = Result.Nodes.getNodeAs<Stmt>(UnusedValueBinding);
+ assert(ApiCallNode);
+
+ // This disables the check for arguments inside macros, since we assume that
+ // such a macro is intended as a handler (even if it just passes the argument
+ // right through)
+ if (Result.SourceManager->isMacroArgExpansion(ApiCallNode->getBeginLoc())) {
+ return;
+ }
+
+ if (HandlerName.empty()) {
+ diag(ApiCallNode->getBeginLoc(), MessagePrefix + NoHandlerMsg);
+ } else if (isStmtInMacro(ApiCallNode)) {
+ diag(ApiCallNode->getBeginLoc(),
+ MessagePrefix + "Consider wrapping it with a call to `" + HandlerName +
+ "`");
+ } else {
+ diag(ApiCallNode->getBeginLoc(), MessagePrefix + HandlerMsg)
+ << FixItHint::CreateReplacement(
+ ApiCallNode->getSourceRange(),
+ (HandlerName + "(" +
+ tooling::fixit::getText(ApiCallNode->getSourceRange(),
+ *Result.Context) +
+ ")")
+ .str());
+ }
+}
+
+void UnsafeApiCallCheck::checkBadHandler(
+ const MatchFinder::MatchResult &Result) {
+ const std::string MessagePrefix = "CUDA API call not checked properly. ";
+
+ const auto ApiCallNode = Result.Nodes.getNodeAs<Stmt>(badlyHandledBinding);
+ assert(ApiCallNode);
+
+ const auto ApiCallNodeMacroLocation = Result.SourceManager->getExpansionLoc(
+ Result.SourceManager->getMacroArgExpandedLocation(
+ ApiCallNode->getBeginLoc()));
+
+ // This disables the check for arguments inside macros, since we assume that
+ // such a macro is intended as a handler (even if it just passes the argument
+ // right through)
+ if (Result.SourceManager->isMacroArgExpansion(ApiCallNode->getBeginLoc()) &&
+ AcceptedHandlerMacroLocations.find(ApiCallNodeMacroLocation) !=
+ AcceptedHandlerMacroLocations.end()) {
+ return;
+ }
+
+ if (HandlerName.empty()) {
+ diag(ApiCallNode->getBeginLoc(), MessagePrefix + NoHandlerMsg);
+ } else if (isStmtInMacro(ApiCallNode) ||
+ UnusedValueNodes.find(ApiCallNode) == UnusedValueNodes.end()) {
+ diag(ApiCallNode->getBeginLoc(),
+ MessagePrefix + "Consider wrapping it with a call to `" + HandlerName +
+ "`");
+ } else {
+ diag(ApiCallNode->getBeginLoc(), MessagePrefix + HandlerMsg)
+ << FixItHint::CreateReplacement(
+ ApiCallNode->getSourceRange(),
+ (HandlerName + "(" +
+ tooling::fixit::getText(ApiCallNode->getSourceRange(),
+ *Result.Context) +
+ ")")
+ .str());
+ }
+}
+
+} // namespace cuda
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/cuda/CudaTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/cuda/CudaTidyModule.cpp
+++ clang-tools-extra/clang-tidy/cuda/CudaTidyModule.cpp
@@ -7,9 +7,9 @@
//===----------------------------------------------------------------------===//
#include "../ClangTidy.h"
-#include "../ClangTidyCheck.h"
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
+#include "UnsafeApiCallCheck.h"
using namespace clang::ast_matchers;
@@ -19,7 +19,9 @@
class CudaModule : public ClangTidyModule {
public:
- void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {}
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<UnsafeApiCallCheck>("cuda-unsafe-api-call");
+ }
};
// Register the CudaTidyModule using this statically initialized variable.
Index: clang-tools-extra/clang-tidy/cuda/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/cuda/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/cuda/CMakeLists.txt
@@ -1,5 +1,6 @@
add_clang_library(clangTidyCudaModule
CudaTidyModule.cpp
+ UnsafeApiCallCheck.cpp
LINK_LIBS
clangTidy
clangTidyUtils
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits