trixirt created this revision.
trixirt added reviewers: nickdesaulniers, alexfh, hokein, aaron.ballman.
trixirt added a project: clang-tools-extra.
Herald added subscribers: cfe-commits, xazax.hun, mgorny.
Herald added a project: clang.
trixirt requested review of this revision.

Cleaning up -Wextra-semi-stmt in the linux kernel shows a high
incidence of macros with trailing semicolons

#define M(a) a++; <-- clang-tidy fixes problem here
 int f() {

  int v = 0;
  M(v); <-- clang reports problem here
  return v;

}


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D91789

Files:
  clang-tools-extra/clang-tidy/linuxkernel/CMakeLists.txt
  clang-tools-extra/clang-tidy/linuxkernel/LinuxKernelTidyModule.cpp
  clang-tools-extra/clang-tidy/linuxkernel/MacroTrailingSemiCheck.cpp
  clang-tools-extra/clang-tidy/linuxkernel/MacroTrailingSemiCheck.h
  clang-tools-extra/test/clang-tidy/checkers/linuxkernel-macro-trailing-semi.c

Index: clang-tools-extra/test/clang-tidy/checkers/linuxkernel-macro-trailing-semi.c
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/linuxkernel-macro-trailing-semi.c
@@ -0,0 +1,27 @@
+// RUN: %check_clang_tidy %s linuxkernel-macro-trailing-semi %t
+
+#define M(a) a++;
+// CHECK-MESSAGES: warning: unneeded semicolon
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+
+int f() {
+  int v = 0;
+  M(v);
+  return v;
+}
+
+// A corner case
+// An empty macro could conditionally contain another path so
+// do not warn or fix.
+#ifdef UNSET_CONDITION
+#define E() ;
+#else
+#define E()
+#endif
+#define N(a) a++;
+
+int g() {
+  int v = 0;
+  N(v) E();
+  return v;
+}
Index: clang-tools-extra/clang-tidy/linuxkernel/MacroTrailingSemiCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/linuxkernel/MacroTrailingSemiCheck.h
@@ -0,0 +1,40 @@
+//===--- MacroTrailingSemiCheck.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_MACROTRAILINGSEMICHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LINUXKERNEL_MACROTRAILINGSEMICHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "clang/Lex/MacroInfo.h"
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace linuxkernel {
+
+class MacroTrailingSemiCheck : public ClangTidyCheck {
+public:
+  MacroTrailingSemiCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+                           Preprocessor *ModuleExpanderPP) override final;
+
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void checkMacro(SourceManager &sourceMgr, const Token &MacroNameTok,
+                  const MacroInfo *MI);
+
+private:
+  std::vector<const MacroInfo *> SuspectMacros;
+};
+
+} // namespace linuxkernel
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LINUXKERNEL_MACROTRAILINGSEMI_CHECK_H
Index: clang-tools-extra/clang-tidy/linuxkernel/MacroTrailingSemiCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/linuxkernel/MacroTrailingSemiCheck.cpp
@@ -0,0 +1,139 @@
+//===--- MacroTrailingSemiCheck.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 "MacroTrailingSemiCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+
+#include <stdio.h>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace linuxkernel {
+
+class MacroTrailingSemiCheckPPCallbacks : public PPCallbacks {
+public:
+  MacroTrailingSemiCheckPPCallbacks(Preprocessor *PP,
+                                    MacroTrailingSemiCheck *Check)
+      : PP(PP), Check(Check) {}
+
+  void MacroDefined(const Token &MacroNameTok,
+                    const MacroDirective *MD) override {
+    auto *MI = MD->getMacroInfo();
+
+    if (MI->isBuiltinMacro() || MI->isObjectLike())
+      return;
+    Check->checkMacro(PP->getSourceManager(), MacroNameTok, MI);
+  }
+
+private:
+  Preprocessor *PP;
+  MacroTrailingSemiCheck *Check;
+};
+
+void MacroTrailingSemiCheck::registerMatchers(MatchFinder *Finder) {
+  // From the reproducer
+  // #define M(a) a++;
+  // int f() {
+  //   int v = 0;
+  //   M(v);
+  //   return v;
+  // }
+  // The AST
+  // `-FunctionDecl
+  //   `-CompoundStmt <--- "comp", 'C'
+  //     ...
+  //     |-UnaryOperator
+  //     | `-DeclRefExpr <-------- 'E'
+  //     |-NullStmt <------ "null" 'N'
+  //     ...
+  Finder->addMatcher(compoundStmt(has(nullStmt().bind("null"))).bind("comp"),
+                     this);
+}
+
+void MacroTrailingSemiCheck::registerPPCallbacks(
+    const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
+  ModuleExpanderPP->addPPCallbacks(
+      std::make_unique<MacroTrailingSemiCheckPPCallbacks>(ModuleExpanderPP,
+                                                          this));
+}
+
+void MacroTrailingSemiCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *C = Result.Nodes.getNodeAs<CompoundStmt>("comp");
+  const auto *N = Result.Nodes.getNodeAs<NullStmt>("null");
+
+  auto Current = C->body_begin();
+  auto Next = Current;
+  Next++;
+  while (Next != C->body_end()) {
+
+    if (*Next == N) {
+      // This code has the same AST as the reproducer
+      //
+      // #define NOT_EMPTY(a) a++;
+      // #define EMPTY()
+      // void foo (int a) {
+      //   NOT_EMPTY(a);
+      //   EMPTY();
+      // }
+      //
+      // But we do not want to remove the ; because the
+      // macro may only be conditionally empty.
+      // ex/ the release version of a debug macro
+      //
+      // So check that the NullStmt does not have a
+      // leading empty macro.
+      if (!N->hasLeadingEmptyMacro() && N->getEndLoc().isValid()) {
+        if (const auto *E = dyn_cast<Expr>(*Current)) {
+          SourceLocation Loc = E->getEndLoc();
+          if (Loc.isMacroID()) {
+            auto &SM = Result.Context->getSourceManager();
+            FullSourceLoc SpellingLoc = FullSourceLoc(Loc, SM).getSpellingLoc();
+
+            for (std::vector<const MacroInfo *>::iterator it =
+                     SuspectMacros.begin();
+                 it != SuspectMacros.end(); ++it) {
+              const MacroInfo *MI = *it;
+              auto TI = MI->tokens_begin();
+              for (; TI != MI->tokens_end(); TI++) {
+                SourceLocation L = TI->getLocation();
+                if (SpellingLoc == L)
+                  break;
+              }
+              if (TI != MI->tokens_end()) {
+                auto Tok = MI->getReplacementToken(MI->getNumTokens() - 1);
+                SourceLocation FixLoc = Tok.getLocation();
+                SourceLocation EndLoc = Tok.getEndLoc();
+                auto H = FixItHint::CreateRemoval(SourceRange(FixLoc, EndLoc));
+                diag(FixLoc, "unneeded semicolon") << H;
+                break;
+              }
+            }
+          }
+        }
+      }
+    }
+    Current = Next;
+    Next++;
+  }
+}
+void MacroTrailingSemiCheck::checkMacro(SourceManager &SM,
+                                        const Token &MacroNameTok,
+                                        const MacroInfo *MI) {
+  unsigned num = MI->getNumTokens();
+  if (num && MI->getReplacementToken(num - 1).is(tok::semi))
+    SuspectMacros.push_back(MI);
+}
+
+} // namespace linuxkernel
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/linuxkernel/LinuxKernelTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/linuxkernel/LinuxKernelTidyModule.cpp
+++ clang-tools-extra/clang-tidy/linuxkernel/LinuxKernelTidyModule.cpp
@@ -9,6 +9,7 @@
 #include "../ClangTidy.h"
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
+#include "MacroTrailingSemiCheck.h"
 #include "MustCheckErrsCheck.h"
 #include "SwitchSemiCheck.h"
 
@@ -20,6 +21,8 @@
 class LinuxKernelModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<MacroTrailingSemiCheck>(
+        "linuxkernel-macro-trailing-semi");
     CheckFactories.registerCheck<MustCheckErrsCheck>(
         "linuxkernel-must-check-errs");
     CheckFactories.registerCheck<SwitchSemiCheck>("linuxkernel-switch-semi");
Index: clang-tools-extra/clang-tidy/linuxkernel/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/linuxkernel/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/linuxkernel/CMakeLists.txt
@@ -5,6 +5,7 @@
 
 add_clang_library(clangTidyLinuxKernelModule
   LinuxKernelTidyModule.cpp
+  MacroTrailingSemiCheck.cpp
   MustCheckErrsCheck.cpp
   SwitchSemiCheck.cpp
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to