Skip to content

Commit 3feb6f9

Browse files
jpienaarvbvictorEugeneZelenko
authored
[clang-tidy] Add MLIR check for old op builder usage. (#149148)
Upstream is moving towards new create method invocation, add check to flag old usage that will be deprecated. --------- Co-authored-by: Baranov Victor <[email protected]> Co-authored-by: EugeneZelenko <[email protected]>
1 parent 889faab commit 3feb6f9

File tree

8 files changed

+267
-0
lines changed

8 files changed

+267
-0
lines changed

clang-tools-extra/clang-tidy/llvm/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ add_clang_library(clangTidyLLVMModule STATIC
1111
PreferRegisterOverUnsignedCheck.cpp
1212
PreferStaticOverAnonymousNamespaceCheck.cpp
1313
TwineLocalCheck.cpp
14+
UseNewMLIROpBuilderCheck.cpp
1415

1516
LINK_LIBS
1617
clangTidy
1718
clangTidyReadabilityModule
1819
clangTidyUtils
20+
clangTransformer
1921

2022
DEPENDS
2123
omp_gen

clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "PreferRegisterOverUnsignedCheck.h"
1919
#include "PreferStaticOverAnonymousNamespaceCheck.h"
2020
#include "TwineLocalCheck.h"
21+
#include "UseNewMLIROpBuilderCheck.h"
2122

2223
namespace clang::tidy {
2324
namespace llvm_check {
@@ -40,6 +41,8 @@ class LLVMModule : public ClangTidyModule {
4041
CheckFactories.registerCheck<readability::QualifiedAutoCheck>(
4142
"llvm-qualified-auto");
4243
CheckFactories.registerCheck<TwineLocalCheck>("llvm-twine-local");
44+
CheckFactories.registerCheck<UseNewMlirOpBuilderCheck>(
45+
"llvm-use-new-mlir-op-builder");
4346
}
4447

4548
ClangTidyOptions getModuleOptions() override {
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
//===--- UseNewMLIROpBuilderCheck.cpp - clang-tidy ------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "UseNewMLIROpBuilderCheck.h"
10+
#include "clang/ASTMatchers/ASTMatchers.h"
11+
#include "clang/Basic/LLVM.h"
12+
#include "clang/Lex/Lexer.h"
13+
#include "clang/Tooling/Transformer/RangeSelector.h"
14+
#include "clang/Tooling/Transformer/RewriteRule.h"
15+
#include "clang/Tooling/Transformer/SourceCode.h"
16+
#include "clang/Tooling/Transformer/Stencil.h"
17+
#include "llvm/Support/Error.h"
18+
#include "llvm/Support/FormatVariadic.h"
19+
20+
namespace clang::tidy::llvm_check {
21+
namespace {
22+
23+
using namespace ::clang::ast_matchers;
24+
using namespace ::clang::transformer;
25+
26+
EditGenerator rewrite(RangeSelector Call, RangeSelector Builder,
27+
RangeSelector CallArgs) {
28+
// This is using an EditGenerator rather than ASTEdit as we want to warn even
29+
// if in macro.
30+
return [Call = std::move(Call), Builder = std::move(Builder),
31+
CallArgs =
32+
std::move(CallArgs)](const MatchFinder::MatchResult &Result)
33+
-> Expected<SmallVector<transformer::Edit, 1>> {
34+
Expected<CharSourceRange> CallRange = Call(Result);
35+
if (!CallRange)
36+
return CallRange.takeError();
37+
SourceManager &SM = *Result.SourceManager;
38+
const LangOptions &LangOpts = Result.Context->getLangOpts();
39+
SourceLocation Begin = CallRange->getBegin();
40+
41+
// This will result in just a warning and no edit.
42+
bool InMacro = CallRange->getBegin().isMacroID();
43+
if (InMacro) {
44+
while (SM.isMacroArgExpansion(Begin))
45+
Begin = SM.getImmediateExpansionRange(Begin).getBegin();
46+
Edit WarnOnly;
47+
WarnOnly.Kind = EditKind::Range;
48+
WarnOnly.Range = CharSourceRange::getCharRange(Begin, Begin);
49+
return SmallVector<Edit, 1>({WarnOnly});
50+
}
51+
52+
// This will try to extract the template argument as written so that the
53+
// rewritten code looks closest to original.
54+
auto NextToken = [&](std::optional<Token> CurrentToken) {
55+
if (!CurrentToken)
56+
return CurrentToken;
57+
if (CurrentToken->getEndLoc() >= CallRange->getEnd())
58+
return std::optional<Token>();
59+
return clang::Lexer::findNextToken(CurrentToken->getLocation(), SM,
60+
LangOpts);
61+
};
62+
std::optional<Token> LessToken =
63+
clang::Lexer::findNextToken(Begin, SM, LangOpts);
64+
while (LessToken && LessToken->getKind() != clang::tok::less) {
65+
LessToken = NextToken(LessToken);
66+
}
67+
if (!LessToken) {
68+
return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
69+
"missing '<' token");
70+
}
71+
std::optional<Token> EndToken = NextToken(LessToken);
72+
for (std::optional<Token> GreaterToken = NextToken(EndToken);
73+
GreaterToken && GreaterToken->getKind() != clang::tok::greater;
74+
GreaterToken = NextToken(GreaterToken)) {
75+
EndToken = GreaterToken;
76+
}
77+
if (!EndToken) {
78+
return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
79+
"missing '>' token");
80+
}
81+
82+
Expected<CharSourceRange> BuilderRange = Builder(Result);
83+
if (!BuilderRange)
84+
return BuilderRange.takeError();
85+
Expected<CharSourceRange> CallArgsRange = CallArgs(Result);
86+
if (!CallArgsRange)
87+
return CallArgsRange.takeError();
88+
89+
// Helper for concatting below.
90+
auto GetText = [&](const CharSourceRange &Range) {
91+
return clang::Lexer::getSourceText(Range, SM, LangOpts);
92+
};
93+
94+
Edit Replace;
95+
Replace.Kind = EditKind::Range;
96+
Replace.Range = *CallRange;
97+
std::string CallArgsStr;
98+
// Only emit args if there are any.
99+
if (auto CallArgsText = GetText(*CallArgsRange).ltrim();
100+
!CallArgsText.rtrim().empty()) {
101+
CallArgsStr = llvm::formatv(", {}", CallArgsText);
102+
}
103+
Replace.Replacement =
104+
llvm::formatv("{}::create({}{})",
105+
GetText(CharSourceRange::getTokenRange(
106+
LessToken->getEndLoc(), EndToken->getLastLoc())),
107+
GetText(*BuilderRange), CallArgsStr);
108+
109+
return SmallVector<Edit, 1>({Replace});
110+
};
111+
}
112+
113+
RewriteRuleWith<std::string> useNewMlirOpBuilderCheckRule() {
114+
return makeRule(
115+
cxxMemberCallExpr(
116+
on(expr(hasType(
117+
cxxRecordDecl(isSameOrDerivedFrom("::mlir::OpBuilder"))))
118+
.bind("builder")),
119+
callee(cxxMethodDecl(hasTemplateArgument(0, templateArgument()))),
120+
callee(cxxMethodDecl(hasName("create"))))
121+
.bind("call"),
122+
rewrite(node("call"), node("builder"), callArgs("call")),
123+
cat("use 'OpType::create(builder, ...)' instead of "
124+
"'builder.create<OpType>(...)'"));
125+
}
126+
} // namespace
127+
128+
UseNewMlirOpBuilderCheck::UseNewMlirOpBuilderCheck(StringRef Name,
129+
ClangTidyContext *Context)
130+
: TransformerClangTidyCheck(useNewMlirOpBuilderCheckRule(), Name, Context) {
131+
}
132+
133+
} // namespace clang::tidy::llvm_check
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//===--- UseNewMLIROpBuilderCheck.h - clang-tidy ----------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_USENEWMLIROPBUILDERCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_USENEWMLIROPBUILDERCHECK_H
11+
12+
#include "../utils/TransformerClangTidyCheck.h"
13+
14+
namespace clang::tidy::llvm_check {
15+
16+
/// Checks for uses of MLIR's old/to be deprecated `OpBuilder::create<T>` form
17+
/// and suggests using `T::create` instead.
18+
class UseNewMlirOpBuilderCheck : public utils::TransformerClangTidyCheck {
19+
public:
20+
UseNewMlirOpBuilderCheck(StringRef Name, ClangTidyContext *Context);
21+
22+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
23+
return getLangOpts().CPlusPlus;
24+
}
25+
};
26+
27+
} // namespace clang::tidy::llvm_check
28+
29+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_USENEWMLIROPBUILDERCHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ Improvements to clang-tidy
9696
New checks
9797
^^^^^^^^^^
9898

99+
- New :doc:`llvm-mlir-op-builder
100+
<clang-tidy/checks/llvm/use-new-mlir-op-builder>` check.
101+
102+
Checks for uses of MLIR's old/to be deprecated ``OpBuilder::create<T>`` form
103+
and suggests using ``T::create`` instead.
104+
99105
New check aliases
100106
^^^^^^^^^^^^^^^^^
101107

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ Clang-Tidy Checks
247247
:doc:`linuxkernel-must-check-errs <linuxkernel/must-check-errs>`,
248248
:doc:`llvm-header-guard <llvm/header-guard>`,
249249
:doc:`llvm-include-order <llvm/include-order>`, "Yes"
250+
:doc:`llvm-use-new-mlir-op-builder <llvm/use-new-mlir-op-builder>`, "Yes"
250251
:doc:`llvm-namespace-comment <llvm/namespace-comment>`,
251252
:doc:`llvm-prefer-isa-or-dyn-cast-in-conditionals <llvm/prefer-isa-or-dyn-cast-in-conditionals>`, "Yes"
252253
:doc:`llvm-prefer-register-over-unsigned <llvm/prefer-register-over-unsigned>`, "Yes"
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.. title:: clang-tidy - llvm-mlir-op-builder
2+
3+
llvm-mlir-op-builder
4+
====================
5+
6+
Checks for uses of MLIR's old/to be deprecated ``OpBuilder::create<T>`` form
7+
and suggests using ``T::create`` instead.
8+
9+
Example
10+
-------
11+
12+
.. code-block:: c++
13+
14+
builder.create<FooOp>(builder.getUnknownLoc(), "baz");
15+
16+
17+
Transforms to:
18+
19+
.. code-block:: c++
20+
21+
FooOp::create(builder, builder.getUnknownLoc(), "baz");
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// RUN: %check_clang_tidy --match-partial-fixes %s llvm-use-new-mlir-op-builder %t
2+
3+
namespace mlir {
4+
class Location {};
5+
class OpBuilder {
6+
public:
7+
template <typename OpTy, typename... Args>
8+
OpTy create(Location location, Args &&...args) {
9+
return OpTy(args...);
10+
}
11+
Location getUnknownLoc() { return Location(); }
12+
};
13+
class ImplicitLocOpBuilder : public OpBuilder {
14+
public:
15+
template <typename OpTy, typename... Args>
16+
OpTy create(Args &&...args) {
17+
return OpTy(args...);
18+
}
19+
};
20+
struct ModuleOp {
21+
ModuleOp() {}
22+
static ModuleOp create(OpBuilder &builder, Location location) {
23+
return ModuleOp();
24+
}
25+
};
26+
struct NamedOp {
27+
NamedOp(const char* name) {}
28+
static NamedOp create(OpBuilder &builder, Location location, const char* name) {
29+
return NamedOp(name);
30+
}
31+
};
32+
} // namespace mlir
33+
34+
#define ASSIGN(A, B, C, D) C A = B.create<C>(B.getUnknownLoc(), D)
35+
36+
template <typename T>
37+
void g(mlir::OpBuilder &b) {
38+
// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use 'OpType::create(builder, ...)' instead of 'builder.create<OpType>(...)' [llvm-use-new-mlir-op-builder]
39+
// CHECK-FIXES: T::create(b, b.getUnknownLoc(), "gaz")
40+
b.create<T>(b.getUnknownLoc(), "gaz");
41+
}
42+
43+
void f() {
44+
mlir::OpBuilder builder;
45+
// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use 'OpType::create(builder, ...)' instead of 'builder.create<OpType>(...)' [llvm-use-new-mlir-op-builder]
46+
// CHECK-FIXES: mlir:: ModuleOp::create(builder, builder.getUnknownLoc())
47+
builder.create<mlir:: ModuleOp>(builder.getUnknownLoc());
48+
49+
using mlir::NamedOp;
50+
// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use 'OpType::create(builder, ...)' instead of 'builder.create<OpType>(...)' [llvm-use-new-mlir-op-builder]
51+
// CHECK-FIXES: NamedOp::create(builder, builder.getUnknownLoc(), "baz")
52+
builder.create<NamedOp>(builder.getUnknownLoc(), "baz");
53+
54+
// CHECK-MESSAGES: :[[@LINE+4]]:3: warning: use 'OpType::create(builder, ...)' instead of 'builder.create<OpType>(...)' [llvm-use-new-mlir-op-builder]
55+
// CHECK-FIXES: NamedOp::create(builder,
56+
// CHECK-FIXES: builder.getUnknownLoc(),
57+
// CHECK-FIXES: "caz")
58+
builder.
59+
create<NamedOp>(
60+
builder.getUnknownLoc(),
61+
"caz");
62+
63+
// CHECK-MESSAGES: :[[@LINE+1]]:3: warning: use 'OpType::create(builder, ...)' instead of 'builder.create<OpType>(...)' [llvm-use-new-mlir-op-builder]
64+
ASSIGN(op, builder, NamedOp, "daz");
65+
66+
g<NamedOp>(builder);
67+
68+
mlir::ImplicitLocOpBuilder ib;
69+
// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use 'OpType::create(builder, ...)' instead of 'builder.create<OpType>(...)' [llvm-use-new-mlir-op-builder]
70+
// CHECK-FIXES: mlir::ModuleOp::create(ib)
71+
ib.create<mlir::ModuleOp>( );
72+
}

0 commit comments

Comments
 (0)