Files
codeql/cpp/ql/test/query-tests/Best Practices/RuleOfTwo/RuleOfTwo.cpp
2018-08-02 17:53:23 +01:00

314 lines
9.4 KiB
C++

// NOT OK
struct CopyButNoAssign {
CopyButNoAssign() : n(0) {}
CopyButNoAssign(const CopyButNoAssign& copy_from) : n(copy_from.n) {}
int n;
};
// NOT OK
struct AssignButNoCopy {
AssignButNoCopy& operator=(const AssignButNoCopy& assign_from) { return *this; }
};
// OK: before C++11, marking a constructor as private was an
// acceptable way to delete it. The constructor still might get used by
// accident within the scope of the class and its friends, but we tolerate that
// in order to avoid false positives on such code.
struct NotCopyable {
NotCopyable() {}
private:
NotCopyable(const NotCopyable& cannot_copy) {}
};
// OK: C++11 version of NotCopyable
struct NotCopyable11 {
NotCopyable11(const NotCopyable11&) = delete;
};
// OK: both defined
struct HasBoth {
HasBoth() {}
HasBoth(const HasBoth& copy_from) {}
HasBoth& operator=(const HasBoth& assign_from) { return *this; }
};
// OK: both generated
struct HasGenerated {
HasBoth m_member;
};
// OK: this class gets a generated copy constructor but no copy
// assignment. We don't flag this as an error -- trying to copy-assign it will
// be a compilation error.
struct ConstMember {
const int x;
};
// OK: this class declares a copy constructor but no copy
// assignment. But because it has a const member, it does not get a
// compiler-defined copy assignment operator, so there is no potential
// memory safety problem.
struct ConstMemberAndCopy {
const int x;
ConstMemberAndCopy(): x(4) {}
ConstMemberAndCopy(const ConstMemberAndCopy& that): x(that.x) {}
};
// OK: Our class won't get a copy constructor.
struct NoAutoCopy {
NotCopyable x; // has no copy constructor but has copy assignment
NoAutoCopy& operator=(const NoAutoCopy& that) { x = that.x; return *this; }
};
// OK: When viewed in isolation, this class seems to use the pre-C++11 idiom of
// suppressing an auto-generated member by making it inaccessible.
struct CopyableByFriend {
friend struct MyClassFriend;
private:
CopyableByFriend(CopyableByFriend &) {} // OK: private
};
// OK: No auto-generated copy constructor
struct NotFriend {
CopyableByFriend x;
NotFriend& operator=(const NotFriend& that) { return *this; }
};
// NOT OK: Gets an auto-generated copy constructor because the class is a
// friend of CopyableByFriend.
struct MyClassFriend {
CopyableByFriend x;
MyClassFriend& operator=(const MyClassFriend& that) { return *this; }
};
// OK or NOT OK? An explicit default and an explicit implementation.
struct UsesDefault {
UsesDefault(UsesDefault&) = default;
UsesDefault& operator=(UsesDefault& ud) { return *this; }
};
// OK: copy constructor is user-defined, and there is no copy assignment.
struct DeletedAssign {
DeletedAssign(DeletedAssign&) {}
DeletedAssign& operator=(DeletedAssign& ud) = delete;
};
// OK: volatile instances can only be copied but not assigned, and that is
// not a correctness problem.
struct ProtectedVolatile {
protected:
ProtectedVolatile(const volatile ProtectedVolatile& that) {}
public:
ProtectedVolatile() {}
ProtectedVolatile(const ProtectedVolatile& that) {}
ProtectedVolatile& operator=(const ProtectedVolatile& that) {
return *this;
}
};
// OK: because `pv` is volatile, the compiler will try to generate a call to
// the protected copy constructor of ProtectedVolatile that takes a volatile
// argument. When access to this constructor fails, this class does not get an
// auto-generated copy constructor at all.
struct HasVPV {
volatile ProtectedVolatile vpv;
HasVPV& operator=(const HasVPV& that) {
return *this;
}
};
// FALSE NEGATIVE: the relevant copy constructor of ProtectedVolatile is
// accessible, so our class will get a generated copy constructor. Our query
// thinks the copy constructor is inaccessible because it picks up the other
// copy constructor. To fix this, our library should be changed to distinguish
// between copy constructors and resolve overloading properly instead of
// assuming that there is at most one.
struct HasPV {
ProtectedVolatile pv;
HasPV& operator=(const HasPV& that) {
return *this;
}
};
// OK: has explicit copy constructor and copy assignment operator.
struct ProtectedAssign {
ProtectedAssign() {}
ProtectedAssign(const ProtectedAssign& that) {}
protected:
ProtectedAssign& operator=(const ProtectedAssign& that) { return *this; }
};
// NOT OK: this class gets a copy assignment operator because it can access the
// (protected) copy assignment operator of its base class.
struct IsAProtectedAssign: public ProtectedAssign {
IsAProtectedAssign(const IsAProtectedAssign& that) {}
};
// OK: this class gets no copy assignment operator. It cannot access the
// (protected) copy assignment operator of its field even though it can access
// the same operator on its base class. For protected members, access is
// special when going through objects with the class of `this`.
struct HasAndIsAProtectedAssign: public ProtectedAssign {
ProtectedAssign fieldB;
HasAndIsAProtectedAssign(const HasAndIsAProtectedAssign& that) {}
};
// OK: has explicit copy constructor and copy assignment operator.
struct ProtectedCC {
ProtectedCC() {}
ProtectedCC& operator=(const ProtectedCC& that) { return *this; }
protected:
ProtectedCC(const ProtectedCC& that) {}
};
// NOT OK: this class gets a copy constructor because it can access the
// (protected) copy constructor of its base class.
struct IsAProtectedCC: public ProtectedCC {
IsAProtectedCC& operator=(const IsAProtectedCC& that) { return *this; }
};
// OK: this class gets no copy constructor. It cannot access the (protected)
// copy constructor of its field even though it can access the same operator on
// its base class. For protected members, access is special when going through
// objects with the class of `this`.
struct HasAndIsAProtectedCC: public ProtectedCC {
ProtectedCC fieldB;
HasAndIsAProtectedCC& operator=(const HasAndIsAProtectedCC& that) {
return *this;
}
};
// OK: defines both
struct DerivesVirtual: virtual public NotCopyable {
DerivesVirtual(const DerivesVirtual& that) {}
DerivesVirtual& operator=(const DerivesVirtual& that) { return *this; }
};
// OK: overrides only assignment but gets no auto-generated copy constructor
// because a virtual non-direct base class is not copyable.
struct DerivesDerivesVirtual: public DerivesVirtual {
DerivesDerivesVirtual& operator=(const DerivesDerivesVirtual& that) {
return *this;
}
};
// OK: templates and their instantiations are ignored, but this class would
// be correct anyway.
template<class T>
struct HasBoth_template {
T t_;
HasBoth_template() {}
HasBoth_template(const HasBoth_template<T>& copy_from)
: t_(copy_from.t_)
{}
HasBoth_template& operator=(const HasBoth_template<T>& assign_from) {
t_ = assign_from.t_;
return *this;
}
};
void use_copy_ctor(HasBoth_template<int> obj) {
HasBoth_template<int> copy = obj;
}
// NOT OK (FALSE NEGATIVE): because this template is never instantiated, it
// does not appear in the database and is not caught by the analysis.
template<class T>
struct CopyButNoAssign_template {
CopyButNoAssign_template() : n() {}
CopyButNoAssign_template(const CopyButNoAssign_template& copy_from) : n(copy_from.n) {}
T n;
};
// OK: copy assignment operator is uncallable because (a) it's private and (b)
// it has only a declaration but no definition, so the program would fail to
// link.
class UncallableCopyAssignment {
public:
UncallableCopyAssignment(const UncallableCopyAssignment& other) { }
UncallableCopyAssignment() { }
private:
void operator=(UncallableCopyAssignment const &);
};
// OK: both members are "default"
struct BothDefault {
BothDefault(BothDefault&) = default;
BothDefault& operator=(BothDefault& bd) = default;
};
// OK: template class _with_ initializer on extra argument of copy constructor.
// We do not extract initializers of template functions or of functions in
// template classes, so our definition of a copy constructor is conservative
// and assumes they are present, which is correct in this case (`x` has `= 0`).
template<typename T>
class TemplateWithInit {
public:
TemplateWithInit() {}
TemplateWithInit(const TemplateWithInit& rhs, int x = 0) {
*this = rhs;
}
TemplateWithInit& operator=(const TemplateWithInit& rhs) {
return *this;
}
};
void useGenericPointerAssign(TemplateWithInit<char> twi) {
TemplateWithInit<char> twi2;
twi2 = twi;
}
// NOT OK (FALSE NEGATIVE): template class _without_ initializer on extra
// argument of copy constructor. Like `TemplateWithInit` above but without `= 0`
// on `x`.
template<typename T>
class TemplateWithExtraArg {
public:
TemplateWithExtraArg() {}
TemplateWithExtraArg(const TemplateWithExtraArg& rhs, int x) {
*this = rhs;
}
TemplateWithExtraArg& operator=(const TemplateWithExtraArg& rhs) {
return *this;
}
};
void useTemplateWithExtraArgAssign(TemplateWithExtraArg<char> twaa) {
TemplateWithExtraArg<char> twaa2;
twaa2 = twaa;
}
// OK: no user-defined copy constructor or assignment.
struct R1_A {
R1_A() { }
// Note: user-declared move constructor means copy constructor and
// assignment are both implicitly deleted.
R1_A(R1_A&& a) { }
};
// OK: copy assignment implicitly deleted due to R1_A field.
class R1_B {
public:
R1_B(const R1_B& b) {}
R1_A t;
};
// NOT OK: copy constructor user-defined and public, but copy assignment
// is generated by the compiler and callable outside the class.
class R1_C {
public:
R1_C(const R1_C& c) {}
};