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

The linux kernel's checkpatch.pl script does several checks
and fixits on the log functions.  This clang-tidy checker repeats
these checks so the fixits can be applied tree wide.

The first fixer looks for unneeded 'h' in format strings.

>From this reproducer

  printk("%hx\n", a);

checkpatch.pl produces this warning

WARNING: Integer promotion: Using 'h' in '%hx' is unnecessary
+  printk("%hx\n", a);

and this fix

- printk("%hx\n", a);

+  printk("%x\n", a);

LKML discussion
https://lore.kernel.org/lkml/5e3265c241602bb54286fbaae9222070daa4768e.ca...@perches.com/


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D93182

Files:
  clang-tools-extra/clang-tidy/linuxkernel/CMakeLists.txt
  clang-tools-extra/clang-tidy/linuxkernel/LinuxKernelTidyModule.cpp
  clang-tools-extra/clang-tidy/linuxkernel/LogFunctionsCheck.cpp
  clang-tools-extra/clang-tidy/linuxkernel/LogFunctionsCheck.h
  clang-tools-extra/test/clang-tidy/checkers/linuxkernel-logfunctions-h.c

Index: clang-tools-extra/test/clang-tidy/checkers/linuxkernel-logfunctions-h.c
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/linuxkernel-logfunctions-h.c
@@ -0,0 +1,20 @@
+// RUN: %check_clang_tidy %s linuxkernel-log-functions %t
+
+extern printk(const char *, ...) __attribute__((format(printf, 1, 2)));
+
+// scripts/checkpatch.pl produces
+// WARNING: Integer promotion: Using 'h' in '%hx' is unnecessary
+// ... linuxkernel-logfunctions-h.c:10:
+// +	printk("%hx\n", a);
+//
+// with --fix-inplace, the change is
+//  void f(unsigned char a)
+// {
+// -  printk("%hx\n", a);
+// +  printk("%x\n", a);
+// }
+void f(unsigned char a) {
+  printk("%hx\n", a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: Integer promotion: Using 'h' in '%hx' is unnecessay
+  // CHECK-FIXES: printk("%x\n", a);{{$}}
+}
Index: clang-tools-extra/clang-tidy/linuxkernel/LogFunctionsCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/linuxkernel/LogFunctionsCheck.h
@@ -0,0 +1,39 @@
+//===--- LogFunctionsCheck.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_LOGFUNCTIONSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LINUXKERNEL_LOGFUNCTIONSCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "clang/Lex/MacroInfo.h"
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace linuxkernel {
+
+class LogFunctionsCheck : public ClangTidyCheck {
+
+  enum LogFunctionsFixerKind { LFFK_None, LFFK_H };
+
+public:
+  LogFunctionsCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  enum LogFunctionsFixerKind FixerKind;
+  const std::string LogFunctionsFixerKindName;
+};
+
+} // namespace linuxkernel
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LINUXKERNEL_LOGFUNCTIONSCHECK_H
Index: clang-tools-extra/clang-tidy/linuxkernel/LogFunctionsCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/linuxkernel/LogFunctionsCheck.cpp
@@ -0,0 +1,95 @@
+//===--- LogFunctionsCheck.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 "LogFunctionsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace linuxkernel {
+
+LogFunctionsCheck::LogFunctionsCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context), FixerKind(LFFK_H) {}
+
+void LogFunctionsCheck::registerMatchers(MatchFinder *Finder) {
+  if (FixerKind == LFFK_H) {
+    // From the reproducer
+    // extern int printk(const char *, ...) __attribute__((format(printf, 1, 2)));
+    // void f(unsigned short a) { printk("%hx\n", a); }
+    //
+    // The AST
+    // |-FunctionDecl printk 'int (const char *, ...)' extern
+    // | |-ParmVarDecl 'const char *'
+    // | `-FormatAttr printf 1 2
+    // ...
+    //
+    //  `-CompoundStmt
+    //    `-CallExpr
+    //     |-ImplicitCastExpr
+    //     | `-DeclRefExpr Function 'printk' 'int (const char *, ...)'
+    //     |-ImplicitCastExpr
+    //     | `-ImplicitCastExpr
+    //     |   `-StringLiteral
+    Finder->addMatcher(
+        compoundStmt(has(
+            callExpr(has(ignoringParenImpCasts(stringLiteral().bind("lit"))),
+                     callee(functionDecl(hasAttr(attr::Format)).bind("func")))
+                .bind("call"))),
+        this);
+  }
+}
+
+void LogFunctionsCheck::check(const MatchFinder::MatchResult &Result) {
+  if (FixerKind == LFFK_H) {
+    const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+    const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("func");
+    const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("lit");
+    FormatAttr *Format = Function->getAttr<FormatAttr>();
+    unsigned ArgNum = Format->getFormatIdx() - 1;
+    if (Format->getType()->getName() == "printf" &&
+        ArgNum < Call->getNumArgs() &&
+        Literal == Call->getArg(ArgNum)->IgnoreCasts() && Literal->isAscii() &&
+        Literal->getCharByteWidth() == 1) {
+      StringRef LiteralString = Literal->getString();
+      StringRef LookForH[] = {"%hi",  "%hd",  "%hu",  "%hx",
+                              "%hhi", "%hhd", "%hhu", "%hhx"};
+      for (auto FoundH : LookForH) {
+        if (LiteralString.contains(FoundH)) {
+          size_t HBegin = LiteralString.find(FoundH) + 1;
+          size_t HEnd = HBegin + 1;
+          StringRef ErrorString = "h";
+          if (FoundH.contains("hh")) {
+            HEnd++;
+            ErrorString = "hh";
+          }
+          SourceLocation BeginLoc = Literal->getLocationOfByte(
+              HBegin, Result.Context->getSourceManager(),
+              Result.Context->getLangOpts(), Result.Context->getTargetInfo());
+          SourceLocation EndLoc = Literal->getLocationOfByte(
+              HEnd, Result.Context->getSourceManager(),
+              Result.Context->getLangOpts(), Result.Context->getTargetInfo());
+          diag(EndLoc, "Integer promotion: Using '%0' in '%1' is unnecessay")
+              << ErrorString << FoundH
+              << FixItHint::CreateRemoval(
+                     CharSourceRange::getCharRange(BeginLoc, EndLoc));
+        }
+      }
+    }
+  }
+}
+
+void LogFunctionsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "Fixer", "");
+}
+
+} // 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
@@ -10,6 +10,7 @@
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
 #include "ExtraSemiCheck.h"
+#include "LogFunctionsCheck.h"
 #include "MustCheckErrsCheck.h"
 
 namespace clang {
@@ -21,6 +22,8 @@
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
     CheckFactories.registerCheck<ExtraSemiCheck>("linuxkernel-extra-semi");
+    CheckFactories.registerCheck<LogFunctionsCheck>(
+        "linuxkernel-log-functions");
     CheckFactories.registerCheck<MustCheckErrsCheck>(
         "linuxkernel-must-check-errs");
   }
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
@@ -6,6 +6,7 @@
 add_clang_library(clangTidyLinuxKernelModule
   ExtraSemiCheck.cpp
   LinuxKernelTidyModule.cpp
+  LogFunctionsCheck.cpp
   MustCheckErrsCheck.cpp
 
   LINK_LIBS
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to