diff --git a/build/clang-plugin/clang-plugin.cpp b/build/clang-plugin/clang-plugin.cpp index 8c307ea700c5..fe1f55e8e074 100644 --- a/build/clang-plugin/clang-plugin.cpp +++ b/build/clang-plugin/clang-plugin.cpp @@ -279,6 +279,8 @@ public: CustomTypeAnnotation(const char *Spelling, const char *Pretty) : Spelling(Spelling), Pretty(Pretty){}; + virtual ~CustomTypeAnnotation() {} + // Checks if this custom annotation "effectively affects" the given type. bool hasEffectiveAnnotation(QualType T) { return directAnnotationReason(T).valid(); @@ -299,6 +301,10 @@ public: private: bool hasLiteralAnnotation(QualType T) const; AnnotationReason directAnnotationReason(QualType T); + +protected: + // Allow subclasses to apply annotations to external code: + virtual bool hasFakeAnnotation(const TagDecl *D) const { return false; } }; static CustomTypeAnnotation StackClass = @@ -313,8 +319,32 @@ static CustomTypeAnnotation NonTemporaryClass = CustomTypeAnnotation("moz_non_temporary_class", "non-temporary"); static CustomTypeAnnotation MustUse = CustomTypeAnnotation("moz_must_use", "must-use"); -static CustomTypeAnnotation NonMemMovable = - CustomTypeAnnotation("moz_non_memmovable", "non-memmove()able"); + +class MemMoveAnnotation final : public CustomTypeAnnotation { +public: + MemMoveAnnotation() + : CustomTypeAnnotation("moz_non_memmovable", "non-memmove()able") {} + + virtual ~MemMoveAnnotation() {} + +protected: + bool hasFakeAnnotation(const TagDecl *D) const override { + // Annotate everything in ::std, with a few exceptions; see bug + // 1201314 for discussion. + if (getDeclarationNamespace(D) == "std") { + // This doesn't check that it's really ::std::pair and not + // ::std::something_else::pair, but should be good enough. + StringRef Name = D->getName(); + if (Name == "pair" || Name == "atomic" || Name == "__atomic_base") { + return false; + } + return true; + } + return false; + } +}; + +static MemMoveAnnotation NonMemMovable = MemMoveAnnotation(); class MozChecker : public ASTConsumer, public RecursiveASTVisitor { DiagnosticsEngine &Diag; @@ -768,7 +798,7 @@ bool CustomTypeAnnotation::hasLiteralAnnotation(QualType T) const { #else if (const CXXRecordDecl *D = T->getAsCXXRecordDecl()) { #endif - return MozChecker::hasCustomAnnotation(D, Spelling); + return hasFakeAnnotation(D) || MozChecker::hasCustomAnnotation(D, Spelling); } return false; } diff --git a/build/clang-plugin/tests/TestNonMemMovableStd.cpp b/build/clang-plugin/tests/TestNonMemMovableStd.cpp new file mode 100644 index 000000000000..9fce52496e41 --- /dev/null +++ b/build/clang-plugin/tests/TestNonMemMovableStd.cpp @@ -0,0 +1,21 @@ +#define MOZ_NEEDS_MEMMOVABLE_TYPE __attribute__((annotate("moz_needs_memmovable_type"))) + +template +class MOZ_NEEDS_MEMMOVABLE_TYPE Mover { T mForceInst; }; // expected-error-re 4 {{Cannot instantiate 'Mover<{{.*}}>' with non-memmovable template argument '{{.*}}'}} + +namespace std { +// In theory defining things in std:: like this invokes undefined +// behavior, but in practice it's good enough for this test case. +template class basic_string { }; +typedef basic_string string; +template class pair { T mT; U mU; }; // expected-note {{std::pair >' is a non-memmove()able type because member 'mU' is a non-memmove()able type 'std::basic_string'}} +class arbitrary_name { }; +} + +class HasString { std::string m; }; // expected-note {{'HasString' is a non-memmove()able type because member 'm' is a non-memmove()able type 'std::string' (aka 'basic_string')}} + +static Mover bad; // expected-note {{instantiation of 'Mover >' requested here}} +static Mover bad_mem; // expected-note {{instantiation of 'Mover' requested here}} +static Mover assumed_bad; // expected-note {{instantiation of 'Mover' requested here}} +static Mover> good; +static Mover> not_good; // expected-note {{instantiation of 'Mover > >' requested here}} diff --git a/build/clang-plugin/tests/moz.build b/build/clang-plugin/tests/moz.build index 50a478dd2ff1..b5e5d3d2c743 100644 --- a/build/clang-plugin/tests/moz.build +++ b/build/clang-plugin/tests/moz.build @@ -24,6 +24,7 @@ SOURCES += [ 'TestNoExplicitMoveConstructor.cpp', 'TestNonHeapClass.cpp', 'TestNonMemMovable.cpp', + 'TestNonMemMovableStd.cpp', 'TestNonTemporaryClass.cpp', 'TestNoRefcountedInsideLambdas.cpp', 'TestRefCountedCopyConstructor.cpp',