Skip to content

Commit

Permalink
[clang-tidy] Fix false in unnecessary-value-param inside templates (l…
Browse files Browse the repository at this point in the history
…lvm#98488)

Summary:
If callExpr is type dependent, there is no way to analyze individual
arguments until template specialization. Before this diff only calls
with dependent callees were skipped so unnecessary-value-param was
processing arguments that had non-dependent type that gave false
positives because the call was not fully resolved till specialization.
So now instead of checking type dependent callee, the whole expression
will be checked for type dependent.

Test Plan: check-clang-tools
  • Loading branch information
dmpolukhin authored and sgundapa committed Jul 23, 2024
1 parent 70cc425 commit 92a5c2d
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 20 deletions.
3 changes: 2 additions & 1 deletion clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,8 @@ Changes in existing checks
<clang-tidy/checks/performance/unnecessary-value-param>` check
detecting more cases for template functions including lambdas with ``auto``.
E.g., ``std::sort(a.begin(), a.end(), [](auto x, auto y) { return a > b; });``
will be detected for expensive to copy types.
will be detected for expensive to copy types. Fixed false positives for
dependent call expressions.

- Improved :doc:`readability-avoid-return-with-void-value
<clang-tidy/checks/readability/avoid-return-with-void-value>` check by adding
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,31 @@

// CHECK-FIXES: #include <utility>

namespace std {
template <typename>
struct remove_reference;

template <typename _Tp>
struct remove_reference {
typedef _Tp type;
};

template <typename _Tp>
struct remove_reference<_Tp &> {
typedef _Tp type;
};

template <typename _Tp>
struct remove_reference<_Tp &&> {
typedef _Tp type;
};

template <typename _Tp>
constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) {
return static_cast<typename std::remove_reference<_Tp>::type &&>(__t);
}
} // namespace std

struct ExpensiveToCopyType {
const ExpensiveToCopyType & constReference() const {
return *this;
Expand Down Expand Up @@ -357,3 +382,12 @@ void fun() {
ExpensiveToCopyType E;
NegativeUsingConstructor S(E);
}

struct B {
static void bar(ExpensiveMovableType a, ExpensiveMovableType b);
};

template <typename T>
void NegativeCallWithDependentAndNondependentArgs(ExpensiveMovableType a, T b) {
B::bar(std::move(a), b);
}
37 changes: 18 additions & 19 deletions clang/lib/Analysis/ExprMutationAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,25 +404,24 @@ ExprMutationAnalyzer::Analyzer::findDirectMutation(const Expr *Exp) {
memberExpr(hasObjectExpression(canResolveToExpr(Exp)))),
nonConstReferenceType());
const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
const auto TypeDependentCallee =
callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(),
cxxDependentScopeMemberExpr(),
hasType(templateTypeParmType()), isTypeDependent())));

const auto AsNonConstRefArg = anyOf(
callExpr(NonConstRefParam, NotInstantiated),
cxxConstructExpr(NonConstRefParam, NotInstantiated),
callExpr(TypeDependentCallee, hasAnyArgument(canResolveToExpr(Exp))),
cxxUnresolvedConstructExpr(hasAnyArgument(canResolveToExpr(Exp))),
// Previous False Positive in the following Code:
// `template <typename T> void f() { int i = 42; new Type<T>(i); }`
// Where the constructor of `Type` takes its argument as reference.
// The AST does not resolve in a `cxxConstructExpr` because it is
// type-dependent.
parenListExpr(hasDescendant(expr(canResolveToExpr(Exp)))),
// If the initializer is for a reference type, there is no cast for
// the variable. Values are cast to RValue first.
initListExpr(hasAnyInit(expr(canResolveToExpr(Exp)))));

const auto AsNonConstRefArg =
anyOf(callExpr(NonConstRefParam, NotInstantiated),
cxxConstructExpr(NonConstRefParam, NotInstantiated),
// If the call is type-dependent, we can't properly process any
// argument because required type conversions and implicit casts
// will be inserted only after specialization.
callExpr(isTypeDependent(), hasAnyArgument(canResolveToExpr(Exp))),
cxxUnresolvedConstructExpr(hasAnyArgument(canResolveToExpr(Exp))),
// Previous False Positive in the following Code:
// `template <typename T> void f() { int i = 42; new Type<T>(i); }`
// Where the constructor of `Type` takes its argument as reference.
// The AST does not resolve in a `cxxConstructExpr` because it is
// type-dependent.
parenListExpr(hasDescendant(expr(canResolveToExpr(Exp)))),
// If the initializer is for a reference type, there is no cast for
// the variable. Values are cast to RValue first.
initListExpr(hasAnyInit(expr(canResolveToExpr(Exp)))));

// Captured by a lambda by reference.
// If we're initializing a capture with 'Exp' directly then we're initializing
Expand Down

0 comments on commit 92a5c2d

Please sign in to comment.