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"
23using namespace ::clang::ast_matchers;
24using namespace ::clang::transformer;
26EditGenerator rewrite(RangeSelector Call, RangeSelector Builder,
27 RangeSelector CallArgs) {
30 return [
Call = std::move(Call), Builder = std::move(Builder),
32 std::move(CallArgs)](
const MatchFinder::MatchResult &Result)
33 -> Expected<SmallVector<transformer::Edit, 1>> {
34 Expected<CharSourceRange> CallRange =
Call(Result);
36 return CallRange.takeError();
37 SourceManager &SM = *Result.SourceManager;
38 const LangOptions &LangOpts = Result.Context->getLangOpts();
39 SourceLocation Begin = CallRange->getBegin();
42 bool InMacro = CallRange->getBegin().isMacroID();
44 while (SM.isMacroArgExpansion(Begin))
45 Begin = SM.getImmediateExpansionRange(Begin).getBegin();
47 WarnOnly.Kind = EditKind::Range;
48 WarnOnly.Range = CharSourceRange::getCharRange(Begin, Begin);
49 return SmallVector<Edit, 1>({WarnOnly});
54 auto NextToken = [&](std::optional<Token> CurrentToken) {
57 if (CurrentToken->getEndLoc() >= CallRange->getEnd())
58 return std::optional<Token>();
59 return clang::Lexer::findNextToken(CurrentToken->getLocation(), SM,
62 std::optional<Token> LessToken =
63 clang::Lexer::findNextToken(Begin, SM, LangOpts);
64 while (LessToken && LessToken->getKind() != clang::tok::less) {
65 LessToken = NextToken(LessToken);
68 return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
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;
78 return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
82 Expected<CharSourceRange> BuilderRange = Builder(Result);
84 return BuilderRange.takeError();
85 Expected<CharSourceRange> CallArgsRange = CallArgs(Result);
87 return CallArgsRange.takeError();
90 auto GetText = [&](
const CharSourceRange &Range) {
91 return clang::Lexer::getSourceText(Range, SM, LangOpts);
95 Replace.Kind = EditKind::Range;
96 Replace.Range = *CallRange;
97 std::string CallArgsStr;
99 if (
auto CallArgsText = GetText(*CallArgsRange).ltrim();
100 !CallArgsText.rtrim().empty()) {
101 CallArgsStr = llvm::formatv(
", {}", CallArgsText);
103 Replace.Replacement =
104 llvm::formatv(
"{}::create({}{})",
105 GetText(CharSourceRange::getTokenRange(
106 LessToken->getEndLoc(), EndToken->getLastLoc())),
107 GetText(*BuilderRange), CallArgsStr);
109 return SmallVector<Edit, 1>({Replace});
113RewriteRuleWith<std::string> useNewMlirOpBuilderCheckRule() {
114 Stencil message = cat(
"use 'OpType::create(builder, ...)' instead of "
115 "'builder.create<OpType>(...)'");
117 ast_matchers::internal::Matcher<Stmt> base =
120 cxxRecordDecl(isSameOrDerivedFrom(
"::mlir::OpBuilder"))))
122 callee(cxxMethodDecl(hasTemplateArgument(0, templateArgument()))),
123 callee(cxxMethodDecl(hasName(
"create"))))
127 {makeRule(cxxMemberCallExpr(unless(on(cxxTemporaryObjectExpr())), base),
128 rewrite(
node(
"call"),
node(
"builder"), callArgs(
"call")),
130 makeRule(base, noopEdit(
node(
"call")), message)});
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
UseNewMlirOpBuilderCheck(StringRef Name, ClangTidyContext *Context)
node(n, label, next_label)