forked from OSchip/llvm-project
[clang-tidy] performance-unnecessary-copy-initialization: Fix false negative.
`isConstRefReturningMethodCall` should be considering `CXXOperatorCallExpr` in addition to `CXXMemberCallExpr`. Clang considers these to be distinct (`CXXOperatorCallExpr` derives from `CallExpr`, not `CXXMemberCallExpr`), but we don't care in the context of this check. This is important because of `std::vector<Expensive>::operator[](size_t) const`. Differential Revision: https://reviews.llvm.org/D114249
This commit is contained in:
parent
b9fd7247a7
commit
ba4411e7c6
|
@ -83,13 +83,19 @@ AST_MATCHER_FUNCTION_P(StatementMatcher, isConstRefReturningMethodCall,
|
|||
// variable being declared. The assumption is that the const reference being
|
||||
// returned either points to a global static variable or to a member of the
|
||||
// called object.
|
||||
return cxxMemberCallExpr(
|
||||
callee(cxxMethodDecl(
|
||||
returns(hasCanonicalType(matchers::isReferenceToConst())))
|
||||
.bind(MethodDeclId)),
|
||||
on(declRefExpr(to(varDecl().bind(ObjectArgId)))),
|
||||
thisPointerType(namedDecl(
|
||||
unless(matchers::matchesAnyListedName(ExcludedContainerTypes)))));
|
||||
const auto MethodDecl =
|
||||
cxxMethodDecl(returns(hasCanonicalType(matchers::isReferenceToConst())))
|
||||
.bind(MethodDeclId);
|
||||
const auto ReceiverExpr = declRefExpr(to(varDecl().bind(ObjectArgId)));
|
||||
const auto ReceiverType =
|
||||
hasCanonicalType(recordType(hasDeclaration(namedDecl(
|
||||
unless(matchers::matchesAnyListedName(ExcludedContainerTypes))))));
|
||||
|
||||
return expr(anyOf(
|
||||
cxxMemberCallExpr(callee(MethodDecl), on(ReceiverExpr),
|
||||
thisPointerType(ReceiverType)),
|
||||
cxxOperatorCallExpr(callee(MethodDecl), hasArgument(0, ReceiverExpr),
|
||||
hasArgument(0, hasType(ReceiverType)))));
|
||||
}
|
||||
|
||||
AST_MATCHER_FUNCTION(StatementMatcher, isConstRefReturningFunctionCall) {
|
||||
|
|
|
@ -21,6 +21,7 @@ struct ExpensiveToCopy {
|
|||
|
||||
struct ConstInCorrectType {
|
||||
const ExpensiveToCopy &secretlyMutates() const;
|
||||
const ExpensiveToCopy &operator[](int) const;
|
||||
};
|
||||
|
||||
using NSVTE = ns::ViewType<ExpensiveToCopy>;
|
||||
|
@ -59,12 +60,28 @@ void excludedConstIncorrectType() {
|
|||
E.constMethod();
|
||||
}
|
||||
|
||||
void excludedConstIncorrectTypeOperator() {
|
||||
ConstInCorrectType C;
|
||||
const auto E = C[42];
|
||||
E.constMethod();
|
||||
}
|
||||
|
||||
void excludedConstIncorrectTypeAsPointer(ConstInCorrectType *C) {
|
||||
const auto E = C->secretlyMutates();
|
||||
E.constMethod();
|
||||
}
|
||||
|
||||
void excludedConstIncorrectTypeAsPointerOperator(ConstInCorrectType *C) {
|
||||
const auto E = (*C)[42];
|
||||
E.constMethod();
|
||||
}
|
||||
|
||||
void excludedConstIncorrectTypeAsReference(const ConstInCorrectType &C) {
|
||||
const auto E = C.secretlyMutates();
|
||||
E.constMethod();
|
||||
}
|
||||
|
||||
void excludedConstIncorrectTypeAsReferenceOperator(const ConstInCorrectType &C) {
|
||||
const auto E = C[42];
|
||||
E.constMethod();
|
||||
}
|
||||
|
|
|
@ -3,11 +3,19 @@
|
|||
template <typename T>
|
||||
struct Iterator {
|
||||
void operator++();
|
||||
const T &operator*() const;
|
||||
T &operator*() const;
|
||||
bool operator!=(const Iterator &) const;
|
||||
typedef const T &const_reference;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ConstIterator {
|
||||
void operator++();
|
||||
const T &operator*() const;
|
||||
bool operator!=(const ConstIterator &) const;
|
||||
typedef const T &const_reference;
|
||||
};
|
||||
|
||||
struct ExpensiveToCopyType {
|
||||
ExpensiveToCopyType();
|
||||
virtual ~ExpensiveToCopyType();
|
||||
|
@ -15,8 +23,6 @@ struct ExpensiveToCopyType {
|
|||
using ConstRef = const ExpensiveToCopyType &;
|
||||
ConstRef referenceWithAlias() const;
|
||||
const ExpensiveToCopyType *pointer() const;
|
||||
Iterator<ExpensiveToCopyType> begin() const;
|
||||
Iterator<ExpensiveToCopyType> end() const;
|
||||
void nonConstMethod();
|
||||
bool constMethod() const;
|
||||
template <typename A>
|
||||
|
@ -24,6 +30,21 @@ struct ExpensiveToCopyType {
|
|||
operator int() const; // Implicit conversion to int.
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Container {
|
||||
bool empty() const;
|
||||
const T& operator[](int) const;
|
||||
const T& operator[](int);
|
||||
Iterator<T> begin();
|
||||
Iterator<T> end();
|
||||
ConstIterator<T> begin() const;
|
||||
ConstIterator<T> end() const;
|
||||
void nonConstMethod();
|
||||
bool constMethod() const;
|
||||
};
|
||||
|
||||
using ExpensiveToCopyContainerAlias = Container<ExpensiveToCopyType>;
|
||||
|
||||
struct TrivialToCopyType {
|
||||
const TrivialToCopyType &reference() const;
|
||||
};
|
||||
|
@ -138,6 +159,94 @@ void PositiveMethodCallConstPointerParam(const ExpensiveToCopyType *const Obj) {
|
|||
VarCopyConstructed.constMethod();
|
||||
}
|
||||
|
||||
void PositiveOperatorCallConstReferenceParam(const Container<ExpensiveToCopyType> &C) {
|
||||
const auto AutoAssigned = C[42];
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
|
||||
// CHECK-FIXES: const auto& AutoAssigned = C[42];
|
||||
AutoAssigned.constMethod();
|
||||
|
||||
const auto AutoCopyConstructed(C[42]);
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
|
||||
// CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
|
||||
AutoCopyConstructed.constMethod();
|
||||
|
||||
const ExpensiveToCopyType VarAssigned = C[42];
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
|
||||
// CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C[42];
|
||||
VarAssigned.constMethod();
|
||||
|
||||
const ExpensiveToCopyType VarCopyConstructed(C[42]);
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
|
||||
// CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C[42]);
|
||||
VarCopyConstructed.constMethod();
|
||||
}
|
||||
|
||||
void PositiveOperatorCallConstValueParam(const Container<ExpensiveToCopyType> C) {
|
||||
const auto AutoAssigned = C[42];
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
|
||||
// CHECK-FIXES: const auto& AutoAssigned = C[42];
|
||||
AutoAssigned.constMethod();
|
||||
|
||||
const auto AutoCopyConstructed(C[42]);
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
|
||||
// CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
|
||||
AutoCopyConstructed.constMethod();
|
||||
|
||||
const ExpensiveToCopyType VarAssigned = C[42];
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
|
||||
// CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C[42];
|
||||
VarAssigned.constMethod();
|
||||
|
||||
const ExpensiveToCopyType VarCopyConstructed(C[42]);
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
|
||||
// CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C[42]);
|
||||
VarCopyConstructed.constMethod();
|
||||
}
|
||||
|
||||
void PositiveOperatorCallConstValueParamAlias(const ExpensiveToCopyContainerAlias C) {
|
||||
const auto AutoAssigned = C[42];
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
|
||||
// CHECK-FIXES: const auto& AutoAssigned = C[42];
|
||||
AutoAssigned.constMethod();
|
||||
|
||||
const auto AutoCopyConstructed(C[42]);
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
|
||||
// CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
|
||||
AutoCopyConstructed.constMethod();
|
||||
|
||||
const ExpensiveToCopyType VarAssigned = C[42];
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
|
||||
// CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C[42];
|
||||
VarAssigned.constMethod();
|
||||
|
||||
const ExpensiveToCopyType VarCopyConstructed(C[42]);
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
|
||||
// CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C[42]);
|
||||
VarCopyConstructed.constMethod();
|
||||
}
|
||||
|
||||
void PositiveOperatorCallConstValueParam(const Container<ExpensiveToCopyType>* C) {
|
||||
const auto AutoAssigned = (*C)[42];
|
||||
// TODO-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
|
||||
// TODO-FIXES: const auto& AutoAssigned = (*C)[42];
|
||||
AutoAssigned.constMethod();
|
||||
|
||||
const auto AutoCopyConstructed((*C)[42]);
|
||||
// TODO-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
|
||||
// TODO-FIXES: const auto& AutoCopyConstructed((*C)[42]);
|
||||
AutoCopyConstructed.constMethod();
|
||||
|
||||
const ExpensiveToCopyType VarAssigned = C->operator[](42);
|
||||
// TODO-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
|
||||
// TODO-FIXES: const ExpensiveToCopyType& VarAssigned = C->operator[](42);
|
||||
VarAssigned.constMethod();
|
||||
|
||||
const ExpensiveToCopyType VarCopyConstructed(C->operator[](42));
|
||||
// TODO-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
|
||||
// TODO-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C->operator[](42));
|
||||
VarCopyConstructed.constMethod();
|
||||
}
|
||||
|
||||
void PositiveLocalConstValue() {
|
||||
const ExpensiveToCopyType Obj;
|
||||
const auto UnnecessaryCopy = Obj.reference();
|
||||
|
@ -443,21 +552,9 @@ void WarningOnlyMultiDeclStmt() {
|
|||
}
|
||||
|
||||
class Element {};
|
||||
class Container {
|
||||
public:
|
||||
class Iterator {
|
||||
public:
|
||||
void operator++();
|
||||
Element operator*();
|
||||
bool operator!=(const Iterator &);
|
||||
WeirdCopyCtorType c;
|
||||
};
|
||||
const Iterator &begin() const;
|
||||
const Iterator &end() const;
|
||||
};
|
||||
|
||||
void implicitVarFalsePositive() {
|
||||
for (const Element &E : Container()) {
|
||||
for (const Element &E : Container<Element>()) {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -621,8 +718,30 @@ void positiveUnusedReferenceIsRemoved() {
|
|||
// CHECK-FIXES: // Comments on a new line should not be deleted.
|
||||
}
|
||||
|
||||
void negativeloopedOverObjectIsModified() {
|
||||
ExpensiveToCopyType Orig;
|
||||
void positiveLoopedOverObjectIsConst() {
|
||||
const Container<ExpensiveToCopyType> Orig;
|
||||
for (const auto &Element : Orig) {
|
||||
const auto Copy = Element;
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:16: warning: local copy 'Copy'
|
||||
// CHECK-FIXES: const auto& Copy = Element;
|
||||
Orig.constMethod();
|
||||
Copy.constMethod();
|
||||
}
|
||||
|
||||
auto Lambda = []() {
|
||||
const Container<ExpensiveToCopyType> Orig;
|
||||
for (const auto &Element : Orig) {
|
||||
const auto Copy = Element;
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:18: warning: local copy 'Copy'
|
||||
// CHECK-FIXES: const auto& Copy = Element;
|
||||
Orig.constMethod();
|
||||
Copy.constMethod();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void negativeLoopedOverObjectIsModified() {
|
||||
Container<ExpensiveToCopyType> Orig;
|
||||
for (const auto &Element : Orig) {
|
||||
const auto Copy = Element;
|
||||
Orig.nonConstMethod();
|
||||
|
@ -630,7 +749,7 @@ void negativeloopedOverObjectIsModified() {
|
|||
}
|
||||
|
||||
auto Lambda = []() {
|
||||
ExpensiveToCopyType Orig;
|
||||
Container<ExpensiveToCopyType> Orig;
|
||||
for (const auto &Element : Orig) {
|
||||
const auto Copy = Element;
|
||||
Orig.nonConstMethod();
|
||||
|
|
Loading…
Reference in New Issue