// RUN: %check_clang_tidy -std=c++14-or-later %s readability-const-return-type %t
// p# = positive test
// n# = negative test
namespace std {
template< class T >
struct add_cv { typedef const volatile T type; };
template< class T> struct add_const { typedef const T type; };
template< class T> struct add_volatile { typedef volatile T type; };
}
const int p1() {
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qualified at the top level, which may reduce code readability without improving const correctness
// CHECK-FIXES: int p1() {
return 1;
}
const int p15();
// CHECK-FIXES: int p15();
template <typename T>
const int p31(T v) { return 2; }
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
// CHECK-FIXES: int p31(T v) { return 2; }
// We detect const-ness even without instantiating T.
template <typename T>
const T p32(T t) { return t; }
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const T' is 'const'-qual
// CHECK-FIXES: T p32(T t) { return t; }
// However, if the return type is itself a template instantiation, Clang does
// not consider it const-qualified without knowing `T`.
template <typename T>
typename std::add_const<T>::type n15(T v) { return v; }
template <bool B>
struct MyStruct {};
template <typename A>
class Klazz {
public:
Klazz(A) {}
};
class Clazz {
public:
Clazz *const p2() {
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'Clazz *const' is 'co
// CHECK-FIXES: Clazz *p2() {
return this;
}
Clazz *const p3();
// CHECK-FIXES: Clazz *p3();
const int p4() const {
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const
// CHECK-FIXES: int p4() const {
return 4;
}
const Klazz<const int>* const p5() const;
// CHECK-FIXES: const Klazz<const int>* p5() const;
const Clazz operator++(int x) { // p12
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz' is 'const
// CHECK-FIXES: Clazz operator++(int x) {
}
struct Strukt {
int i;
};
const Strukt p6() {}
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Strukt' i
// CHECK-FIXES: Strukt p6() {}
// No warning is emitted here, because this is only the declaration. The
// warning will be associated with the definition, below.
const Strukt* const p7();
// CHECK-FIXES: const Strukt* p7();
// const-qualifier is the first `const` token, but not the first token.
static const int p8() {}
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const'-
// CHECK-FIXES: static int p8() {}
static const Strukt p9() {}
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Strukt' i
// CHECK-FIXES: static Strukt p9() {}
int n0() const { return 0; }
const Klazz<const int>& n11(const Klazz<const int>) const;
};
Clazz *const Clazz::p3() {
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
// CHECK-FIXES: Clazz *Clazz::p3() {
return this;
}
const Klazz<const int>* const Clazz::p5() const {}
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const int> *
// CHECK-FIXES: const Klazz<const int>* Clazz::p5() const {}
const Clazz::Strukt* const Clazz::p7() {}
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz::Strukt *con
// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
Clazz *const p10();
// CHECK-FIXES: Clazz *p10();
Clazz *const p10() {
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
// CHECK-FIXES: Clazz *p10() {
return new Clazz();
}
const Clazz bar;
const Clazz *const p11() {
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz *const' is
// CHECK-FIXES: const Clazz *p11() {
return &bar;
}
const Klazz<const int> p12() {}
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const int>'
// CHECK-FIXES: Klazz<const int> p12() {}
const Klazz<const Klazz<const int>> p33() {}
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<
// CHECK-FIXES: Klazz<const Klazz<const int>> p33() {}
const Klazz<const int>* const p13() {}
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const int> *
// CHECK-FIXES: const Klazz<const int>* p13() {}
const Klazz<const int>* const volatile p14() {}
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const int> *
// CHECK-FIXES: const Klazz<const int>* volatile p14() {}
const MyStruct<0 < 1> p34() {}
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const MyStruct<0 < 1>'
// CHECK-FIXES: MyStruct<0 < 1> p34() {}
MyStruct<0 < 1> const p35() {}
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const MyStruct<0 < 1>'
// CHECK-FIXES: MyStruct<0 < 1> p35() {}
Klazz<MyStruct<0 < 1> const> const p36() {}
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const MyStru
// CHECK-FIXES: Klazz<MyStruct<0 < 1> const> p36() {}
const Klazz<MyStruct<0 < 1> const> *const p37() {}
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const MyStru
// CHECK-FIXES: const Klazz<MyStruct<0 < 1> const> *p37() {}
Klazz<const MyStruct<0 < 1>> const p38() {}
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const MyStru
// CHECK-FIXES: Klazz<const MyStruct<0 < 1>> p38() {}
const Klazz<const MyStruct<0 < 1>> p39() {}
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<
// CHECK-FIXES: Klazz<const MyStruct<0 < 1>> p39() {}
const Klazz<const MyStruct<(0 > 1)>> p40() {}
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const MyStru
// CHECK-FIXES: Klazz<const MyStruct<(0 > 1)>> p40() {}
// re-declaration of p15.
const int p15();
// CHECK-FIXES: int p15();
const int p15() {
// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
// CHECK-FIXES: int p15() {
return 0;
}
// Exercise the lexer.
const /* comment */ /* another comment*/ int p16() { return 0; }
// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
// CHECK-FIXES: /* comment */ /* another comment*/ int p16() { return 0; }
/* comment */ const
// CHECK-MESSAGES: [[@LINE-1]]:15: warning:
// CHECK-FIXES: /* comment */
// more
/* another comment*/ int p17() { return 0; }
// Test cases where the `const` token lexically is hidden behind some form of
// indirection.
// Regression tests involving macros, which are ignored by default because
// IgnoreMacros defaults to true.
#define CONCAT(a, b) a##b
CONCAT(cons, t) int n22(){}
#define CONSTINT const int
CONSTINT n23() {}
#define CONST const
CONST int n24() {}
#define CREATE_FUNCTION() \
const int n_inside_macro() { \
return 1; \
}
CREATE_FUNCTION();
using ty = const int;
ty p21() {}
typedef const int ty2;
ty2 p22() {}
// Declaration uses a macro, while definition doesn't. In this case, we won't
// fix the declaration, and will instead issue a warning.
CONST int p23();
// CHECK-NOTE: [[@LINE-1]]:1: note: could not transform this declaration
const int p23();
// CHECK-FIXES: int p23();
const int p23() { return 3; }
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
// CHECK-FIXES: int p23() { return 3; }
int const p24() { return 3; }
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
// CHECK-FIXES: int p24() { return 3; }
int const * const p25(const int* p) { return p; }
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int *const' is 'co
// CHECK-FIXES: int const * p25(const int* p) { return p; }
// We cannot (yet) fix instances that use trailing return types, but we can
// warn.
auto p26() -> const int { return 3; }
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
auto p27() -> int const { return 3; }
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
std::add_const<int>::type p28() { return 3; }
// p29, p30 are based on
// llvm/projects/test-suite/SingleSource/Benchmarks/Misc-C++-EH/spirit.cpp:
template <class T>
Klazz<T const> const p29(T const &t) { return {}; }
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const T>' is
// CHECK-FIXES: Klazz<T const> p29(T const &t) { return {}; }
Klazz<char const *> const p30(char const *s) { return s; }
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const char *
// CHECK-FIXES: Klazz<char const *> p30(char const *s) { return s; }
const int n1 = 1;
const Clazz n2 = Clazz();
const Clazz* n3 = new Clazz();
Clazz *const n4 = new Clazz();
const Clazz *const n5 = new Clazz();
constexpr int n6 = 6;
constexpr int n7() { return 8; }
const int eight = 8;
constexpr const int* n8() { return &eight; }
Klazz<const int> n9();
const Klazz<const int>* n10();
const Klazz<const int>& Clazz::n11(const Klazz<const int>) const {}
// Declaration only.
const int n14();
int **const * n_multiple_ptr();
int *const & n_pointer_ref();
class PVBase {
public:
virtual const int getC() = 0;
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const'-qualified at the top level, which may reduce code readability without improving const correctness
// CHECK-NOT-FIXES: virtual int getC() = 0;
};
class NVDerive : public PVBase {
public:
// Don't warn about overridden methods, because it may be impossible to make
// them non-const as the user may not be able to change the base class.
const int getC() override { return 1; }
};
class NVDeriveOutOfLine : public PVBase {
public:
// Don't warn about overridden methods, because it may be impossible to make
// them non-const as one may not be able to change the base class
const int getC();
};
const int NVDeriveOutOfLine::getC() { return 1; }
// Don't warn about const auto types, because it may be impossible to make them non-const
// without a significant semantics change. Since `auto` drops cv-qualifiers,
// tests check `decltype(auto)`.
decltype(auto) n16() {
static const int i = 42;
return i;
}
// Don't warn about `decltype(<expr>)` types
const int n17i = 1;
decltype(n17i) n17() {
return 17;
}
// Do warn when on decltype types with the local const qualifier
// `const decltype(auto)` won't compile, so check only `const decltype(<expr>)`
const decltype(n17i) n18() {
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const decltype(n17i)
// CHECK-FIXES: decltype(n17i) n18() {
return 18;
}
// `volatile` modifier doesn't affect the checker
volatile decltype(n17i) n19() {
return 19;
}
// Don't warn about `__typeof__(<expr>)` types
__typeof__(n17i) n20() {
return 20;
}
// Don't warn about `__typeof__(type)` types
__typeof__(const int) n21() {
return 21;
}
template <typename T>
struct n25 {
T foo() const { return 2; }
};
template struct n25<const int>;
template <typename T>
struct p41 {
const T foo() const { return 2; }
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const
// CHECK-MESSAGES: [[@LINE-2]]:3: warning: return type 'const
// CHECK-FIXES: T foo() const { return 2; }
};
template struct p41<int>;
namespace PR73270 {
template<typename K, typename V>
struct Pair {
using first_type = const K;
using second_type = V;
};
template<typename PairType>
typename PairType::first_type getFirst() {
return {};
}
void test() {
getFirst<Pair<int, int>>();
}
}