diff --git a/clang-tools-extra/clang-tidy/linuxkernel/CMakeLists.txt b/clang-tools-extra/clang-tidy/linuxkernel/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/linuxkernel/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/linuxkernel/CMakeLists.txt @@ -6,6 +6,7 @@ add_clang_library(clangTidyLinuxKernelModule LinuxKernelTidyModule.cpp MustCheckErrsCheck.cpp + SwitchSemiCheck.cpp LINK_LIBS clangTidy diff --git a/clang-tools-extra/clang-tidy/linuxkernel/LinuxKernelTidyModule.cpp b/clang-tools-extra/clang-tidy/linuxkernel/LinuxKernelTidyModule.cpp --- a/clang-tools-extra/clang-tidy/linuxkernel/LinuxKernelTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/linuxkernel/LinuxKernelTidyModule.cpp @@ -10,6 +10,7 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" #include "MustCheckErrsCheck.h" +#include "SwitchSemiCheck.h" namespace clang { namespace tidy { @@ -21,6 +22,8 @@ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck( "linuxkernel-must-check-errs"); + CheckFactories.registerCheck( + "linuxkernel-switch-semi"); } }; // Register the LinuxKernelTidyModule using this statically initialized diff --git a/clang-tools-extra/clang-tidy/linuxkernel/SwitchSemiCheck.h b/clang-tools-extra/clang-tidy/linuxkernel/SwitchSemiCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/linuxkernel/SwitchSemiCheck.h @@ -0,0 +1,30 @@ +//===--- SwitchSemiCheck.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_LINUXKERNEL_SWITCHSEMICHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LINUXKERNEL_SWITCHSEMICHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace linuxkernel { + +class SwitchSemiCheck : public ClangTidyCheck { +public: + SwitchSemiCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace linuxkernel +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LINUXKERNEL_SWITCHSEMICHECK_H diff --git a/clang-tools-extra/clang-tidy/linuxkernel/SwitchSemiCheck.cpp b/clang-tools-extra/clang-tidy/linuxkernel/SwitchSemiCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/linuxkernel/SwitchSemiCheck.cpp @@ -0,0 +1,50 @@ +//===--- SwitchSemiCheck.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 "SwitchSemiCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace linuxkernel { + +void SwitchSemiCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + compoundStmt(has(switchStmt().bind("switch"))).bind("comp"), this); +} + +void SwitchSemiCheck::check(const MatchFinder::MatchResult &Result) { + auto *C = Result.Nodes.getNodeAs("comp"); + auto *S = Result.Nodes.getNodeAs("switch"); + + auto Current = C->body_begin(); + auto Next = Current; + Next++; + while (Next != C->body_end()) { + if (*Current == S) { + if (const auto *N = dyn_cast(*Next)) { + if (!N->hasLeadingEmptyMacro() && S->getBody()->getEndLoc().isValid() && + N->getSemiLoc().isValid()) { + auto H = FixItHint::CreateReplacement( + SourceRange(S->getBody()->getEndLoc(), N->getSemiLoc()), "}"); + diag(N->getSemiLoc(), "unneeded semicolon") << H; + break; + } + } + } + Current = Next; + Next++; + } +} + +} // namespace linuxkernel +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/test/clang-tidy/checkers/linuxkernel-switch-semi.c b/clang-tools-extra/test/clang-tidy/checkers/linuxkernel-switch-semi.c new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/linuxkernel-switch-semi.c @@ -0,0 +1,13 @@ +// RUN: %check_clang_tidy %s linuxkernel-switch-semi %t + +int f(int a) { + switch (a) { + case 1: + return 0; + default: + break; + }; + // CHECK-MESSAGES: warning: unneeded semicolon + // CHECK-MESSAGES: note: FIX-IT applied suggested code changes + return 0; +}