mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-03-03 08:02:41 +00:00
[clang-tidy] performance-unnecessary-copy-init: Add a hook... (#73921)
... so that derived checks can can observe for which variables a warning has been emitted. Does nothing by default, which makes this an NFC.
This commit is contained in:
parent
fc42a2f5c4
commit
04ce9a34ea
@ -15,6 +15,7 @@
|
|||||||
#include "clang/AST/Decl.h"
|
#include "clang/AST/Decl.h"
|
||||||
#include "clang/Basic/Diagnostic.h"
|
#include "clang/Basic/Diagnostic.h"
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace clang::tidy::performance {
|
namespace clang::tidy::performance {
|
||||||
namespace {
|
namespace {
|
||||||
@ -263,19 +264,25 @@ void UnnecessaryCopyInitialization::registerMatchers(MatchFinder *Finder) {
|
|||||||
|
|
||||||
void UnnecessaryCopyInitialization::check(
|
void UnnecessaryCopyInitialization::check(
|
||||||
const MatchFinder::MatchResult &Result) {
|
const MatchFinder::MatchResult &Result) {
|
||||||
const auto *NewVar = Result.Nodes.getNodeAs<VarDecl>("newVarDecl");
|
const auto &NewVar = *Result.Nodes.getNodeAs<VarDecl>("newVarDecl");
|
||||||
|
const auto &BlockStmt = *Result.Nodes.getNodeAs<Stmt>("blockStmt");
|
||||||
|
const auto &VarDeclStmt = *Result.Nodes.getNodeAs<DeclStmt>("declStmt");
|
||||||
|
// Do not propose fixes if the DeclStmt has multiple VarDecls or in
|
||||||
|
// macros since we cannot place them correctly.
|
||||||
|
const bool IssueFix =
|
||||||
|
VarDeclStmt.isSingleDecl() && !NewVar.getLocation().isMacroID();
|
||||||
|
const bool IsVarUnused = isVariableUnused(NewVar, BlockStmt, *Result.Context);
|
||||||
|
const bool IsVarOnlyUsedAsConst =
|
||||||
|
isOnlyUsedAsConst(NewVar, BlockStmt, *Result.Context);
|
||||||
|
const CheckContext Context{
|
||||||
|
NewVar, BlockStmt, VarDeclStmt, *Result.Context,
|
||||||
|
IssueFix, IsVarUnused, IsVarOnlyUsedAsConst};
|
||||||
const auto *OldVar = Result.Nodes.getNodeAs<VarDecl>(OldVarDeclId);
|
const auto *OldVar = Result.Nodes.getNodeAs<VarDecl>(OldVarDeclId);
|
||||||
const auto *ObjectArg = Result.Nodes.getNodeAs<VarDecl>(ObjectArgId);
|
const auto *ObjectArg = Result.Nodes.getNodeAs<VarDecl>(ObjectArgId);
|
||||||
const auto *BlockStmt = Result.Nodes.getNodeAs<Stmt>("blockStmt");
|
|
||||||
const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctorCall");
|
const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctorCall");
|
||||||
const auto *Stmt = Result.Nodes.getNodeAs<DeclStmt>("declStmt");
|
|
||||||
|
|
||||||
TraversalKindScope RAII(*Result.Context, TK_AsIs);
|
TraversalKindScope RAII(*Result.Context, TK_AsIs);
|
||||||
|
|
||||||
// Do not propose fixes if the DeclStmt has multiple VarDecls or in macros
|
|
||||||
// since we cannot place them correctly.
|
|
||||||
bool IssueFix = Stmt->isSingleDecl() && !NewVar->getLocation().isMacroID();
|
|
||||||
|
|
||||||
// A constructor that looks like T(const T& t, bool arg = false) counts as a
|
// A constructor that looks like T(const T& t, bool arg = false) counts as a
|
||||||
// copy only when it is called with default arguments for the arguments after
|
// copy only when it is called with default arguments for the arguments after
|
||||||
// the first.
|
// the first.
|
||||||
@ -289,74 +296,71 @@ void UnnecessaryCopyInitialization::check(
|
|||||||
// instantiations where the types differ and rely on implicit conversion would
|
// instantiations where the types differ and rely on implicit conversion would
|
||||||
// no longer compile if we switched to a reference.
|
// no longer compile if we switched to a reference.
|
||||||
if (differentReplacedTemplateParams(
|
if (differentReplacedTemplateParams(
|
||||||
NewVar->getType(), constructorArgumentType(OldVar, Result.Nodes),
|
Context.Var.getType(), constructorArgumentType(OldVar, Result.Nodes),
|
||||||
*Result.Context))
|
*Result.Context))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (OldVar == nullptr) {
|
if (OldVar == nullptr) {
|
||||||
handleCopyFromMethodReturn(*NewVar, *BlockStmt, *Stmt, IssueFix, ObjectArg,
|
// `auto NewVar = functionCall();`
|
||||||
*Result.Context);
|
handleCopyFromMethodReturn(Context, ObjectArg);
|
||||||
} else {
|
} else {
|
||||||
handleCopyFromLocalVar(*NewVar, *OldVar, *BlockStmt, *Stmt, IssueFix,
|
// `auto NewVar = OldVar;`
|
||||||
*Result.Context);
|
handleCopyFromLocalVar(Context, *OldVar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnnecessaryCopyInitialization::handleCopyFromMethodReturn(
|
void UnnecessaryCopyInitialization::handleCopyFromMethodReturn(
|
||||||
const VarDecl &Var, const Stmt &BlockStmt, const DeclStmt &Stmt,
|
const CheckContext &Ctx, const VarDecl *ObjectArg) {
|
||||||
bool IssueFix, const VarDecl *ObjectArg, ASTContext &Context) {
|
bool IsConstQualified = Ctx.Var.getType().isConstQualified();
|
||||||
bool IsConstQualified = Var.getType().isConstQualified();
|
if (!IsConstQualified && !Ctx.IsVarOnlyUsedAsConst)
|
||||||
if (!IsConstQualified && !isOnlyUsedAsConst(Var, BlockStmt, Context))
|
|
||||||
return;
|
return;
|
||||||
if (ObjectArg != nullptr &&
|
if (ObjectArg != nullptr &&
|
||||||
!isInitializingVariableImmutable(*ObjectArg, BlockStmt, Context,
|
!isInitializingVariableImmutable(*ObjectArg, Ctx.BlockStmt, Ctx.ASTCtx,
|
||||||
ExcludedContainerTypes))
|
ExcludedContainerTypes))
|
||||||
return;
|
return;
|
||||||
if (isVariableUnused(Var, BlockStmt, Context)) {
|
diagnoseCopyFromMethodReturn(Ctx);
|
||||||
auto Diagnostic =
|
|
||||||
diag(Var.getLocation(),
|
|
||||||
"the %select{|const qualified }0variable %1 is copy-constructed "
|
|
||||||
"from a const reference but is never used; consider "
|
|
||||||
"removing the statement")
|
|
||||||
<< IsConstQualified << &Var;
|
|
||||||
if (IssueFix)
|
|
||||||
recordRemoval(Stmt, Context, Diagnostic);
|
|
||||||
} else {
|
|
||||||
auto Diagnostic =
|
|
||||||
diag(Var.getLocation(),
|
|
||||||
"the %select{|const qualified }0variable %1 is copy-constructed "
|
|
||||||
"from a const reference%select{ but is only used as const "
|
|
||||||
"reference|}0; consider making it a const reference")
|
|
||||||
<< IsConstQualified << &Var;
|
|
||||||
if (IssueFix)
|
|
||||||
recordFixes(Var, Context, Diagnostic);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnnecessaryCopyInitialization::handleCopyFromLocalVar(
|
void UnnecessaryCopyInitialization::handleCopyFromLocalVar(
|
||||||
const VarDecl &NewVar, const VarDecl &OldVar, const Stmt &BlockStmt,
|
const CheckContext &Ctx, const VarDecl &OldVar) {
|
||||||
const DeclStmt &Stmt, bool IssueFix, ASTContext &Context) {
|
if (!Ctx.IsVarOnlyUsedAsConst ||
|
||||||
if (!isOnlyUsedAsConst(NewVar, BlockStmt, Context) ||
|
!isInitializingVariableImmutable(OldVar, Ctx.BlockStmt, Ctx.ASTCtx,
|
||||||
!isInitializingVariableImmutable(OldVar, BlockStmt, Context,
|
|
||||||
ExcludedContainerTypes))
|
ExcludedContainerTypes))
|
||||||
return;
|
return;
|
||||||
|
diagnoseCopyFromLocalVar(Ctx, OldVar);
|
||||||
|
}
|
||||||
|
|
||||||
if (isVariableUnused(NewVar, BlockStmt, Context)) {
|
void UnnecessaryCopyInitialization::diagnoseCopyFromMethodReturn(
|
||||||
auto Diagnostic = diag(NewVar.getLocation(),
|
const CheckContext &Ctx) {
|
||||||
"local copy %0 of the variable %1 is never modified "
|
auto Diagnostic =
|
||||||
"and never used; "
|
diag(Ctx.Var.getLocation(),
|
||||||
"consider removing the statement")
|
"the %select{|const qualified }0variable %1 is "
|
||||||
<< &NewVar << &OldVar;
|
"copy-constructed "
|
||||||
if (IssueFix)
|
"from a const reference%select{%select{ but is only used as const "
|
||||||
recordRemoval(Stmt, Context, Diagnostic);
|
"reference|}0| but is never used}2; consider "
|
||||||
} else {
|
"%select{making it a const reference|removing the statement}2")
|
||||||
auto Diagnostic =
|
<< Ctx.Var.getType().isConstQualified() << &Ctx.Var << Ctx.IsVarUnused;
|
||||||
diag(NewVar.getLocation(),
|
maybeIssueFixes(Ctx, Diagnostic);
|
||||||
"local copy %0 of the variable %1 is never modified; "
|
}
|
||||||
"consider avoiding the copy")
|
|
||||||
<< &NewVar << &OldVar;
|
void UnnecessaryCopyInitialization::diagnoseCopyFromLocalVar(
|
||||||
if (IssueFix)
|
const CheckContext &Ctx, const VarDecl &OldVar) {
|
||||||
recordFixes(NewVar, Context, Diagnostic);
|
auto Diagnostic =
|
||||||
|
diag(Ctx.Var.getLocation(),
|
||||||
|
"local copy %1 of the variable %0 is never modified%select{"
|
||||||
|
"| and never used}2; consider %select{avoiding the copy|removing "
|
||||||
|
"the statement}2")
|
||||||
|
<< &OldVar << &Ctx.Var << Ctx.IsVarUnused;
|
||||||
|
maybeIssueFixes(Ctx, Diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnnecessaryCopyInitialization::maybeIssueFixes(
|
||||||
|
const CheckContext &Ctx, DiagnosticBuilder &Diagnostic) {
|
||||||
|
if (Ctx.IssueFix) {
|
||||||
|
if (Ctx.IsVarUnused)
|
||||||
|
recordRemoval(Ctx.VarDeclStmt, Ctx.ASTCtx, Diagnostic);
|
||||||
|
else
|
||||||
|
recordFixes(Ctx.Var, Ctx.ASTCtx, Diagnostic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,14 +32,32 @@ public:
|
|||||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
|
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// A helper to manipulate the state common to
|
||||||
|
// `CopyFromMethodReturn` and `CopyFromLocalVar`.
|
||||||
|
struct CheckContext {
|
||||||
|
const VarDecl &Var;
|
||||||
|
const Stmt &BlockStmt;
|
||||||
|
const DeclStmt &VarDeclStmt;
|
||||||
|
clang::ASTContext &ASTCtx;
|
||||||
|
const bool IssueFix;
|
||||||
|
const bool IsVarUnused;
|
||||||
|
const bool IsVarOnlyUsedAsConst;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create diagnostics. These are virtual so that derived classes can change
|
||||||
|
// behaviour.
|
||||||
|
virtual void diagnoseCopyFromMethodReturn(const CheckContext &Ctx);
|
||||||
|
virtual void diagnoseCopyFromLocalVar(const CheckContext &Ctx,
|
||||||
|
const VarDecl &OldVar);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleCopyFromMethodReturn(const VarDecl &Var, const Stmt &BlockStmt,
|
void handleCopyFromMethodReturn(const CheckContext &Ctx,
|
||||||
const DeclStmt &Stmt, bool IssueFix,
|
const VarDecl *ObjectArg);
|
||||||
const VarDecl *ObjectArg,
|
void handleCopyFromLocalVar(const CheckContext &Ctx, const VarDecl &OldVar);
|
||||||
ASTContext &Context);
|
|
||||||
void handleCopyFromLocalVar(const VarDecl &NewVar, const VarDecl &OldVar,
|
void maybeIssueFixes(const CheckContext &Ctx, DiagnosticBuilder &Diagnostic);
|
||||||
const Stmt &BlockStmt, const DeclStmt &Stmt,
|
|
||||||
bool IssueFix, ASTContext &Context);
|
|
||||||
const std::vector<StringRef> AllowedTypes;
|
const std::vector<StringRef> AllowedTypes;
|
||||||
const std::vector<StringRef> ExcludedContainerTypes;
|
const std::vector<StringRef> ExcludedContainerTypes;
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user