forked from OSchip/llvm-project
[clang-tidy] performance-unnecessary-copy-initialization: Directly examine the initializing var's initializer.
This fixes false positive cases where a reference is initialized outside of a block statement and then its initializing variable is modified. Another case is when the looped over container is modified. Differential Revision: https://reviews.llvm.org/D103021 Reviewed-by: ymandel
This commit is contained in:
parent
ac87133f1d
commit
bdd5da9dec
|
@ -12,6 +12,7 @@
|
|||
#include "../utils/LexerUtils.h"
|
||||
#include "../utils/Matchers.h"
|
||||
#include "../utils/OptionsUtils.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
|
||||
namespace clang {
|
||||
|
@ -72,14 +73,14 @@ AST_MATCHER_FUNCTION(StatementMatcher, isConstRefReturningFunctionCall) {
|
|||
.bind(InitFunctionCallId);
|
||||
}
|
||||
|
||||
AST_MATCHER_FUNCTION(StatementMatcher, isInitializedFromReferenceToConst) {
|
||||
AST_MATCHER_FUNCTION(StatementMatcher, initializerReturnsReferenceToConst) {
|
||||
auto OldVarDeclRef =
|
||||
declRefExpr(to(varDecl(hasLocalStorage()).bind(OldVarDeclId)));
|
||||
return declStmt(has(varDecl(hasInitializer(
|
||||
return expr(
|
||||
anyOf(isConstRefReturningFunctionCall(), isConstRefReturningMethodCall(),
|
||||
ignoringImpCasts(OldVarDeclRef),
|
||||
ignoringImpCasts(unaryOperator(
|
||||
hasOperatorName("&"), hasUnaryOperand(OldVarDeclRef))))))));
|
||||
ignoringImpCasts(unaryOperator(hasOperatorName("&"),
|
||||
hasUnaryOperand(OldVarDeclRef)))));
|
||||
}
|
||||
|
||||
// This checks that the variable itself is only used as const, and also makes
|
||||
|
@ -106,18 +107,14 @@ static bool isInitializingVariableImmutable(const VarDecl &InitializingVar,
|
|||
if (!isa<ReferenceType, PointerType>(T))
|
||||
return true;
|
||||
|
||||
auto Matches =
|
||||
match(findAll(declStmt(has(varDecl(equalsNode(&InitializingVar))))
|
||||
.bind("declStmt")),
|
||||
BlockStmt, Context);
|
||||
// The reference or pointer is not initialized in the BlockStmt. We assume
|
||||
// its pointee is not modified then.
|
||||
if (Matches.empty())
|
||||
// The reference or pointer is not declared and hence not initialized anywhere
|
||||
// in the function. We assume its pointee is not modified then.
|
||||
if (!InitializingVar.isLocalVarDecl() || !InitializingVar.hasInit()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto *Initialization = selectFirst<DeclStmt>("declStmt", Matches);
|
||||
Matches =
|
||||
match(isInitializedFromReferenceToConst(), *Initialization, Context);
|
||||
auto Matches = match(initializerReturnsReferenceToConst(),
|
||||
*InitializingVar.getInit(), Context);
|
||||
// The reference is initialized from a free function without arguments
|
||||
// returning a const reference. This is a global immutable object.
|
||||
if (selectFirst<CallExpr>(InitFunctionCallId, Matches) != nullptr)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_UNNECESSARY_COPY_INITIALIZATION_H
|
||||
|
||||
#include "../ClangTidyCheck.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
// RUN: %check_clang_tidy %s performance-unnecessary-copy-initialization %t
|
||||
|
||||
template <typename T>
|
||||
struct Iterator {
|
||||
void operator++();
|
||||
const T &operator*() const;
|
||||
bool operator!=(const Iterator &) const;
|
||||
typedef const T &const_reference;
|
||||
};
|
||||
|
||||
struct ExpensiveToCopyType {
|
||||
ExpensiveToCopyType();
|
||||
virtual ~ExpensiveToCopyType();
|
||||
const ExpensiveToCopyType &reference() const;
|
||||
const ExpensiveToCopyType *pointer() const;
|
||||
Iterator<ExpensiveToCopyType> begin() const;
|
||||
Iterator<ExpensiveToCopyType> end() const;
|
||||
void nonConstMethod();
|
||||
bool constMethod() const;
|
||||
};
|
||||
|
@ -589,3 +599,41 @@ void positiveUnusedReferenceIsRemoved() {
|
|||
// CHECK-FIXES-NOT: auto TrailingCommentRemoved = ExpensiveTypeReference(); // Trailing comment.
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
void negativeloopedOverObjectIsModified() {
|
||||
ExpensiveToCopyType Orig;
|
||||
for (const auto &Element : Orig) {
|
||||
const auto Copy = Element;
|
||||
Orig.nonConstMethod();
|
||||
Copy.constMethod();
|
||||
}
|
||||
|
||||
auto Lambda = []() {
|
||||
ExpensiveToCopyType Orig;
|
||||
for (const auto &Element : Orig) {
|
||||
const auto Copy = Element;
|
||||
Orig.nonConstMethod();
|
||||
Copy.constMethod();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void negativeReferenceIsInitializedOutsideOfBlock() {
|
||||
ExpensiveToCopyType Orig;
|
||||
const auto &E2 = Orig;
|
||||
if (1 != 2 * 3) {
|
||||
const auto C2 = E2;
|
||||
Orig.nonConstMethod();
|
||||
C2.constMethod();
|
||||
}
|
||||
|
||||
auto Lambda = []() {
|
||||
ExpensiveToCopyType Orig;
|
||||
const auto &E2 = Orig;
|
||||
if (1 != 2 * 3) {
|
||||
const auto C2 = E2;
|
||||
Orig.nonConstMethod();
|
||||
C2.constMethod();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue