CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
144 lines
5.9 KiB
C++
144 lines
5.9 KiB
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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "UseNewMLIROpBuilderCheck.h"
|
|
#include "../utils/LexerUtils.h"
|
|
#include "clang/ASTMatchers/ASTMatchers.h"
|
|
#include "clang/Basic/LLVM.h"
|
|
#include "clang/Tooling/Transformer/RangeSelector.h"
|
|
#include "clang/Tooling/Transformer/RewriteRule.h"
|
|
#include "clang/Tooling/Transformer/Stencil.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/FormatVariadic.h"
|
|
|
|
namespace clang::tidy::llvm_check {
|
|
|
|
using namespace ::clang::ast_matchers;
|
|
using namespace ::clang::transformer;
|
|
|
|
static EditGenerator rewrite(RangeSelector Call, RangeSelector Builder) {
|
|
// This is using an EditGenerator rather than ASTEdit as we want to warn even
|
|
// if in macro.
|
|
return [Call = std::move(Call),
|
|
Builder = std::move(Builder)](const MatchFinder::MatchResult &Result)
|
|
-> Expected<SmallVector<transformer::Edit, 1>> {
|
|
Expected<CharSourceRange> CallRange = Call(Result);
|
|
if (!CallRange)
|
|
return CallRange.takeError();
|
|
SourceManager &SM = *Result.SourceManager;
|
|
const LangOptions &LangOpts = Result.Context->getLangOpts();
|
|
SourceLocation Begin = CallRange->getBegin();
|
|
|
|
// This will result in just a warning and no edit.
|
|
const bool InMacro = CallRange->getBegin().isMacroID();
|
|
if (InMacro) {
|
|
while (SM.isMacroArgExpansion(Begin))
|
|
Begin = SM.getImmediateExpansionRange(Begin).getBegin();
|
|
Edit WarnOnly;
|
|
WarnOnly.Kind = EditKind::Range;
|
|
WarnOnly.Range = CharSourceRange::getCharRange(Begin, Begin);
|
|
return SmallVector<Edit, 1>({WarnOnly});
|
|
}
|
|
|
|
// This will try to extract the template argument as written so that the
|
|
// rewritten code looks closest to original.
|
|
auto NextToken = [&](std::optional<Token> CurrentToken) {
|
|
if (!CurrentToken)
|
|
return CurrentToken;
|
|
if (CurrentToken->is(tok::eof))
|
|
return std::optional<Token>();
|
|
return utils::lexer::findNextTokenSkippingComments(
|
|
CurrentToken->getLocation(), SM, LangOpts);
|
|
};
|
|
std::optional<Token> LessToken =
|
|
utils::lexer::findNextTokenSkippingComments(Begin, SM, LangOpts);
|
|
while (LessToken && LessToken->getKind() != tok::less)
|
|
LessToken = NextToken(LessToken);
|
|
if (!LessToken) {
|
|
return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
|
|
"missing '<' token");
|
|
}
|
|
|
|
std::optional<Token> EndToken = NextToken(LessToken);
|
|
std::optional<Token> GreaterToken = NextToken(EndToken);
|
|
for (; GreaterToken && GreaterToken->getKind() != tok::greater;
|
|
GreaterToken = NextToken(GreaterToken)) {
|
|
EndToken = GreaterToken;
|
|
}
|
|
if (!EndToken) {
|
|
return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
|
|
"missing '>' token");
|
|
}
|
|
|
|
std::optional<Token> ArgStart = NextToken(GreaterToken);
|
|
if (!ArgStart || ArgStart->getKind() != tok::l_paren) {
|
|
return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
|
|
"missing '(' token");
|
|
}
|
|
std::optional<Token> Arg = NextToken(ArgStart);
|
|
if (!Arg) {
|
|
return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
|
|
"unexpected end of file");
|
|
}
|
|
const bool HasArgs = Arg->getKind() != tok::r_paren;
|
|
|
|
Expected<CharSourceRange> BuilderRange = Builder(Result);
|
|
if (!BuilderRange)
|
|
return BuilderRange.takeError();
|
|
|
|
// Helper for concatting below.
|
|
auto GetText = [&](const CharSourceRange &Range) {
|
|
return Lexer::getSourceText(Range, SM, LangOpts);
|
|
};
|
|
|
|
Edit Replace;
|
|
Replace.Kind = EditKind::Range;
|
|
Replace.Range.setBegin(CallRange->getBegin());
|
|
Replace.Range.setEnd(ArgStart->getEndLoc());
|
|
const Expr *BuilderExpr = Result.Nodes.getNodeAs<Expr>("builder");
|
|
std::string BuilderText = GetText(*BuilderRange).str();
|
|
if (BuilderExpr->getType()->isPointerType()) {
|
|
BuilderText = BuilderExpr->isImplicitCXXThis()
|
|
? "*this"
|
|
: llvm::formatv("*{}", BuilderText).str();
|
|
}
|
|
const StringRef OpType = GetText(CharSourceRange::getTokenRange(
|
|
LessToken->getEndLoc(), EndToken->getLastLoc()));
|
|
Replace.Replacement = llvm::formatv("{}::create({}{}", OpType, BuilderText,
|
|
HasArgs ? ", " : "");
|
|
|
|
return SmallVector<Edit, 1>({Replace});
|
|
};
|
|
}
|
|
|
|
static RewriteRuleWith<std::string> useNewMlirOpBuilderCheckRule() {
|
|
const Stencil Message = cat("use 'OpType::create(builder, ...)' instead of "
|
|
"'builder.create<OpType>(...)'");
|
|
// Match a create call on an OpBuilder.
|
|
auto BuilderType = cxxRecordDecl(isSameOrDerivedFrom("::mlir::OpBuilder"));
|
|
const ast_matchers::internal::Matcher<Stmt> Base =
|
|
cxxMemberCallExpr(
|
|
on(expr(anyOf(hasType(BuilderType), hasType(pointsTo(BuilderType))))
|
|
.bind("builder")),
|
|
callee(cxxMethodDecl(hasTemplateArgument(0, templateArgument()),
|
|
hasName("create"))))
|
|
.bind("call");
|
|
return applyFirst(
|
|
// Attempt rewrite given an lvalue builder, else just warn.
|
|
{makeRule(cxxMemberCallExpr(unless(on(cxxTemporaryObjectExpr())), Base),
|
|
rewrite(node("call"), node("builder")), Message),
|
|
makeRule(Base, noopEdit(node("call")), Message)});
|
|
}
|
|
|
|
UseNewMlirOpBuilderCheck::UseNewMlirOpBuilderCheck(StringRef Name,
|
|
ClangTidyContext *Context)
|
|
: TransformerClangTidyCheck(useNewMlirOpBuilderCheckRule(), Name, Context) {
|
|
}
|
|
|
|
} // namespace clang::tidy::llvm_check
|